SDK setup and examples
Use the TypeScript, Python, and PHP reference clients safely from a backend service.
Before you install anything
The repository clients are reference clients, not public npm, PyPI, or Packagist packages. Copy or package the client for your backend, or call the REST API directly.
First prove one cURL request. A language client cannot fix a wrong API key, API-key role, sender assignment, receiver number, or plan limit.
- Free Sandboxuse only the base URL and API key. Do not configure a sender ID.
- Paid connected senderalso configure `HOOKMESSAGE_SENDER_ID` after connecting the number and assigning it to the API key.
- Browser safetyReact client components and browser JavaScript must call your own backend route, never HookMessage with the secret key.
Environment variables
Use `https://api.hookmessage.com` for the hosted HookMessage API. The sender variable is optional and must be absent for Free Sandbox.
HOOKMESSAGE_BASE_URL=https://api.hookmessage.com
HOOKMESSAGE_API_KEY=was_xxx
# Paid connected-sender mode only:
# HOOKMESSAGE_SENDER_ID=9f4b5d4c-0000-4000-9000-123456789abcTypeScript reference client
The TypeScript reference client lives in `backend/src/sdk/wa-sender-client.ts`. The local package metadata is private, so import the built client from your own workspace or copy it into your backend.
Use it only from a server route, worker, or backend service. The client sends `x-api-key`, normalizes the base URL, applies a timeout, and throws `WaApiError` on non-2xx responses.
- Free Sandboxleave `HOOKMESSAGE_SENDER_ID` undefined; the client omits `sender_id`.
- Paid modeset `HOOKMESSAGE_SENDER_ID` and the same code sends through that assigned connected sender.
- Errorscatch `WaApiError` and inspect `statusCode`, `code`, and `body`.
import { WaApiError, WaSenderClient } from "@whsender/client";
const wa = new WaSenderClient({
baseUrl: process.env.HOOKMESSAGE_BASE_URL!,
apiKey: process.env.HOOKMESSAGE_API_KEY!,
timeoutMs: 15000,
});
export async function sendOrderConfirmation(phone: string, orderId: string) {
try {
return await wa.sendMessage({
senderId: process.env.HOOKMESSAGE_SENDER_ID,
receiverNumber: phone,
message: `Your order ${orderId} is confirmed.`,
sourceSystem: "checkout",
sourceReference: orderId,
idempotencyKey: `order:${orderId}:confirmed`,
});
} catch (error) {
if (error instanceof WaApiError) {
console.error(error.statusCode, error.code, error.body);
}
throw error;
}
}Python reference client
The Python reference client lives in `backend/sdk/python/wa_sender_client.py` and uses `requests`.
Use the client from backend code, cron jobs, or queue workers. It raises `WaApiError` with `status_code` and `body` when the API rejects a request.
- Installadd `requests>=2.31.0` to your application requirements.
- Importcopy or package `wa_sender_client.py` inside your backend project.
- Senderpass `sender_id=os.getenv('HOOKMESSAGE_SENDER_ID')`; it is `None` in Free Sandbox.
import os
from wa_sender_client import WaApiError, WaSenderClient
wa = WaSenderClient(
base_url=os.environ["HOOKMESSAGE_BASE_URL"],
api_key=os.environ["HOOKMESSAGE_API_KEY"],
timeout=20.0,
)
def send_login_otp(phone: str) -> dict:
try:
return wa.send_otp(
receiver_number=phone,
sender_id=os.getenv("HOOKMESSAGE_SENDER_ID"),
app_name="My Store",
ttl_minutes=10,
source_system="login",
idempotency_key=f"login:{phone}",
)
except WaApiError as exc:
print(exc.status_code, exc.body)
raisePHP reference client
The PHP reference client lives in `backend/sdk/php/src` and uses the local Composer metadata in `backend/sdk/php/composer.json`.
Use it from Laravel services, Symfony services, plain PHP jobs, or any backend process. It throws `WaApiException` with `statusCode()` and `body()` for API errors.
- RequirementsPHP 8.1 or newer with the cURL extension.
- Installadd the SDK folder as a local Composer path repository or copy the `WhSender` namespace.
- Sender`getenv('HOOKMESSAGE_SENDER_ID') ?: null` is safe for Free Sandbox and paid mode.
use WhSender\WaApiException;
use WhSender\WaSenderClient;
$wa = new WaSenderClient(
getenv('HOOKMESSAGE_BASE_URL'),
getenv('HOOKMESSAGE_API_KEY'),
15,
);
try {
$message = $wa->sendMessage([
'senderId' => getenv('HOOKMESSAGE_SENDER_ID') ?: null,
'receiverNumber' => $customerPhone,
'message' => 'Your appointment is confirmed.',
'sourceSystem' => 'appointments',
'sourceReference' => (string) $appointmentId,
'idempotencyKey' => "appointment:{$appointmentId}:confirmed",
]);
} catch (WaApiException $exception) {
error_log(json_encode($exception->body()));
throw $exception;
}PHP Composer path install
This is a local path install, not a Packagist download. Adjust the path to the location where your application stores the reference client.
The package name from `sdk/php/composer.json` is `whsender/wa-sender-client`.
composer config repositories.whsender path ../sdk/php
composer require whsender/wa-sender-client:*Choose the correct method
Use the method that matches the API-key role. A role mismatch is rejected even when the key itself is valid.
- sendMessagerequires a `send_message` key and queues text or a supported attachment.
- sendOtprequires an `otp` key and generates or sends a verification code.
- getMessageStatusreads queue state, delivery timestamps, and the failure reason.
- listSendersuseful for paid connected-sender mode; an empty list is expected in managed Free Sandbox.
- healthCheckchecks API reachability only; it does not validate an API key or sender assignment.
Verify the result
Store `message_id` from every accepted send. Check that ID rather than treating HTTP 201 as proof of delivery.
Only paid connected-sender integrations need to list senders and confirm the configured sender is connected.
const queued = await wa.sendMessage({
senderId: process.env.HOOKMESSAGE_SENDER_ID,
receiverNumber: "212612345678",
message: "Test message",
idempotencyKey: "test-message-001",
});
const status = await wa.getMessageStatus(queued.message_id);
if (status.status === "failed") {
throw new Error(status.error_message ?? "HookMessage delivery failed");
}Need help?
Use Book Integration Help if you want setup help for OTP or automated customer messaging.
Continue building
Next steps
REST API
The REST API is JSON over HTTPS with API-key authentication for customer integrations.
Complete integration examples
Use these end-to-end examples after cURL succeeds.
Message status and history
Use message IDs to trace status, delivery attempts, timestamps, and failure reasons.
Troubleshooting
Use this page when a new integration does not send, status stays queued, or webhook verification fails.