Skip to main content
Use these methods for one-to-one and group conversation activity after a number is connected.

Send a message

Send text, media, interactive, location, contact, or native Flow content.

Message state

Mark messages as seen, react, edit, delete, archive, or show typing state.

Media

Inspect, upload, download, or persist media.

Presence and calls

Observe presence where enabled and control supported call actions.

Message content

Send conversation and a content object containing exactly one message kind. Do not send type or place message fields at the request root.
For text, use "content": { "text": "Hello" }. For media, put its URL or base64 bytes and caption inside content.image, content.video, content.file, or content.voice. Supply exactly one media source. mentions, quotedMessage, and isForwarded remain alongside conversation and content. Requests with missing content, multiple message kinds, or misplaced fields fail validation. requestPhoneNumber requires the contact’s conversation.id.

Interactive message request rules

Use one of content.product, content.productList, content.order, content.list, content.buttons, content.addressMessage, or content.flow. Products and product lists use businessOwnerId; orders use sellerId. These are public identity IDs for a business with a known phone number. Catalog responses expose the owner’s public identity ID in belongsTo. Product and collection IDs remain their original commerce identifiers. For content.addressMessage, requirements depend on the connected backend. Meta Cloud API sessions require content.addressMessage.body and a two-letter uppercase content.addressMessage.country code. Linked Device API sessions require content.addressMessage.body and a nonblank content.addressMessage.buttonText call to action. The method page is the request contract. Open its playground for parameters, schemas, responses, and raw cURL. Verified SDK tabs appear only when the corresponding client method exists.

Conversation identity

Send with conversation.id, conversation.phoneNumber, or conversation.bsuid. At least one is required. If you supply several selectors, they must already identify the same conversation. Supplying aliases does not link accounts. IDs are opaque decimal strings scoped to the connected Number. Keep them as strings. BSUIDs are scoped to the connected business portfolio. A Linked Device connection requires an already known routable identity for a BSUID. Phone numbers use E.164, including +. A username alone is not a send destination. On a Meta Cloud API number, a conversation known only by its BSUID is sent to that BSUID. When a phone number is also known for the conversation, the message goes to the phone number. Reply to a user who messaged you without a visible phone number by sending to the received conversation.id. WhatsApp does not deliver one-tap, zero-tap, or copy-code authentication templates to a BSUID; those sends fail with invalid_parameter. Received messages identify their conversation with id and any available phoneNumber, bsuid, or username. Received group messages include the author under conversation.sender, with id and any available phoneNumber, bsuid, or username. Send requests and send results omit sender. A hidden phone number is omitted; a BSUID is never converted into a Linked Device user ID. Message actions such as marking a message seen, reacting, and starring use id for the message identifier and conversation for its destination:

Message identifiers

Message responses contain a Polymorfa id and a whatsapp_ids object with exact provider references. linked_devices contains the Linked Device reference; official_api contains the Official API reference. Unknown references are omitted. Use id for message actions and quotedMessage.id for replies. Keep every ID as a string. The Polymorfa ID does not encode test mode or connection type. Acknowledgements return messages, an array of { id, whatsapp_ids, whatsapp_id? }. The singular field is a temporary alias and is omitted when its provider reference is unavailable. The earlier whatsapp_id field remains in native message responses and events during the API version migration. Read whatsapp_ids for new integrations. Polymorfa retains these message and routing identifiers with Hosted Message Storage disabled. This does not enable message-content storage. Cloud requests return their result synchronously, including when you send Prefer: respond-async. Linked-device requests that accept the preference return 202 with Preference-Applied: respond-async; their result arrives in command.result. Do not assume a preference was applied without that header. When a message action fails, its docs URL points to the matching failure section on that method’s reference page. result_unknown means the command’s result was not received; it does not prove the action failed. Check resulting events before retrying. A dispatch failure returns command_dispatch_failed with status 503.

Retry sends safely

Send an Idempotency-Key header with a message write to make it safe to retry. Use a new random value, such as a UUID, for each message or action, and send the same value when you retry it. The key is 1 to 255 bytes of UTF-8. These methods accept the header: Other messaging methods ignore the header.
For 24 hours after the first request, Polymorfa answers a request that reuses the key as follows: Keys belong to the credential and API version that sent them. Retry with the same credential and Polymorfa-Version: the same key sent with another API key, project token, client token, or API version is a separate request. Polymorfa records successful results and server errors. A server error such as result_unknown or command_dispatch_failed means the message may have been sent, so a retry with the same key returns that error again instead of sending a second copy. Check message events for the outcome, and use a new key only when you have confirmed that the message was not sent. A 4xx error, session_not_ready, or credential_verification_unavailable means nothing ran; retry with the same key after you fix the cause. A server error from the catalog check on a Graph product or product-list send also means nothing was sent; a retry with the same key runs the send again. Polymorfa keeps the key, a hash of the request, and the response status and error code for 24 hours. It does not keep message content, message IDs, recipients, or the response body, so a retry after a success can’t return the original response. Keep the first response when you receive it, and use message events or webhooks to find a message whose response was lost. If the first request started but its outcome wasn’t recorded, the message might have been sent. The key then returns idempotency_outcome_unknown for 24 hours instead of sending again. Check message events, and use a new key only after you confirm that the message was not sent. In the TypeScript SDK, pass the key in the request options: messages.send(session, body, { idempotencyKey }). The SDK retries a request that carries a key after a network failure or a retryable status, using the same key. If the first attempt succeeded but its response was lost, the retry fails with idempotency_completed. See SDKs.