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:
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 }] }}Sending an image
Section titled “Sending an image”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 |
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"Sending a file
Section titled “Sending a file”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.
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.
Reading the response
Section titled “Reading the response”| 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:
uncertainmeans 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.deliveryStateis the recipient’s receipt (delivered/read), which arrives later and is separate fromsendState.
Do not poll for delivery. Subscribe.
Section titled “Do not poll for delivery. Subscribe.”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:
POST /devices/{id}/webhookwith a URL you own, one time.- Deliveries arrive signed, with an
x-maukirim-deliveryid that repeats across retries. GET /webhook-deliveriesshows 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.