واتس ديف
Back to home العربية

Changelog

Every customer-visible change to the API and the portal, newest first.

Versioning and deprecation policy

The API is versioned in the URL: every endpoint lives under /v1.

Additive changes ship without prior notice. New endpoints, new optional request parameters, new fields inside existing responses, new values in existing lists and new error codes for new failure modes can appear at any time. Your integration must therefore ignore response fields it does not recognise rather than fail on them.

Nothing is removed or repurposed without 90 days' notice. No response field, request parameter, endpoint or error code inside /v1 will be removed, renamed or given a new meaning without a dated deprecation entry published on this page at least 90 days beforehand. The meaning of an existing error code will not change either.

Breaking changes ship as a new version prefix. When a change cannot be made additively it goes to a new prefix, and /v1 keeps working for the notice period announced here.

2026-09-29 — Restarts that recover a lost number, and a fresh month after a restart

Numbers (sessions)

  • POST /v1/sessions/{id}/restart on a number WhatsApp's engine no longer holds creates it again under the same id, instead of failing. It comes back waiting for a scan, with its phone unlinked.
  • A renewal restarts a number stopped for want of a subscription even when the engine had lost it. If the restart still fails, the portal tells you to restart or relink the number yourself.
  • The daily check also restarts every number stopped for want of a subscription whose account is subscribed again, so none stays stopped after a renewal.
  • A restarted number gets a fresh 30 days to be linked before it can be stopped as unlinked.
  • Reactivating a deleted number now says how many messages it can send on its first day of warm-up.

2026-09-28 — Numbers stopped for want of a subscription come back on renewal

Numbers (sessions)

  • A number stopped because the account had no subscription cannot be restarted, relinked or asked for a QR or pairing code until a subscription is active again: restart, logout, qr and pairing-code answer 403 subscription_required, and the portal says why. A number stopped for staying unlinked can still be restarted, so it can be linked.
  • Renewing the subscription restarts those numbers by itself, and the portal tells you each one was restarted.
  • A number stopped as idle no longer shows its stop reason after it reconnects on any path.

API reference

  • GET /v1/media/{message} and GET /v1/scheduled-messages/{id} document their real success response instead of an error example.

2026-09-28 — Number spellings, the phone after a relink, and idle numbers

Sending

  • Every recipient field (to, bulk recipients, group participants, and the suppression phone) accepts any spelling of a number: +, 00, spaces, dashes, dots, parentheses, Arabic-Indic, Persian or full-width digits, invisible direction marks, upper-case servers such as @C.US or @S.WhatsApp.Net, and :N device suffixes. They all fold to one spelling, so an opt-out matches however the number was typed. A scheduled to and a campaign recipient's phone are stored and returned in that spelling.
  • Refused now (422 invalid_recipient for a send or a schedule; skipped as invalid_format in a campaign; 422 validation_failed for group participants): a national number without its country code (leading 0), text around a number, digits from other scripts, and servers other than c.us, s.whatsapp.net, lid, g.us, newsletter and broadcast. Such numbers reached nobody before.

Numbers (sessions)

  • When GET /v1/sessions/{id} or the periodic check sees a number connected before WhatsApp's own event arrives, it fills phone straight away. The connected session.status event is sent once per connection, whichever of them sees it first.
  • A number whose account has had no subscription for 30 days, or that has waited for a scan or stayed failed for 30 days, is now stopped. It is never deleted: its id, settings and history stay, and a restart or a relink brings it back. The portal shows why it was stopped.

Bot

  • WhatsApp Business greeting and away messages no longer pause the bot, so a new customer gets an answer. A reply typed on the phone within 10 seconds of the customer's message does not start a pause, but it does extend one that is already running.

2026-09-27 — Late receipts, input limits and human takeover

Delivery

  • A receipt WhatsApp sends before the send's own answer arrives, or under another spelling of the chat (such as a @lid chat), is no longer lost: it reaches message.status.
  • A message that failed with delivery_unconfirmed becomes sent, then delivered or read, when WhatsApp confirms it later. Its error becomes null, usage counts it as sent instead of failed, and a campaign counts it as sent.
  • message.status only moves forward: never from read back to delivered, and a failed receipt never follows delivered or read. A send WhatsApp rejects before its own answer arrives fails with send_failed.

Bot

  • Human takeover works: a reply typed on the phone or on WhatsApp Web pauses the bot in that chat for the configured minutes. It never triggered before. Messages sent through the API or by the bot never pause it, and phone-sent messages are still not forwarded as message.received.

Numbers (sessions)

  • New endpoint: POST /v1/sessions/{id}/reactivate brings a deleted number back under the same id, waiting for a scan. It takes a plan slot like creating a number, and needs sessions:write and an active subscription.
  • 409 session_state_conflict carries details.reason next to details.status: linked, not_ready, stopped, deleted or not_deleted.
  • GET /v1/sessions/{id} checks the status with WhatsApp's engine as it is read, and falls back to the last known status after about 3 seconds. The list still shows the stored status.
  • Session objects gain deleted_at.
  • Deleted numbers now really send no status events: despite the entry of 2026-09-26, the periodic check still sent one after a delete. The session.status events that check sends include phone.

Sending

  • text, caption and an edited message's text are limited to 65,536 characters.
  • A contact send takes 1–50 cards (fullName, phoneNumber, organization, whatsappId, or a raw vcard). Each field is at most 1,024 characters and vcard at most 65,536; unknown keys are refused.
  • A group's subject is at most 100 characters and its description at most 2,048.
  • Over a limit is 422 validation_failed naming the field.
  • A …@s.whatsapp.net recipient, with or without a :N device suffix, is accepted and stored as …@c.us. Opt-outs match that spelling in campaigns and scheduled sends, and the chat_id filter folds it the same way. Group participants with a device suffix are treated as the plain number.
  • One campaign runs per number at a time, now enforced under a lock. A campaign started or resumed while another runs on the same number waits as pending; resuming one that is already running is 409 invalid_bulk_state.

Webhooks

  • POST /v1/webhooks/{id}/deliveries/{delivery}/retry answers 409 webhook_delivery_not_retryable unless the delivery's status is failed.

Account and API keys

  • GET /v1/usage adds subscription_active. Without an active subscription, remaining is 0 for both windows instead of null.
  • New and rotated keys look like <id>|wd_live_… (live) or <id>|wd_test_… (test). Existing keys are unchanged and keep working. Treat a key as opaque.

Portal and sign-in

  • A flood of sign-ups can no longer block verification codes for existing customers: codes sent to their verified numbers have their own budget.

2026-09-26 — Number lifecycle and API key allowlists

Numbers (sessions)

  • New endpoint: POST /v1/sessions/{id}/logout unlinks the phone and keeps the session: same id, same number slot. The session then waits for a new QR scan or pairing code. A number linked afterwards starts its warm-up again.
  • qr, pairing-code, restart and logout check the session's state first. When the action makes no sense in that state, they answer 409 session_state_conflict with details.status instead of asking WhatsApp.
  • New ability sessions:connect covers qr, pairing-code, restart and logout. sessions:write still includes it, so existing keys keep working.
  • The pairing-code phone must be an international number; anything else is 422 validation_failed.
  • A failed number now counts against your plan's number limit until it is deleted or relinked. A number whose creation failed gives its slot back.
  • A late session.status event can no longer move a session's status backwards, and deleted numbers send no more status events.
  • A session's egress proxy is checked again regularly. One found pointing at a private or internal address on two checks in a row has its session stopped and the proxy removed.

API keys

  • A key's IP allowlist accepts single addresses and CIDR ranges, for IPv4 and IPv6. It can be set and edited in the portal.
  • A refused call's message names the IP it came from.

Portal

  • Re-link links a phone to an existing number under the same id, with a QR code or a pairing code.
  • Log out number unlinks the phone and keeps the id.
  • Reactivate brings a deleted number back under its id when your plan has a free slot.
  • Delete number says plainly that the id, and every integration built on it, stop working.

2026-09-25 — Delivery confirmation, exact quota and account safety

Delivery

  • Every send now carries its own id. When WhatsApp's engine took a send but its answer was lost, the message stays queued while delivery is confirmed, for up to about 30 minutes. It then becomes sent or fails with delivery_unconfirmed, and it is never sent twice. A message confirmed this way emits sent and then delivered or read.
  • A failure's error is now a stable code, never raw error text. This applies to messages, to campaign recipients' error, to scheduled messages' last_error and to the message.status webhook. Examples: upstream_timeout, upstream_unreachable, delivery_unconfirmed, media_too_large, suppressed, account_suspended; the full list is under List messages. Stored values were rewritten to these codes.

Quota, limits and keys

  • quota_exceeded, daily_cap_reached and quota_insufficient now count messages still queued and the unsent recipients of open campaigns, so a burst can no longer overshoot the plan. A paused campaign keeps holding its recipients' quota until it finishes or is canceled.
  • GET /v1/usage gains today.reserved and month.reserved. remaining and the X-Quota-* headers are net of them.
  • The API rate limit applies to the account as a whole, separately for live and test keys, and no longer to each key.
  • At most 10 active API keys per account. An expired or revoked key can no longer be rotated: create a new one. An expiry date in the past is refused.

Idempotency

  • An Idempotency-Key is also bound to the session it was first used on. Reusing it on another session, or across a test and a live key, returns 409 idempotency_key_reused.
  • Scheduled sends now compare the request body too.

Validation

  • A scheduled send with an invalid recipient is refused with 422 invalid_recipient when it is scheduled.
  • PUT and DELETE /v1/messages/{id} refuse a received message with 422 validation_failed, and an outbound message that was never sent with 409 message_not_sent.
  • Polls need a name of up to 255 characters and 2 to 12 unique options of up to 100 characters each; anything else is 422 validation_failed.

Groups

  • Adding participants, and creating a group, take at most 20 people per request. Removing, promoting and demoting take at most 1,024. Every id is validated.
  • Suppressed numbers are skipped and listed in skipped (meta.skipped on create).
  • Each number has a daily budget of participant additions. Past it the answer is 429 daily_cap_reached with details.scope: group_adds.
  • Participant changes return WhatsApp's answer for each person: {ok, participants, skipped}. ok is true only when everyone succeeded.
  • Group list and get now return id, subject, owner and participants_count on the engine this service runs; they were empty before.

Inbound messages and opt-outs

  • In message.received and the messages API, from is the sender's phone chat id whenever WhatsApp reveals it. A new from_lid field carries the sender's @lid id when the message was addressed that way. The messages export gains the same column.
  • Suppressions may hold …@lid ids. POST /v1/suppressions accepts them and refuses group, channel and broadcast ids with 422. DELETE removes every form of the person.
  • Adding a suppression skips that person in pending campaigns, and campaigns check each recipient again right before sending (skipped as suppressed). A scheduled message to a suppressed number fails with suppressed.

Account suspension

  • A suspended account sends nothing:
    • API sends are refused with 403 account_suspended;
    • running campaigns pause with account_suspended;
    • scheduled messages that come due fail with account_suspended;
    • the bot stays silent.
  • The account can still list, read and cancel its campaigns and scheduled messages through the API.

Sessions

  • session.status no longer repeats connected for the same connection.
  • WhatsApp's new passkey pairing steps are reported as scan_qr.
  • The number-check endpoint returns the phone chat id even when WhatsApp answers with a @lid.

Billing

  • Paid plans keep full service through their grace period after the end date.
  • The portal sends renewal reminders 7, 3 and 1 day(s) before a subscription ends, and a notice when the grace period starts. During grace, the subscription card says when service stops.

2026-09-25 — Safer sending and stricter input

Links and media

  • Plain-text messages no longer get an automatic link preview, and neither does an edited message. For a preview card, send type: link_preview.

  • A file given by URL (media.url, preview.image.url, and file.url on profile and group pictures) is now downloaded by the platform just before it is sent, and handed to WhatsApp as the file itself. For scheduled and campaign sends that is at send time, not when you made the request. A campaign downloads its file once for all its recipients.

  • The URL must be http or https and its host must resolve to a public address. Any other URL is refused with 422 validation_failed. Up to three redirects are followed, and each one is checked the same way. The download must finish within 20 seconds.

  • Size limits: 16 MB for message media, and 5 MB for profile and group pictures and link-preview images. They apply to a downloaded file and to inline data alike.

  • The file's type must suit the message:

    • image: JPEG, PNG, WebP or GIF;
    • video: any video type;
    • voice: any audio type;
    • document: any type.
  • A message whose file cannot be used fails, with one of these stable values in its error (on the message and in the message.status webhook):

    • media_url_blocked
    • media_too_large
    • media_type_not_allowed
    • media_unavailable

    A temporary network or server error is retried before the message fails. A picture that cannot be used is refused at once with 422 validation_failed.

  • preview, buttons and list accept only their documented keys. Any other key is refused with 422 validation_failed.

  • A session's delay between sends is capped at 10 seconds.

Webhooks

  • A webhook URL whose host does not resolve to a public address is refused with 422 validation_failed when you save it, instead of being saved.
  • Each delivery goes only to the address that was checked, and redirects are no longer followed. A 3xx answer counts as a failed attempt.
  • Webhook deliveries now carry an error field. It is set for a delivery that was never attempted, to one of:
    • blocked_address
    • unresolvable_host
    • invalid_host
    • invalid_url
    • scheme_not_allowed
    • userinfo_not_allowed
    • endpoint_inactive

Sessions

  • proxy.server must be host:port with a public host. It takes no scheme, path or credentials.
  • proxy.username and proxy.password must be sent together, and use only the characters listed in the reference.
  • Any other key under proxy is refused.

Errors and input

  • New error codes:
    • invalid_cursor (422): a cursor this list did not issue;
    • invalid_encoding (400): a value that is not valid UTF-8 or contains a NUL character.
  • to: "status@broadcast" is refused with invalid_recipient.
  • An Idempotency-Key longer than 255 characters, or containing control characters, is refused with 422 validation_failed.
  • A path id that is not a number now returns 404 not_found instead of a server error.
  • WhatsApp Status updates posted by your contacts are no longer treated as messages. They are not stored and not sent to your webhooks, and the ones already stored have been removed.

Portal

  • Sign-in and verification codes are rate-limited per account, per phone number and per network. After too many wrong codes the portal says so and when to try again, and sends no new code until then.
  • Phone numbers are stored in international form, so the same number cannot be registered twice under two spellings.

2026-08-15 — Official PHP and Node clients

Two hand-written API clients are now published:

composer require whatsdev/whatsdev-php
npm install @whatsdev/sdk

Both cover the endpoints an integration touches daily and reach the rest through a generic request() method. Each carries a typed exception per error code, attaches an Idempotency-Key to every send so a dropped connection cannot deliver twice, iterates paginated lists, exposes the remaining-quota headers on send results, and verifies webhook signatures in constant time. Neither has any runtime dependency; the PHP package auto-registers a Laravel facade when Laravel is present.

Nothing about the API changed — these are clients for the surface already documented.

2026-08 — Your data: export, deletion and retention

Export a copy of your data

  • New in the portal. Your profile page can now build a zip archive of the account: account and subscription details, users, messages, contacts and their custom fields, lists, scheduled messages, campaigns, templates, suppressed numbers, sessions, webhook endpoints, payments and API-key metadata, as CSV files with a manifest.json that names every category left out of the archive and why.
  • It carries no credential. An API key exists only as an irreversible hash, and webhook signing secrets, session proxy credentials and password hashes are never exported.
  • One archive every 24 hours. You are notified when it is ready, it is downloadable only by a signed-in user of that same account and never from a public link, and it is deleted 7 days after it is produced.

Delete your account

  • New in the portal. You can close the account yourself, from the same page.
  • The moment you confirm, access ends: every API key is revoked, every WhatsApp number is disconnected and handed back, and every pending scheduled message and running campaign is cancelled. None of that is restored if you change your mind.
  • Everything else the account holds, stored files included, is erased permanently 14 days later. Until then you can cancel the request from the portal and the account is reopened.
  • Payment records are kept in anonymised form for accounting: they are detached from the account and stripped of the fields that could name it, and cannot be traced back to it afterwards.

Sandbox retention

  • Sandbox sessions are deleted once they pass the sandbox retention window, and the messages that belong to them are deleted with them. It is a session age limit, not a blanket age limit on sandbox data: a sandbox session you keep alive keeps its messages.

2026-08 — Sandbox and cursor pagination

Sandbox (test mode)

  • New accounts are issued a test API key at signup. A test key needs no active subscription and carries its own rate-limit budget, separate from your plan's.
  • Sandbox sessions run against a simulated WhatsApp. They connect on their own, take no slot from your plan's session allowance, and are never attached to a real WhatsApp account.
  • Sends made with a test key go through the ordinary send endpoint and return the ordinary response, but never reach WhatsApp and never count against your plan's message quota. A separate daily sandbox limit applies to them instead.
  • New endpoint: POST /v1/sandbox/inbound injects a simulated inbound message through the same pipeline a real one travels, so your webhook receiver, its signature check and your auto-replies can be exercised end to end without asking anyone to message you.
  • Test and live are kept apart in both directions: a test key is refused on a live session and a live key is refused on a sandbox session, and GET /v1/sessions returns only the sessions matching the calling key's mode.
  • A simulated opt-out ("إيقاف") is detected and answered with the same confirmation reply, which travels the simulated transport and so reaches nobody. The one difference is that it is never written to your real suppression list: that list is account-wide rather than per-session, so a simulated opt-out must never block a real number from your live sending. For the same reason a test key cannot change that list (POST and DELETE /v1/suppressions), though it may read it.
  • A webhook created with a test key must be scoped to a sandbox session, so whatsapp_session_id is required with one: a webhook with no session is account-wide and receives your live sessions' events, bodies and numbers included, which no test key may reach.
  • Sandbox sessions — and the messages that belong to them — are deleted once they pass the sandbox retention window. Nothing outside the sandbox is touched by that sweep.

Cursor pagination

  • ?cursor= is now accepted on the session-message, scheduled-message, contact, bulk-operation, bulk-recipient and webhook-delivery lists.
  • It is opt-in and additive. Leave cursor off the request and page-based pagination is unchanged: same ordering, same meta, same links.
  • It exists for traversal stability, not for speed. Walking a list by page number while new rows are still arriving repeats some rows and skips others; a cursor walk does not.
  • In cursor mode the response carries meta.next_cursor and meta.prev_cursor in place of meta.total, meta.current_page, meta.last_page, meta.from and meta.to, and links.first and links.last are null. Follow links.next, which preserves the filters and per_page you sent.

Portal

  • The message list hides sandbox messages by default and offers a filter to show them, so test traffic never clutters the real inbox.
Terms of Service Privacy Policy Acceptable Use Policy