Changelog
Latest changes in the public API. Integrations that do not use the new fields or features keep working unchanged, except where a change is marked as Behavior change.
1. SMS — Get a message
New GET /v1/sms/messages/{message_id} endpoint that returns the current status
and data of a message. It works with every message_id you receive: a single
send, each number of a batch, or the fallback_id of a WhatsApp/RCS send with
SMS fallback.
statususes the same values as the status webhook.messageis the final text that was sent, including the short link when the send had acomplement.tois the number as it was sent: 10 digits for a national mask, E.164 without+for an international one.batch_idis included only for numbers of a batch, andorigin_identifier(themessage_idof the original WhatsApp/RCS send) only for fallback SMS.- Any SMS App-Key of the same account can query the message. A message from
another account responds
404witherrorCode591. A single send rejected with400is not stored, so its id also responds404. - A
message_idthat is not a UUID responds400witherrorCode400. - Each query counts toward the App-Key’s rate limit like any other request.
See SMS → Messages → Get message for the full reference.
2. SMS — Specific errors for the destination number
POST /v1/sms/messages now tells you why the destination number was rejected.
Before, every rejection of to responded errorCode 352. Now:
352is only for a national mask whosetois not 10 digits. A national mask also accepts the Mexico country code52/+52or the old mobile prefix521/+521(for example,525555555555or+5215555555555); the prefix is removed before sending and storing the number.571: international mask andtois not E.164. It must always include the country code, for example+16462713111, or+525512345678for Mexico.572: national mask and the number does not exist in the numbering plan.573: national mask and the number exists but is not a mobile line (for example, a landline).- A temporary failure while checking the numbering plan now responds
500and can be retried, instead of rejecting the number as invalid. - Error messages are now in Spanish, like the rest of the SMS errors.
Behavior change: if your integration uses errorCode 352 to detect any
invalid number, also handle 571, 572 and 573.
In POST /v1/sms/batches nothing changes: rejected numbers are still reported
per number as INVALID_FORMAT, INVALID_SOURCE or OUTPUT_NOT_FOUND.
SMS-specific errors
See SMS → Messages → Send message for the full reference.
3. SMS — Short link validity range (complement.ttl_events)
complement.ttl_events (minutes during which clicks on the short link are
recorded) now accepts values from 60 (1 hour) to 4320 (72 hours). The
default is still 1440 (24 hours) when it is omitted.
The range applies to:
POST /v1/sms/messagesPOST /v1/sms/batches, both the batchcomplementand each number’sfallback.sms.complementin WhatsApp and RCS sends
A value outside the range responds 400 with errorCode 400:
Behavior change: values below 60 or above 4320 used to be accepted and
are now rejected.
4. RCS — Batch sends
New POST /v1/rcs/batches endpoint to send the same RCS content (a template or
free-form content) to up to 250 recipients in a single request, personalizing
each number’s variables. It responds 202 immediately with a batch_id and one
message_id per number (in the order of the request); the batch is processed in
the background.
- The content and delivery settings (
agent_id,type,template_id,text,contentInfo,richCard,suggestions,ttl_message,ttl_events,fallback) go once at the root; each item ofmessagescarries only itstoandvariables. - Content validations run once and reject the whole batch if any fails; numbers
rejected by destination (
INVALID_FORMAT,INVALID_SOURCE,OUTPUT_NOT_FOUND) do not stop the others. webhook_mode: INDIVIDUAL(default) keeps the regular per-number webhooks now withbatch_id;webhook_mode: BATCHsends a singlebatchStatuswebhook with the result of every number.- A batch counts as a single request for the rate limit.
See RCS → Messages → Send batch and RCS → Webhooks → Batch status for the full reference.
5. WhatsApp — Batch sends
New POST /v1/whatsapp/batches endpoint to send WhatsApp messages to up to 250
recipients in a single request. It responds 202 immediately with a batch_id
and one message_id per recipient (in the order of the request); the messages
are sent in the background.
typegoes at the root and each item ofmessagescarries its owntoand the content of that type (text,image,template…). All 11 message types are supported.fallback.smsat the root is the SMS fallback of every recipient; each item ofmessagescan carry its ownfallback.sms, which replaces the root one for that recipient. The response returns afallback_idper recipient with fallback.webhook_mode: INDIVIDUAL(default) keeps the regular status webhook per recipient, now withbatch_id;webhook_mode: BATCHsends a singlebatchStatuswebhook with the status of every recipient.- Media URLs are validated against public destinations only: URLs pointing to
private networks are rejected with
errorCode575, also inPOST /v1/whatsapp/messages. - A batch counts as a single request for the rate limit.
See WhatsApp → Messages → Send batch and WhatsApp → Webhooks → Batch status for the full reference.
6. Email — Transactional sends
New Email channel: POST /v1/email/messages sends an email from a template of
your account. The send is asynchronous: it responds 202 with a message_id,
and the events (SENT, DELIVERED, BOUNCED, OPENED, CLICKED…) are
notified through webhooks. The App-Key is created with the sender (mailing
output) linked, so the request only carries the recipient, the template, the
subject, and the template variables. GET /v1/email/sender and
GET /v1/email/templates let you query the sender and the templates (with the
variables each one expects).
Variable values are HTML-escaped, and every placeholder of the template must be sent. See the Email API reference for the full contract and the webhook payload.
7. SMS — Batch sends
New POST /v1/sms/batches endpoint to send SMS to up to 250 numbers in a
single request. It responds 202 immediately with a batch_id and one
message_id per number (in the order of the request); the numbers are
processed in the background.
text,has_accentsandcomplementat the root are the batch defaults; each number can override any of them. A number’scomplementreplaces the batch one entirely, and every number with acomplementgets its own short link.- An invalid number does not reject the batch: it is reported per number as
INVALID_FORMAT,INVALID_SOURCEorOUTPUT_NOT_FOUND. webhook_mode: INDIVIDUAL(default) keeps the regular status webhook per number, now withbatch_id.webhook_mode: BATCHsends a singlebatchStatuswebhook with every number’s status once all of them have a send result; later changes arrive as per-message webhooks.- A batch counts as one request for the rate limit.
See SMS → Messages → Send batch and SMS → Webhooks → Batch status for the full reference.
8. OAuth 2.0 client credentials
Besides the x-api-key header, every endpoint now accepts an OAuth 2.0
access token in the Authorization: Bearer header. The token is obtained
from the new POST /api/oauth/token endpoint (grant client_credentials)
with the client_id / client_secret pair the Chat Bridge team issues for
your App-Key.
- Tokens last 1 hour. Request a new one before it expires and reuse it: the token endpoint is limited to 30 requests per minute per IP, and 5 failed attempts block the IP for 15 minutes.
- Credentials can go in the JSON body, as
application/x-www-form-urlencoded, or asAuthorization: Basic base64(client_id:client_secret). - The token identifies the same App-Key: account, channel, webhooks and rate
limit do not change. If both headers are sent,
Bearerwins. - When a secret is rotated the previous one keeps working for 24 hours.
- An App-Key can be switched to OAuth only; from then on
x-api-keyresponds401witherrorCode450.
See OAuth 2.0 in the Authentication section for the full reference.
9. Tools — Short Links
New Tools → Short Links section with a URL shortener on the sl2.mx
domain, available to every App-Key regardless of its channel:
POST /shortlinksshortens an absolutehttp/httpsURL (up to 2048 characters) and returns a 10-characteridentifierplus the ready-to-useshort_link. Links expire after a fixed 3 days.GET /shortlinks/{code}returns whether the link is still active, its creation and expiry dates and the accumulatedtotal_clicks. Only the user of the App-Key that created the link can query it (403,errorCode484 otherwise;404,errorCode483 if the code does not exist).
Invalid URLs respond 400 with errorCode 482 and metadata.reason
(url_required, url_too_long, url_not_absolute, invalid_scheme,
url_control_chars).
Previous changes (WhatsApp and RCS)
10. WhatsApp — Send by BSUID
Besides the E.164 number, POST /v1/whatsapp/messages now accepts a BSUID
(Business Solution User ID) in the to field. A BSUID is the anonymous
identifier Meta assigns to a contact when the conversation comes from a
“Click to WhatsApp” flow or another channel where Meta does not expose the
user’s real number — it lets you reply without knowing their phone. Inbound
webhooks carry it in message.from_user_id.
If to matches neither format, the API rejects with 400. The format applies
equally to template sends (type: TEMPLATE) and every other message type.
11. WhatsApp — Automatic SMS fallback
POST /v1/whatsapp/messages now admits an optional fallback to SMS: if the
WhatsApp send fails definitively (synchronously or through a status webhook),
the API automatically retries the same recipient over SMS — without the
integrator having to detect the failure and resend.
Each send decides whether it wants the fallback and with which content, in the
new fallback field:
fallback.sms uses exactly the same shape as the body of
POST /v1/sms/messages (mask, text, has_accents, complement)
except to — the recipient is inherited from the original WhatsApp
message. The mask is validated at the moment of the original send (an
invalid or disabled mask rejects the whole request with 400 before
attempting WhatsApp). The fallback fires only once per message (atomic
lock) and it is a single hop: WhatsApp → SMS, no further cascade.
The feature must be enabled for the account by whoever manages it:
Synchronous response when the fallback activates. If the send fails
synchronously and the request included fallback.sms, the 201 response
includes fallback_id — the identifier to correlate the backup SMS in the
webhooks:
Webhooks. The status events of the backup SMS arrive as SMS webhooks
carrying origin_identifier — the message_id of the WhatsApp send that
originated the fallback. If the backup SMS also fails (or could not be
generated, e.g. the to was a BSUID with no real phone behind it), a final
Fallback exhausted event is emitted:
See the Fallback exhausted webhook in the WhatsApp API reference for the
message_id / origin_identifier semantics of each case.
12. RCS — Automatic SMS fallback
Same mechanism as section 11, applied to POST /v1/rcs/messages: if the RCS
message is not delivered, the API automatically retries over SMS.
What fires it:
- The RCS delivery status webhook reports
FAILEDorEXPIREDfor the message. - A late
FAILED/EXPIREDthat the RCS status priority logic already discards (a suppressed downgrade, an out-of-order event) does not fire the fallback — only a failure that actually advances the message status does. - Unlike WhatsApp, RCS has no synchronous failure point (the send always
responds
202 Accepted) — the RCS fallback fires only through webhooks.
Webhooks work as in section 11: the backup SMS statuses arrive as SMS webhooks
with origin_identifier, and the chain ends with a Fallback exhausted
event. The mask of the fallback is validated when sending the original RCS
message (fast 400), not when the fallback fires hours later.
13. RCS — Free-form sends (no template)
POST /v1/rcs/messages now accepts the message content directly in the
body, without a pre-created and approved template. The mode is selected
with the new discriminator field type:
If type is omitted, TEMPLATE is assumed: existing integrations do not
change.
Rules to keep in mind:
template_idis forbidden (400) with any free-formtype, and required withTEMPLATE.variablesalso applies to free-form content: the{{placeholders}}written intext,contentInfo, orrichCardare substituted with the same type rules (URL infileUrl/thumbnailUrl/iconUrl/url, free text elsewhere) and the same HEAD media validation.variables.UUID_MESSAGEis a reserved name, now rejected explicitly with400in the five types (before, in the template path, it was silently overwritten — the only behavior change in theTEMPLATEflow).- One content per request → 1 credit per free-form send.
- The billing channel derives from the agent, not the content: a
CONVERSATIONALagent bills asRCS_CONVERSATIONALwhether it sends text or a rich card; same for the conversation window. - Root-level
suggestions(message-level chips, max 11) are independent from the ones embedded inside a card, and can be combined. Never sendpostbackData— the backend generates it. - Agent ↔ content compatibility (replaces
RCS_TYPE_MISMATCH, which stays exclusive toTEMPLATEmode):
New errors:
Unchanged: the 202 Accepted response, the webhooks (same envelope, same
correlation by message_id, clicks on free-form suggestions arrive
classified with action/element_type/element_value exactly like template
ones), and the agents/templates endpoints. In the App-Key CSV report,
free-form sends show template_name = Envío libre, with the full
content (including type) in the data column.
14. WhatsApp — Inbound contacts messages (vCard)
The Inbound message webhook supports a new content type: contacts. It
fires when a user shares one or more contacts (vCard) in the conversation.
message.type can now be "contacts" (previously: text, image,
video, document, audio, button, location).
New message.contacts field (array): the shared contacts. Every field is
optional — Meta omits the ones the user did not share from their address book.
Note: in real traffic we have also observed the fields vcard (the full
vCard in Base64) and origin (e.g. "other"), which are not part of Meta’s
officially documented schema but do arrive in production. Those fields — and
any other not listed above — are propagated as-is: do not assume a closed
shape.
Example:

