Developer Portal

ClickLink API

Authentication, send SMS, balance, delivery status, webhooks, and a safe sandbox — everything you need to integrate before going live.

Authentication

Send your API key on every request using one of:

X-ClickLink-Key: clk_xxxxxxxx
Authorization: Bearer clk_xxxxxxxx
X-Api-Key: clk_xxxxxxxx

Base path: /api/v1

Send SMS

POST /api/v1/sms/send202

{
  "to": "05xxxxxxxx",
  "senderId": "ClickLink",
  "message": "مرحباً من ClickLink",
  "registeredDelivery": true
}

Success response

{
  "ok": true,
  "status": "ACCEPTED",
  "encoding": "UCS2",
  "parts": 1,
  "totalCost": 0.05,
  "currency": "SAR",
  "balance": 999.95,
  "messages": [
    {
      "messageId": "9f1a5ba3-e7be-4378-a36c-0ffc226539a8",
      "to": "9665xxxxxxxx",
      "status": "QUEUED",
      "routeId": "sa-stc",
      "cost": 0.05
    }
  ]
}

Balance

GET /api/v1/sms/balance

{ "ok": true, "balance": 999.95, "currency": "SAR", "pricePerPart": 0.05, "estimatedParts": 19999 }

Message status

GET /api/v1/sms/messages/{messageId}

{
  "ok": true,
  "message": {
    "messageId": "…",
    "status": "delivered",
    "dlrStatus": "delivered",
    "deliveredAt": "2026-07-24T10:00:12.000Z"
  }
}

Webhooks (DLR)

Configure via PUT /api/v1/billing/webhook (HTTPS URL + secret). We sign every callback:

X-ClickLink-Signature: sha256=<HMAC_SHA256(secret, timestamp + "." + rawBody)>
X-ClickLink-Timestamp: <unix seconds>
X-ClickLink-Event: message.dlr

Payload

{
  "event": "message.dlr",
  "messageId": "…",
  "status": "delivered",
  "isFinal": true,
  "refunded": false,
  "destAddr": "9665xxxxxxxx"
}

Sandbox

No charge · no real SMS. Same request body as live send.

MethodPathPurpose
GET/api/v1/sms/sandbox/pingAuth check
POST/api/v1/sms/sandbox/sendValidate + estimate cost
POST/api/v1/sms/sandbox/webhook-verifyVerify HMAC

Error codes

{ "ok": false, "error": { "code": "DEST_INVALID", "message": "…" } }
HTTPCodeMeaning
401AUTH_MISSINGNo API key
401AUTH_INVALIDBad credentials
403SENDER_NOT_ALLOWEDSender not approved
400DEST_INVALIDBad MSISDN
402INSUFFICIENT_BALANCEWallet too low
503QUEUE_FULLBackpressure
404NOT_FOUNDMessage missing

Code snippets

curl -sS -X POST "https://YOUR_HOST/api/v1/sms/sandbox/send" \
  -H "Content-Type: application/json" \
  -H "X-ClickLink-Key: clk_xxxxxxxx" \
  -d '{"to":"05xxxxxxxx","senderId":"ClickLink","message":"Sandbox test"}'

curl -sS -X POST "https://YOUR_HOST/api/v1/sms/send" \
  -H "Content-Type: application/json" \
  -H "X-ClickLink-Key: clk_xxxxxxxx" \
  -d '{"to":"05xxxxxxxx","senderId":"ClickLink","message":"Hello from ClickLink"}'
import axios from "axios";

const client = axios.create({
  baseURL: "https://YOUR_HOST/api/v1",
  headers: {
    "Content-Type": "application/json",
    "X-ClickLink-Key": process.env.CLICKLINK_API_KEY,
  },
});

await client.post("/sms/sandbox/send", {
  to: "05xxxxxxxx",
  senderId: "ClickLink",
  message: "Sandbox test",
});

const { data } = await client.post("/sms/send", {
  to: "05xxxxxxxx",
  senderId: "ClickLink",
  message: "Hello from ClickLink",
});
console.log(data.messages[0].messageId);
import os, requests

BASE = "https://YOUR_HOST/api/v1"
H = {
  "Content-Type": "application/json",
  "X-ClickLink-Key": os.environ["CLICKLINK_API_KEY"],
}

requests.post(f"{BASE}/sms/sandbox/send", headers=H, json={
  "to": "05xxxxxxxx", "senderId": "ClickLink", "message": "Sandbox test",
}).raise_for_status()

r = requests.post(f"{BASE}/sms/send", headers=H, json={
  "to": "05xxxxxxxx", "senderId": "ClickLink", "message": "Hello from ClickLink",
})
print(r.json()["messages"][0]["messageId"])
<?php
use GuzzleHttp\Client;

$client = new Client([
  'base_uri' => 'https://YOUR_HOST/api/v1/',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-ClickLink-Key' => getenv('CLICKLINK_API_KEY'),
  ],
]);

$client->post('sms/sandbox/send', ['json' => [
  'to' => '05xxxxxxxx', 'senderId' => 'ClickLink', 'message' => 'Sandbox test',
]]);

$res = $client->post('sms/send', ['json' => [
  'to' => '05xxxxxxxx', 'senderId' => 'ClickLink', 'message' => 'Hello from ClickLink',
]]);
echo json_decode($res->getBody(), true)['messages'][0]['messageId'];

Full markdown reference lives in the repository at docs/API.md. Use the interactive sandbox in the portal under Developers (/app/developers).