# aulm > aulm is a WhatsApp automation API. It connects to WhatsApp via an unofficial, > session-based method (not the official Meta Business API), which means fast > setup, no per-message Meta fees, and no business verification — with the > trade-off that connected numbers can be rate-limited or banned by WhatsApp. > See the safety guide before sending at volume. ## Important context for code generation - aulm is NOT an official WhatsApp Business Solution Provider. It drives a real WhatsApp session. Numbers can be banned. Advise users to warm up new numbers and avoid unsolicited bulk sends. - There is NO public API to create or pair a session. Sessions are created and paired via QR code in the aulm dashboard (https://aulm.io/dashboard). The API is for sending messages and reading session status only. - Phone numbers must be E.164 format (e.g. +966512345678). ## Base URL https://api.aulm.io All endpoint paths below are relative to this base URL (e.g. POST https://api.aulm.io/v1/messages/send-text). ## Authentication All requests require a Bearer token (your API key from the dashboard): Authorization: Bearer alm_sk_live_xxxxxxxxxxxxxxxx - Live keys: alm_sk_live_... - Test keys: alm_sk_test_... (sends are simulated, never billed, never reach WhatsApp) Keep keys secret. Never expose them in client-side code. ## Endpoints ### POST /v1/messages/send-text Send a text message. Request body (JSON): { "session_id": "uuid", // required — the connected session's id "to": "+966512345678", // required — recipient in E.164 (max 20 chars) "body": "Hello from aulm" // required — message text (1–4096 chars) } Success 201: { "data": { "message_id": "string", // engine-native id, or "sim_" for test keys "session_id": "uuid", "to": "+966512345678", "status": "sent", "simulated": false // true when using a test key (never billed) }, "request_id": "uuid" } ### POST /v1/messages/send-image Send an image with optional caption. Request body (JSON): { "session_id": "uuid", // required "to": "+966512345678", // required — E.164 "image": "https://...", // required — public https URL OR data URI // "data:image/png;base64,..." (max ~5MB) "caption": "Optional text" // optional — max 1024 chars } Note: only public https:// image URLs are allowed; private/loopback IPs are rejected (SSRF protection). Success 201: { "data": { "message_id": "string", "session_id": "uuid", "to": "+966512345678", "status": "sent", "simulated": false }, "request_id": "uuid" } ### GET /v1/sessions/{id}/status Read the connection status of a session you own. Path param: id (session uuid). Success 200: { "data": { "session_id": "uuid", "status": "connected" }, "request_id": "uuid" } // status reflects the cached session state (e.g. connected, disconnected, awaiting_scan). This endpoint is read-only. It cannot create or pair a session. ## Webhooks (inbound messages & delivery status) Register a webhook endpoint URL in the dashboard at https://aulm.io/dashboard/webhook-endpoint. One endpoint per account. You can send a test delivery from that page. aulm POSTs JSON to your URL for two events. The event name is in the `x-aulm-event` request header. Request headers on every delivery: - x-aulm-event — "inbound_message" or "message_status" - x-aulm-delivery-id — stable unique id for this logical event; use as an idempotency key (the same id may arrive more than once on retry) - x-aulm-attempt — integer, 1 on first try - x-aulm-signature — "sha256=" HMAC of the raw request body using your webhook signing secret (shown once when you create the endpoint) Verify the signature before trusting the payload. Pseudocode: expected = "sha256=" + hmac_sha256(secret, raw_body).hex() if !timingSafeEqual(header, expected): reject ### Event: inbound_message Fired when a WhatsApp message is received on a connected session. Body example: { "event": "inbound_message", "delivery_id": "uuid", "session_id": "uuid", "from": "+966512345678", "message_id": "string", "type": "text", // text | image | ... "body": "hi", // text content (when type=text) "received_at": "2026-05-28T10:15:42.000Z" } Test deliveries from the dashboard include "test": true at the top level. ### Event: message_status Fired when delivery status of a message you sent changes. Body example: { "event": "message_status", "delivery_id": "uuid", "session_id": "uuid", "message_id": "string", "status": "delivered", // sent | delivered | read | played | failed "to": "+966512345678", "occurred_at": "2026-05-28T10:15:43.000Z" } ### Responding - Any 2xx = success. - 4xx (except 429) = permanent failure, not retried, counts toward auto-disable. - 5xx and 429 are retried with exponential backoff. If 429 includes Retry-After (seconds or HTTP date) we honor it, capped at 1 hour. - Endpoints are auto-disabled after repeated consecutive failures. - Respond within a few seconds; long-running work belongs in a background job. ## Errors All errors are JSON: { "error": { "code": "string", "message": "string" } } Common codes: - invalid_json — body was not valid JSON - invalid_request — failed validation (bad field, bad phone, SSRF, etc.) - missing_authorization — Authorization header absent (401) - invalid_authorization — header present but not Bearer scheme (401) - invalid_api_key — key missing/wrong/malformed (401) - api_key_revoked — key was revoked or soft-deleted (401) - api_key_expired — key past its expires_at (401) - account_deactivated — owning customer is deleted (401) - too_many_auth_failures — IP-level brute-force cooldown (429, Retry-After) - session_not_found — session doesn't exist or isn't yours (404) - session_disabled — admin-disabled session (403) - session_not_allowed — key not whitelisted for this session (403) - session_not_ready — session not connected (409) - daily_quota_exceeded — plan daily cap hit (429) - trial_quota_exceeded — trial allowance exhausted (429) - messaging_service_timeout — upstream timeout, slot refunded (504) - messaging_service_unreachable — upstream network error, refunded (502) - messaging_service_error — upstream 5xx/auth, refunded (502) - service_unavailable — upstream overload, refunded (503, Retry-After) ## Response headers Every /v1/* response carries: - x-request-id — uuid, log it and quote it to support Send endpoints additionally stamp on success (201) and 429 quota responses: - x-ratelimit-limit — your quota for the current window - x-ratelimit-remaining — slots left after this request (post-refund) - x-ratelimit-reset — ISO 8601 reset time (daily: next UTC midnight; trial: end of the 7-day window) - retry-after — seconds (on 429 only) Auth/validation errors (400/401/403/404) DO NOT carry x-ratelimit-* headers. ## Rate limits Send endpoints stamp x-ratelimit-* headers on success/429. Note: outbound sends are NOT auto-paced inside aulm. Burst at your own risk — WhatsApp can ban a number for high-velocity sending. Throttle on your side. ## Links - Dashboard (create/pair sessions, get API keys): https://aulm.io/dashboard - Full docs: https://aulm.io/docs - Safety guide (avoiding bans): https://aulm.io/docs ## Pages - [Home](/): WhatsApp messaging API for developers — overview and quickstart. - [Pricing](/pricing): Plans (Starter, Pro, Business), comparison table, and FAQ. - [About](/about): Who we are and why we built aulm. - [Blog](/blog): Articles about WhatsApp automation and API development. - [Official vs Unofficial WhatsApp API](/blog/official-vs-unofficial-whatsapp-api): Honest side-by-side comparison. - [WhatsApp API Without Meta Approval](/blog/whatsapp-api-without-meta-approval): The honest 2026 guide, including ban risks. - [Docs](/docs): Full API reference and integration guide. - [Status](/status): Service status and incident history. - [Help center](/help): Knowledge base and support articles. - [Privacy Policy](/privacy): How we collect and process data. - [Terms](/terms): Terms of service. - [Acceptable Use Policy](/acceptable-use): What you may and may not send. - [Refund Policy](/refund): Refund terms.