Notice: AULM is not the official WhatsApp Business Platform
AULM is built on an unofficial WhatsApp Web session layer. We are not affiliated with Meta or WhatsApp and we do not provide the WhatsApp Business API (WABA). Use the service only for legitimate, opt-in communication. Spam, bulk marketing, or unusual sending behaviour may result in your number being restricted by WhatsApp — we do not guarantee delivery, ban-immunity, or Meta approval. For official business use cases, follow Meta's WhatsApp Business API requirements.
Introduction
aulm is a WhatsApp messaging API. You connect a phone by scanning a QR code in the dashboard, then send messages and receive inbound events over HTTP. There is no Meta business verification, no template approval, no WABA.
Why aulm
- Refund on failure. Transient upstream failures refund your quota slot automatically.
- Traceable. Every response carries
x-request-id. Log it; quote it. - Honest rate limits. Send endpoints stamp
retry-afteron every 429. - Signed webhooks. Published retry ladder — delivery is our problem, not yours.
Set up with AI
Developer AI assistants (Claude, ChatGPT, Cursor) work best when given a clear specification. Copy the prompt below into your assistant to get correct, production-ready aulm code for your project immediately.
You are helping me integrate the aulm WhatsApp API into my project...
ABOUT
aulm sends WhatsApp messages via an HTTP API. It is unofficial...
Authentication
Every request takes a Bearer token in the Authorization header. Keys are generated in the dashboard and match the regex ^alm_sk_(live|test)_[0-9a-f]{64}$.
- alm_sk_live_… — real sends. Billable. Counts against your plan quota.
- alm_sk_test_… — every call is simulated. The messaging service is never invoked, no WhatsApp message is sent, the response shape is identical to live with
simulated: trueandmessage_idshapedsim_<hex>. Test sends are never billed.
Keys may also be restricted to specific session IDs. Requests against a non-whitelisted session return 403 session_not_allowed.
Authorization: Bearer alm_sk_live_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefQuickstart — first message in under 60 seconds
Connect a session.
Go to the dashboard → Sessions → New session → scan the QR code with the WhatsApp app on the phone you want to send from. Wait until status is connected.
Grab the session ID and an API key.
Copy the session UUID from the dashboard. Generate an alm_sk_test_… key first — you can ship the same code with alm_sk_live_… later.
Send.
curl -X POST https://api.aulm.io/v1/messages/send-text \ -H "Authorization: Bearer alm_sk_test_..." \ -H "Content-Type: application/json" \ -d '{"session_id":"<SESSION_ID>","to":"+14155551234","body":"Hello!"}'Sessions are dashboard-only.
There is no public API endpoint to create a session, pair it via QR, or disconnect it. All session lifecycle is managed in the aulm dashboard. The API only operates on sessions that are already connected. GET /v1/sessions/{id}/status is read-only.
Base URL & versioning
All endpoints are served from a single base URL. The path is versioned with /v1/. Breaking changes get a new path prefix; additive changes (new optional fields, new response headers) ship under /v1/.
https://api.aulm.ioResponses & request IDs
All /v1/* responses are JSON. Success bodies use { data: ..., request_id: ... }; errors use { error: { code, message } }.
Every response — success or error — sets x-request-id. Log it on your side and quote it to support; we can find the exact send in one query.
Send endpoints additionally stamp x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset (ISO 8601), and retry-after on 429s and on upstream-overload 503s.
Platform headers to ignore
x-deployment-idInjected by the hosting edge on 2xx responses. Not part of the
aulm contract — ignore.
Reliability
When a send fails, two things matter: did it cost you a quota slot, and should you retry the same request. aulm publishes both answers explicitly.
Quota refund matrix
What happens to your reserved quota slot when a send fails. Transient = refunded and not billed. Client error = slot kept.
| Condition | Status | error.code | Refunded? | Billable? | Retry? |
|---|---|---|---|---|---|
| Upstream timeout | 504 | messaging_service_timeout | Yes | No | Yes, same request |
| Network / unreachable | 502 | messaging_service_unreachable | Yes | No | Yes, same request |
| Upstream 5xx | 502 | messaging_service_error | Yes | No | Yes, same request |
| Upstream auth failure | 502 | messaging_service_error | Yes | No | Yes (ops alerted) |
| Upstream 429 (overload) | 503 | service_unavailable | Yes | No | Yes, honor Retry-After |
| Recipient unreachable (upstream 404) | 404 | invalid_request | No | Yes | No — fix recipient |
| Session not ready (upstream 409/412/422) | 409 | session_not_ready | No | Yes | No — connect session first |
| Other client error (4xx) | 400 | invalid_request | No | Yes | No — fix request |
Webhook retries
After a failed attempt, the next attempt is scheduled per this table. Attempt count is 1-indexed and matches `x-aulm-attempt`. `Retry-After` from your response overrides the ladder (capped at 1h).
| Attempt just failed | Next retry |
|---|---|
| 1 | 1 minute |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 12 hours |
| 6 | Dead-lettered |
- Default max attempts per delivery:
6. - Endpoint auto-disabled after
25consecutive failures (across deliveries). - Per-attempt HTTP timeout:
10s. - Target first-delivery latency:
~60s.
Errors
Errors always come back as { error: { code, message } }. The HTTP status tells you the broad class; the error.code tells you the exact cause. See the full error-code index at the bottom of this page.
{ "error": { "code": "session_not_ready", "message": "Session is not ready to send messages. Connect the session first." }}Send a text message
/v1/messages/send-textResponses
201Message accepted by the messaging service.400401402403404409429500502503504
curl -X POST https://api.aulm.io/v1/messages/send-text \ -H "Authorization: Bearer $AULM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "session_id": "f1e2d3c4-5678-4abc-9def-0123456789ab", "to": "+14155551234", "body": "Hello from aulm!"}'{ "data": { "message_id": "true_14155551234@c.us_3EB0...", "session_id": "f1e2d3c4-5678-4abc-9def-0123456789ab", "to": "+14155551234", "status": "sent", "simulated": false }, "request_id": "9c8b7a6d-1234-4abc-def0-112233445566"}Send an image message
/v1/messages/send-imageResponses
201Image accepted by the messaging service.400401402403404409429500502503504
curl -X POST https://api.aulm.io/v1/messages/send-image \ -H "Authorization: Bearer $AULM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "session_id": "f1e2d3c4-5678-4abc-9def-0123456789ab", "to": "+14155551234", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...", "caption": "Receipt attached"}'{ "data": { "message_id": "true_14155551234@c.us_3EB0...", "session_id": "f1e2d3c4-5678-4abc-9def-0123456789ab", "to": "+14155551234", "status": "sent", "simulated": false }, "request_id": "9c8b7a6d-1234-4abc-def0-112233445566"}Get session connection status
/v1/sessions/{id}/statusResponses
200Current session status.400401403404500
curl https://api.aulm.io/v1/sessions/<SESSION_ID>/status \ -H "Authorization: Bearer $AULM_API_KEY"{ "data": { "session_id": "f1e2d3c4-5678-4abc-9def-0123456789ab", "status": "connected" }}Webhooks
Register a single HTTPS endpoint in the dashboard. aulm POSTs JSON events to it with a signed header, retries failures per the published ladder, and gives you a stable x-aulm-delivery-id for idempotency.
Request headers
| x-aulm-event | Event name: inbound_message or message_status. |
| x-aulm-delivery-id | UUID for this logical event. Use as your idempotency key — the same value may arrive across retries. |
| x-aulm-signature | t=<unix>,v1=<hex>. HMAC_SHA256(secret, `${t}.${rawBody}`). Verify over the raw bytes. |
| x-aulm-attempt | 1-indexed attempt number. |
Verifying the signature
Always verify over the raw request body bytes — do not re-serialize the JSON.
const crypto = require("crypto");
function verifyAulmSignature(rawBody, header, secret) {
// header looks like: "t=1716893742,v1=abc123..."
const parts = Object.fromEntries(
header.split(",").map((p) => p.split("=")),
);
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
// timing-safe compare
const a = Buffer.from(expected, "hex");
const b = Buffer.from(parts.v1 ?? "", "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Events
x-aulm-event: inbound_messageA WhatsApp message was received on a connected session.
Delivered to your registered webhook endpoint. The `x-aulm-event` request header equals `inbound_message`. Test deliveries triggered from the dashboard include `"test": true` at the top level of the body.
{ "event": "inbound_message", "delivery_id": "00000000-0000-4000-8000-000000000000", "timestamp": "2026-05-28T10:15:42.000Z", "session_id": "00000000-0000-4000-8000-000000000000", "test": false, "message": { "id": "string", "from": "string", "to": "string", "chat_id": "string", "type": "string", "body": "string", "is_group": false, "from_me": false, "media_url": "https://example.com/...", "received_at": "2026-05-28T10:15:42.000Z" }}x-aulm-event: message_statusDelivery status changed for a message you sent.
Delivered to your registered webhook endpoint. The `x-aulm-event` request header equals `message_status`. The `status` field is one of: `sent`, `delivered`, `read`, `played`, `failed`.
{ "event": "message_status", "delivery_id": "00000000-0000-4000-8000-000000000000", "timestamp": "2026-05-28T10:15:42.000Z", "session_id": "00000000-0000-4000-8000-000000000000", "message_id": "string", "status": "sent", "from": "string", "to": "string", "status_at": "2026-05-28T10:15:42.000Z"}Plans & limits
Sessions per plan
| Plan | Active sessions | Message limit |
|---|---|---|
| trial | 1 | 20 total across a fixed 7-day window (not per day) |
| starter | 1 | 1000 / day (UTC) |
| pro | 2 | 10000 / day (UTC) |
| business | 6 | 100000 / day (UTC) |
Trial is a fixed 7-day window with a monotonic counter — it ends at whichever comes first: hitting the cap or 7 days from signup. Daily plans reset at next UTC midnight.
Error-code index
| Status | error.code | Meaning | Quota slot |
|---|---|---|---|
| 400 | invalid_json | Request body is not valid JSON. | — |
| 400 | invalid_request | Validation failed (missing field, bad phone, bad image). | — |
| 401 | missing_authorization | No Authorization header. | — |
| 401 | invalid_authorization | Authorization header isn't Bearer scheme. | — |
| 401 | invalid_api_key | Key does not match any active record. | — |
| 401 | api_key_revoked | Key was revoked or soft-deleted. | — |
| 401 | api_key_expired | Key passed its expiry date. | — |
| 401 | account_deactivated | Customer account is soft-deleted. | — |
| 402 | subscription_required | No active plan and trial is over. | — |
| 403 | session_not_allowed | API key is restricted from this session. | — |
| 404 | session_not_found | Session UUID doesn't exist for this account. | — |
| 404 | invalid_request | Recipient unreachable on the messaging service. | Kept |
| 409 | session_not_ready | Session exists but is not connected. | Kept |
| 429 | trial_quota_exceeded | Hit your trial cap. See x-ratelimit-reset. | — |
| 429 | daily_quota_exceeded | Hit your daily cap. Resets at next UTC midnight. | — |
| 500 | internal_error | Server-side failure. Quote x-request-id to support. | — |
| 502 | messaging_service_unreachable | Network failure to upstream. | Refunded |
| 502 | messaging_service_error | Upstream returned 5xx or auth-failed. | Refunded |
| 503 | service_unavailable | Upstream overload. Honor Retry-After. | Refunded |
| 504 | messaging_service_timeout | Upstream did not respond in time. | Refunded |