Skip to main content
POST
One endpoint sends everything. The 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

Passing 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 of message.type, and which channels can deliver it: A combination outside this table fails with 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

JavaScript
Python

One body per type

Copy any of these into the message 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.
Swap link for media_id to use a file uploaded through POST /v1/media/upload. Exactly one of the two.
voice: true sends a voice note (PTT). Omit it for a plain audio file.
Must be WebP.
Shares a contact card in the chat. It does not create a contact in Keebai — that is POST /v1/contacts.
An empty emoji clears a previous reaction.
The only type that reaches a closed 24-hour window. Add a flow block to bind one of its FLOW buttons:
Max 3 buttons, titles up to 20 characters.
Max 10 sections, 10 rows each.
Requires a product catalog connected to the WhatsApp Business Account.
Create and publish the Flow first. Add "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 in context:
If the quoted message is too old for the provider to reference, Keebai retries once without the quote rather than failing the send.

Response

202 Accepted

400 Bad Request

401 Unauthorized

Missing, invalid, revoked, or expired token.

403 Forbidden

The token does not have the messages: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 agent role, since a PAT send is an integrator acting on the tenant’s behalf rather than the AI assistant.
  • meta_data round-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

Authorization
string
header
required

Personal Access Token con prefijo kbai_pk_. Generar desde el portal con permiso developer.manage_tokens.

Body

application/json
channel
enum<string>
required

Channel the message is delivered through.

Available options:
whatsapp,
whatsapp_qr,
instagram,
messenger
Example:

"whatsapp"

to
string
required

Recipient. E.164 phone number on whatsapp and whatsapp_qr; the Meta-scoped id (IGSID / PSID) on instagram and messenger.

Example:

"+5491155555555"

message
object
required
channel_id
string

Keebai channel id. Optional when the tenant has exactly one active channel of the given type; required to disambiguate otherwise.

Example:

"665f1a2b3c4d5e6f70819234"

phone_number_id
string

WhatsApp-only alias for channel_id: the Meta phone_number_id of the sending number. Ignored on other channels.

Example:

"100000000000001"

context
object
meta_data
object

Free-form metadata persisted with the message. Returned on webhooks for correlation.

Response

202 - application/json
message_id
string
required

Meta message_id (wamid.*) of the sent WhatsApp message.

status
string
required
Example:

"sent"

sent_at
string
required

ISO-8601 timestamp of the dispatch.