REST API reference
Base URL https://api.notibase.com. Authenticate with
Authorization: Bearer <key>. Two key scopes exist —
read the key model before integrating.
Conventions
| Idempotency | Send an Idempotency-Key header on POST /v1/messages. Same key → same message returned with "replayed": true, never a second send. |
| Rate limits | Per key, plan-based (free: 10 rps server / 50 rps client). Exceeding returns 429. |
| Quotas | Monthly message quota per plan; exceeded sends return 429 with an upgrade note. |
| Errors | 400 validation (with issues) · 401 bad/missing key · 403 wrong scope / suspended app / failed identity check · 404 not yours or missing · 429 limits · 5xx ours (raw details are logged server-side, never leaked). |
Endpoints
| Endpoint | Key | Purpose |
|---|---|---|
POST /v1/messages | sk | Send now, or schedule with send_at (ISO-8601, ≥30s ahead) |
GET /v1/messages/:id/deliveries | sk | Per-device delivery timeline, raw provider responses included |
POST /v1/users | sk | Upsert a user profile by external_id; custom properties auto-catalog |
GET /v1/properties | sk | The property catalog (drives what's filterable) |
POST /v1/segments | sk | Save a named filter (validated against the catalog) |
POST /v1/segments/preview | sk | Live count for any filter |
POST /v1/devices | ck / sk | Register a device (web subscription JSON, APNs or FCM token) |
GET /v1/devices | sk only | List devices — client keys get a structural 403 |
POST /v1/identify | ck / sk | Alias device → user; HMAC-verified when an identify secret is set |
POST /v1/events | ck / sk | Track custom events, single or {"events":[…]} batch ≤ 50 |
GET /v1/channels | public | Channels this Notibase supports |
Send a message
POST /v1/messages
{
"audience": { "all": true },
// or: { "device_ids": ["…"] } | { "segment_id": "…" } | { "filter": <AST> }
"content": {
"title": "Back in stock: {{properties.wishlist_item}}",
"body": "Grab it before it goes again.",
"url": "https://shop.example/item"
},
"send_at": "2026-08-21T10:00:00+05:45" // optional — omit to send now
}
Response (immediate send): {"id","status":"sent","replayed":false,"report":{"accepted","sent","failed","skipped"}}.
Scheduled: {"id","status":"scheduled","run_at"}.
Per-channel content: top-level fields apply everywhere; override per channel with
"content": {"webpush": {…}, "apns": {…}, "fcm": {…}}.
Personalization: {{first_name | default: 'there'}} and any
{{properties.x}}.
Register a device
POST /v1/devices // Bearer ck_live_... (from your app)
{ "platform": "web" | "ios" | "android",
"token": "<push subscription JSON | APNs hex token | FCM token>",
"locale": "en-US", "timezone": "Asia/Kathmandu" }
Track events
POST /v1/events
{ "events": [
{ "name": "purchase", "properties": { "amount": 9.99, "currency": "USD" }, "device_id": "…" },
{ "name": "session_start" } ] }
Reserved names
install, session_start, purchase
(with amount/currency) power upcoming revenue & attribution reports —
use them from day one and the reports will be retroactive.Upsert a user
POST /v1/users
{ "external_id": "user-42", "first_name": "Aasha", "country": "NP",
"language": "ne", "timezone": "Asia/Kathmandu",
"properties": { "plan": "pro", "lifetime_value": 420.5 },
"device_id": "<optional: alias an anonymous device>" }
New property keys auto-register with inferred types; a later type conflict is
reported in property_conflicts, never silently coerced.