Skip to content

Changelog

Only changes that affect your integration are listed here. Those that need action on your side say what to change.

October 2026: failure and storage notices, connect answers

  • The error of number.connect.failed is a fixed sentence: for each reason the field always carries the same English sentence, in place of the free text the field used to carry, which changed from one release to the next. Decide by reason and use error only in your logs. The sentences are listed in Webhooks. The text for pairing_timeout changed again.
  • storage.degraded has closed lists: reason is always healthcheck_failed and error_code is one of access_denied, bucket_unreachable, region_mismatch, endpoint_unreachable, endpoint_url_blocked, network_error, quota_exceeded, object_not_found, provider_unavailable or unknown_error. Each value has what it means and what to do in Webhooks. Treat an error_code you do not know as unknown_error.
  • Connecting and disconnecting answer 202 with alias, status and at, on all four routes: POST /v1/numbers/{ref}/connect and /disconnect answered 200 with an empty at, and the main number routes answered 202 without the field. All four now return 202 Accepted with alias, status (connecting or disconnecting) and at, the moment the request was accepted. If you checked for 200 on the extra number routes, accept 202. The number’s current status and its own at stay in GET /v1/numbers/{ref}. See Connecting extra numbers.

October 2026: scheduled removal

  • Removing does not require disconnecting: DELETE /v1/numbers/{ref} accepts a connected number and no longer answers 400 invalid_status_for_deletion. When the removal is scheduled, the number stays connected and keeps working, with its usual status, until the date. See Remove.
  • The date is the end of the period already paid: scheduled_deletion_at is now the end of the subscription period (or the moment of the request, when there is no paid period), not a 30-day cycle of the number.
  • pending_deletion is no longer a status, breaking: status shows only the real connection state, and ?status=pending_deletion on GET /v1/numbers answers 422. If you tested that value, read scheduled_deletion_at, which now comes on every item of GET /v1/numbers (null on the main number and on extras with no removal) and in the PATCH response. See Status.
  • Connecting and disconnecting never charge or touch the removal: POST /v1/numbers/{ref}/connect no longer cancels a scheduled removal or charges the activation, and no_payment_method, payment_declined, payment_requires_authentication and payment_gateway_unavailable left its error table. POST /v1/numbers/{ref}/cancel-deletion is what keeps the number, and it only charges what remains of the paid period when the invoice of the next period was already issued without the number.
  • cancel-deletion returns the real state: the response carries status (the connection state) and at (can be null), in place of pending_deletion_canceled.
  • MCP server: no tool creates a charge. connect_number is no longer the exception (reconnecting a number with a scheduled deletion used to cancel the deletion and could charge) and disconnect_number still never charges. Cancelling a removal stays outside the catalog. See MCP server.

October 2026: pairing with a QR code

  • Pair an extra number with a QR code, through the API: POST /v1/numbers/{ref}/pair now accepts method: "qr", besides "pairing_code" (the default, when the body is left out). Any other value answers 422. In the launch note, "qr" answered 422: that has changed. The dashboard already offered both methods. See Pairing with the API and Pairing in the dashboard.
  • pairing.method can be qr: in GET /v1/numbers/{ref}, with method: "qr", pairing.data is the QR code image as a PNG data URI (data:image/png;base64,...), to use as the src of an <img>, never as text. If your screen only handled codes, check pairing.method before showing pairing.data.
  • The QR code renews: WhatsApp replaces the QR code on its own and Turbo Notify keeps pairing.data current. Read again and replace the image on every read. For both methods, pairing.expires_at is when the whole attempt stops being usable.
  • Replacing the attempt: POST /v1/numbers/{ref}/pair while the number is pairing replaces the attempt in progress, including one started in the dashboard, and can switch the method.
  • Neutral timeout text: the data.error of number.connect.failed with reason: "pairing_timeout" no longer says “Pairing code expired”. It now says pairing was not completed in time, for the code and for the QR code. The reasons pairing_timeout, code_generation_failed and paired_phone_mismatch apply to both methods. See Status.

October 2026: editing captions

  • Edit the caption of an image, video or document: POST /v1/numbers/{ref}/messages/{id}/edit now accepts caption (1 to 1,024 characters, with the same @<number> mentions as a send). The file stays as it was and the recipient sees the new caption marked as edited. The outcome arrives as message.edited (with the content_type of the file and the new caption in preview) or message.edit.failed. See Edit a caption.
  • What can be changed after sending: text, the caption of an image, video or document, and calendar events can be edited. A location, contact card, link button, sticker or audio message cannot: deleting it is the only change. A body that does not fit the message answers 400 message_not_editable and a text or caption edited more than 15 minutes after sending answers 400 edit_window_expired. See What can be changed after sending.
  • Time limit for editing a text or a caption: WhatsApp gives 15 minutes after sending to change a text or a caption. After that the API answers 400 edit_window_expired instead of accepting an edit the recipient may never see. The 15 minutes do not apply to a calendar event, although WhatsApp may still refuse an event edit.
  • Fixed: a caption edited in the WhatsApp app reached the webhook as message.edited with an empty preview. It now carries the new caption and the content_type of the file.
  • MCP server, breaking: the edit_message tool is removed. Four tools replace it, one per action: edit_text_message, edit_media_caption, edit_calendar_event and cancel_calendar_event. Each takes only the fields of its own type. The server now has 36 tools. Agents that called edit_message must call the tool for their type. See MCP server.

October 2026: calendar events

  • Edit and cancel a sent event: POST /v1/numbers/{ref}/messages/{id}/edit now accepts, besides text, a calendar event body (name, start_at and the other send fields) and the canceled field. The edit replaces the whole event. The outcome arrives as message.edited (with content_type: "calendar" and canceled: true when canceled) or message.edit.failed. Before, editing an event answered 400 message_not_editable. See Edit and delete.
  • Events edited in the WhatsApp app: when the creator of an event edits or cancels it in the app, you receive message.edited with origin: "native".
  • Guest answers: the new message.calendar_responded event carries the answer (going, not_going or maybe) and, when there are any, the number of extra guests. See Calendar events.
  • Read the answers: GET /v1/numbers/{ref}/messages/{message_id}/responses returns the latest answer of each guest and the totals. See Get event responses.
  • New codes: message_not_calendar (reading answers on a message that is not an event) and message_secret_unavailable (edit failure on an event the number can no longer change). When the platform cannot change or cancel events at the moment, message.edit.failed carries calendar_edit_unavailable: send a new event. See Error codes.

October 2026: launch

  • WhatsApp API: sending text, media, location, contact, calendar event and link button; replies, reactions, editing and deleting; delivery and read receipts; contacts and groups per number. See the Quickstart.
  • Events by signed webhook or by polling, in the same format.
  • MCP server with 36 tools for AI agents, which you reach by signing in with your account or with a key. On the hosted address, a call with neither a sign-in nor a key now receives 401 with the invitation to sign in (it used to receive 200 with an error inside the tool), and a value that is neither a sign-in nor a key also receives 401. See Build an AI agent.
  • Resale: extra numbers, the status filter on the number list and the Resale key template. Every number has a fixed id (num_…) that works in place of the alias on any /v1/numbers/... route, comes on events as number_id (and as data.id on number.* events) and filters event queries with ?number_id=. See Resell to your customers.
  • Message list filters: GET /v1/numbers/{ref}/messages accepts group_id, contact_id and direction, and each item carries the same envelope as the message’s event (including number_id). See List messages.
  • Calendar: an event sent as calendar now shows up on the recipient’s WhatsApp, in a chat and in a group. Before, the message was delivered but did not appear on the phone.
  • Interactive API reference, error code index and Insomnia collection.
  • Authentication errors: a missing, malformed, unknown, revoked or expired key answers 401 not_authenticated, with the WWW-Authenticate: Bearer header; 403 is for a valid key without permission (insufficient_scope). Before launch these cases answered 403 not_authorized: if your test integration handled that code, switch it to the 401. See the error code index.
  • Quota warnings: message.quota.warning and contact.quota.warning carry the threshold reached in data.threshold_percent (80, 90 or 95, in percent). Before launch the field was called threshold_bps, with the same values: if your test integration read that name, switch to threshold_percent. See Message quota events.
  • New extra number and pairing (adjust if your test integration used the old names): the body of POST /v1/numbers takes the phone as phone_number (the name phone is no longer accepted and answers 422), and the body of POST /v1/numbers/{ref}/pair only accepts method: "pairing_code" ("qr" used to answer with no effect and now answers 422). See Add numbers and Pairing with the API.
  • Failures of supporting services: when the API cannot reach a service it depends on, the answer is 503 service_unavailable; when an outside service answers with an error (for example, the payment processor when you add an extra number), the answer is 502 integration_error. Before, some cases returned internal codes that are not part of the contract. See the error code index.
  • MCP server: opening a streaming connection without a sign-in or a key answers 401 not_authenticated, with WWW-Authenticate: Bearer, as the API does (the code used to be unauthorized). See MCP server.
  • Audio transcription: when the transcription provider configured for your account is not supported, data.transcription.reason is provider_not_supported (before launch it was no_adapter). The other reason values are unchanged. See Get a message.
  • Documentation in Portuguese and English.