1. HTTP REST API
Use the HTTP REST API when your product, backend, CRM, or campaign platform needs a simple stateless integration for SMS submission.
1.1 Base URL and Authentication
All HTTP send requests go to your assigned xSignal host.
POST https://<your-assigned-host>/api/http/send
POST https://<your-assigned-host>/api/http/send/bulkEvery request must include X-API-Key and X-API-Secret headers. Invalid or inactive credentials return 401 Unauthorized.
1.2 Send to One or More Recipients
POST /api/http/send sends the same sender and text to every address in receivers. One message record is created per receiver.
{
"sender": "ABCDE",
"receivers": ["2348012345678", "2348098765432"],
"text": "Your OTP is 482913. It expires in 5 minutes.",
"sms_type": "TRANSACTIONAL"
}| Field | Required | Notes |
|---|---|---|
| sender | Yes | Sender ID or shortcode |
| receivers | Yes | MSISDNs in international format, no + |
| text | Yes | Plain GSM text |
| sms_type | Conditional | PROMOTIONAL or TRANSACTIONAL. Omit only if credential has BOTH route configured. |
{
"sent": 2,
"messages": [
{
"our_message_id": "hx_4f3a2b1c9d8e7f6a",
"provider_message_id": "MC-998877",
"receiver": "2348012345678",
"status": "SENT",
"provider_status": "PENDING",
"error_code": null,
"error_message": null
}
]
}1.3 Send a Batch of Independent Messages
POST /api/http/send/bulk lets each item have its own sender, receiver, text, and sms_type. The response preserves request order.
{
"messages": [
{"sender": "ABCDEF", "receiver": "2348012345678", "text": "Your OTP is 482913.", "sms_type": "TRANSACTIONAL"},
{"sender": "ABCDEF", "receiver": "2348098765432", "text": "50% off today only!", "sms_type": "PROMOTIONAL"}
]
}1.4 Message Status Values
| Status | Meaning |
|---|---|
| SENT | Accepted by upstream provider, awaiting final delivery confirmation |
| DELIVERED | Confirmed delivered to handset |
| FAILED | Rejected by provider or failed to send |
1.5 Routing Rules
Every HTTP credential has one or more active routes tied to sms_type: PROMOTIONAL or TRANSACTIONAL. xSignal first looks for a route matching the request sms_type, then falls back to a BOTH-type route if one is configured.
1.6 Error Responses
| HTTP Status | Meaning |
|---|---|
| 401 | Invalid X-API-Key or X-API-Secret |
| 404 | No active route for this credential / sms_type combination |
| 400 | Invalid sms_type value |
| 502 | Upstream provider call failed |
2. WhatsApp API
Use WhatsApp when you need app-based OTPs, structured customer notifications, rich campaign messages, or support conversations. WhatsApp sending uses either free-text messages inside the 24-hour customer care window or approved templates outside that window.
2.1 Send a Free-text Message
POST /api/wa/nv/send uses the same X-API-Key and X-API-Secret authentication model as the HTTP SMS API.
{
"receiver": "447700900000",
"message": "Your order has shipped!",
"sender": "SUPPORT"
}| Field | Required | Notes |
|---|---|---|
| receiver | Yes | Recipient MSISDN, full international format, no + or 00 |
| message | Yes | Free-text message body |
| sender | No | Your sender alias. Omit to use your default sender. |
{
"our_message_id": "wa_4f3a2b1c9d8e7f6a",
"message_uuid": "b1ed0340-f501-4d27-9598-886477478fab",
"status": "SENT"
}2.2 Send an Approved Template
POST /api/wa/nv/send-template is used for structured notifications, OTPs, order updates, alerts, and messages outside the 24-hour care window. The template must already be approved.
{
"receiver": "447700900000",
"template_name": "order_shipped",
"body_params": ["Jane", "TRK123456"],
"button_param": null,
"sender": "SUPPORT"
}| Field | Required | Notes |
|---|---|---|
| receiver | Yes | Recipient MSISDN, same format as free-text sends |
| template_name | Yes | One of your approved WhatsApp templates |
| body_params | No | Positional values for {{1}}, {{2}}, and other body placeholders |
| button_param | No | Required only when the template has a dynamic URL button |
| sender | No | Your sender alias |
2.3 Sender Aliases
GET /api/wa/nv/senders lists the aliases provisioned for your account, such as SUPPORT or BILLING. Pass an alias as sender when you want logs and routing to reflect a specific line of business.
2.4 Managing Templates
Template management uses client portal authentication, not API key authentication. Use X-Client-ID and X-Token headers when creating, listing, syncing, or uploading media for templates.
| Endpoint | Purpose |
|---|---|
| POST /api/wa/nv/templates | Create a new template for WhatsApp review |
| GET /api/wa/nv/templates | List your templates, optionally filtered by status |
| GET /api/wa/nv/templates/{template_id} | Retrieve one template |
| POST /api/wa/nv/templates/{template_id}/sync | Refresh approval status |
| POST /api/wa/nv/templates/media | Upload image, video, or document media for media-header templates |
2.5 WhatsApp Errors
| HTTP Status | Meaning |
|---|---|
| 401 | Invalid or missing authentication |
| 404 | No active WhatsApp route, unknown sender alias, or template not found |
| 409 | Route misconfigured or template not approved yet |
| 400 | Invalid request body or unsupported media type |
| 402 | Insufficient wallet balance |
| 502 | WhatsApp send or template-management call failed upstream |
3. Reverse OTP Verification
Reverse OTP proves phone possession by asking the end user to send a one-tap WhatsApp or SMS message back to xSignal. After xSignal confirms the inbound sender, your app asks the user for the OTP you generated and completes the verification with the same session.
3.1 Verification Flow
This public flow diagram shows the integration handoff between your app, the user, xSignal, and the messaging channel. Sensitive security controls are intentionally abstracted.
sequenceDiagram
autonumber
actor U as User
participant CA as Client App
participant XS as xSignal API
participant CH as WhatsApp / SMS
U->>CA: Starts phone verification
CA->>XS: POST /api/verify/start with encrypted request
XS-->>CA: Encrypted response with session ID and channel links
CA-->>U: Shows WhatsApp, SMS, or QR verification option
U->>CH: Sends the prefilled verification message
CH->>XS: Delivers inbound verification signal
alt Possession confirmed
XS-->>U: Sends OTP on the same channel
XS-->>CA: Signed webhook: verification.msisdn_confirmed
U->>CA: Enters OTP or follows prefilled verify link
CA->>XS: POST /api/verify/confirm with session ID
XS-->>CA: Signed webhook: verification.success
else Verification cannot continue
XS-->>CA: Signed webhook: verification.failed with reason code
opt Fallback enabled
XS->>CH: Sends fallback SMS to registered phone
CH-->>U: Fallback verification prompt
U->>XS: Completes fallback verification
XS-->>CA: Signed webhook: verification.success
end
end3.2 What You Need
- client_id: your enabled platform client identifier.
- encryption credential: shared through a secure channel and stored server-side only.
- webhook signing secret: used to verify signed xSignal webhook events.
- Webhook URL: your HTTPS endpoint for verification events.
3.3 Encrypted Start Request
POST /api/verify/start starts a verification session. The request and response payloads are encrypted. Your assigned integration pack provides the exact implementation details privately.
POST /api/verify/start
Content-Type: application/json
{
"client_id": "your-client-id",
"payload": "encrypted_payload"
}{
"phone": "08012345678",
"otp": "482913",
"nonce": "fresh-random-value",
"timestamp": 1785600000,
"wa_number": "2348011119999",
"sms_channel": "ACMEBANK"
}| Field | Required | Notes |
|---|---|---|
| phone | Yes | User phone number. xSignal normalizes common Nigerian formats server-side. |
| otp | Yes | You generate the code. xSignal relays it after possession is proven. |
| nonce | Yes | Fresh random value per request. Reuse is rejected as replay. |
| timestamp | Yes | Unix seconds. Requests older than 5 minutes or more than 30 seconds in the future are rejected. |
| wa_number | No | Approved branded WhatsApp number. Omit to use the shared xSignal default. |
| sms_channel | No | Approved shortcode or alphanumeric Sender ID. Unapproved values return 403. |
3.4 Start Response
Read the protected response payload using your private integration details. Render the WhatsApp and SMS links as tap-to-send buttons. Keep the session_id for status polling and confirmation.
{
"session_id": "4783fdd6-c6a7-41d0-bfb9-bd3ba74aecbe",
"wa_link": "https://wa.me/234XXXXXXXXX?text=VERIFY%20abc123",
"sms_link": "sms:XXXXX?body=VERIFY%20abc123",
"ttl_seconds": 300
}3.5 Confirm and Status
POST /api/verify/confirm is a plain JSON call. Use it only after the session reaches MSISDN_CONFIRMED and your app has validated the OTP entered by the user.
POST /api/verify/confirm
{ "session_id": "4783fdd6-c6a7-41d0-bfb9-bd3ba74aecbe" }GET /api/verify/status/{session_id}
{
"session_id": "...",
"status": "PENDING",
"attempts": 1
}Status values are PENDING, MSISDN_CONFIRMED, VERIFIED, and FAILED.
3.6 Webhooks
xSignal sends signed JSON events to your webhook URL. Verify every event with the webhook signing secret and the private verification instructions supplied during onboarding.
| Event | Meaning |
|---|---|
| verification.msisdn_confirmed | User inbound message matched. Channel may be whatsapp, sms, or fallback_mt_sms. |
| verification.success | Your /verify/confirm call succeeded. This is the completion and billing event. |
| verification.failed | Session failed or needs retry handling. Read reason_code. |
3.7 Failure Codes and Billing
| Reason Code | Meaning | Retry |
|---|---|---|
| SENDER_MISMATCH | Inbound message came from a different number than submitted. | User can retry from the correct SIM or WhatsApp number. |
| SESSION_EXPIRED | No matching inbound message within the 5-minute window. | Fallback may reissue the same session through MT SMS. |
| TOKEN_REUSED | Single-use link/message token was reused. | No action required. |
| RATE_LIMITED | Attempt cap exceeded. | Fallback may reissue through MT SMS. |
Billing is charged on a successful /verify/start attempt. A later mismatch, expiry, or failure does not refund the initiated verification cost. If /verify/start returns 402, no session was created and no charge was taken.
3.8 HTTP Errors
| Status | When |
|---|---|
| 400 | Malformed protected payload, stale timestamp, missing field, or invalid phone. |
| 401 | Unknown client_id or not enabled yet. |
| 402 | Insufficient wallet balance. |
| 403 | Suspended client_id or unapproved wa_number / sms_channel. |
| 404 | Unknown or expired session_id. |
| 409 | Nonce reused, or confirm called before MSISDN_CONFIRMED. |
| 429 | Per-phone or per-IP rate limit exceeded. |
| 503 | Server-side service misconfiguration. Contact support. |
4. SMPP Guide
Use SMPP 3.4 when you already operate SMPP infrastructure, need persistent high-throughput binds, or want real-time delivery reports pushed over the same session.
4.1 Connection
| Parameter | Value |
|---|---|
| Protocol | SMPP v3.4 |
| Host / Port | Provided by your account manager. Default service port: 2775 |
| Bind types | bind_transceiver, bind_transmitter, bind_receiver |
| Enquire-link interval | 30 seconds recommended |
| Response timeout | 30 seconds |
4.2 Binding
Send bind_transceiver, bind_transmitter, or bind_receiver with your system_id and password. bind_transceiver is recommended because it lets you submit and receive delivery reports on the same session.
Authentication checks your source IP whitelist where configured, then validates your system_id and password against an active credential.
4.3 Sending with submit_sm
Once bound, send a submit_sm PDU with source_addr, destination_addr, short_message, and data_coding. Use registered_delivery = 1 when you want a delivery receipt pushed back to your session.
xSignal acknowledges with submit_sm_resp before routing upstream. Insufficient wallet balance returns ESME_RSUBMITFAIL. Exceeding TPS returns ESME_RTHROTTLED, so slow down and retry.
4.4 Delivery Reports
If registered_delivery = 1 and your session is still bound when final carrier status returns, xSignal sends deliver_sm to you. Respond with deliver_sm_resp to acknowledge.
4.5 Keepalive and Unbind
Send enquire_link every 30 seconds. Send unbind to close gracefully. If no valid PDU is received within the response timeout, the server may drop the connection.
4.6 SMPP Status Codes
| Code | Name | Meaning |
|---|---|---|
| 0x00000000 | ESME_ROK | Success |
| 0x00000004 | ESME_RINVBNDSTS | PDU sent before binding |
| 0x00000005 | ESME_RALYBND | Already bound on this connection |
| 0x0000000D | ESME_RBINDFAIL | Bind failed: bad IP or credentials |
| 0x0000000E | ESME_RINVPASWD | Invalid password |
| 0x0000000F | ESME_RINVSYSID | Invalid system ID |
| 0x00000014 | ESME_RMSGQFUL | Message queue full |
| 0x00000058 | ESME_RTHROTTLED | Rate limit exceeded |
| - | ESME_RSUBMITFAIL | Submission failed, for example insufficient balance |
5. Getting Credentials
Credentials are provisioned by xSignal. You do not self-register API credentials.
- HTTP and WhatsApp sending: you receive an X-API-Key and X-API-Secret pair. Multiple HTTP credentials can be issued per app, environment, or message type.
- Reverse OTP: you receive a client_id, private encryption credential, webhook signing secret, and approved WhatsApp/SMS channels where applicable.
- SMPP: you receive a system_id, password, and IP whitelist where applicable.
- WhatsApp templates: template management uses client portal session headers, not API key headers.
Keep API secrets, encryption credentials, webhook signing secrets, and SMPP passwords server-side only.
6. Retrieving API Keys
API keys are retrievable through the client portal, gated behind your session login and a separate numeric passcode. This protects your keys even if a session token leaks.
https://cl.xsignalling.xyz/apiPortal key retrieval uses X-Client-ID, X-Token, and X-Passcode headers. Keep your keys secret. They are returned as plain text on request and should be treated like database passwords.
7. Delivery Report Reconciliation
Regardless of protocol, delivery status updates are tracked centrally. For SMPP, delivery reports can also be pushed live through deliver_sm. For Reverse OTP, signed webhooks and /api/verify/status/{session_id} provide the verification backstop.
GET /api/portal/webhooksThis uses the same X-Client-ID / X-Token authentication as the rest of the portal.
8. Rate Limits and Best Practices
- Use sms_type correctly: TRANSACTIONAL or PROMOTIONAL.
- Use WhatsApp templates for messages outside the 24-hour care window.
- For Reverse OTP, generate a fresh nonce per request, never log private credentials, and verify webhook signatures before trusting events.
- For SMPP, prefer bind_transceiver and keep the session alive with enquire_link.
- For HTTP, use /send/bulk for mixed batches instead of looping individual send requests.
- Never hardcode API secrets or SMPP passwords in client-side code.
9. Support
Contact your xSignal account manager for credential provisioning, Reverse OTP pilot access, sender ID registration, WhatsApp template onboarding, or integration issues.