Quick Start
Send your first SMS in under two minutes.
Prerequisites
- Create a free test account at portal2.quicksms.com/signup
- Create an API connection in Management > API Connections
- Copy your API key (starts with
sk_live_orsk_test_)
Send a Test Message
Use your sk_test_ key to send in sandbox mode. No credits are deducted and no real SMS is delivered.
curl -X POST https://api.quicksms.com/v1/messages \ -H "Authorization: Bearer sk_test_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "MSISDN": "447700900001", "msg": "Hello from QuickSMS!", "sender": "QuickSMS" }'
QuickSMS default test sender. To use a custom sender ID:• Register an SMS Sender ID
• Purchase a Virtual Mobile Number
sk_test_ keys) can only send to numbers in your approved test allowlist. Add test numbers in API Connections. The text "This is a QuickSMS test message" is automatically appended.
Expected Response
{
"sandbox": true,
"total": 1,
"accepted": 1,
"rejected": 0,
"messages": [{
"quicksms_id": "msg_80fac5c752941b27",
"MSISDN": "447700900001",
"sender": "QuickSMS",
"status": "ACCEPTED",
"channel": "SMS",
"encoding": "GSM-7",
"message_parts": 1
}],
"created_at": "2026-03-26T12:00:00+00:00"
}Switch to Production
Replace your sk_test_ key with your sk_live_ key. Ensure your sender ID is approved for the destination country. Credits will be deducted per message segment.
Authentication
All API requests require a valid API key in the Authorization header.
Bearer Token (Recommended)
Authorization: Bearer sk_live_EXAMPLE_KEY_REPLACE_ME
Basic Authentication
For backwards compatibility. Send your API key as the username with an empty password.
Authorization: Basic base64(sk_live_EXAMPLE_KEY_REPLACE_ME:)
your_api_key: (note the trailing colon). The password field is ignored.
API Key Types
| Prefix | Environment | Behaviour |
|---|---|---|
sk_live_ | Production | Sends real messages. Deducts credits. |
sk_test_ | Test | Forces sandbox mode. Can only send to approved test numbers. |
Connection Status
Only active connections can send messages.
| Status | Can Send? | Description |
|---|---|---|
draft | No | Created but not yet activated |
active | Yes | Fully operational |
suspended | No | Temporarily disabled by admin |
archived | No | Permanently disabled |
IP Allowlisting
Optionally restrict access to specific IPs. Unlisted IPs receive 403 with error code IP_NOT_ALLOWED. Configure at portal2.quicksms.com/account/security.
Brute-Force Protection
After 10 consecutive failed auth attempts from the same IP within 60 seconds, that IP is locked out for 5 minutes. The response includes a Retry-After header.
Best Practices
- Store API keys in environment variables or a secrets manager — never commit to source control.
- Use
sk_test_keys in development/staging; reservesk_live_keys for production. - If a key may be compromised, rotate it immediately in portal2.quicksms.com.
- Enable IP allowlisting in production.
Send a Message
Send an SMS or RCS message to a single recipient. The API validates, prices, and queues the message in a single request.
Request Headers
| Header | Description | |
|---|---|---|
Authorization | Required | Bearer sk_live_xxx or Basic base64(sk_live_xxx:) |
Content-Type | Required | application/json |
Idempotency-Key | Optional | Unique key (max 128 chars) to prevent duplicate sends |
Request Body
| Field | Type | Description | |
|---|---|---|---|
MSISDN | string | Required | Recipient phone number. International format, digits only, no + prefix. 7–15 digits. E.g. 447700900000 |
msg | string | Required | Message content. 1–1,530 characters. |
sender | string | Required | Sender ID. Must be pre-registered and approved. |
smart_routing | boolean | Optional | Enable RCS with SMS fallback. Default false. |
sms_failover_sender | string | Optional | SMS sender ID to use if RCS falls back to SMS. |
expiry | integer | Optional | Validity in seconds. Min 60, max 259,200 (72h). Default 172,800 (48h). |
customer_id | string | Optional | Your reference. Max 128 chars. Returned in DLR webhooks. |
metadata | object | Optional | Key-value pairs. Max 10 keys, each value max 256 chars. |
sandbox | boolean | Optional | Force sandbox mode. Default false. |
callback_url | string | Optional | Per-message DLR webhook URL. Max 2,048 chars. |
MSISDN Format
MSISDN (Mobile Station International Subscriber Directory Number) is the international phone number format used by telecom APIs. Digits only, country code prefix, no +.
| Example | Country |
|---|---|
447700900000 | United Kingdom (+44) |
353871234567 | Ireland (+353) |
12025551234 | United States (+1) |
Sender ID Formats
| Type | Rules | Example |
|---|---|---|
| Alphanumeric | Max 11 chars. Must contain at least one letter. | MyStore |
| Numeric | Max 15 digits. | 447700900000 |
| RCS agent | Max 25 chars. Only when smart_routing is true. | MyBrandAgent |
Example Request
curl -X POST https://api.quicksms.com/v1/messages \ -H "Authorization: Bearer sk_live_EXAMPLE_KEY_REPLACE_ME" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-12345-sms" \ -d '{ "MSISDN": "447700900000", "msg": "Your order #12345 has been dispatched.", "sender": "MyStore", "customer_id": "cust_abc123", "metadata": { "order_id": "12345", "type": "dispatch_notification" }, "callback_url": "https://example.com/webhooks/sms" }'
Try It
Understanding Responses
Every send request returns one of three outcomes.
Success — Message Queued
{
"sandbox": false,
"total": 1,
"accepted": 1,
"rejected": 0,
"messages": [{
"quicksms_id": "msg_80fac5c752941b27",
"MSISDN": "447700900000",
"sender": "MyStore",
"status": "ACCEPTED",
"channel": "SMS",
"encoding": "GSM-7",
"message_parts": 1,
"customer_id": "cust_abc123"
}],
"created_at": "2026-03-26T12:00:00+00:00"
}quicksms_id. If you configured a callback_url, you will receive a delivery receipt when the message reaches a final status. There is no need to poll.Success — Message Held
{
"sandbox": false,
"messages": [{
"quicksms_id": "msg_80fac5c752941b27",
"status": "HELD",
"scheduled_send_at": "2026-03-27T08:00:00+00:00"
}]
}scheduled_send_at. Credits are reserved immediately.Rejection — Message Not Sent
{
"messages": [{
"MSISDN": "447700900000",
"status": "REJECTED",
"error": {
"code": "CONTENT_BLOCKED",
"message": "Message content was blocked by the content filter."
}
}]
}error.code field and refer to the Error Handling section for corrective action.Response Fields
| Field | Type | Description |
|---|---|---|
sandbox | boolean | Whether sandbox mode was used |
total | integer | Total messages in the request |
accepted / rejected | integer | Counts of accepted and rejected messages |
messages[].quicksms_id | string | Unique message ID (msg_{16 hex}). Only present when accepted. |
messages[].status | string | ACCEPTED, HELD, or REJECTED |
messages[].channel | string | SMS or RCS |
messages[].encoding | string | GSM-7 or UCS-2. null for RCS. |
messages[].message_parts | integer | SMS segment count. Always 1 for RCS. |
messages[].scheduled_send_at | string | ISO 8601. Only when status is HELD. |
messages[].error | object | code and message. Only when REJECTED. |
created_at | string | ISO 8601 timestamp |
Message Encoding
The API automatically selects the most efficient encoding for your message content.
GSM-7 (Standard)
| Segments | Characters |
|---|---|
| 1 | Up to 160 |
| 2 | 161–306 |
| 3 | 307–459 |
| N | ⌈length ÷ 153⌉ |
UCS-2 (Unicode)
| Segments | Characters |
|---|---|
| 1 | Up to 70 |
| 2 | 71–134 |
| 3 | 135–201 |
| N | ⌈length ÷ 67⌉ |
Delivery Webhooks (DLR)
When a message reaches a final delivery status, QuickSMS sends a POST request to your configured callback URL.
Callback URL Priority
| Priority | Source | How to Configure |
|---|---|---|
| 1 (highest) | Per-message callback_url | Include in the send request body |
| 2 | Connection-level URL | Set at portal2.quicksms.com/management/api-connections |
If neither is configured, no webhook is delivered.
Webhook Headers
POST {your_callback_url}
Content-Type: application/json
X-QuickSMS-Timestamp: 1711468800
X-QuickSMS-Event: dlr
X-QuickSMS-Delivery-Attempt: 1
X-QuickSMS-Signature: sha256=5d7a8c3e...Webhook Payload
{
"quicksms_id": "msg_80fac5c752941b27",
"customer_id": "cust_abc123",
"channel": "SMS",
"message_status": "DELIVERED",
"status_code": 0,
"message_parts": 1,
"message_sent_time": "2026-03-26T12:00:01+00:00",
"delivery_receipt_time": "2026-03-26T12:00:03+00:00",
"read_receipt": null,
"read_receipt_timestamp": null,
"done_time": "2026-03-26T12:00:03+00:00",
"metadata": { "order_id": "12345" }
}Payload Fields
| Field | Type | Description |
|---|---|---|
quicksms_id | string | Message ID from send response |
customer_id | string|null | Your reference (if provided) |
channel | string | Actual channel: SMS or RCS. May differ from original if RCS fell back. |
message_status | string | Final delivery status |
status_code | integer | GSM status code |
message_parts | integer | Segment count |
read_receipt | bool|null | true if read (RCS only). null for SMS. |
read_receipt_timestamp | string|null | ISO 8601 — when read (RCS only) |
done_time | string | ISO 8601 — when message reached final status |
metadata | object|null | Your metadata (if provided) |
Retry Policy
Return any 2xx to acknowledge. Failures are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 10 minutes |
| 5 | 1 hour |
Best Practices
- Respond quickly. Return
200immediately and process asynchronously. - Handle duplicates. Use
quicksms_idas a deduplication key. - Verify signatures. Validate
X-QuickSMS-Signaturebefore processing.
Message Lifecycle
A message moves through distinct statuses from submission to final delivery.
Send Response Statuses
| Status | Meaning | Next Step |
|---|---|---|
ACCEPTED | Queued for delivery | Wait for DLR webhook |
HELD | Delayed by out-of-hours rules | Auto-dispatched at scheduled_send_at |
REJECTED | Failed validation — not sent | Check error.code and fix |
Delivery Statuses (via Webhook)
| Status | Final? | Meaning |
|---|---|---|
PENDING | No | Awaiting carrier confirmation |
DELIVERED | Yes | Confirmed delivered to handset |
UNDELIVERED | Yes | Delivery failed (e.g. phone off) |
EXPIRED | Yes | Validity period elapsed |
REJECTED | Yes | Rejected by carrier |
Status Flow
Send Response Delivery Webhook
───────────── ─────────────────
ACCEPTED ──────────────▶ PENDING
│
├──▶ DELIVERED
├──▶ UNDELIVERED
├──▶ EXPIRED
└──▶ REJECTED
HELD ───(at scheduled)──▶ PENDING ──▶ (same as above)
REJECTED (no webhook — message was never sent)GSM Status Codes
| Code | Meaning |
|---|---|
0 | Delivered successfully |
1 | Invalid destination number |
13 | Rejected by operator |
27 | Absent subscriber (phone off or out of range) |
32 | Network error |
255 | Unknown / expired |
Retry Strategy
Not all errors should be retried. Use the HTTP status code to decide.
| Status | Retry? | Strategy |
|---|---|---|
429 | Yes | Wait for Retry-After header value, then retry. |
500 | Yes | Retry with exponential backoff. |
503 | Yes | Service degraded. Retry with backoff. |
400–422 | No | Fix the request before resending. |
Recommended Backoff
| Attempt | Base Delay | With Jitter |
|---|---|---|
| 1 | 1 second | 0.8–1.2s |
| 2 | 2 seconds | 1.6–2.4s |
| 3 | 4 seconds | 3.2–4.8s |
| 4 | 8 seconds | 6.4–9.6s |
| 5 | 16 seconds | 12.8–19.2s |
request_id. Always include the same Idempotency-Key when retrying sends to guarantee at-most-once delivery.Rate Limits
Applied per API connection using a 60-second sliding window. Default: 1,000 requests per minute.
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per minute |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
X-RateLimit-Remaining. Throttle when below 10% of your limit. On 429, wait for Retry-After seconds. Ramp up gradually after reset.Idempotency
Include an Idempotency-Key header (max 128 chars) to prevent duplicate sends.
| Scenario | Result |
|---|---|
| Same key + same payload | Returns cached 202. No duplicate send. |
| Same key + different payload | 409 Conflict — IDEMPOTENCY_CONFLICT |
| New key | Normal processing |
| Key exceeds 128 chars | 422 validation error |
Keys are scoped to your API connection and cached for 24 hours.
order-12345-dispatch-sms) rather than random UUIDs. This makes retries naturally use the same key.Smart Routing (RCS)
When smart_routing is true and the sender is an approved RCS agent:
- Message is routed via RCS
- If the recipient doesn't support RCS, the system falls back to SMS automatically
- Credits are adjusted — if SMS costs more, the difference is deducted; if less, refunded
- DLR webhook
channelfield indicates which channel was actually used - RCS is billed per message (always 1 part), not per segment
read_receipt: true and read_receipt_timestamp when a recipient reads an RCS message. These are null for SMS.SMS Failover Sender
When an RCS message falls back to SMS, the sender ID is resolved in this order:
| Priority | Source | Description |
|---|---|---|
| 1 (highest) | sms_failover_sender field | Explicit sender ID in the API request |
| 2 | Default registered sender | Sender marked is_default = true on your account |
| 3 | First approved sender | First approved sender on your account |
| 4 (last resort) | Sanitised agent name | RCS agent name stripped of non-alphanumeric chars, truncated to 11 chars |
sms_failover_sender explicitly. Without either, the fallback sender may not match your brand.Out-of-Hours Sending
Accounts can restrict dispatch to a defined business hours window. Configured per account in the portal.
| Action | API Behaviour | Credits |
|---|---|---|
reject | Message rejected with OUT_OF_HOURS. Response includes the window and resume time. | Not deducted |
hold | Accepted with status HELD. Dispatched when window opens. Includes scheduled_send_at. | Reserved immediately |
Anti-Flood Protection
Prevents duplicate messages to the same recipient within a configurable window. A duplicate is the same account sending identical content to the same MSISDN within the window.
| Mode | Behaviour |
|---|---|
off | No duplicate checking |
enforce | Duplicates rejected with DUPLICATE_MESSAGE. Response includes original message ID. |
monitor | Duplicates logged but message still sent |
Country Restrictions
The recipient's country is determined by MSISDN prefix (e.g. 44 = UK, 353 = Ireland). Per-account overrides are checked first, then the platform-wide setting. Blocked destinations return COUNTRY_NOT_ALLOWED.
Sandbox Mode
Simulates the full send-and-deliver flow without carrier delivery. No credits deducted.
How to Activate
| Method | Description |
|---|---|
Use a sk_test_ key | Always forces sandbox mode |
Set "sandbox": true | Works with any key type |
Connection environment = test | Configured at portal2.quicksms.com/management/api-connections |
Differences from Production
| Behaviour | Production | Sandbox |
|---|---|---|
| Real SMS/RCS delivery | Yes | No |
| Credits deducted | Yes | No |
| Anti-flood checks | Active | Bypassed |
| Out-of-hours checks | Active | Bypassed |
| Delivery webhooks | Real carrier DLR | Simulated after 3–5 seconds |
| RCS read receipts | From recipient device | Simulated after 5–10 seconds |
Simulated Delivery Outcomes
| MSISDN Ends In | Simulated Status | Status Code |
|---|---|---|
0000 | UNDELIVERED | 27 (Absent subscriber) |
9999 | EXPIRED | 255 |
| Anything else | DELIVERED | 0 |
Webhook Security
When a signing secret is configured, every webhook includes an X-QuickSMS-Signature header with an HMAC-SHA256 signature.
Verification Steps
- Extract the
X-QuickSMS-Timestampheader value - Get the raw request body (do not parse or re-serialize)
- Concatenate:
{timestamp}.{body} - Compute HMAC-SHA256 using your signing secret as the key
- Compare with the header signature using constant-time comparison
- Reject if timestamp is more than 5 minutes old (replay protection)
Verification Examples
const crypto = require('crypto'); function verifyWebhook(req, secret) { const timestamp = req.headers['x-quicksms-timestamp']; const signature = req.headers['x-quicksms-signature']; const body = req.rawBody; if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) { throw new Error('Timestamp too old'); } const payload = `${timestamp}.${body}`; const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(payload).digest('hex'); if (!crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(signature) )) { throw new Error('Invalid signature'); } return JSON.parse(body); }
import hmac, hashlib, time, json def verify_webhook(request, secret): timestamp = request.headers.get('X-QuickSMS-Timestamp', '') signature = request.headers.get('X-QuickSMS-Signature', '') body = request.get_data(as_text=True) if abs(time.time() - int(timestamp)) > 300: raise ValueError('Timestamp too old') payload = f'{timestamp}.{body}' expected = 'sha256=' + hmac.new( secret.encode(), payload.encode(), hashlib.sha256 ).hexdigest() if not hmac.compare_digest(expected, signature): raise ValueError('Invalid signature') return json.loads(body)
$secret = 'your_webhook_signing_secret'; $timestamp = $_SERVER['HTTP_X_QUICKSMS_TIMESTAMP'] ?? ''; $signature = $_SERVER['HTTP_X_QUICKSMS_SIGNATURE'] ?? ''; $body = file_get_contents('php://input'); if (abs(time() - (int) $timestamp) > 300) { http_response_code(401); die('Timestamp too old'); } $payload = "{$timestamp}.{$body}"; $expected = 'sha256=' . hash_hmac('sha256', $payload, $secret); if (!hash_equals($expected, $signature)) { http_response_code(401); die('Invalid signature'); } $data = json_decode($body, true); http_response_code(200);
Security Best Practices
| Practice | Detail |
|---|---|
| Environment-specific keys | Use sk_test_ in dev/staging. sk_live_ in production only. |
| Store keys securely | Environment variables or secrets manager. Never source control. |
| Rotate compromised keys | Rotate immediately in portal2.quicksms.com. |
| IP allowlisting | Restrict production API access to known server IPs. |
| Verify webhook signatures | Always validate X-QuickSMS-Signature before processing. |
| Timestamp freshness | Reject webhooks with timestamps older than 5 minutes. |
| HTTPS callback URLs | Always configure webhook endpoints over HTTPS. |
| Idempotency keys | Protect against duplicate sends from network retries. |
Error Handling
All errors return consistent JSON with a unique request_id for support troubleshooting.
{
"error": {
"type": "validation_error",
"code": "INVALID_PHONE_NUMBER",
"message": "The MSISDN is not valid international format.",
"param": "MSISDN",
"request_id": "req_a1b2c3d4"
}
}HTTP Status Codes
| Code | Meaning | Retryable? | What to Do |
|---|---|---|---|
202 | Accepted / held | — | — |
400 | Bad request | No | Fix request format or missing fields |
401 | Auth failed | No | Check API key |
403 | Forbidden | No | Check account status or IP allowlist |
404 | Not found | No | Check URL path |
409 | Idempotency conflict | No | Use a new key for different payloads |
422 | Validation failed | No | Check error code and fix |
429 | Rate limited | Yes | Wait Retry-After seconds |
500 | Server error | Yes | Retry with backoff |
Message Rejection Codes
| Code | Description | What to Do |
|---|---|---|
SENDER_NOT_REGISTERED | Sender ID not approved | Register at portal2.quicksms.com/management/sms-sender-id/register |
TEST_NUMBER_NOT_APPROVED | Test: number not in allowlist | Add at portal2.quicksms.com/management/api-connections |
COUNTRY_NOT_ALLOWED | Destination country blocked | Check country settings |
INSUFFICIENT_CREDITS | Balance too low | Top up account |
CONTENT_BLOCKED | Content filter triggered | Revise content or contact support |
OUT_OF_HOURS | Outside business hours (reject mode) | Retry during hours or switch to hold |
DUPLICATE_MESSAGE | Anti-flood: duplicate detected | Wait or change content |
Request-Level Error Codes
| Code | HTTP | What to Do |
|---|---|---|
MISSING_FIELD | 400 | Add the missing field (check param) |
BAD_REQUEST | 400 | Validate JSON format |
INVALID_PHONE_NUMBER | 422 | Digits only, no +, 7–15 digits, non-zero start |
CONTENT_TOO_LONG | 422 | Shorten to ≤1,530 chars |
VALIDATION_ERROR | 422 | Check message field for detail |
UNAUTHORIZED | 401 | Verify API key and header format |
IP_NOT_ALLOWED | 403 | Add IP at portal2.quicksms.com/account/security |
ACCOUNT_INACTIVE | 403 | Contact admin or QuickSMS support |
RATE_LIMITED | 429 | Wait Retry-After seconds |
AUTH_RATE_LIMITED | 429 | Wait lockout; verify key is correct |
IDEMPOTENCY_CONFLICT | 409 | Use a unique key per distinct message |
INTERNAL_ERROR | 500 | Retry with backoff; contact support if persistent |
Health Endpoint
Check the API's operational status. No authentication required.
{ "status": "healthy" }{ "status": "degraded" }Use this endpoint for uptime monitoring. A 200 means all critical services are operational. A 503 indicates temporary degradation — retry with backoff.
Code Examples
Basic SMS
curl -X POST https://api.quicksms.com/v1/messages \ -H "Authorization: Bearer sk_live_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "MSISDN": "447700900000", "msg": "Hello from QuickSMS!", "sender": "MyApp" }'
RCS with SMS Fallback
curl -X POST https://api.quicksms.com/v1/messages \ -H "Authorization: Bearer sk_live_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "MSISDN": "447700900000", "msg": "Your parcel is out for delivery!", "sender": "MyBrandAgent", "smart_routing": true, "sms_failover_sender": "MyBrand", "callback_url": "https://example.com/webhooks/dlr" }'
$ch = curl_init('https://api.quicksms.com/v1/messages'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer sk_live_your_key_here', 'Content-Type: application/json', 'Idempotency-Key: order-12345-sms', ], CURLOPT_POSTFIELDS => json_encode([ 'MSISDN' => '447700900000', 'msg' => 'Hello from QuickSMS!', 'sender' => 'MyApp', ]), ]); $response = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $data = json_decode($response, true); if ($status === 202) { $msg = $data['messages'][0]; echo "Sent: {$msg['quicksms_id']} — Status: {$msg['status']}"; } else { echo "Error: " . ($data['error']['message'] ?? $data['messages'][0]['error']['message']); }
import requests response = requests.post( 'https://api.quicksms.com/v1/messages', headers={ 'Authorization': 'Bearer sk_live_your_key_here', 'Idempotency-Key': 'order-12345-sms', }, json={ 'MSISDN': '447700900000', 'msg': 'Hello from QuickSMS!', 'sender': 'MyApp', } ) if response.status_code == 202: msg = response.json()['messages'][0] print(f"Sent: {msg['quicksms_id']} — Status: {msg['status']}") elif response.status_code == 422: err = response.json()['messages'][0]['error'] print(f"Rejected: {err['code']} — {err['message']}") else: print(f"Error: {response.json()['error']['message']}") print(f"Rate limit remaining: {response.headers.get('X-RateLimit-Remaining')}")
const response = await fetch('https://api.quicksms.com/v1/messages', { method: 'POST', headers: { 'Authorization': 'Bearer sk_live_your_key_here', 'Content-Type': 'application/json', 'Idempotency-Key': 'order-12345-sms', }, body: JSON.stringify({ MSISDN: '447700900000', msg: 'Hello from QuickSMS!', sender: 'MyApp', }), }); const data = await response.json(); if (response.status === 202) { const msg = data.messages[0]; console.log(`Sent: ${msg.quicksms_id} — Status: ${msg.status}`); } else { console.log(`Error: ${data.error?.message || data.messages?.[0]?.error?.message}`); }
using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer sk_live_your_key_here"); client.DefaultRequestHeaders.Add("Idempotency-Key", "order-12345-sms"); var payload = new { MSISDN = "447700900000", msg = "Hello from QuickSMS!", sender = "MyApp" }; var response = await client.PostAsJsonAsync( "https://api.quicksms.com/v1/messages", payload); var json = await response.Content.ReadAsStringAsync(); Console.WriteLine(response.StatusCode == System.Net.HttpStatusCode.Accepted ? $"Sent: {json}" : $"Error ({(int)response.StatusCode}): {json}");
Receiving Incoming Messages V1
https://api.quicksms.com/rcs/v2/api. A V2 version with enhanced features is coming soon.
When an incoming message is received by the QuickSMS system, it is checked against a list of registered numbers and RCS senders. If a match is found, a POST request is made to your configured endpoint.
Register your numbers at portal2.quicksms.com/management/numbers.
Incoming Message Fields
| Information | POST Field | Example |
|---|---|---|
| Unique Reference | ID | 42 |
| Number/Sender message sent from | MSISDN | 447123456789 |
| Message | Message | Hello |
| Number message sent to | Number | 44789011829 |
| Received Date and Time | DateRecieved | 2012-06-27 12:33:00 |
Respond within ~1 second with RECEIVED.
Example Incoming Message
POST https://www.website.co.uk/incoming.php
Content-Type: application/json
{
"MSISDN": "NotifyTest",
"Message": "Testing",
"Number": "44789100000",
"DateRecieved": "2024-01-28 11:54:32",
"ID": "MxJFwONIywR7yZysg7iAtDXQ"
}Coming Soon
Planned capabilities and enhancements.
Batch/Bulk Sending
Send messages to multiple recipients in a single API request. Ideal for campaigns and high-volume transactional messaging.
Message Scheduling
Schedule messages for future delivery. Perfect for appointment reminders and time-zone-aware campaigns.
Receive Message V2
Enhanced inbound messaging with HMAC webhook signing, structured JSON payloads, and metadata support. Upgrading from the current V1 inbound API.
Manage Contacts API
Create, update, and manage contacts and contact lists programmatically via the API.
Manage Account API
Manage account settings, users, and billing programmatically via the API.
Changelog
- Bearer token & Basic auth
- Idempotency keys (24-hour cache)
- Smart routing — RCS with automatic SMS fallback
- Sandbox mode with simulated delivery
- Webhook signing (HMAC-SHA256)
- Structured JSON error responses with
request_id - Rate limit headers (
X-RateLimit-*) - Anti-flood protection & out-of-hours controls
- Country restrictions with MSISDN prefix matching
- Receive incoming SMS & RCS messages via webhook
- Registered number and RCS sender matching
- Webhook delivery to configured endpoint