Before you begin
- Connect and register the WhatsApp business phone numbers that will send messages.
- Store your YCloud API key on the server.
- Configure a signed webhook endpoint for
whatsapp.message.updated. - Define how your system records consent, opt-outs, message purpose, and retention.
- Assign owners for sending, webhook processing, and incident response.
Choose the sending endpoint
Use the queued endpoint by default. Use direct sending only when the application must know whether WhatsApp accepted the submission before continuing.
A successful response from either endpoint is not proof of delivery. Store the
returned message
id and use whatsapp.message.updated events to learn whether
the message is sent, failed, delivered, or read.
Create one internal send record
Create a durable record before calling the API. Give the record a unique business key, such as an order event ID plus the message purpose. Enforce that uniqueness in your database so concurrent workers cannot send the same business event twice. Record at least:
Use an opaque
externalId that does not contain message content or personal
data. The API recommends a unique value, but externalId is a reference field.
It is not a server-side idempotency key and does not make repeated POST
requests safe.
Connect the response to status webhooks
The following example uses the same identifiers through the send workflow.1. Send the message
2. Store the accepted response
MESSAGE_ID, accepted, and the response time to the existing internal
record. Do not mark the business notification as delivered.
3. Apply later status events
whatsappMessage.id. Use externalId for business
reconciliation and wamid for provider-side investigation.
Build a convergent status model
The common progression isaccepted → sent → delivered → read.
failed can occur before or after a sent update. Webhooks can be duplicated,
delayed, or delivered out of order. A read update can also arrive without a
separate delivered event.
Process each event as follows:
- Verify the webhook signature against the raw request body.
- Durably store the event, using event
idas the deduplication key. - Return a
2xxresponse promptly, then process the event asynchronously. - Match
whatsappMessage.idto the internal send record. - Store the event status and its available message timestamps. Keep the raw event metadata needed for an audit, but remove unnecessary message content.
- Update the current business view without discarding conflicting or later evidence. Treat
readas evidence that delivery occurred even when the separatedeliveredevent is absent. - Retrieve
GET /whatsapp/messages/{id}when events conflict, a terminal status is missing beyond your service objective, or the webhook pipeline was unavailable.
Retry without creating duplicate sends
Classify the failure before retrying.
A safe application policy can start with a small number of attempts, exponential
delays, full jitter, and a maximum elapsed time. These are application controls,
not API guarantees. Send exhausted attempts to a review queue instead of
retrying forever.
Before each retry:
- Lock or atomically claim the internal business key.
- Check whether the record already has a YCloud
idor a status event. - Do not use a new
externalIdto hide an earlier ambiguous attempt. - Stop after the configured attempt or age limit.
- Require a deliberate operator action before replaying an ambiguous send.
Choose templates and session messages
Use an approved template when you initiate a business message or send outside the 24-hour customer service window. Select the template category from the user’s reason for receiving the message, and keep its name, language, and variable contract in application configuration. Use text, media, interactive, location, contact, or reaction messages only when the customer service window is open and that content type is allowed. Determine the window from the customer’s most recent message. Do not infer an open window from your last outbound message. See Manage WhatsApp templates for template versioning, approval gates, locales, and rollback.Handle media efficiently
- Validate the file’s supported MIME type and size before upload. Do not retry an oversized or unsupported file unchanged.
- Upload with the business phone number that will send the message.
- Reuse the returned media ID for repeated sends of the same approved asset while it remains valid. Uploaded media persists for 30 days.
- Store the asset checksum, MIME type, media ID, sender, and expiration time so workers do not upload the same file for every recipient.
- Upload again after expiration or when the sender context changes.
- Use a public URL instead when the message schema requires a link, including media in interactive message headers.
- Stream large uploads from storage, set request timeouts, and delete temporary local files after use.
Enforce consent and minimize data
Record the consent source, purpose, time, and permitted channel before sending. Apply the latest valid opt-out across campaigns, transactional workflows where policy requires it, retries, and manual replays. ForPOST /whatsapp/messages, set filterUnsubscribed: true and
filterBlocked: true when the workflow must enforce YCloud suppression lists.
These fields default to false. They do not apply to sendDirectly, so a
direct-send workflow must check suppression before the API call.
Suppression filters are a final safety check, not a replacement for consent.
Store only the identifiers and delivery metadata needed for the stated purpose.
Exclude API keys, template variables, message bodies, and phone numbers from
general application logs. Apply retention and access controls to message and
webhook records.
Control batch throughput
Place batch work in a bounded queue and send through a fixed worker pool. Track concurrency separately by account and business phone number so one sender or tenant cannot consume every worker. Apply backpressure when any of these signals increase:429responses- request latency and timeouts
5xxresponses- queue age or retry backlog
- webhook lag and unresolved
acceptedmessages
accepted to each later status,
queue depth, oldest queue age, retry count, webhook lag, deduplication count,
and reconciliation drift. Alert on sustained changes from your normal baseline,
not on a single failed message.
Common anti-patterns
- Marking a message delivered when the API returns
accepted. - Treating
externalIdas a YCloud idempotency key. - Retrying every non-
2xxresponse or timeout with no attempt limit. - Using
sendDirectlyfor all traffic. - Assuming webhooks are unique, ordered, or complete.
- Sending free-form messages outside the customer service window.
- Uploading the same media file for every recipient.
- Relying on suppression filters without recording consent.
- Logging API keys, full payloads, or unnecessary personal data.
- Starting a batch with unbounded concurrency and no backpressure.
Go-live checklist
- The endpoint choice matches the workload and latency requirement.
- A database uniqueness rule protects the internal business key.
-
externalId, YCloudid, andwamidhave distinct documented roles. - Initial responses remain non-final until status evidence arrives.
- Webhook signatures, event deduplication, fast acknowledgement, and replay are tested.
- A scheduled retrieval job reconciles delayed or missing events.
- Retryable and non-retryable failures have bounded handling paths.
- Template and session-window rules are enforced before sending.
- Media uploads are validated, reused, expired, and cleaned up safely.
- Consent, unsubscribe, block-list, retention, and logging controls are verified.
- Batch queues have concurrency limits, backpressure, dashboards, and alerts.
- Operators can pause sends and review ambiguous attempts without replaying them automatically.
Send a WhatsApp message
Review request types, fields, examples, and response data.
Configure webhooks
Verify signatures and process repeated event deliveries safely.
Upload WhatsApp media
Upload supported media and reuse the returned media ID.
Handle API errors
Parse error responses and apply bounded retries.

