Guide

Send messages

Message customers on WhatsApp, Instagram and Messenger, manage WhatsApp templates, send campaigns and login codes.

Everything here uses the same token as the rest of the API (a workspace key mik_… or an app's mat_…). Find the channel ids with GET https://api.warid.chat/api/v1/channels.

The 24-hour rule

WhatsApp, Instagram and Messenger only accept normal messages (text and images) within 24 hours of the customer's last message. After that:

  • WhatsApp: send an approved template. Templates are also the only way to message someone first.
  • Instagram and Messenger: wait until the customer writes again.

If the window is closed, the API answers 422 with "error": "window_closed" and sends nothing. Each conversation's windowExpiresAt tells you when it closes.

Send a message

Reply in a conversation with text, an image (a public JPEG or PNG link, up to 5 MB), or both:

Terminal
curl -X POST https://api.warid.chat/api/v1/conversations/CONVERSATION_ID/messages \  -H "Authorization: Bearer $META_INBOX_KEY" -H "Content-Type: application/json" \  -d '{"text": "Here is your receipt", "imageUrl": "https://yourapp.com/receipts/1042.png"}'

Or skip looking up the conversation. Send to a contact on any channel, or to a phone number on a WhatsApp channel:

Terminal
# A contact (their latest conversation, or the one on channelId)curl -X POST https://api.warid.chat/api/v1/messages \  -H "Authorization: Bearer $META_INBOX_KEY" -H "Content-Type: application/json" \  -d '{"contactId": "CONTACT_ID", "text": "Your table is ready"}' # A phone number, first message on WhatsApp: use a templatecurl -X POST https://api.warid.chat/api/v1/messages \  -H "Authorization: Bearer $META_INBOX_KEY" -H "Content-Type: application/json" \  -d '{"channelId": "CHANNEL_ID", "to": "+9647701234567", "template": {"name": "order_ready", "language": "en_US", "params": ["Sara", "#1042"]}}'

The response is the stored message. status is sent, then moves to delivered and read (listen for message.updated webhooks); failed comes with an error. Messages from the API show in the inbox and don't pause the AI agent.

WhatsApp templates

A template is a message Meta approved in advance. Templates belong to the WhatsApp account behind a number, so numbers on one account share them. Businesses can also manage them in Channels → Templates.

List them (scope templates:read). params is how many values a send needs, one per entry in vars: the body's blanks, then the title's, then each link button's; only APPROVED ones can be sent.

Terminal
curl https://api.warid.chat/api/v1/channels/CHANNEL_ID/templates -H "Authorization: Bearer $META_INBOX_KEY"
Response
[  {    "id": "1273849204",    "name": "order_ready",    "language": "en_US",    "category": "UTILITY",    "status": "APPROVED",    "rejectedReason": null,    "body": "Hi {{1}}, your order {{2}} is ready.",    "params": 2,    "vars": [{ "key": "1", "part": "body" }, { "key": "2", "part": "body" }],    "header": null,    "footer": null,    "buttons": []  }]

Create one (scope templates:write). Put {{1}}, {{2}}, … where values go and give an example for each:

Terminal
curl -X POST https://api.warid.chat/api/v1/channels/CHANNEL_ID/templates \  -H "Authorization: Bearer $META_INBOX_KEY" -H "Content-Type: application/json" \  -d '{    "name": "order_ready",    "language": "en_US",    "category": "UTILITY",    "body": "Hi {{1}}, your order {{2}} is ready.",    "examples": ["Sara", "#1042"],    "buttons": [{ "type": "url", "text": "Track order", "url": "https://yourapp.com/orders" }]  }'
  • name: lowercase letters, numbers and underscores.
  • category: UTILITY (orders, bookings, reminders), MARKETING (offers, news) or AUTHENTICATION (login codes, see below).
  • Optional: header and footer (short text lines), up to 10 buttons (quick_reply, url or phone).

It starts as PENDING. Meta usually reviews it within minutes; then it's APPROVED or REJECTED with a rejectedReason. Check with GET …/templates?name=order_ready, or subscribe a webhook to template.updated.

Delete one with DELETE https://api.warid.chat/api/v1/channels/CHANNEL_ID/templates/order_ready. It's removed in every language, and Meta blocks the name for 30 days.

Campaigns

Send one approved WhatsApp template to many customers, now or later, and follow how far each message got. Scopes campaigns:write to send and campaigns:read for results. WhatsApp channels only: Instagram and Messenger don't allow marketing broadcasts. Businesses can also do all of this from Campaigns in the dashboard.

Terminal
# Everyone tagged vip, with their name in {{1}}curl -X POST https://api.warid.chat/api/v1/campaigns \  -H "Authorization: Bearer $META_INBOX_KEY" -H "Content-Type: application/json" \  -d '{"channelId": "CHANNEL_ID", "name": "Eid sale", "template": {"name": "eid_offer", "language": "en"}, "params": [{"field": "name", "fallback": "there"}, {"text": "EID20"}], "audience": {"type": "tags", "tags": ["vip"]}}'
  • audience: { "type": "tags", "tags": [...] } (conversations on that number with any of the tags), { "type": "everyone" } (everyone who chatted on it), or { "type": "list", "recipients": [{ "phone": "+9647701234567", "name": "Sara", "values": { "code": "SARA10" } }] } for your own list, up to 10,000 people.
  • params: one per entry in the template's vars, in order. { "text": "EID20" } is the same for everyone; { "field": "name" } is the customer's name and { "field": "code" } reads their values.code. Add a fallback for people without one, or they're skipped.
  • headerMedia: { "link": "https://…" } when the template has a picture, video or file on top (headerFormat in the template list).
  • scheduledAt: an ISO time up to 30 days ahead. Leave it out to send now.

Customers who reply STOP (or *إيقاف*) are opted out and skipped from then on; START opts them back in. You can set it yourself with PATCH /contacts/{id} and { "optedOut": true }.

Messages go out at about 20 a second and land in each customer's conversation, so replies show up in the inbox. Follow along with GET https://api.warid.chat/api/v1/campaigns/CAMPAIGN_ID:

Response
{  "id": "…",  "status": "sent",  "stats": { "total": 1200, "queued": 0, "sent": 1180, "delivered": 1150, "read": 820, "replied": 96, "failed": 12, "skipped": 8 }}

status is scheduled, sending, paused (out of credit: error is no_credit, and it carries on by itself after a top-up), sent, canceled, or failed when WhatsApp stopped it early (error has its code, e.g. 132015 for a paused template). GET …/campaigns/CAMPAIGN_ID/recipients?status=failed lists people with a reason: opted_out, invalid_phone, missing_value, canceled, or WhatsApp's code (131049 means WhatsApp held back a marketing message to that person). POST …/campaigns/CAMPAIGN_ID/cancel stops one that hasn't finished. Subscribe a webhook to campaign.updated to hear when it starts and finishes.

Verification codes (OTP)

Send a one-time code on WhatsApp and check what the user types. We make the code, send it, keep only a hash, and expire it. Scope otp:send, WhatsApp channels only.

1. Create the template once. Meta writes the text ("123456 is your verification code.") and adds a Copy code button:

Terminal
curl -X POST https://api.warid.chat/api/v1/channels/CHANNEL_ID/templates \  -H "Authorization: Bearer $META_INBOX_KEY" -H "Content-Type: application/json" \  -d '{"name": "login_code", "language": "en", "category": "AUTHENTICATION", "codeExpiresMinutes": 10}'

Wait until it's APPROVED. (Or click Verification codes in Channels → Templates.)

2. Send a code when the user asks to sign in:

Terminal
curl -X POST https://api.warid.chat/api/v1/otp \  -H "Authorization: Bearer $META_INBOX_KEY" -H "Content-Type: application/json" \  -d '{"channelId": "CHANNEL_ID", "to": "+9647701234567"}'
Response
{  "id": "6f1c2a7e-0b7d-4e57-9a51-2f0c9e3b8d11",  "to": "+9647701234567",  "channelId": "CHANNEL_ID",  "expiresAt": "2026-10-16T12:10:00.000Z",  "template": { "name": "login_code", "language": "en" }}

Options: length (4–8 digits, default 6), expiresInMinutes (1–30, default 10), and template ({ "name": "login_code", "language": "ar" }) to pick one when you have several. The first approved authentication template is used otherwise.

3. Check the code the user typed:

Terminal
curl -X POST https://api.warid.chat/api/v1/otp/verify \  -H "Authorization: Bearer $META_INBOX_KEY" -H "Content-Type: application/json" \  -d '{"to": "+9647701234567", "code": "482913"}'
Response
{ "valid": true, "id": "6f1c2a7e-0b7d-4e57-9a51-2f0c9e3b8d11", "to": "+9647701234567" }

A wrong or old code returns 200 with "valid": false and a reason: wrong_code (with attemptsLeft), expired, already_used, too_many_attempts or not_found. Sign the user in only when valid is true.

Rules that keep it safe:

  • Each code works once. Sending a new code to a number ends the previous one.
  • 5 wrong tries end a code; send a new one.
  • Per number: one code every 30 seconds and 5 an hour. Over that, 429 with otp_too_soon or otp_rate_limited and a Retry-After header (seconds).
  • Codes aren't shown in the inbox.

Errors

StatuserrorWhat to do
422window_closedMore than 24 hours since the customer wrote. On WhatsApp send a template.
422template_not_foundNo approved template with that name and language.
422template_params_mismatchSend exactly params values, none empty.
422templates_need_whatsappTemplates and codes only work on official WhatsApp channels.
422template_rejectedMeta refused the new template; message says why (often a duplicate name).
422examples_must_match_paramsGive one example per {{n}} in the body.
422otp_template_missingCreate an approved AUTHENTICATION template first.
422invalid_phoneUse international format, e.g. +9647701234567.
422no_conversationThe contact has no conversation (on that channel).
422no_recipientsNobody in the audience can get it (all opted out, invalid or missing a value).
422header_media_requiredThe template has a picture, video or file on top; send headerMedia.link.
409campaign_finishedThe campaign already finished, so it can't be canceled.
429otp_too_soon, otp_rate_limitedWait Retry-After seconds.
502send_failed, whatsapp_unavailableWhatsApp didn't accept the request; message has its reason when there is one.