POST /v1/messages
Single endpoint for every outbound message, on every connected channel. The message body is discriminated by its type field.
channel field decides where the message goes, and the type inside message decides what it looks like — text, media, a location pin, an approved template, an interactive block, or a WhatsApp Flow.
Endpoint
Required scope
messages:send
Headers
Envelope
channel_id and phone_number_id together is fine — channel_id wins. Passing neither is fine too, as long as the project has exactly one active channel of that type. With more than one, the request fails with 400 CHANNEL_AMBIGUOUS and the response lists the candidate ids.Message types
Every value ofmessage.type, and which channels can deliver it:
422 MESSAGE_TYPE_NOT_SUPPORTED_FOR_CHANNEL, and the response lists the types that channel does accept. WhatsApp QR flattens buttons and list into a plain text message — the options are rendered as text, not as tappable controls.
For media, link and media_id are mutually exclusive: exactly one is required. Upload a file with POST /v1/media/upload to get a media_id.
Example requests
One body per type
Copy any of these into themessage field. The envelope (channel, to, …) is the same for all of them, and every one is also available as a named example in the playground.
text
text
image
image
link for media_id to use a file uploaded through POST /v1/media/upload. Exactly one of the two.video
video
audio
audio
voice: true sends a voice note (PTT). Omit it for a plain audio file.document
document
sticker
sticker
location — WhatsApp only
location — WhatsApp only
contacts — WhatsApp only
contacts — WhatsApp only
POST /v1/contacts.reaction
reaction
emoji clears a previous reaction.template — WhatsApp only
template — WhatsApp only
flow block to bind one of its FLOW buttons:list
list
cta_url
cta_url
catalog — WhatsApp only
catalog — WhatsApp only
carousel
carousel
.webp images on carousel headers — those cards are dropped.flow — WhatsApp only
flow — WhatsApp only
"mode": "draft" to test an unpublished one against a registered WABA tester.Quoting a message
Reply to a specific message by passing its provider id incontext:
Response
202 Accepted
400 Bad Request
401 Unauthorized
Missing, invalid, revoked, or expired token.403 Forbidden
The token does not have themessages:send scope.
404 Not Found
CHANNEL_NOT_FOUND — the project has no active channel matching the selector.
422 Unprocessable Entity
MESSAGE_TYPE_NOT_SUPPORTED_FOR_CHANNEL — the channel cannot carry that message type. supported_types lists what it accepts.
Operational notes
- 24-hour window: free-form messages (everything except
template) only reach a recipient whose last inbound message is under 24 hours old. Keebai attempts the send regardless; a closed window surfaces as the provider’s own error passed through. - The message lands in the Keebai inbox: every send is persisted against the recipient’s contact and pushed to the portal in realtime, on all four channels. It shows up with the
agentrole, since a PAT send is an integrator acting on the tenant’s behalf rather than the AI assistant. meta_dataround-trips: whatever you put there is stored with the message and echoed back on delivery and read webhooks, which is the intended way to correlate a send with your own records.- Markdown: WhatsApp interprets
*text*as bold,_text_as italic and~text~as strikethrough. A backslash escapes them.
Authorizations
Personal Access Token con prefijo kbai_pk_. Generar desde el portal con permiso developer.manage_tokens.
Body
Channel the message is delivered through.
whatsapp, whatsapp_qr, instagram, messenger "whatsapp"
Recipient. E.164 phone number on whatsapp and whatsapp_qr; the Meta-scoped id (IGSID / PSID) on instagram and messenger.
"+5491155555555"
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
- Option 7
- Option 8
- Option 9
- Option 10
- Option 11
- Option 12
- Option 13
- Option 14
- Option 15
- Option 16
Keebai channel id. Optional when the tenant has exactly one active channel of the given type; required to disambiguate otherwise.
"665f1a2b3c4d5e6f70819234"
WhatsApp-only alias for channel_id: the Meta phone_number_id of the sending number. Ignored on other channels.
"100000000000001"
Free-form metadata persisted with the message. Returned on webhooks for correlation.