All integration guides

PHP integration

Send WhatsApp messages with PHP

A complete, runnable PHP example for sending WhatsApp messages through the SPWP REST API. There is no SDK to install — PHP talks to the API over ordinary HTTP, so you can paste this straight into an existing project.

About 5 minutes to your first delivered message

1. Setup

Create an API key in the dashboard under Accounts → your account → API keys, then make it available to your PHP code as an environment variable. Never commit the key or ship it in client-side code.

# The cURL extension ships with virtually every PHP install.
php -m | grep curl

2. Send a text message

One POST with a recipient and a body. The API replies 202 Accepted with a message id in QUEUED state; delivery happens asynchronously and you can track it by id or webhook.

<?php

const SPWP_API_BASE = "https://api.spwp.app/api/v1";

function spwp_send_text(string $to, string $text): array
{
    $ch = curl_init(SPWP_API_BASE . "/public/messages/text");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            "X-API-Key: " . getenv("SPWP_API_KEY"),
            "Content-Type: application/json",
        ],
        CURLOPT_POSTFIELDS => json_encode(["to" => $to, "text" => $text]),
        CURLOPT_TIMEOUT => 10,
    ]);

    $response = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $body = json_decode($response, true);
    if ($status >= 400) {
        throw new RuntimeException("SPWP {$body['code']}: {$body['message']}");
    }

    return $body["data"];
}

$message = spwp_send_text("9647503505440", "Your verification code is 482310");
echo $message["id"], " ", $message["status"], PHP_EOL;

3. Send an image, document or other media

Media endpoints take the same shape, with a source that is either a public HTTPS URL we fetch server-side or a base64 data URL you upload inline. Swap the path segment for video, audio, document or location.

<?php

function spwp_send_image(string $to, string $source, ?string $caption = null): array
{
    $ch = curl_init(SPWP_API_BASE . "/public/messages/image");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            "X-API-Key: " . getenv("SPWP_API_KEY"),
            "Content-Type: application/json",
        ],
        CURLOPT_POSTFIELDS => json_encode([
            "to" => $to,
            "source" => $source,
            "caption" => $caption,
        ]),
    ]);

    $body = json_decode(curl_exec($ch), true);
    curl_close($ch);

    return $body["data"];
}

4. Receive replies and delivery receipts

Inbound messages and status changes arrive as signed POSTs to your webhook URL. Recompute the HMAC over the timestamp and the raw request body — re-serialised JSON will not match — and reject anything older than five minutes.

<?php
// webhook.php — point your SPWP webhook URL here.

$ts = $_SERVER["HTTP_X_WA_TIMESTAMP"] ?? "";
$sig = $_SERVER["HTTP_X_WA_SIGNATURE"] ?? "";
preg_match("/v1=([a-f0-9]+)/", $sig, $m);
$v1 = $m[1] ?? "";

if (!$ts || !$v1) {
    http_response_code(400);
    exit;
}

// Replay protection: five-minute window.
if (abs(microtime(true) * 1000 - (int) $ts) > 5 * 60000) {
    http_response_code(400);
    exit;
}

$body = file_get_contents("php://input");
$expected = hash_hmac("sha256", "$ts.$body", getenv("SPWP_WEBHOOK_SECRET"));

if (!hash_equals($expected, $v1)) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);
// …queue the work, respond immediately…
http_response_code(200);

Error handling

Errors carry a non-2xx status and a stable code string you can branch on: VALIDATION_FAILED, INVALID_API_KEY, ACCOUNT_NOT_CONNECTED, RATE_LIMITED, MEDIA_FETCH_FAILED. Retry on 429 while honouring Retry-After; treat 401 as a configuration problem rather than a transient one.

Next steps

The full reference covers every endpoint, field and error code, plus the webhook payload shapes.

Other languages and frameworks