WhatsApp API Documentation

The Macra WhatsApp API lets your business connect a WhatsApp Business number to your own systems, send messages programmatically, and receive inbound messages on a webhook. It sits in front of a managed WhatsApp connection so you never handle QR codes or session state directly. You only talk to a small JSON/HTTPS API.

Getting Started

  1. Apply for access. Submit the signup form with your business details, the WhatsApp Business number you want to connect, and a business permit or company registration certificate. Every application is reviewed manually to keep the platform limited to verified businesses.
  2. Wait for review. Applications are typically reviewed within a few business days. You'll be contacted on the phone number you provided if anything is missing.
  3. Get your API key. Once approved, Macra Systems issues your account with an initial API key. From then on you can create and revoke your own keys anytime from the dashboard. Each key is shown once at creation, so store it somewhere safe, since it can't be retrieved again afterwards (only revoked and replaced with a new one).
  4. Pay the onboarding fee. See Onboarding Fee — required before you can connect WhatsApp or create additional API keys.
  5. Connect your WhatsApp number. Call POST /v1/whatsapp/connect and enter the returned pairing code on the WhatsApp Business phone.
  6. Start integrating. Send messages with POST /v1/messages/send and receive inbound messages on your webhook URL.
New accounts start on the free plan with a default daily sending limit. See Rate Limits.

Onboarding Fee

Every account owes the same one-time onboarding fee — a standard amount set platform-wide by Macra Systems, not negotiated per account. It's separate from the daily message limit and is charged once, not on a recurring schedule.

Pay it from the dashboard, under "Onboarding Fee" — card and mobile money (M-Pesa) are both supported. Payment is processed by Paystack and verified server-side before your account is marked paid. There's no API endpoint for this: it's dashboard-only.

Until the fee is paid (or waived by Macra Systems), POST /v1/whatsapp/connect returns 402, and the dashboard's "Connect WhatsApp" and "New API Key" actions are disabled.

Authentication

Every request to a /v1/ endpoint must include your API key in an X-Api-Key header. There is no OAuth flow, session, or bearer-token exchange: the key itself is the credential.

Header
X-Api-Key: mwa_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are prefixed mwa_ and are account-scoped: one key authenticates as one account and everything that account is entitled to (its connected WhatsApp number, its message history, its rate limit).

An account can hold multiple active keys at once (for example, one per environment). Create and revoke keys anytime from the dashboard under API Keys; revoking one takes effect immediately.

Treat your API key like a password. Call the API only from your own backend. Never embed it in a mobile app, browser page, or other client-side code where it could be extracted.

Base URL

Base URL
https://macrasystems.com/wa/v1

All endpoint paths below are relative to this base URL. The API only accepts and returns application/json, and every response, success or error, is a JSON object.

Connect WhatsApp

POST/v1/whatsapp/connectRequires API key

Starts linking your account's verified WhatsApp Business number: the number reviewed during onboarding, shown on your account. On a fresh connection this returns a pairing code you enter on the phone; if the account is already linked and ready, it returns immediately with no pairing step.

This endpoint takes no request body. It always connects the number Macra Systems verified for your account, not an arbitrary one, since that's what was checked during application review. To connect a different number, contact us to update it on file first.
Requires the account's onboarding fee to be paid first. Until then this returns 402.

Example request

cURL
curl -X POST https://macrasystems.com/wa/v1/whatsapp/connect \
  -H "X-Api-Key: mwa_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Response fields

FieldTypeDescription
statusstringqr_ready: a pairing code was issued and is waiting to be entered on the phone. ready: the number was already linked; nothing further to do.
pairing_codestring | nullThe code to enter on the phone when status is qr_ready. null when status is ready.

Example response: new number

200 OK
{
  "pairing_code": "ABCD-1234",
  "status": "qr_ready"
}

Example response: already connected

200 OK
{
  "pairing_code": null,
  "status": "ready"
}
To complete pairing: on the WhatsApp Business phone, open WhatsApp → Linked Devices → Link a Device → Link with phone number instead, then enter the pairing_code. Codes expire after a short window. If it expires before you enter it, call /v1/whatsapp/connect again to get a new one.

Errors

400No verified WhatsApp number on file for this account; contact support to add one
401 / 403Missing/invalid API key, or the account is suspended (see Errors)
402Onboarding fee not paid yet; see Onboarding Fee
502The WhatsApp engine is unavailable or rejected the request
504Timed out waiting for the session to become ready for pairing; retry

Connection Status

GET/v1/whatsapp/statusRequires API key

Returns the current state of your account's WhatsApp connection. Useful for polling after /v1/whatsapp/connect until pairing completes, and before attempting to send.

Example request

cURL
curl https://macrasystems.com/wa/v1/whatsapp/status \
  -H "X-Api-Key: mwa_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Response fields

FieldTypeDescription
statusstringOne of the four values below.

Example response

200 OK
{
  "status": "connected"
}

Status values

not_connectedNo connection attempt has been made yet on this account.
pendingA pairing code was issued and the platform is waiting for the phone to complete linking.
connectedLinked and ready. Messages can be sent and inbound messages will be relayed to your webhook.
disconnectedWas connected but the phone logged the session out (or it failed). Call /v1/whatsapp/connect again to relink.

Send a Message

POST/v1/messages/sendRequires API key

Sends a plain-text WhatsApp message from your connected number to any WhatsApp number. Requires the account's connection status to be connected (see Connection Status).

Request body

FieldTypeDescription
tostringrequiredDestination number, with country code. Formatting is flexible: spaces and symbols are stripped server-side (e.g. +254 712 345 678 and 254712345678 are equivalent).
messagestringrequiredThe text body to send.

Example request

cURL
curl -X POST https://macrasystems.com/wa/v1/messages/send \
  -H "X-Api-Key: mwa_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"to": "254712345678", "message": "Hello from Macra WhatsApp API"}'

Example response

200 OK
{
  "id": "true_254712345678@c.us_3EB0...",
  "ack": "sent"
}

The response body is the send confirmation from the underlying WhatsApp connection. Its exact fields aren't part of the platform's stable contract, so check for a 200 status to confirm the send succeeded rather than relying on specific field names.

Errors

400Missing field: to / Missing field: message
400WhatsApp not connected (connect a number first, see Connect WhatsApp)
401 / 403Missing/invalid API key, or the account is suspended
429Daily message limit reached (see Rate Limits)
502The WhatsApp engine rejected or failed to deliver the send

Send a Document

POST/v1/messages/send-documentRequires API key

Sends a document (PDF, etc.) from your connected number to any WhatsApp number, with an optional caption. Requires the account's connection status to be connected.

Request body

FieldTypeDescription
tostringrequiredDestination number, with country code. Same flexible formatting as Send a Message.
document_urlstringrequired*A public HTTPS URL the document is fetched from directly. Up to 50MB. *Provide exactly one of document_url or document_base64.
document_base64stringrequired*Raw base64-encoded document data. Limited to ~15MB decoded (larger files: use document_url instead). *Provide exactly one of document_url or document_base64.
document_mimetypestringMIME type of the document, e.g. application/pdf. Defaults to application/pdf when using document_base64. Ignored for document_url.
filenamestringOptional file name shown in WhatsApp. Defaults to a generic name if omitted.
captionstringOptional caption text sent alongside the document (max 1024 characters).

Example request

cURL
curl -X POST https://macrasystems.com/wa/v1/messages/send-document \
  -H "X-Api-Key: mwa_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"to": "254712345678", "document_base64": "JVBERi0xLjQK...", "document_mimetype": "application/pdf", "filename": "Invoice-1024.pdf", "caption": "Invoice #1024"}'

Example response

201 Created
{
  "messageId": "true_254712345678@c.us_3EB0...",
  "timestamp": 1719312000
}

Errors

400Missing field: to / neither or both of document_url and document_base64 given / WhatsApp not connected
401 / 403Missing/invalid API key, or the account is suspended
413Document exceeds the maximum allowed size (~15MB for document_base64, 50MB for document_url)
429Daily message limit reached (see Rate Limits)
501Document sending isn't supported on the current connection
502The WhatsApp engine rejected or failed to deliver the send

Receiving Messages (Webhooks)

To receive WhatsApp messages sent to your connected number, set a webhook URL on your account. Every inbound message is relayed there as an HTTP POST as soon as it arrives.

Set or change your webhook URL yourself anytime from the dashboard, under "Inbound Webhook". Leave it blank to stop receiving inbound message webhooks.

Payload

POST to your webhook URL
{
  "event": "message_received",
  "from": "254712345678",
  "message": "Hi, is this order ready?",
  "timestamp": 1732012345
}
FieldTypeDescription
eventstringAlways message_received for inbound text messages.
fromstringThe sender's WhatsApp number, digits only with country code.
messagestringThe message text.
timestampintegerUnix timestamp (seconds) of when the message was received.

Verifying the signature

Every delivery includes an X-Macra-Signature header so you can confirm it came from Macra Systems and wasn't sent by someone who simply guessed or found your webhook URL:

Header
X-Macra-Signature: sha256=<hex-encoded HMAC-SHA256>

The signature is an HMAC-SHA256 of the raw request body, keyed with your account's webhook signing secret (visible under "Inbound Webhook" in the dashboard, and rotatable there at any time). Recompute it over the exact bytes you received and compare with a constant-time check - don't re-serialize the parsed JSON first, since that isn't guaranteed to produce identical bytes.

PHP
$raw = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_MACRA_SIGNATURE'] ?? '';
$signature = str_starts_with($header, 'sha256=') ? substr($header, 7) : '';
$expected = hash_hmac('sha256', $raw, $yourWebhookSecret);

if ($signature === '' || !hash_equals($expected, $signature)) {
    http_response_code(401);
    exit;
}

Notes

  • Requests are sent as Content-Type: application/json with a 10-second delivery timeout and are not retried on failure, so your endpoint should respond quickly with a 2xx status.
  • Reject any delivery with a missing or invalid X-Macra-Signature before acting on it.
  • Only the message text is relayed today; media, location, and other message types aren't forwarded yet.

Rate Limits

Each account has a daily limit on messages sent via /v1/messages/send, counted from midnight and reset daily. The default on the free plan is 100 messages/day. Receiving messages is never rate-limited.

Once the limit is reached, /v1/messages/send returns 429 Daily message limit reached until it resets. There is no rate limit on /v1/whatsapp/connect or /v1/whatsapp/status beyond normal fair use.

Need a higher daily limit for your use case? Contact us to discuss a plan upgrade.

Errors

Errors are returned as a JSON object with a single detail field describing what went wrong, alongside the HTTP status code.

Example error
{
  "detail": "Invalid API key"
}
StatusMeaningTypical cause
400Bad RequestInvalid or missing JSON body, a required field is missing, or the WhatsApp connection isn't ready for the action requested.
401UnauthorizedThe X-Api-Key header is missing or doesn't match a valid key.
402Payment RequiredThe account's one-time onboarding fee hasn't been paid yet.
403ForbiddenThe API key is valid but the account is suspended.
405Method Not AllowedWrong HTTP method for the endpoint (e.g. GET on a POST-only endpoint).
429Too Many RequestsThe account's daily message-sending limit has been reached.
500Internal Server ErrorAn unexpected error on the platform. Safe to retry; contact us if it persists.
502Bad GatewayThe underlying WhatsApp connection was unavailable or returned an error.
504Gateway TimeoutTimed out waiting on the WhatsApp connection (typically during connect). Safe to retry.

Support

For questions about your application, API keys, webhook setup, or rate limits, reach out via the contact page and include your account email so we can look you up quickly.