> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pilotstatus.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Product updates and API changes for the Pilot Status platform.

What's new on the Pilot Status platform — API additions, changes and notable fixes. Subscribe via the RSS icon above.

<Update label="2026-08-26" tags={["API", "Numbers", "Breaking"]}>
  ## 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:**

  ```bash theme={null}
  curl -X POST https://pilotstatus.com.br/v1/numbers \
    -H "x-api-key: $PILOT_STATUS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Support",
      "number": "5511999999999",
      "settings": { "historyImportEnabled": true }
    }'
  ```

  `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.
</Update>

<Update label="2026-08-25" tags={["API", "Numbers", "Webhooks"]}>
  ## Turn WhatsApp Channels off on a number: `ignoreNewsletters`

  **Documented.** `settings.ignoreNewsletters` on [`PATCH /v1/numbers/{id}`](/api/numbers/settings) 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.

  ```bash theme={null}
  curl -X PATCH https://pilotstatus.com.br/v1/numbers/num_01HZX... \
    -H "x-api-key: $PILOT_STATUS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "settings": { "ignoreNewsletters": true } }'
  ```

  **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.
</Update>

<Update label="2026-08-24" tags={["API", "Messages", "Media"]}>
  ## 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:

  ```json theme={null}
  {
    "error": "A video cannot be sent as a document on the official WhatsApp API — Meta accepts no video type in the document class. Send it with mediaType \"video\" (up to 16 MB); above that, compress it or send a link.",
    "code": "META_VIDEO_AS_DOCUMENT_NOT_SUPPORTED"
  }
  ```

  `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](/guides/media-messages).
</Update>

<Update label="2026-08-18" tags={["API", "Messages"]}>
  ## Send stickers: `mediaType: "sticker"`

  **New.** The direct-media mode of [`POST /v1/messages/send`](/api/messages/send-media) 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.

  ```json theme={null}
  {
    "destinationNumber": "+5511999999999",
    "media": "https://cdn.example.com/sticker.webp",
    "mediaType": "sticker"
  }
  ```

  **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](/guides/media-messages#stickers).

  **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.
</Update>

<Update label="2026-08-18" tags={["API", "Ads"]}>
  ## 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`.

  <Warning>
    **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.
  </Warning>

  **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](/api/referrals).
</Update>

<Update label="2026-08-17" tags={["Connect", "Embed"]}>
  ## 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](/guides/embedded-signup) and [Button mode](/integrations/embed-connect#button-mode).
</Update>

<Update label="2026-08-17" tags={["Webhooks", "Docs"]}>
  ## Every webhook event, in one place — including Meta's

  [Webhook events](/api/webhooks/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`.
</Update>

<Update label="2026-08-14" tags={["API", "Webhooks"]}>
  ## `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.
</Update>

<Update label="2026-08-07" tags={["API", "Webhooks", "Dashboard"]}>
  ## 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](/api/numbers/settings). 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.
</Update>

<Update label="2026-07-31" tags={["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`](/api/numbers/logout) reference.
</Update>

<Update label="2026-07-29" tags={["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`](/api/messages/send-interactive) reference.
</Update>

<Update label="2026-07-23" tags={["API", "Dashboard"]}>
  ## Carousel: editor preview, variables, and the OpenAPI

  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`](/api/messages/send-interactive), 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.
</Update>

<Update label="2026-07-20" tags={["API", "Dashboard"]}>
  ## Carousel messages

  Send a **carousel** — a horizontally scrollable set of media cards — in a single message. A new `carousel` field on [`POST /v1/messages/send`](/api/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.
</Update>

<Update label="2026-07-20" tags={["API"]}>
  ## Template deletion reports the truth

  [`DELETE /v1/templates/{id}`](/api/templates) 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 }`.
</Update>

<Update label="2026-07-20" tags={["Dashboard", "Chat"]}>
  ## 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.
</Update>

<Update label="2026-07-20" tags={["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.
</Update>

<Update label="2026-07-20" tags={["Fix"]}>
  ## URL-button links and template variable counts

  **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.
</Update>

<Update label="2026-07-20" tags={["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.
</Update>

<Update label="2026-07-20" tags={["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](/api/authentication).
</Update>

<Update label="2026-07-20" tags={["Dashboard", "Webhooks"]}>
  ## 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.
</Update>

<Update label="2026-07-19" tags={["Dashboard", "Templates"]}>
  ## 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.
</Update>

<Update label="2026-07-19" tags={["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.
</Update>

<Update label="2026-07-19" tags={["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.
</Update>

<Update label="2026-07-18" tags={["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.
</Update>

<Update label="2026-07-18" tags={["API"]}>
  ## List messages

  **New `list` field** on [`POST /v1/messages/send`](/api/messages/send#lists) — 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.
</Update>

<Update label="2026-07-18" tags={["Dashboard", "Templates"]}>
  ## 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).
</Update>

<Update label="2026-07-18" tags={["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.
</Update>

<Update label="2026-07-18" tags={["API", "Templates", "Payments"]}>
  ## Service window API, provider-aware template preview and PIX payments

  **New endpoint** [`GET /v1/service-window`](/playground/messages/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.
</Update>

<Update label="2026-07-13" tags={["API"]}>
  ## Message pricing API

  **New endpoint** [`GET /v1/meta/pricing`](/api/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`](/api/pricing) MCP tool and in the [interactive playground](/playground/pricing/get-message-rates).
</Update>

<Update label="2026-07-07" tags={["API"]}>
  ## List your message logs over the API

  **New endpoint** [`GET /v1/messages`](/api/messages/list) — 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}`](/api/messages/status), so a list call and a status call line up. Also available as the [`messages_list`](/api/messages/list) MCP tool.
</Update>

<Update label="2026-07-07" tags={["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](/dashboard/logs-analytics).
</Update>
