Quickstart
This page assumes you have an API key with the otp and notification scopes and a positive credit
balance. If not, read Authentication first.
Set two shell variables for the examples:
export BASE="https://app.maukirim.com/api/v1"export KEY="mk_live_…"1. Send an OTP
Section titled “1. Send an OTP”curl -sS -X POST "$BASE/otp/send" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: signup-$USER_ID" \ -d "{\"phone\": \"$PHONE\", \"purpose\": \"signup\"}"{ "ok": true, "challengeId": "otp_9k2m…", "batchId": "gwb_…", "expiresAt": "2026-09-28T13:20:00.000Z" }phonemust be E.164 (+and 7–15 digits). Spaces, dots, dashes and parentheses are stripped for you, so+62 812-3456-7890works.purposeis one oflogin,signup,payment,passwordReset,phoneChange.- The code is valid for 5 minutes and is never returned by the API — it goes to the user’s WhatsApp.
Idempotency-Keyis required. Reuse the same key to retry safely; a different body with the same key is refused with409 batch_idempotency_conflict.
Store challengeId against your user, then ask them for the six digits.
2. Verify what they typed
Section titled “2. Verify what they typed”curl -sS -X POST "$BASE/otp/verify" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d '{"challengeId": "otp_9k2m…", "code": "481902"}'{ "ok": true, "verified": true }A wrong code is not an error. The response is 200 with "verified": false — that is what your login
form should branch on. Expired, already-used, or out-of-attempts codes are all verified: false too. Only
a malformed request or an unknown challengeId fails.
3. Send a notification
Section titled “3. Send a notification”Notifications use a template that an operator approved for your account. Templates are managed in the
dashboard; POST /messages/send names one by id and supplies its variables.
curl -sS -X POST "$BASE/messages/send" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-8821-shipped" \ -d '{ "phone": "+6281234567890", "templateId": "tpl_…", "variables": { "name": "Rizky", "order": "8821" } }'{ "ok": true, "batchId": "gwb_…" }variables must match the template’s placeholders exactly — same names, same count — or the request is
refused with 400 invalid_input. Scalar values are strings of up to 64 characters matching letters,
digits and _ .,:!{}@+/-()&; invoice numbers, common email addresses, names with parentheses, and
company names with ampersands are preserved. A reviewed list section instead takes an array of item
objects with the same string-value limits; see Send a notification for
the section syntax.
4. Send from a number you rented
Section titled “4. Send from a number you rented”Rented numbers are operated under /devices. List the ones your account rents:
curl -sS "$BASE/devices" -H "Authorization: Bearer $KEY"Then send from one of them (scope send):
curl -sS -X POST "$BASE/devices/gwd_…/messages" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: reply-$CONVERSATION_ID" \ -d '{"phone": "+6281234567890", "message": "Your order is on the way."}'{ "ok": true, "batchId": "gwb_…", "created": true, "progress": { "total": 1, "counts": { "queued": 1 }, "terminal": false } }The send is queued and dispatched by MauKirim’s worker; delivery events come to your webhook. See Send a message from your number.
Where to go next
Section titled “Where to go next”- Idempotency — how retries stay safe, and why the header is not optional.
- Errors — every code, with the condition that produces it.
- Webhooks — signature verification and the delivery log.