On this page

#Webhooks

Vatio POSTs what happened to your server, so you never poll: what became of each message your backend sent, every rating a visitor gives a conversation, and account-level channel issues. Create an endpoint with vatio webhooks create, in the inbox under Webhooks (the New endpoint button at the top right), or over the API.

#Events

EventWhen
message.sentA message your backend sent went out to WhatsApp
message.failedVatio has no safe attempts left; data.message.error says why
message.delivered, message.readMeta says it reached the phone, or was read
message.delivery_failedMeta marked it undelivered and Vatio has no attempts left
message.repliedThe person answered, once per message; data.reply has the text
feedback.submittedA visitor rated the conversation, or added a comment to their rating
channel.issueMeta reported an account-level WhatsApp problem, such as billing or eligibility; emitted once until the number becomes healthy again
instagram.commentSomeone commented on your Instagram post, reel or live; see Instagram comments
pingA test from vatio webhooks test or Send test in the inbox

An endpoint hears the events it lists, or all of them when its list is empty. New types are added over time, so answer 2xx to one you do not handle: a type that keeps failing switches the whole endpoint off (Delivery).

Every event comes in the same envelope:

{
"id": "evt_3141",
"type": "message.replied",
"created_at": "…",
"workspace": "acme",
"environment": "live",
"data": { "…": "…" }
}

data is written when it happened, so a retry an hour later still says what was true then.

#Messages

data.message is the message as GET /messages/:id returns it. Internal retries keep the same message ID and idempotency key; only a terminal failure produces a failure event. message.sent may occur again if Meta first accepts an attempt and later marks its delivery failed. message.replied adds data.reply:

{
"type": "message.replied",
"data": {
"message": { "id": 81, "status": "sent", "external_ref": "order-1042", "...": "…" },
"reply": { "chat_message_id": 90433, "content": "Great, send me the link", "at": "…" }
}
}

#Channel issues

An account-level WhatsApp error marks the number as needing attention in the inbox and sends one channel.issue event, even if many messages fail for the same reason. Vatio automatically retries confirmed failures, including payment eligibility code 131042; fix the cause in Meta so those attempts can succeed. Use Check on the inbox's WhatsApp page to refresh the channel health.

{
"type": "channel.issue",
"data": {
"channel": "whatsapp",
"phone_number_id": "123456789",
"error": { "code": "131042", "message": "Business eligibility payment issue" }
}
}

#Instagram comments

Each comment on your Instagram account's posts, reels and lives, once, so your backend can answer it with a private reply. Your own comments are not sent. data.comment is the comment as GET /instagram/comments/:id returns it:

{
"type": "instagram.comment",
"environment": "live",
"data": {
"comment": {
"id": "17895695668004550",
"text": "Price?",
"from": { "id": "17841400000000000", "username": "ana.perez" },
"media": { "id": "17918195224117851", "product_type": "FEED" },
"parent_id": null,
"live": false,
"created_at": "…",
"reply_until": "…",
"replied_message_id": null
}
}
}

#Ratings

When a conversation wraps up, Vatio asks the visitor how it went: on WhatsApp, in the web widget, and in your app through the SDK. Every answer is a feedback.submitted:

{
"type": "feedback.submitted",
"data": {
"feedback": {
"rating": "good",
"comment": null,
"question": "¿Cómo te ayudó Sofía?",
"chat_message_id": 90433,
"submitted_at": "…"
},
"chat": { "id": 9120, "channel": "whatsapp", "url": "https://inbox.vatio.ai/acme/9120" },
"contact": { "id": 4211, "name": "Ana Pérez", "phone_number": "+56911112222", "email": null }
}
}
  • rating is good, neutral (So-so) or bad.
  • comment is what the visitor wrote with a So-so or Bad rating in the widget or your app; WhatsApp has none. A comment added after the rating sends the event again, with the same rating and the comment.
  • chat_message_id is the agent's reply that was rated, and chat.url opens the conversation in the inbox.
  • channel is where the conversation is: whatsapp, widget, web (your page on the browser SDK), ios or android.
  • contact is null when Vatio knows nothing about the visitor; on WhatsApp it has the phone.

On WhatsApp the agent answers the rating itself, so asking a happy customer for a review is an instruction, not code: see Ratings. The webhook is for what your backend does with it, like storing it next to the customer or alerting someone about a bad one.

#Signature

Every delivery carries Vatio-Event, Vatio-Delivery (the event id) and Vatio-Signature: t=1696262400,v1=…. To verify it, compute the hex HMAC-SHA256 of "#{t}.#{raw body}" with the endpoint's secret, compare it in constant time, and reject a t older than five minutes. In Ruby, the vatio-sdk gem's receiver does it for you.

The same secret signs the one request Vatio sends that is not an event: the contact.identify lookup to your auth.identify_url, which asks who a WhatsApp number is and waits for your answer.

#Delivery

Answer 2xx within ten seconds. Anything else, including a redirect, is retried with backoff for about a day. After 50 failures in a row the endpoint is switched off until you enable it again. Deliveries are at least once, so dedupe on id.

#Endpoints

You register and manage endpoints with vatio webhooks (list, create, test, deliveries, enable, rm) or in the inbox under Webhooks. create prints the signing secret once; test queues a ping and deliveries shows the last 50 deliveries and how each went.

The URL must be https on a public host. Each endpoint belongs to one environment and hears only that environment's events, so a staging server never receives a real customer's rating.

Last updated:

Latest CLI version: 0.114.0 · Changelog · The whole contract as Markdown: /docs/docs.md · or vatio docs