Introduction
The TrustForce Hosting API lets your app send and receive WhatsApp messages, manage automated keyword replies, and pull message history — all through one REST API backed by your linked WhatsApp number(s).
Base URL for every endpoint below:
Authentication
Every protected request needs your API key (from Dashboard → API keys) in the Authorization header, or X-API-Key if your hosting strips Authorization headers:
Most endpoints also accept an optional session parameter — the session key shown on your WhatsApp number's card in the dashboard. If you leave it out, the key's default number is used.
Errors
Errors come back as JSON with an error field and a standard HTTP status code:
| Code | Meaning |
|---|---|
| 401 | Missing, invalid, or revoked API key |
| 403 | Key doesn't have the required permission, or account suspended |
| 404 | Endpoint or resource not found |
| 409 | WhatsApp number not connected yet |
| 422 | Missing or invalid parameter |
| 429 | Plan limit reached (messages, webhooks, or rules) |
| 502 | The Netronical server returned an error |
Send a message
Quick testers included in the project root: /test.php for send-only and /api-test.php for health, sessions, status, webhooks, rules, logs, and optional sending.
Requires the send scope. Sends a text message, image, document, or video by URL. Authentication supports Authorization: Bearer <api_key> or X-API-Key: <api_key>.
| Param | Description |
|---|---|
| to | Destination number, digits with country code (e.g. 27825550142) |
| message | Text body (required unless sending media). text is also accepted. |
| media_url | Public URL of an image/document to send instead of text |
| media_type | image | document | video (default image) |
| caption | Optional caption for media |
| session | Optional session key, if not using the key's default |
Session status
Requires the read scope. Returns connection state for one number.
List your numbers
Requires the read scope. Returns every WhatsApp number on your account.
Webhooks
Requires the webhook scope. List or create webhooks. Creating one returns a forward_secret used to sign forwarded events (see below).
| Param (POST) | Description |
|---|---|
| session | Session key this webhook listens on |
| name | A label for the webhook |
| forward_url | Optional — your server's URL to receive raw events |
Keyword rules
Requires the webhook scope. Manage the keyword → auto-reply rules attached to a webhook.
| Param | Description |
|---|---|
| webhook_id | Required on every call |
| keyword | The text or regex pattern to match (POST) |
| match_type | contains | exact | starts_with | regex |
| case_sensitive | true/false, default false |
| reply_type | text | image | document | no_reply |
| reply_text | Reply body or caption |
| reply_media_url | URL for image/document replies |
| priority | Lower number is checked first |
| id | Rule id (DELETE only) |
Message logs
Requires the read scope. Returns your most recent inbound and outbound messages, newest first.
Your SMS number
Every account needs exactly one dedicated SMS number before it can send or receive SMS — numbers aren't shared between customers. Claim one from the dashboard (Your SMS number) or via the API below.
GET requires read; POST/DELETE require send. POST body: {"device_id": 3} — get available device IDs from GET /api/v1/sms/devices first. DELETE releases your number back to the available pool.
Send an SMS
Requires the send scope, and a claimed number (see above) — sends always go out from your assigned number, never a shared pool. SMS is billed from your prepaid SMS credits — a credit is only ever spent once the send has actually succeeded. (If your account has been given a monthly SMS allowance directly by support, that's used first before credits.)
| Param | Description |
|---|---|
| to | Destination number, with country code (e.g. +27825550142) |
| message | Text body (required). text / content also accepted. |
| scheduled_at | Optional. ISO 8601 date/time — queues the send instead of sending inline (see below). |
billed_via is "plan" when it came out of your monthly allowance, or "credit" when it was billed against your prepaid credit balance. A 409 means you haven't claimed a number yet; a 429 means your allowance and credits are both exhausted.
Bulk & scheduled SMS
Requires the send scope. Queues up to 500 messages at once instead of sending inline — each is picked up by the send queue within a minute or so. Billing (credits, or a support-granted allowance if you have one) is checked per-message at send time, not when you submit the batch.
Every POST /api/v1/sms/send also accepts scheduled_at directly for a single scheduled message. Scheduled/queued messages appear immediately in GET /api/v1/sms/messages with status "scheduled" or "queued", then update to "sent"/"failed" once the queue processes them.
SMS message history
Requires the read scope. Lists your SMS history, or fetches a single message by its message_id.
SMS device pool
Requires the read scope. Returns available numbers plus your own (never another customer's assigned number) — label, number, active status, last heartbeat. The underlying gateway credentials are never exposed.
SMS webhook
Requires the webhook scope. Configure one forward URL to receive incoming SMS and delivery-status updates. Creating one returns a forward_secret — events are signed with it the same way WhatsApp forwarded events are, in the X-TFH-SMS-Signature header.
Other event types: sms.status_updated (data: message_id, status — sent / delivered / failed / expired).
Receiving messages: forwarded events
If you set a forward_url on a webhook, every inbound message on that number is POSTed to your server as it arrives — whether or not it matched a keyword rule:
matched_rule is the id of the keyword rule that fired, or null if nothing matched.
Verifying signatures
Every forwarded request includes an X-TFH-Signature header: an HMAC-SHA256 of the raw request body, signed with the webhook's forward_secret. Verify it before trusting the payload:
Need something the API doesn't cover yet? Contact support.