Recipe: send WhatsApp notifications and templates from your website

Last updated: 11 November 2026 · 10 min read

This recipe sends WhatsApp messages to customers from your website, mobile app or CRM: verification codes, orders, deliveries, reminders. Every message is written to the customer's Clomni conversation, so when the customer replies the agent sees what was sent before.

Steps

  1. 1

    What you get

    • A WhatsApp notification with one request from your system. Clomni finds or creates the contact and the conversation itself.
    • Free text while the 24 hour window is open, an approved template when it is closed. Clomni checks the window itself.
    • The message status sent, delivered, read, failed, also by webhook.
    • Notifications do not fill the agents' "Open" list: by default the conversation stays resolved and opens when the customer replies.
  2. 2

    Two kinds of WhatsApp

    • WhatsApp Business API (Cloud): connected with a Meta account. It has the 24 hour window; outside it only an approved template goes out, and Meta charges for it. The /v1/whatsapp/* endpoints work only with this kind.
    • WhatsApp QR: the WhatsApp on a phone is linked by QR code. No window and no templates, but a number that sends a lot can be blocked. /v1/whatsapp/messages does not send from a QR number. On QR you only reply in an open conversation (step 6).
  3. 3

    Scopes you need

    • messages:send.template (high risk): send templates and list them.
    • messages:send (high risk): send text. When you send text together with fallback_template, both scopes are needed.
    • channels:read (low risk): the list of WhatsApp numbers and their channel_id.
    • messages:read (medium risk): check the window and the message status, and the message.* webhooks.

    In the review form describe the daily volume, who creates the messages (a person, a system, AI) and how you collect consent for marketing messages: Review: checking scopes and rejection reasons.

    The WhatsApp API that works with a profile API key (Get started with the WhatsApp API: API key, account_id and channel_id) is a separate product: its paths are /v1/accounts/{account_id}/whatsapp/.... With a developer token the path has no workspace ID: /v1/whatsapp/.... Bulk sending (bulk_messages) exists only in that API.

  4. 4

    1. Find the channel

    curl "https://api.clomni.ai/v1/whatsapp/channels" \
      -H "Authorization: Bearer clomni_pat_xxx"

    Each number in the response has a channel_id and a sendable field. sendable: false is a QR number; this API does not send from it.

  5. 5

    2. Pick a template

    curl "https://api.clomni.ai/v1/whatsapp/templates?channel_id=5" \
      -H "Authorization: Bearer clomni_pat_xxx"

    Only templates approved by Meta (APPROVED) come back. example_parameters is a ready skeleton of the parameters the template expects: copy it and replace the values with your own data. The API does not create templates: create the template in Meta, then sync templates in the WhatsApp channel settings in the dashboard. synced_at shows when the list was last fetched.

  6. 6

    3. Understand the 24 hour window

    The customer opens the window: for 24 hours after they write to you, any text goes out. A template you send does not open the window; the customer's reply or a tap on a quick reply button in the template does. If you send several notifications, put a quick reply button in the first template. To check in advance:

    curl "https://api.clomni.ai/v1/whatsapp/messages/window?channel_id=5&phone_number=%2B994501234567" \
      -H "Authorization: Bearer clomni_pat_xxx"

    open: false with last_inbound_at: null means this number has never written to you: the first message must be a template.

  7. 7

    4. Send

    Verification code (template):

    curl -X POST "https://api.clomni.ai/v1/whatsapp/messages" \
      -H "Authorization: Bearer clomni_pat_xxx" \
      -H "Content-Type: application/json" \
      -d '{"channel_id": 5, "phone_number": "+994501234567", "template": {"name": "verification_code", "language": "az", "parameters": {"body": {"1": "483920"}}}, "contact": {"name": "Leyla Hasanova", "identifier": "crm-10422"}, "source_reference": "otp-10422-1794120000"}'

    Delivery notification (text if the window is open, template if it is closed):

    curl -X POST "https://api.clomni.ai/v1/whatsapp/messages" \
      -H "Authorization: Bearer clomni_pat_xxx" \
      -H "Content-Type: application/json" \
      -d '{"channel_id": 5, "phone_number": "+994501234567", "text": "Your parcel AZ-10422 is with the courier.", "fallback_template": {"name": "cargo_status", "language": "az", "parameters": {"body": {"1": "AZ-10422", "2": "with the courier"}}}}'
    • sent_as in the response says which one went out: session (text) or template. If the window is closed and there is no fallback_template, nothing goes out and 422 comes back, so a paid message never leaves without you knowing.
    • source_reference is your own unique key. Within 24 hours a request with the same key does not create a new message but returns the first one: the customer does not get two codes and Meta is not paid twice.
    • conversation_status defaults to resolved: the conversation does not land in the agents' list. Send open if an agent should see it right away.
    • The number must be international, 8 to 15 digits. Spaces, hyphens and + are removed. 0501234567 is wrong because it has no country code.
    • Template parameters: {"1": "..."} or the names you gave in Meta for the body, media_url and media_type (image, video, document) for the header, url or copy_code for buttons.
  8. 8

    5. Track the status

    status: "sent" means Meta accepted the message, not that it arrived. To read the latest state:

    curl "https://api.clomni.ai/v1/whatsapp/messages/918234" \
      -H "Authorization: Bearer clomni_pat_xxx"

    Statuses: sent, delivered, read, failed (the reason is in external_error). Instead of asking about every message, subscribe to the message.updated webhook. Add message.created to get the customer's reply. See App webhooks and events: setup, topics, signature verification.

  9. 9

    6. Reply in an open conversation (Cloud and QR)

    To write inside a conversation use POST /v1/conversations/{conversation_id}/messages, where {conversation_id} is the number the dashboard shows. This path works on both Cloud and QR. On Cloud, if the window is closed, send template_params, which needs messages:send.template. Sending on this path happens in the background: if the message cannot go out it becomes failed later, with the reason in content_attributes.external_error. For automatic notifications the path in step 4 is simpler.

  10. 10

    Common mistakes

    • /v1/whatsapp/messages is called for a QR number and 422 "This API requires a WhatsApp Cloud API inbox" comes back. Check sendable.
    • The template name or language code is wrong, or the template was not synced in the dashboard: 422 "No approved template named".
    • The number of parameters does not match the template and Meta rejects the message: the status becomes failed. Compare with example_parameters.
    • A send is repeated without source_reference and the customer gets two codes.
    • sent is read as "delivered". Delivery shows as delivered.
    • On QR, 463 or recipient_restricted comes back and is retried automatically. Do not retry: WhatsApp counts each attempt separately and extends the restriction.
    • A first message is sent to a contact whose number is hidden (identifier ends with @lid). The customer has to write first or share their number.
    • Marketing goes out without consent. Complaints lower the number's quality and its daily limit.

    All message endpoints: Scopes and endpoints: conversations and messages.

Document