POST to
your server whenever something happens in your operation: a message arrives, a contact is
created, a field is filled in by the agent.
Each company has at most one webhook. It’s not a collection: you configure one URL and
choose which events it receives. Trying to create a second one returns
409.Configure
POST /v1/webhooks with the URL and the list of events. Required scope: webhooks:create.
What Nuvia sends
Every delivery is aPOST with these headers:
And the body always has the same three-field structure:
event: the event that triggered the delivery.timestamp: ISO 8601 UTC, generated when the delivery was queued.data: the resource related to the event (see the table below).
Available events
For message, conversation, and contact events,
data carries the resource in the same format the
API reference returns on the corresponding GET requests, for example,
GET /v1/messages/conversation/{id} for messages.
Treat
data tolerantly: read the fields you use and ignore the rest. New fields may appear as
the platform evolves, and that isn’t considered a breaking change.FIELD_UPDATED is different
This event doesn’t return an entity, but a description of the change:
field.context indicates what the field belongs to: contact, conversation, or business.
value has the field’s type: string, number, boolean, or list. conversationId only comes
through when the change happened inside a conversation.
Validate that the call came from Nuvia
The available mechanism iscustom_headers: register a secret value and check it on every
request.
Node.js
PUT /v1/webhooks/{id} if you suspect a leak.
Delivery guarantees
These are the actual characteristics of delivery. Read this before building anything that depends on a webhook:One attempt per event, no retry
One attempt per event, no retry
If your server is down, returns
5xx, or times out, the event is lost. The failure is
logged on Nuvia’s side, but nothing is resent. For critical data, reconcile periodically with
the API (for example, by listing a conversation’s messages) instead of relying solely on the
webhook.10-second timeout
10-second timeout
Nuvia waits at most 10 seconds for your response. Respond
2xx immediately and process in the
background. If you process before responding, legitimate deliveries turn into timeouts.Order isn't guaranteed
Order isn't guaranteed
Status events come from WhatsApp, which may deliver them out of order (a
MESSAGE_DELIVERED
arriving after the MESSAGE_READ for the same message). Nuvia doesn’t reorder them. Treat each
event by the state it carries, without assuming sequence.Status events only fire on a real transition
Status events only fire on a real transition
MESSAGE_DELIVERED and MESSAGE_READ are only sent when the status actually changes. WhatsApp
resends the same status multiple times, and those repeats are discarded: you don’t get a
duplicate because of it. Still, write an idempotent handler: use the message identifier as the
key.Nothing is delivered while the webhook is inactive
Nothing is delivered while the webhook is inactive
With
status: "INACTIVE", or for events you didn’t include in events, Nuvia doesn’t queue
any delivery, and there’s no history to recover afterward. Reactivating doesn’t recover the
time it was stopped.Manage the configuration
The
GET returns a single object with the company’s configuration, or null if there is
none. It’s not a list. The id used in PUT and DELETE comes from that object. After removal,
the company can create a new webhook again.
To pause temporarily without losing the configuration, prefer PUT with status: "INACTIVE"
instead of deleting.
Next steps
Integration examples
The webhook inside a complete use case, with an automatic reply.
API reference
Parameters and responses for the webhook endpoints.