Skip to content

Send a message from your number

When you rent a number, sending from it is yours: free-form text, images, files, and the number’s own identity on WhatsApp. This is the right tool for conversational replies — where the content depends on what the customer wrote — as opposed to the template-bound notification service.

Send with a key that holds the send scope, and name the device:

Terminal window
curl -X POST https://app.maukirim.com/api/v1/devices/gwd_…/messages \
-H "Authorization: Bearer mk_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reply-8821-1" \
-d '{"phone": "+6281234567890", "message": "Your order is on the way."}'
{
"ok": true,
"batchId": "gwb_…",
"created": true,
"progress": {
"batchId": "gwb_…", "total": 1,
"counts": { "queued": 1, "sending": 0, "accepted": 0, "failed": 0, "uncertain": 0, "cancelled": 0 },
"terminal": false,
"items": [{ "id": "gwi_…", "recipient": "+6281234567890", "kind": "text",
"sendState": "queued", "deliveryState": null, "attempts": 0,
"messageId": null, "errorCode": null, "completedAt": null }]
}
}

Send multipart/form-data instead of JSON. The image is stored privately and streamed to the gateway at dispatch time — it is never a public URL:

Part Required Notes
file yes JPEG or PNG, up to 5 MiB. The bytes are checked against the declared type.
phone yes E.164
caption no Up to 1024 characters; omitted when blank
Terminal window
curl -X POST https://app.maukirim.com/api/v1/devices/gwd_…/messages \
-H "Authorization: Bearer mk_live_…" \
-H "Idempotency-Key: reply-8821-2" \
-F "file=@receipt.png;type=image/png" \
-F "phone=+6281234567890" \
-F "caption=Your receipt"

The same call with a document instead of an image: no field says which it is, because the file’s own content type decides. An image is rendered inline with its caption; a document arrives as a file the recipient opens, under the name you send it with — so send Invoice-8821.pdf, not upload.pdf.

Terminal window
curl -X POST https://app.maukirim.com/api/v1/devices/gwd_…/messages \
-H "Authorization: Bearer mk_live_…" -H "Idempotency-Key: invoice-8821-1" \
-F "file=@Invoice-8821.pdf;type=application/pdf" \
-F "phone=+6281234567890" -F "caption=Your invoice"

Accepted named files are PDF, DOCX, XLSX, PPTX, CSV, TXT, ZIP, WebP, GIF, MP3, ICP and ICS, up to 16 MiB. WebP, GIF and MP3 arrive as files, not inline images or voice notes; their filenames must end in .webp, .gif and .mp3 respectively. Use text/calendar for .ics; .icp has no registered MIME/signature, so use application/octet-stream and a filename ending in .icp. The response adds "media": { "id": "gwm_…" } for a multipart file send.

A successful image send adds "media": { "id": "gwm_…" } to the response. Uploaded media expires after seven days if it is never attached to a send.

Field Meaning
batchId The send attempt. One request is one batch, even for one recipient.
created false when the Idempotency-Key replayed an identical request — the existing batch came back instead of a new send.
progress A snapshot taken when the response was built: state counts, and per-item states.
progress.terminal true once every item has finished — see below for why you should not poll for this.

Item states you will see: queued → sending → accepted (the gateway took it) or failed. Two states deserve respect:

  • uncertain means the attempt may have reached WhatsApp — a lease expired or a transport timed out. It is neither a failure nor a success, and it is never retried automatically. Decide per message whether a duplicate is worse than a miss.
  • deliveryState is the recipient’s receipt (delivered/read), which arrives later and is separate from sendState.

There is no batch-status endpoint on the public API — the progress object in the send response is a snapshot, not a subscription. Delivery state is pushed:

  1. POST /devices/{id}/webhook with a URL you own, one time.
  2. Deliveries arrive signed, with an x-maukirim-delivery id that repeats across retries.
  3. GET /webhook-deliveries shows what MauKirim forwarded and what your endpoint answered — the fastest way to answer “did it reach my server, and what did I reply?”.

Read Webhooks for the signature scheme and the retry schedule.

Why a message can be accepted yet not delivered

Section titled “Why a message can be accepted yet not delivered”

sendState: accepted means WhatsApp accepted the message, not that the recipient’s phone received it. Their device can be off, their data can be off, or the number can simply not be on WhatsApp. The deliveryState on a later event is the honest answer, and the delivery log is where you see it.