Sending
Every send endpoint triggers a Novu workflow by its workflowId — the workflow
decides which channels fire and what the message looks like. Call
manage/list-workflows first to
discover the IDs you can trigger.
There is no "send this text to this user" endpoint: content lives in the workflow
template, and payload supplies the variables it renders.
All endpoints on this page are management endpoints — they need a secret key (sk_*) or
an admin JWT, not an end-user token.
Send to One User
POST /api/v1/notifications/manage/send
{
"workflowId": "push-notification",
"userId": "recipient-user-id",
"payload": {
"title": "New Message",
"body": "You have a new coaching update",
"deep_link": "travila://conversation/conv_abc"
},
"transactionId": "optional-idempotency-key"
}
Response:
{
"acknowledged": true,
"status": "processed",
"transactionId": "txn_abc123"
}
| Field | Type | Description |
|---|---|---|
workflowId | string | Novu workflow identifier (required) |
userId | string | Recipient user ID (required) |
payload | object | Arbitrary JSON passed to the workflow template |
overrides | object | Per-provider overrides passed through to Novu (e.g. FCM data payload) |
transactionId | string | Optional idempotency/tracking key |
Keep the transactionId you sent — it is the handle for
cancelling and for
checking delivery.
status comes from the delivery provider, not from usOn every send endpoint, acknowledged, status, transactionId and errors are
passed through verbatim from Novu's trigger response — the platform does not normalise
them. "processed" is the usual value, but treat the field as an opaque provider string
and key your logic off acknowledged.
Send to Many Users
Up to 100 recipients per request. Each event carries its own workflow, payload and transaction ID, so a bulk call can mix workflows.
POST /api/v1/notifications/manage/send-bulk
{
"events": [
{
"workflowId": "push-notification",
"userId": "user-1",
"payload": {"title": "Update", "body": "New feature available"}
},
{
"workflowId": "push-notification",
"userId": "user-2",
"payload": {"title": "Update", "body": "New feature available"}
}
]
}
Response:
{
"results": [
{"acknowledged": true, "status": "processed", "transactionId": "txn_1"},
{"acknowledged": true, "status": "processed", "transactionId": "txn_2"}
]
}
Results are per-event: one recipient failing does not fail the others, so check each
entry's acknowledged rather than the HTTP status alone.
Broadcast to Everyone
manage/broadcast has no route on api.travila.ai and returns 404. Broadcasting from
the admin console works — it reaches the same handler through its own gateway — so
this is about the public API only.
It is held back deliberately: management endpoints are not scope-gated at the gateway, so routing this would make "notify every subscriber in the tenant" reachable by anyone holding a valid key for that tenant.
Triggers the workflow for every subscriber in the tenant. There is no recipient list and no dry run — on staging that means every real beta tester.
POST /api/v1/notifications/manage/broadcast
{
"workflowId": "product-announcement",
"payload": {
"category": "product",
"title": "Scheduled maintenance",
"body": "The app will be briefly unavailable at 02:00 UTC."
},
"transactionId": "maintenance-2026-08-14"
}
Response:
{
"acknowledged": true,
"status": "processed",
"transactionId": "maintenance-2026-08-14"
}
payload.category is required and must be system or product — any other value is
rejected. It exists so that subscribers who have muted product news still receive
system messages.
To reach a defined group rather than everyone, use a topic instead.
Cancel a Pending Notification
Cancels a notification that has not been delivered yet — one waiting on a workflow
delay step, a digest window, or the delivery queue. Already-delivered notifications
cannot be recalled; the call succeeds with cancelled: false.
POST /api/v1/notifications/manage/cancel
{
"transactionId": "txn_abc123"
}
Response:
{
"cancelled": true,
"message": "Notification successfully cancelled"
}
When it was already delivered you get cancelled: false with
"Notification could not be cancelled (may have already been delivered)" — a 200, not an
error, so branch on cancelled rather than on the status code.
Scheduling and Delays
There is no send-at-a-time or send-after-a-delay endpoint on this API. Deferred delivery comes from one of two places:
- Inside a workflow — add a
delaystep to the Novu workflow definition. Every trigger of that workflow then waits, andmanage/cancelwith the transaction ID stops it before it fires. - Outside the workflow — create a
scheduled job that calls
manage/sendwhen it runs. Use this for calendar-shaped sends (0 9 * * 1-5, a one-off timestamp, a repeating interval) and anything a user can reschedule.
Digest / Batching
A digest collapses many events into one message. The window and grouping are configured in the workflow's digest step; this endpoint just feeds events into it.
POST /api/v1/notifications/manage/send-with-digest
{
"workflowId": "activity-digest",
"userId": "user-1",
"payload": {
"title": "Activity Update",
"body": "New activity in your coaching plan"
},
"transactionId": "digest-event-123"
}
Response:
{
"acknowledged": true,
"status": "processed",
"transactionId": "digest-event-123"
}
Remove a single event from a digest that has not yet been delivered. Once the digest has gone out this is a no-op rather than an error.
POST /api/v1/notifications/manage/cancel-digest-event
{
"transactionId": "digest-event-123"
}
Response:
{
"cancelled": true,
"message": "Digest event successfully cancelled"
}
As with manage/cancel, an already-processed digest returns cancelled: false and
"Digest event could not be cancelled (may have already been processed)" rather than an
error.