Overview
The API channel is a first-class inbox. Any module of the platform reaches it the same way it
reaches WhatsApp or the web widget: it creates a message on a conversation. That message is then
POSTed β signed β to your inbox webhook URL. Because every message payload carries the full
content_type and content_attributes, structured content (interactive buttons, payment CTAs,
catalog cards, calendar events and reactions) reaches your integration natively β you render it.
Everything here is additive and backward-compatible: existing integrations keep working and can ignore any field they don't recognize. There are no new mandatory fields.
Prerequisites
- An API inbox with a configured webhook URL.
- The inbox secret to verify the webhook signature (and optionally
hmac_tokenfor inbound HMAC). - An access token for the REST API when you send data back.
Step by step
- Create an API inbox and set its webhook URL.
- Receive events at that URL and verify the signature with the inbox secret.
- Read
content_type+content_attributeson each message payload to render rich content. - Send inbound messages, delivery receipts and reactions back through the REST API.
- (Optional) Restrict which events you receive with
webhook_subscriptions.
Outbound content types (what the webhook delivers)
Each message_created / message_updated payload includes content_type, content,
content_attributes and attachments. Beyond plain text, the API inbox may deliver:
input_selectβ interactive options incontent_attributes.items([{title, value}]). When a channel can't render options, the platform also appends a numbered menu tocontent.content_attributes.payment_interactiveβ a pay action:{ body, buttons: [{ type: "cta_url", text, url } | { type: "cta_copy", text, code }] }. The full charge (amount, PIX code, boleto, links) also arrives ascontent_attributes.payment_chargeon the summary message.content_attributes.catalog_productβ a product card:{ mode: "single" | "list", products: [{ id, name, price, currency, image_url, ... }] }.content_attributes.calendar_eventβ a booking confirmation or reminder:{ id, title, starts_at, ends_at, location, meet_url, kind: "confirmation" | "reminder" }(ISO-8601 times).content_attributes.reactionsβ an array of{ emoji, sender_jid }. The operator isme; a contact is stored under its own identifier. Reactions ride themessage_updatedevent.- Templates β the sent template metadata rides
additional_attributes.template_params({ name, language, category, processed_params }). - Media, location, contacts, stickers β delivered as real
attachments(pluscontent_attributes.media_kindfor stickers).
Inbound (what you send back)
- Incoming message β
POST /api/v1/accounts/{account_id}/conversations/{conversation_id}/messageswithmessage_type: "incoming". It acceptscontent,content_type,content_attributesandattachments, so an interactive reply is symmetric with the outbound contract. - Delivery receipts β
PUT/PATCH .../messages/{id}(API inboxes only) withstatusofsent/delivered/read/failed(and an optionalexternal_error). This flips the message status, which is relayed viamessage_updated. - Reactions β
POST .../messages/{id}/reactwithemoji(an empty emoji removes it). Passby=contactto record the contact's reaction (the default records the operator's). No provider is contacted for an API inbox β the reaction is persisted and relayed viamessage_updated.
Campaigns
One-off campaigns can target an API inbox. Each audience contact receives a real conversation and an outgoing message, which rides your webhook exactly like any other outbound message.
Settings & options
webhook_subscriptionsβ an optional array on the API channel'sadditional_attributes(update the inbox via the API withchannel[additional_attributes][webhook_subscriptions]). When set, only those events are delivered; when absent or empty, all events are delivered (the default). Event names must be valid platform events (e.g.message_created,message_updated,conversation_created,conversation_status_changed,conversation_typing_on).hmac_mandatoryβ reject inbound requests that can't be HMAC-verified.
Use cases
- Bridge the inbox into a custom app that renders payment CTAs, catalog cards and calendar bookings.
- Relay a contact's reaction from your own client back onto the message.
- Mirror booking confirmations and reminders into the customer's channel of record.
Tips, limits & best practices
- Always verify the webhook signature before acting on a payload.
- Deduplicate by the delivery id / event; expect retries.
- Prefer
content_attributesfor structured rendering;contentis the plain-text fallback. - Subscribe only to the events you use to reduce noise.
Troubleshooting
- No events arriving: confirm the webhook URL is set and, if you configured
webhook_subscriptions, that the event you expect is in the list. 422on subscriptions: the list contains an unknown event name β remove it.- Reaction not attributed to the contact: pass
by=contacton the react call.