Developer Tools

SDK setup and examples

Install and use the TypeScript, Python, and PHP clients from a backend service.

Environment variables

All SDK examples assume your backend has a base URL, API key, and sender ID configured as environment variables.

`HOOKMESSAGE_BASE_URL` is the backend API origin, not the frontend dashboard origin. `HOOKMESSAGE_API_KEY` must be created from the dashboard and kept server-side.

.env
bash
HOOKMESSAGE_BASE_URL=https://your-api-domain.com
HOOKMESSAGE_API_KEY=was_xxx
HOOKMESSAGE_SENDER_ID=9f4b5d4c-0000-4000-9000-123456789abc

TypeScript client

The TypeScript client lives in `src/sdk/wa-sender-client.ts` and is exposed by the local package metadata in `sdk/typescript/package.json`.

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.

typescript-sdk.ts
ts
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 client

The Python client lives in `sdk/python/wa_sender_client.py` and depends on `requests` from `sdk/python/requirements.txt`.

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.

python-sdk.py
py
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(
            sender_id=os.environ["HOOKMESSAGE_SENDER_ID"],
            receiver_number=phone,
            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)
        raise

PHP client

The PHP client lives in `sdk/php/src` and uses Composer PSR-4 autoloading from `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.

php-sdk.php
php
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'),
        '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

Until the PHP SDK is published to a registry, install it as a local Composer path repository or copy the `WhSender` namespace into your backend.

The package name from `sdk/php/composer.json` is `whsender/wa-sender-client`.

composer
bash
composer config repositories.whsender path ../sdk/php
composer require whsender/wa-sender-client:*

SDK method list

Use these supported SDK methods as the stable integration surface. If your use case needs a route not listed here, call the REST API directly or generate a client from OpenAPI.

SDK health and status checks

Before enabling production sends, call `listSenders()` and confirm the sender you configured is returned with `status: connected`.

After a send response, store `message_id`. Use `getMessageStatus(message_id)` when your product needs to display delivery state or diagnose a failed delivery.

status-check.ts
ts
const senders = await wa.listSenders();
const sender = senders.find((item) => item.sender_id === process.env.HOOKMESSAGE_SENDER_ID);

if (!sender || sender.status !== "connected") {
  throw new Error("Configured sender is not connected or not assigned to this API key.");
}

const queued = await wa.sendMessage({
  senderId: sender.sender_id,
  receiverNumber: "212612345678",
  message: "Test message",
  idempotencyKey: "test-message-001",
});

const status = await wa.getMessageStatus(queued.message_id);

Need help?

Use Book Integration Help if you want setup help for OTP or automated customer messaging.

Next steps