WA Gatewayby SUPATMINI Sign in

API documentation

Send WhatsApp messages and verify phone numbers from your app through the WhatsApp Cloud API.

Base URL https://wa.supatmini.com

Getting started

  1. 1

    Add a device and connect your WhatsApp Business number with Embedded Signup.

  2. 2

    Create a token on the device page, then copy it.

  3. 3

    Call the API below with that token.

You can also read and reply to chats by hand in the Console tab of each device.

Authentication

Send the token in the header. Each token belongs to one device.

Authorization: Bearer <token>
401Invalid token
403Token is not linked to a device
503Device is not connected
GET/api/status— check the device before sending
{"id":8,"device":"main-store","phone":"628…","channel":"cloud","status":"connected","connected":true}

Send messages

order updates, reminders, replies

For regular messages. You can add a random delay in seconds, prevent duplicates with ref, and check the status at any time.

POST/api/messages
toRecipient number, e.g. 628123456789 (a leading 0 becomes 62)
textMessage text, up to 4,096 characters
delay_minOptional. Minimum delay before sending (seconds, decimals allowed)
delay_maxOptional. Maximum delay. The delay is random between the two; use the same value for an exact delay. Up to 300 seconds
refOptional, recommended. Your unique key (up to 120 characters). The same ref = the same message, never sent twice
curl -X POST https://wa.supatmini.com/api/messages \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"to":"628123456789","text":"We received the payment for order QL-123","delay_min":5,"delay_max":10,"ref":"order-123:paid"}'

# 202 — accepted, waiting for the delay
{"id":42,"status":"queued","code":"","to":"628123456789","send_after":"2026-…","sent_at":"","duplikat":false,…}

# 200 — this ref was used before: the original message is returned, not sent again
{"id":42,"status":"sent",…,"duplikat":true}

# 422 — invalid number, not recorded and never sent to WhatsApp
{"error":"…","code":"nomor_tidak_valid"}
GET/api/messages/42— status of one message
queuedWaiting for its delay
sentAccepted by WhatsApp (sent_at is set)
failedNot sent. code says why: gagal_kirim (WhatsApp rejected it), antrean_penuh (queue full), perangkat_terputus (device disconnected), kedaluwarsa (held for more than 30 minutes), terputus (the gateway stopped while sending — not retried, to avoid duplicates)

Who decides what

Your appWhen and whether to send: the schedule ("remind 2 minutes after checkout"), re-checking the situation right before sending (paid? cancelled?), the recipient, the text, and one message per event (ref)
WA GatewayHow to send it: the delay in seconds, the send queue, delivery through the WhatsApp Cloud API, and the final status
Delays here are for seconds, so messages don't feel robotic. Keep longer schedules in your app: WA Gateway can't know whether the order is paid when the time comes.

Verification codes

Sends a one-time code that the user types into your app. On WhatsApp Cloud API numbers the code is sent with your approved AUTHENTICATION template, which also works outside the 24-hour window. Meta charges for each authentication message, so this is off by default: turn it on in Overview → WABA credentials.

POST/api/send
toRecipient number, e.g. 628123456789
textMessage text containing the code
otp_codeThe code itself — filled into the template
curl -X POST https://wa.supatmini.com/api/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to":"628123456789","text":"Your code: 4821","otp_code":"4821"}'

# 200
{"queued":true,"channel":"cloud","to":"628123456789"}

Prefer the next section when you can: the user messages you first, so no code and no template are needed.

Verification: the user messages first

User

opens wa_link, sends

message + token

WA Gateway

sender matches → verified

check status

Your app

lets the user in

The message comes from the number itself — that is the proof. No code to type.

POST/api/verify/start
phoneThe number to verify
success_textOptional. Automatic reply when verification succeeds
prefillOptional. Ready-to-send message text; may contain {token}
curl -X POST https://wa.supatmini.com/api/verify/start \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"phone":"628123456789","prefill":"Verify my account: {token}"}'

# 200
{"token":"AB2CD","wa_number":"628999…","wa_link":"https://wa.me/628999…?text=…","expires_in":600}
GET/api/verify/status?token=AB2CD
{"status":"pending"}    # not yet
{"status":"verified"}   # success
{"status":"expired"}    # expired
{"status":"not_found"}
Only let the user in after verified is confirmed by your own server.

Webhooks

set on the device page

Verification succeeded

POST your-webhook-URL
{"event":"verify.verified","token":"AB2CD","phone":"628…","device_id":1,"at":"2026-…"}

Not signed. Treat it as a notification only: re-check with /api/verify/status before letting the user in.

Incoming message — only when a secret key is set

POST your-webhook-URL
X-Qwa-Signature: sha256=<HMAC-SHA256 of the body, using the secret key>

{"event":"message.in","device_id":1,"from":"628…","text":"Hi, has my order shipped?","at":"2026-…"}

Reject requests whose signature doesn't match. To reply, answer within 15 seconds:

{"reply":"Yes, the tracking number is JX123"}

# with follow-up messages (optional)
{"reply":"One moment","replies":[{"text":"The tracking number is JX123","delay_seconds":12}]}

# no reply
{"reply":""}

Up to 3 messages, at most 60 seconds apart, 4,000 characters each.

Laravel example

// .env → WA_GATEWAY_URL, WA_GATEWAY_TOKEN, WA_GATEWAY_SECRET
$wa = Http::withToken(env('WA_GATEWAY_TOKEN'))->baseUrl(env('WA_GATEWAY_URL'));

// Send an order update (best done in a queued job)
$wa->post('/api/messages', ['to' => $phone, 'text' => "Order $number has shipped", 'ref' => "order-$number:shipped"]);

// Verification: the user messages first
$v = $wa->post('/api/verify/start', ['phone' => $phone])->json();
// show $v['wa_link'], then check:
$ok = $wa->get('/api/verify/status', ['token' => $v['token']])->json('status') === 'verified';

// Receive incoming messages
Route::post('/webhook/wa', function (Request $r) {
    $sig = 'sha256=' . hash_hmac('sha256', $r->getContent(), env('WA_GATEWAY_SECRET'));
    abort_unless(hash_equals($sig, (string) $r->header('X-Qwa-Signature')), 401);
    return ['reply' => 'Thank you, we have received your message.'];
});

WhatsApp rules

Messages go through the official WhatsApp Cloud API, so WhatsApp's rules apply:

  • 24-hour window. Free-form messages are only delivered within 24 hours of the customer's last message to you. Outside that window WhatsApp requires an approved template.
  • Delivery reports. When WhatsApp rejects a message after accepting it, the Console marks it as failed and shows Meta's reason.
  • Opt-in. Only message people who gave you their number and agreed to hear from you.