Skip to main content
What’s new on the Pilot Status platform — API additions, changes and notable fixes. Subscribe via the RSS icon above.
APINumbersBreaking

Default changed: a new number no longer imports the device’s history

settings.historyImportEnabled now defaults to false. A number you create from today on starts existing at the moment it connects — the up-to-30-days of conversation the device hands over on connection is discarded instead of stored.Numbers created before today keep the value they had. Nothing was rewritten, and a number that is already connected is unaffected.If you want the history, ask for it when you CREATE the number:
POST /v1/numbers and POST /v1/numbers/remote-pairing accept the whole settings block now, and the 201 echoes back what the number was created with.Creating and then PATCHing does not work, and that is why the block exists. WhatsApp hands the history over in a single burst right after the connection; the flag is read per message as those arrive; nothing can request them again. A PATCH sent after POST races that burst — on the pairing-link flow it has no chance at all, because whoever opens the link connects on the spot.In the dashboard the question is now part of the connect wizard, next to the phone number — the only moment where the answer still changes anything.Why the default flipped. Storing a month of conversations nobody asked for is the expensive default: it fills /chat with history the tenant already read on the phone, and every reconnect replays it. Off is the reversible choice for a new number; on is not, once the burst is gone.
APINumbersWebhooks

Turn WhatsApp Channels off on a number: ignoreNewsletters

Documented. settings.ignoreNewsletters on PATCH /v1/numbers/{id} drops WhatsApp Channel (@newsletter) posts on arrival: no conversation, no stored message, no message.newsletter webhook. It defaults to false, so nothing changes for a number that receives channels today.
The behaviour is not new — the field is reachable now. The API has accepted it for a week, but it was missing from the OpenAPI spec, so the playground had no box for it, and missing from the SDKs, the MCP server and the n8n node. The same shape as the caption fix in the 18 August entry: accepted by the API, invisible everywhere you would look for it.It reads like ignoreGroups and is not one of the advanced fields. The unofficial provider applies ignoreGroups itself, and has no @newsletter gate at all — a channel post always reaches the platform, so the only place it can be refused is here. The field takes a plain true / false, never the null that resets an advanced field to the provider default: for channels there is no provider default to return to.Python SDK: numbers.update() copies settings key by key from an allowlist that did not include this one, so the field was dropped in silence — the call returned 200 and the channels kept arriving. Fixed, shipping in the next release; curl and the playground are unaffected.
APIMessagesMedia

Video: .mov is converted for you, and the 16 MB ceiling is now explicit

Fixed. Sending a .mov video (QuickTime) to a Meta Cloud API number failed. The Cloud API accepts exactly two video types on upload — video/mp4 and video/3gpp — and .mov, which is what iPhone and most Android cameras record, is not one of them. The send failed with Received file of type 'video/quicktime'.You do not have to convert anything: .mov and .webm are now converted to MP4 automatically before the send. When the codecs already fit (H.264 + AAC, which is what phone cameras write) it is a container rewrite with no re-encode, so there is no quality loss.Video is capped at 16 MB. That is WhatsApp’s limit, not ours, and there is no way around it. Above it the send fails with the new META_MEDIA_TOO_LARGE code, which now carries the measured size instead of relaying a raw provider error. The other ceilings, for reference: image 5 MB, audio 16 MB, document 100 MB, sticker 100 KB static / 500 KB animated.Sending a video as mediaType: "document" no longer returns 202. It never worked — the document class has no video type in it, so a video sent that way was always refused downstream — and it is a common attempt at trading the 16 MB video ceiling for the 100 MB document one. On Meta Cloud API numbers the request is now rejected up front:
422, before the message is queued, instead of a 202 followed by an opaque failure. Unofficial (Pilot Status web) numbers are unaffected — they deliver a video-as-document fine, and the restriction is Meta’s alone.The conversion is decided by the file, not by the URL. The container is identified from the file’s own bytes, so a .mov is converted even when the URL carries no extension at all (a .bin object key, a signed URL) or carries a misleading one. This matters because a mislabelled video is the worst case there is: Meta accepts the upload, returns a message id, and then never delivers — with no failure surfaced. See the media guide.
APIMessages

Send stickers: mediaType: "sticker"

New. The direct-media mode of POST /v1/messages/send accepts mediaType: "sticker". It works on Meta Cloud API numbers and on unofficial (Pilot Status web) numbers alike — each one has its own sticker route underneath, and the API picks it for you.
A sticker carries no text. Sending caption alongside it is rejected with 400 rather than delivered without the text — no provider has a caption field on a sticker, so accepting it would mean the send “succeeds” minus what you wrote. mediaFilename is rejected for the same reason, and a sticker cannot be used as template media: a template header is TEXT, IMAGE, VIDEO or DOCUMENT, and there is no sticker header.The file must be image/webp and exactly 512×512 pixels — static up to 100 KB, animated up to 500 KB. The dimensions are not a guideline: a 361×363 sticker is rejected outright. These rules are WhatsApp’s, not ours, and WhatsApp is what enforces them: a wrong-sized, wrong-dimension or wrong-format file is still accepted here with 202 and fails later in the message.failed webhook, as META_MEDIA_UPLOAD_ERROR (Meta code 131053) with the exact reason in error_data.details. Convert before sending rather than relying on the API to catch it. See the media guide.If you were sending a .webp as mediaType: "image" to approximate a sticker, that delivered an ordinary image — switch it to "sticker" to get a real one.Also in this release: caption is now documented in the API playground. The field has always been accepted for image, video and document sends, but it was missing from the OpenAPI spec, so the playground had no box for it.
APIAds

Which ad each conversation came from — and the campaign’s name

New: GET /v1/referrals and GET /v1/referrals/summary. When someone taps a Click-to-WhatsApp ad and writes to your official-API number, WhatsApp tells us which ad they clicked. That was being received and discarded; it is now stored and readable. The first endpoint lists the inbound messages that came from an ad, the second groups them per ad with a conversation count.Meta attaches the origin to the first message of a conversation only and never repeats it, so one row is one conversation started by that ad. conversations counts distinct conversations and messages counts messages — two messages in one thread are 1 and 2, so conversations is the lead count.The names. Beside the raw sourceId, the referral object now carries adName, adsetId, adsetName, campaignId, campaignName and namesResolvedAt — the same labels you use in Ads Manager, rather than an opaque 120249828053880703.
Those six come back null until the advertiser shares the ad account with us, and null is a supported state, not an error. Reading an ad’s name needs ads_read on the advertiser’s own ad account, granted through Meta partner sharing: they add the Pilot Status business portfolio as a partner, with “view performance”. Until then the conversation is still counted and sourceId is still returned — only the names are null.
No endpoint resolves a name on demand. Resolution runs asynchronously, off the request path, so querying never triggers a Marketing API call and a null can become a name minutes later with nothing done on your side. namesResolvedAt is what makes that observable: null means “not resolved yet”, while a timestamp with null names means “we asked and Meta returned nothing” — a deleted ad, or sharing still missing.Attribution starts at this release: there is no backfill, so conversations that arrived before today carry no ad origin.Docs: Click-to-WhatsApp attribution.
ConnectEmbed

Styling the Connect button now renders the button

Behaviour change. A remote-pairing link minted with branding.button now renders only the button — no header, no card, transparent background. Until today that took a second step the response never mentioned: the host page also had to append ?mode=button to the iframe URL. Sending branding.button and opening the returned remotePairingUrl gave you the full connect page, which read as “my styling was dropped”.One object, both effects: style a button, get a button. Paste the returned remotePairingUrl into a browser and you see exactly what your customer will see.If you want the full page for a styled link, append ?mode=page (or pass mode: "page" to the SDK). That is the new explicit opt-out, and it keeps the token’s styling on the button inside the card. Nothing else moved: an unbranded link still renders the page, and an explicit ?mode=button still renders the button. metaFlow: "credentials" and QR links are unaffected — a credentials form and a QR timeline do not fit in a button, so they always render the page.@pilot-status/embed 0.2.0. ConnectOptions.mode now defaults to auto instead of "page": omit it and the SDK resolves the mode from the token, then sizes the iframe to match (48 px for a button instead of a 520 px minimum). Pass an explicit "page" or "button" to decide it yourself.Fixed: the served SDK bundle was stale. The embed.js bundle shipped to host pages had not been rebuilt since 1 July, so it predated button mode entirely — PilotStatus.connect.mount(el, { mode: "button" }) silently rendered the full page for every SDK caller. Rebuilt; the raw-iframe path was never affected.Fixed: the docs told you to load the SDK from a host that does not exist. Four pages — both Embed Connect pages and both Embed Chat pages — carried <script src="https://embed.pilotstatus.com.br/embed.js">. That hostname has no DNS record, so the script tag failed to resolve and PilotStatus was never defined: nothing on the page worked, and the browser console blamed DNS rather than us. The SDK has always been served from https://pilotstatus.com.br/embed.js (which is what the Embed Sessions page already used). All six references now point there. If you copied the old snippet, change that one URL.Docs: Embedded Signup and Button mode.
WebhooksDocs

Every webhook event, in one place — including Meta’s

Webhook events now opens with a map of which vocabulary each number speaks, and documents the Meta Cloud API side that was missing: all 23 subscribable field names (messages, message_echoes, the five template fields, phone_number_quality_update, the account family, calls, flows, the four group fields, payment_configuration_update, user_preferences), what each one reports, and which need a paid plan.Also written down for the first time: Meta numbers deliver Meta’s native envelope and never message.received (events: ["*"] does not convert it); each change is delivered on its own, so entry and entry[0].changes always hold exactly one element; and nine of Meta’s fields are never forwarded to a customer webhook, not even to a "*" subscription — account_alerts, automatic_events, history, messaging_handovers, partner_solutions, security, smb_app_state_sync, standby, tracking_events.
APIWebhooks

message.stories, and group/channel events are no longer paid-only

New event: message.stories. WhatsApp Status (stories) posted by your contacts are now delivered as a webhook event, with the re-hosted media in mediaLink. Subscribe to it — or to the "*" wildcard — and it starts arriving.It is demand-driven, like groups: the provider does not send Status at all until some webhook asks for it, and stops again when the last subscription goes away. That is deliberate. Before this change a connected number received the Status of every contact it could see and the platform discarded all of them at the end of the pipeline — measured at 1,869 events a day, 17% of everything entering ingestion, with no consumer.A Status is not persisted: it creates no conversation and no message, and does not appear in GET /v1/messages. WhatsApp expires stories after 24h; the mediaLink we re-host is durable, the story is not. There is no to field either — a Status is broadcast to the poster’s contact list, not addressed to your number.message.group, message.newsletter and message.stories are no longer paid-only. They were hidden from the event picker on non-paid plans; that restriction was legacy and has been removed. All three are selectable on any plan.
APIWebhooksDashboard

Number settings, and history no longer floods your webhook

Behaviour change. When a number connects, WhatsApp hands the provider the device’s message history. Until now those old messages were forwarded to your webhook as message.received / message.group / message.newsletter — meaning every reconnect re-delivered up to 30 days of conversation, with nothing in the payload to tell it apart from traffic that had just arrived. They are still imported into the chat; they are no longer delivered. If you want the old behaviour on a number, turn on settings.webhookHistoricalMessages.createdAt now means what it says. On message webhooks it carries when the message actually happened, per the provider, instead of when we processed it. Same field, same type, same ISO 8601 format.New: PATCH /v1/numbers/{id}. Per-number configuration, partial — retention policy plus a settings block covering history behaviour and, for unofficial (QR-paired) numbers, the provider’s advanced settings: rejectCall, msgRejectCall, alwaysOnline, readMessages, ignoreGroups, ignoreStatus. See the reference. If you were calling POST /v1/numbers/{id}/settings, that route never existed and was silently 404-ing.Unknown /v1 paths now return JSON. A wrong path used to render an HTML 404 page, which broke typed HTTP clients without telling you why. It now answers { "error": "Not found", "code": "ROUTE_NOT_FOUND" } like every other error on the API.Available in the Node (0.5.0), Python (1.4.0) and n8n (1.3.0) SDKs, and in the MCP server.
API

Logout WhatsApp instance endpoint

New endpoint POST /v1/numbers/{id}/logout to disconnect a WhatsApp instance without removing it from the database. This is useful when you need to temporarily disconnect a number for troubleshooting or reconnection to a different device, without losing the instance configuration. See the POST /v1/numbers/{id}/logout reference.
API

Voice notes with waveform (voice mediaType)

A new "voice" mediaType gives you control over how audio messages render. "audio" sends a voice note (PTT) in the default player; "voice" sends it with the waveform (“ondinha”) visualisation. On Meta Cloud API numbers the waveform is enabled when the server sends voice: true in the audio payload. Both types are normalised server-side to OGG/Opus mono (stripped metadata, zero start-time), so every audio file that reaches the recipient is compatible even when the original upload was a browser WebM or an iOS recording. See the POST /v1/messages/send reference.
APIDashboard
Building a carousel in the template editor is now faithful end to end. The live preview renders the card strip again (it previously showed nothing), the editor enforces a carousel’s exclusivity — a carousel can’t carry a header, template buttons, a list button or an order card, so those controls hide or disable while a carousel is present — and {{variables}} inside a card (its body or a button) are now recognised and substituted in the preview instead of being left as raw tokens.On the API side, the carousel field is now in the OpenAPI spec for POST /v1/messages/send, so it shows up in the interactive playground and in generated clients. We also corrected the docs: a URL button in a session carousel is impossible only on Meta numbers — on unofficial (Pilot Status web) numbers a URL button sends ad-hoc.
APIDashboard
Send a carousel — a horizontally scrollable set of media cards — in a single message. A new carousel field on POST /v1/messages/send carries 2–10 cards, each with its own image, a short body line and up to two buttons, and the template editor gains a carousel builder with a live, draggable preview so you can reorder cards as you compose them. A session carousel is free-form and needs no approval, but its buttons are quick-reply only and it only delivers inside the 24-hour service window — the same request works on official and unofficial numbers. A template carousel is an approved Meta template instead, so it delivers at any time and a card may mix a quick-reply button with a URL button.
API

Template deletion reports the truth

DELETE /v1/templates/{id} no longer reports success when Meta refuses to remove a template. When Meta rejects the deletion — for example when the access token lacks permission on the WhatsApp Business account — the endpoint now answers 409 with a bilingual message naming the template and the reason, and the template is kept locally because it still exists on Meta. Only a genuine removal on both sides returns { "deleted": true }.
DashboardChat

Delivered buttons, carousels and order cards render in chat

Messages that arrive carrying buttons, a carousel or an order card now render as their real bubble in the conversation and the message logs, instead of collapsing to plain text. Template replies show their buttons, carousels scroll through their cards, and order cards list their items — so the thread reads the way the recipient actually saw it.
Dashboard

A revoked Meta number is detected

When a number connected through the official Meta API loses access — the app was removed from the WhatsApp Business account, or the WABA itself was deleted — the dashboard now detects it and badges the number “Conexão perdida”. Previously the number simply went quiet with no sign that Meta had cut access; the badge now tells you to reconnect.
Fix
URL-button examples no longer double the base URL when the example value already includes it, so the previewed and sent link is the real one. And a template send now passes the body’s own variables, so a template that uses a {{variable}} in both its body and a button no longer fails with a parameter-count error.
Connect

Button-only embed for Embedded Signup

The hosted Embedded Signup gains a button-only embed mode: place just the Facebook connect button on your own page instead of the full flow. The button’s styling travels in the embed token, and a completed connection reports back paired with the connected number.
API

The tenant API key can now act on any number

A tenant-scoped key could manage numbers but not use them — sending required a separate per-number key. It can now call any per-number endpoint of any number in the tenant, including sending, by naming the number in the x-whatsapp-number-id header (it accepts the id from GET /v1/numbers). Without the header, per-number endpoints still answer 403 TENANT_SCOPE_NOT_ALLOWED; an id from another tenant answers 404. See Authentication.
DashboardWebhooks

Webhook deliveries are scoped to the active number

The Webhooks screen lists deliveries for the number you have selected. Delivery rows recorded without a number — message-status events — used to appear under every number at once; they are now attributed to the number that produced them.
DashboardTemplates

Build lists and payments as template buttons

Botão de Lista in the template editor builds an interactive list — button label, sections and rows — with live counters and a live preview that opens the options as a bottom drawer, the way the WhatsApp client does. A template carrying a list is sent as an interactive message rather than an approved template, so on official numbers it is not submitted to Meta and only delivers inside the 24-hour service window; the editor says so while you build.Botão de Pagamento now holds both payment shapes in one place: the PIX copy-code button and the itemized order card, which used to live in a separate section below the editor. Templates saved before this keep working unchanged.
Dashboard

Numbers that stop receiving are detected

A WhatsApp session can wedge one way: the number keeps sending, the connection reports itself healthy, and no incoming message arrives for hours. The dashboard now watches each number’s own inbound rhythm and flags it when messages stop arriving while sending still works, so the badge stops claiming health it has not verified.
Dashboard

Web shadowban flagged on the first refusal

When WhatsApp refuses sends over an unofficial connection, the number is now flagged immediately instead of after three refusals in the same window — a tenant that sends through the API a few times a week could previously stay blocked for weeks behind a green badge. The flag clears on its own as soon as a send succeeds again.
Dashboard

Compose lists from the chat

The chat composer gains a Lista action: build the button label, sections and rows with live validation and a live preview, then send — on official and unofficial numbers alike. Sent lists now render as a real list bubble in the conversation and in the message logs, and replying to a message while sending a list keeps the quote.
API

List messages

New list field on POST /v1/messages/send — send an interactive single-select list: text becomes the message body and buttonText opens a menu of up to 10 rows grouped in up to 10 sections. Works on official and unofficial numbers with the same request, and the recipient’s selection arrives as a normal inbound reply.
DashboardTemplates

Template insights

Every template already submitted to Meta gains an insights button on the templates list: sent, delivered and read counts with read rate, clicks per button, quality and status badges, and a 7/30/90-day period selector — powered by Meta’s template analytics (enabled automatically on your account the first time you open it).
Dashboard

Business profile preview on official numbers

The block of a number connected via the official Meta API now shows the WhatsApp business profile preview exactly as customers see it — photo, name, phone, description, category and website — with a Compartilhar button that copies the number’s wa.me link.
APITemplatesPayments

Service window API, provider-aware template preview and PIX payments

New endpoint GET /v1/service-window — check whether the 24-hour service window is open or closed for a contact before sending: pass destinationNumber, get { open, windowType, lastInboundAt, expiresAt }. Meta numbers report the real 24h window; unofficial numbers always report open.Template editor preview now renders exactly what the recipient sees for the connected number: the Meta template card on official numbers, and the delivered text + buttons form on unofficial numbers — with live {{variable}} substitution using your example values, and the preview column following the page scroll.PIX payment button (PAYMENT_REQUEST) now works on every number type: official numbers with WhatsApp Pay render the native payment card, and unofficial numbers deliver a “Copiar código Pix” copy button carrying the full BR Code (no length limit).Itemized order card: templates can carry an order block (items, quantities, total and payment method). Unofficial EVO_V2 numbers deliver it as a real order card — accompanied by the template’s text and buttons in a first message when present. Numeric fields (price, quantity, totals) accept a {{variable}} resolved at send time.Also: the “send via API” modal now shows the template name as templateId, and ORDER_DETAILS template submission to Meta is attempted for payments-enabled accounts instead of being refused locally.
API

Message pricing API

New endpoint GET /v1/meta/pricing — the published Meta / WhatsApp per-message rate for a market + currency + category (e.g. ?market=BR&currency=BRL&category=marketing{ "pricePerMessage": 0.3217 }). Call it with no params to list the available markets, currencies and categories, and add ?tiers=1 for the volume tiers. Also available as the meta_pricing_get MCP tool and in the interactive playground.
API

List your message logs over the API

New endpoint GET /v1/messages — page through the messages sent and received by your number without opening the dashboard. Filter by direction, status, date range and phone number, and each row carries the same messageId you already use with GET /v1/messages/{id}, so a list call and a status call line up. Also available as the messages_list MCP tool.
Dashboard

Logs filters, and a badge for the 24-hour window

The Logs screen gained the filter set it was missing: search a phone number in any format, search by template name, and narrow by status, direction, origin and whether the message errored. A new “Janela 24h” badge marks the messages that were sent inside the 24-hour service window — the ones that did not need an approved template — and doubles as a filter, so you can separate free-form traffic from template traffic at a glance. See Logs & Analytics.