messages:send permission. This permission
includes templates:read and conversations:read.
1. Find the channel
Read the channel list once and store the id of your WhatsApp channel:When the workspace has one WhatsApp channel, you can omit
channel_id in
the next steps. Kai finds the channel for you.2. List the templates, with hints
Ask for the templates when your user opens the send window. Add the email address of the teammate that sends the message, and the customer:variables names
the placeholders, in the order that your send must fill them.
Templates with a title
A template can have a title above the message. WhatsApp shows the title in bold on the first line.header holds the title, or null when the
template has none.
The title can have its own variables, and Kai fills them. WhatsApp numbers
and names the variables of the title and of the message separately, so
{{1}} in the title and {{1}} in the message are two different values.
Each variable therefore has an address, which is the name you use when
you send:
The
variables field lists these addresses in the order that a send fills
them: the variables of the title first, then the variables of the message.
Each hint also holds part, which is header or body, so you can group
your inputs.
Kai cannot send a template with an image, a video or a document title,
because that title needs a file and not a value. Kai also cannot send a
template with a variable link in a button. These templates are absent from
the response. Add
status=all to see them, where sendable is false.Use the hints
Each entry invariable_hints tells you how to fill one input:
- A
valuethat is notnullis the value that Kai would use. Prefill your input with it. Your user can change it before they send. - A
valueofnullmeans that Kai cannot fill this variable. Ask your user for it, and do not send until they answer.
binding says where the value comes from. agent_name reads the profile of
the teammate in agent. customer_name reads the contact. free means
that a person types it every time. The workspace configures a binding under
Settings → Channels. Kai guesses one from the name of the variable when
nobody configured it, so {{consultant_name}} and {{customer_name}} work
without setup.
An agent address that no member holds returns "agent": null and empty
hints for the agent variables. Read this field when you debug an empty
input.
Kai never fills a variable at send time. The send needs every value. Your
form is what the person reads before the message leaves, so a value that
Kai adds after that screen is a value that nobody approved.
3. Send the template
Two shapes for the values
A template that names its variables accepts an object, as above. The keys are the addresses from step 2. Kai puts the values in the order of the template, so you do not need to know that order. A template that numbers all of its variables ({{1}}, {{2}}) accepts an
array, in the order of the variables field from step 2. The values of the
title come first:
header. in front of it.
One call does all of it. Kai finds or makes the conversation for the number.
Kai finds or makes the contact behind it. Kai fills the title and the body
of the template from its mirror and sends. Then Kai assigns the conversation to the assignee.
You must set opt_in_confirmed to true on every call. This is your
assertion that this person agreed to receive your messages. Kai stores the
assertion on the message.
existing_conversation is true when the thread already existed. Then the
message joined that thread, and no second thread exists.
You can also name the person with "to": { "contact_id": "..." }. Kai then
uses the phone number that the contact holds. A contact without a phone
number returns a no_phone error.
4. Retry safely
A send costs money and reaches a person. Send anIdempotency-Key header
with a unique value per intended message.
- When a call with the same key already succeeded, Kai returns the stored response and does not send again.
- When the first call failed, Kai releases the key. Your retry runs.
- When the first call still runs, Kai returns a 409
idempotency_in_flighterror. Retry after a moment. - The same key with a different body returns a 422
idempotency_mismatcherror. Use a new key for a new message.
5. Decide who answers the reply
The send itself does not decide who handles the conversation. Theassignee
field does.
- With
assignee, the conversation belongs to that teammate. Kai does not answer on an assigned conversation. The reply of the customer waits in the inbox of that teammate, and in their Element X rooms. - Without
assignee, the conversation stays with the reply policy of the channel. When the channel runs on autopilot, Kai can answer the customer.
assignee when a person must handle what comes back, for example a
sales call. Omit it when Kai can handle the reply, for example a payment
reminder.
Errors
For the error format and the general codes, see
Error handling.