Docs · API v1
Sending
Loods sends a chat message only when you ask for exactly that message, from a business number you connected. It never sends on its own, and it never sends mail.
Send a message
POST /v1/whatsapp/messages sends from a channel's number to to, in E.164. There are three ways to ask:
text, with up to three replybuttons, when the customer wrote to you in the last 24 hours. Up to 4096 characters; a button title is up to 20.template, an approved template with itsparameters, when they didn't.- Both. The text goes, and if the platform says the 24-hour window is shut, the template goes instead, once.
shape in the answer says what really went out: text, template or flow. Keep the id: the whatsapp.status events about this message carry it.
/v1/whatsapp/messagescurl https://loods.dev/v1/whatsapp/messages \
-H "Authorization: Bearer $LOODS_KEY" \
-H "Idempotency-Key: act-812" \
-H "Content-Type: application/json" -d '{
"channel": "whatsapp-main",
"to": "+31623456789",
"text": "Hoi Marieke, …",
"template": {
"name": "mail_notice",
"language": "nl",
"parameters": ["Marieke", "Voorbeeld", "uw offerte"]
}
}'
{ "id": "wamid.HBgLMzE2MjM0NTY3ODkVAgARGBI", "shape": "template", "channel": { "id": "ch_01k5twa3c4d5e6f7g8h9j0k1m2", "ref": "whatsapp-main", "type": "whatsapp" }, "to": "+31623456789" }
Idempotency
Every send needs an Idempotency-Key header, up to 255 characters. Use the id of the action a person confirmed, so a retry can never send twice. Loods remembers a key and its answer for a day.
| You send | Loods answers |
|---|---|
| The same key with the same body | The first answer again. Nothing is sent. |
| The same key with another body | 409 idempotency_key_reused |
| The same key while the first is still being handled | 409 request_in_progress |
| No key | 400 idempotency_key_required |
A key keeps its answer, except when nothing was sent: the platform refused the message, or Loods itself failed with a 500 or 503. Then the key is free, and the same request with the same key sends again.
So a retry with the same key is always safe. If it answers the same 502 with the header Idempotent-Replayed: true, the first attempt kept the key, because the message may have gone out: the platform couldn't be reached, or failed on its own side. Wait for a whatsapp.status event before you send it under a new key.
Forms
A template can carry a form button. Give template.flow a token of your own, and optional data to start the form with.
When the customer completes the form, it arrives as a whatsapp.received event with flow.token set to your token and the answers in flow.response.
/v1/whatsapp/messagesForm{ "channel": "whatsapp-main", "to": "+31623456789", "template": { "name": "quote_confirm", "language": "nl", "parameters": ["Marieke"], "flow": { "token": "tok-abc", "data": { "quote": "Q-2026-118" } } } }
When it fails
If the platform refuses the message, the answer is a 502 with the code whatsapp_error. meta_code holds the platform's own error code, when it gave one. Nothing was sent, so fix the request and send it again with the same Idempotency-Key.
A message the platform accepted can still fail later. That arrives as a whatsapp.status event with "status": "failed".
Media
GET /v1/whatsapp/media/{media_id}?channel={ref} streams the bytes of a photo, video, document or voice note from a whatsapp.received event, under the type the platform gives.
Loods keeps none of it. Media ids expire at the platform after about 30 days, so save what you need.
/v1/whatsapp/media/{media_id}curl "https://loods.dev/v1/whatsapp/media/1234567890123456?channel=whatsapp-main" \ -H "Authorization: Bearer $LOODS_KEY" -o achtergevel.jpg