Skip to main content
A webhook is the reverse path of the API: instead of you querying Nuvia, Nuvia sends a 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 a POST 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).
The body doesn’t identify the company: there’s no companyId in the envelope. If your server serves multiple Nuvia companies, use one URL per company or a distinct value in custom_headers to know whose event it is.

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

There’s no HMAC signature. Nuvia doesn’t sign the request body, so there’s no way to cryptographically verify the origin.
The available mechanism is custom_headers: register a secret value and check it on every request.
Node.js
Use HTTPS and a long, random value. Since the secret travels in a header on every delivery, treat it as a credential: rotate it with 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:
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.
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.
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.
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.
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.