Skip to content

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:

Terminal window
export BASE="https://app.maukirim.com/api/v1"
export KEY="mk_live_…"
Terminal window
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" }
  • phone must be E.164 (+ and 7–15 digits). Spaces, dots, dashes and parentheses are stripped for you, so +62 812-3456-7890 works.
  • purpose is one of login, 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-Key is required. Reuse the same key to retry safely; a different body with the same key is refused with 409 batch_idempotency_conflict.

Store challengeId against your user, then ask them for the six digits.

Terminal window
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.

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.

Terminal window
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.

Rented numbers are operated under /devices. List the ones your account rents:

Terminal window
curl -sS "$BASE/devices" -H "Authorization: Bearer $KEY"

Then send from one of them (scope send):

Terminal window
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.

  • 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.