How to Integrate the WhatsApp API in PHP (2026 Guide)
Last updated: June 3, 2026
Most WhatsApp API tutorials assume Node.js or Python. If you're working in PHP — Laravel, a plain script, or a legacy app — this guide gets you sending real WhatsApp messages from PHP in a few minutes, using nothing but cURL (built into PHP) or Guzzle. By the end you'll send your first message and receive replies via a webhook. No Meta Business verification, no template approval.
Prerequisites
- PHP 7.4+ with the cURL extension (enabled by default in most installs).
- An aulm account and a connected WhatsApp session (pair your number with a QR code from the dashboard).
- Your API key (
alm_sk_live_…oralm_sk_test_…) from Dashboard → API Keys, and your session UUID from Dashboard → Sessions.
Send your first WhatsApp message (plain cURL, zero dependencies)
The fastest way to send a WhatsApp message from PHP is the built-in cURL extension. No Composer, no packages.
<?php
$apiKey = "alm_sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$sessionId = "your-session-uuid";
$ch = curl_init("https://api.aulm.io/v1/messages/send-text");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$apiKey}",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"session_id" => $sessionId,
"to" => "14155550100", // recipient, digits incl. country code
"body" => "Hello from PHP 👋",
]),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status === 201) {
echo "Message sent!\n";
print_r(json_decode($response, true));
} else {
echo "Failed (HTTP {$status}): {$response}\n";
}A successful send returns 201 Created with a JSON body containing the message ID and queued status. The recipient phone should be digits only, including the country code, no + or spaces.
The cleaner way with Guzzle (Composer / Laravel)
If you already have Composer in the project (most Laravel apps do), Guzzle gives you typed exceptions and a nicer API.
<?php
require "vendor/autoload.php";
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
$client = new Client(["base_uri" => "https://api.aulm.io"]);
try {
$res = $client->post("/v1/messages/send-text", [
"headers" => ["Authorization" => "Bearer alm_sk_live_xxx"],
"json" => [
"session_id" => "your-session-uuid",
"to" => "14155550100",
"body" => "Hello from PHP via Guzzle",
],
]);
echo "Sent: " . $res->getStatusCode();
} catch (RequestException $e) {
echo "Error: " . $e->getResponse()?->getBody();
}In Laravel you can drop this straight into a controller, or use Illuminate\Support\Facades\Http instead — same endpoint, same payload.
Handling responses and errors
Every aulm error response is JSON with a stable shape: { "error": { "code": "...", "message": "..." } }, plus an x-request-id response header. The status codes you'll actually see:
- 201 Created — the message was accepted and queued for delivery.
- 400 Bad Request — invalid JSON, missing fields, body over 4096 chars, or a malformed phone number.
- 401 Unauthorized — missing, malformed, revoked, or expired API key.
- 403 Forbidden — this API key isn't allowed to use this session (per-key session whitelist).
- 404 Not Found — the
session_iddoesn't exist or isn't owned by your account. - 429 Too Many Requests — rate limit hit; back off and retry.
When you contact support, always include the x-request-id header from the failing response — it lets us find the exact request in our logs.
Check a session is connected before sending
WhatsApp sessions can drop (phone offline, logout, etc.). A quick status check avoids spending retries on a disconnected session.
<?php
$ch = curl_init("https://api.aulm.io/v1/sessions/your-session-uuid/status");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer alm_sk_live_xxx"],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
// e.g. { "status": "connected" } — only send when connected
if (($result["status"] ?? null) !== "connected") {
exit("Session not ready: " . ($result["status"] ?? "unknown"));
}Receiving messages — a signed PHP webhook
To receive incoming WhatsApp messages, configure a webhook URL in the dashboard. aulm POSTs every event to that URL signed with HMAC-SHA256, using these headers:
x-aulm-signature: t=<unix-ts>,v1=<hex-hmac>x-aulm-event— event type (e.g.message.received)x-aulm-delivery-id— unique delivery ID (use it to dedupe)x-aulm-attempt— attempt number for retries
The signature is computed as HMAC_SHA256(secret, "{t}.{rawBody}"). Always verify against the raw request body — not the parsed JSON, and not a re-encoded version — and always use a constant-time comparison.
<?php
// webhook.php — receives incoming WhatsApp messages from aulm
$secret = "your_webhook_signing_secret"; // shown once when you create the webhook
$raw = file_get_contents("php://input");
$sigHdr = $_SERVER["HTTP_X_AULM_SIGNATURE"] ?? "";
// header format: "t=<unix>,v1=<hex>"
$parts = [];
foreach (explode(",", $sigHdr) as $kv) {
[$k, $v] = array_pad(explode("=", $kv, 2), 2, "");
$parts[$k] = $v;
}
$ts = $parts["t"] ?? "";
$sig = $parts["v1"] ?? "";
$expected = hash_hmac("sha256", "{$ts}.{$raw}", $secret);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
exit("invalid signature");
}
// (Optional) reject events older than 5 minutes to prevent replay
if (abs(time() - (int) $ts) > 300) {
http_response_code(401);
exit("stale timestamp");
}
$event = json_decode($raw, true);
// $event["event_type"], $event["data"], etc.
file_put_contents("incoming.log", print_r($event, true), FILE_APPEND);
http_response_code(200);
echo "ok";Respond 2xx within a few seconds. Any non-2xx response triggers a retry with exponential backoff, so make your handler idempotent — use x-aulm-delivery-id to skip events you've already processed.
Next steps
- Browse the full API reference for media messages, group sends, and session management.
- Read WhatsApp API Without Meta Approval for the honest trade-offs of the unofficial path.
- Create a free aulm account and get your API key — you can send your first message in under five minutes.