Skip to main content

Changelog

Changes that affect people integrating. Additions with no contract impact are left out.

What counts as a breaking change​

ChangeBreaking?
New optional field in a responseno
New optional field in a requestno
New endpointno
New value in an enumno — handle unknown values with a default case
Field removed from a responseyes
Optional field became requiredyes
A field's type changedyes
A status code changedyes

Breaking changes only happen in a major API version, announced by email to every active account beforehand.


2.2.0​

Everything in this version is additive.

Agencies and workspaces​

  • GET /workspaces — an agency's group, with what each client consumed in the billing period. Only with the agency account's unrestricted key.
  • GET /account/me returns parent: the agency that manages and pays for the account when it is a client's workspace.
  • Inside a workspace, /wallet and /affiliate answer 409 billed_by_parent.

See Agencies and workspaces.

2.1.0​

Everything in this version is additive: nothing that already worked stops working.

New endpoints​

  • Contacts and consent — /contacts, custom fields, tags, bulk import and /suppressions. See Contacts and consent.
  • Triggering flows — POST /flows/{id}/trigger, with GET /flows and GET /flows/{id}. See Flows through the API.
  • Campaigns and audiences — /broadcasts and /audiences. See Campaigns.
  • Inbox — /inbox, with the live stream. See Inbox.
  • GET /plans/quote, GET /audit/operator-access, GET /wallet, GET /wallet/entries, GET /affiliate and GET /affiliate/referrals.

New events​

contact.created, contact.opted_in, contact.opted_out, broadcast.completed, broadcast.paused, flow.run.completed, flow.answer.captured, conversation.assigned and conversation.closed. See Event catalog.

New fields and headers​

  • POST /messages accepts an Idempotency-Key. See Retrying without duplicating.
  • Every 402 carries action, with what to do and where. See Errors.
  • The request limit is now per key, under an account ceiling. Responses carry X-RateLimit-Scope, and a 429 carries error.scope. See Rate limits.
  • GET /billing carries subscriptions, one per subscription of the account. The old subscription field stays, marked deprecated.
  • Plans gained stackable, currencyPrices, rateLimits and the caps on contacts, campaigns and the inbox.

2.0.0​

Major version — your code has to change

The old routes no longer answer. There is no alias: the old path returns 404, or 405 where it survives under a different method.

One sending endpoint​

What changed between a text and a template was the message type, which is a body field — not a path. The per-type routes became one, with type.

BeforeNow
POST /messages/send/textPOST /messages with {"type":"text"}
POST /messages/send/templatePOST /messages with {"type":"template"}
POST /messages/send/testPOST /messages with {"type":"test"}
POST /messages/text, /template, /testsame — the undocumented aliases are gone

The rest of the body is unchanged: to, from, body, name, language and components stay as they were. So do the success and error responses.

Scheduling​

The same POST /messages takes sendAt (an ISO 8601 instant) or delaySeconds and answers 202. New: GET /messages/scheduled, GET /messages/scheduled/{id} and DELETE /messages/scheduled/{id}. See Sending messages.

Uniform pagination​

Every listing now takes ?limit= and ?offset= and answers { data, total, limit, offset }. Code reading res.data keeps working; code relying on total being absent does not.

New endpoints​

GET /account/senders, GET /account/usage, DELETE /account/wabas/{id}, GET /audit, GET /plans (public, no credential), GET /billing, GET /billing/invoices, GET /billing/license and POST /templates/media.

Billing events​

billing.past_due, billing.blocked and billing.restored — the first events we originate rather than relay from Meta. See Event catalogue.

Other contract changes​

  • POST /auth/logout became DELETE /auth/session.
  • Both invite /revoke routes became DELETE on the resource itself.
  • A wrong method on an existing path answers 405 with an Allow header, where it used to answer 404.
  • 402 now carries reason, including the new license_expired.

Removed from the documentation​

The endpoints marked "coming soon" were taken out of the reference: they answered 404, and the reference now describes only what exists. Image, video, document, buttons, list, CTA, carousel, location, contact, reaction, sticker, typing indicator and read receipts are all still available through the mirror, POST /v1/{phone_number_id}/messages, which takes the full Cloud API payload — see The other message types.


1.0.0​

First public release.

Simplified API — sending by message type, with the sender number resolved from the token. Listing, creating and removing templates with up-front validation of variables and examples. API keys with creation and immediate revocation.

/v1 mirror — the Meta Cloud API contract, byte for byte. Swapping the domain and the token is enough to move an existing integration.

Webhooks — deliveries signed with HMAC-SHA256 over "{timestamp}.{body}", six attempts with backoff from 60s to 6h, a delivery log and manual redelivery.

Coexistence — a number shared between the WhatsApp Business app and the Cloud API, with history and contact sync in the first 24 hours.

Rate limits — 500 sends per minute, 60 media uploads per minute, 60 reads per minute, with X-RateLimit-* headers on every response.