SMS Preflight API

API Documentation

Automate SMS delivery testing from your CI pipeline, monitoring stack, or any HTTP client. Available on Pro and Team plans.

Base URL: https://api.smsprobe.co.uk/api

Quick start

  1. Generate an API key in Dashboard → API Keys
  2. Create a test session via POST /sessions with your Sender ID
  3. From your SMS platform, send a message to each phone_number in the response
  4. Poll GET /sessions/:id every few seconds until status is complete or expired
  5. Read the per-network verdictclean, intercepted, or lost

Authentication

All protected endpoints require an API key sent as a Bearer token. Generate keys in your API Keys dashboard. Keys are prefixed sp_ and never expire until revoked.

Required headers
Authorization: Bearer sp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

Account endpoints

GET/auth/meAPI key

Returns the authenticated user, their active plan, and usage this month.

Example request
curl
curl -s https://api.smsprobe.co.uk/api/auth/me \
  -H "Authorization: Bearer sp_xxxx"
Response
{
  "user": {
    "id": "019d...",
    "name": "Jane Smith",
    "email": "jane@example.com",
    "plan": "pro",
    "company": "Acme Ltd",
    "webhook_url": "https://yourapp.com/webhook",
    "has_webhook_secret": true
  },
  "plan": {
    "tests_per_month": 100,
    "api_access": true,
    "webhooks": true,
    "history_days": 90
  },
  "tests_this_month": 12
}

API Keys

GET/api-keysAPI key

List all API keys on your account (names and metadata only — key values are never returned after creation).

Example request
curl
curl -s https://api.smsprobe.co.uk/api/api-keys \
  -H "Authorization: Bearer sp_xxxx"
Response
{
  "api_keys": [
    {
      "id": "019d...",
      "name": "Production",
      "last_used_at": "2026-04-29T10:00:00Z",
      "created_at": "2026-04-01T09:00:00Z"
    }
  ]
}
POST/api-keysAPI key

Create a new API key. The plain-text key is returned once — store it securely.

Example request
curl
curl -s -X POST https://api.smsprobe.co.uk/api/api-keys \
  -H "Authorization: Bearer sp_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "Production"}'
Parameters
ParameterTypeDescription
namerequiredstringA label for this key (e.g. Production, Staging). Max 100 chars.
Request body
{ "name": "Production" }
Response
{
  "api_key": {
    "id": "019d...",
    "name": "Production",
    "key": "sp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "created_at": "2026-04-29T09:00:00Z"
  },
  "message": "Store this key securely - it will not be shown again."
}
DELETE/api-keys/:idAPI key

Revoke an API key immediately. Any requests using it will return 401.

Example request
curl
curl -s -X DELETE https://api.smsprobe.co.uk/api/api-keys/019d... \
  -H "Authorization: Bearer sp_xxxx"
Response
{ "message": "API key revoked" }

Networks

GET/networks

List all monitored UK networks and their current health. No authentication required.

Example request
curl
curl -s https://api.smsprobe.co.uk/api/networks
Response
{
  "networks": [
    {
      "id": "...",
      "name": "EE",
      "slug": "ee",
      "is_active": true,
      "status": "live",
      "last_heartbeat_at": "2026-04-29T12:00:00Z",
      "uptime_24h": 100,
      "avg_latency_ms": 312,
      "signal_bars": 4,
      "signal_dbm": -88,
      "network_type": "LTE",
      "recent_heartbeats": [
        { "success": true, "latency_ms": 310, "signal_dbm": -88, "at": "2026-04-29T11:58:00Z" },
        ...
      ]
    },
    ...
  ]
}

Test Sessions

POST/sessionsAPI key

Create a new test session. Returns a phone number for each active network — send your SMS to those numbers from your platform. Sessions expire after 5 minutes.

Example request
curl
curl -s -X POST https://api.smsprobe.co.uk/api/sessions \
  -H "Authorization: Bearer sp_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"sender_id": "MYAPP", "expected_body": "Hello!"}'
Parameters
ParameterTypeDescription
sender_idrequiredstringThe Sender ID or phone number your platform sends SMS from (max 20 chars)
expected_bodystringThe exact message body you will send — enables body-match detection and the intercepted verdict
client_refstringYour own reference string, echoed in results and webhook payloads (max 255 chars)
webhook_urlstringHTTPS URL to receive the webhook for this session only — overrides your account-level webhook URL
Request body
{
  "sender_id": "MYAPP",
  "expected_body": "Hello!",
  "client_ref": "deploy-42",
  "webhook_url": "https://yourapp.com/webhook"
}
Response
{
  "id": "019d...",
  "test_code": "A1B2C3D4",
  "status": "pending",
  "sender_id": "MYAPP",
  "expected_body": "Hello!",
  "client_ref": "deploy-42",
  "webhook_url": null,
  "expires_at": "2026-04-29T12:05:00Z",
  "created_at": "2026-04-29T12:00:00Z",
  "results": [
    {
      "network": "ee",
      "network_name": "EE",
      "phone_number": "+447...",
      "status": "pending",
      "verdict": null,
      "latency_ms": null,
      "sender_received": null,
      "body_received": null,
      "body_matched": null,
      "received_at": null
    },
    ...
  ]
}
GET/sessions/:idAPI key

Fetch a session and its live per-network results. Poll every 3–5 seconds while status is pending.

Example request
curl
curl -s https://api.smsprobe.co.uk/api/sessions/019d... \
  -H "Authorization: Bearer sp_xxxx"
Response
{
  "id": "019d...",
  "status": "complete",
  "sender_id": "MYAPP",
  "results": [
    {
      "network": "ee",
      "network_name": "EE",
      "phone_number": "+447...",
      "status": "received",
      "verdict": "clean",
      "latency_ms": 4200,
      "sender_received": "MYAPP",
      "body_received": "Hello!",
      "body_matched": true,
      "received_at": "2026-04-29T12:00:04Z"
    },
    {
      "network": "vodafone",
      "network_name": "Vodafone",
      "phone_number": "+447...",
      "status": "received",
      "verdict": "intercepted",
      "latency_ms": 6100,
      "sender_received": "447...",
      "body_received": "Hello!",
      "body_matched": true,
      "received_at": "2026-04-29T12:00:06Z"
    },
    ...
  ]
}
GET/sessionsAPI key

List your test sessions newest-first. Filter by status using filter[status]=pending|complete|expired. Paginated, 20 per page.

Example request
curl
curl -s "https://api.smsprobe.co.uk/api/sessions?filter[status]=complete&page=1" \
  -H "Authorization: Bearer sp_xxxx"
Parameters
ParameterTypeDescription
filter[status]stringFilter by status: pending, complete, or expired
pageintegerPage number (default: 1, 20 results per page)
Response
{
  "data": [ { "id": "...", "status": "complete", "sender_id": "MYAPP", ... }, ... ],
  "meta": { "current_page": 1, "last_page": 3, "per_page": 20, "total": 48 }
}

Idempotency

Pass an Idempotency-Key: <uuid> header on POST /sessions to safely retry without creating duplicate sessions.

Verdicts

Once a result is received, a verdict is assigned to each network result:

VerdictMeaning
cleanSMS received with the original Sender ID intact
interceptedSMS received but the Sender ID was altered by the network
lostSession expired with no SMS received on this network
nullResult still pending — session has not yet expired

Webhooks

Configure a webhook URL in Dashboard → Webhooks to receive a POST when each session completes. You can also override the URL per-session via the webhook_url field on POST /sessions.

Request headers sent to your endpoint

Content-Type: application/json
User-Agent: SMSProbe-Webhook/1.0
X-SMSProbe-Event: session.complete
X-SMSProbe-Signature: sha256=<hmac-hex>
X-SMSProbe-Delivery: <delivery-id>

Retry policy

Attempt 1Immediate — on session completion
Attempt 230 seconds after failure
Attempt 35 minutes after second failure

Retries occur on connection errors or any 5xx response. A 2xx response marks the delivery as successful. 10 second request timeout.

Payload

{
  "event": "session.complete",
  "session": {
    "id": "019d...",
    "status": "complete",
    "sender_id": "MYAPP",
    "client_ref": "deploy-42",
    "results": [
      { "network": "ee", "verdict": "clean", "latency_ms": 4200, ... },
      { "network": "vodafone", "verdict": "intercepted", ... },
      ...
    ]
  }
}

Signature verification

The X-SMSProbe-Signature header is formatted as sha256=<hex>. Verify it using your signing secret from Dashboard → Webhooks. Always reject requests where the header is missing or the comparison fails — never make verification optional.

Node.js example
const crypto = require('crypto');

function verifySignature(secret, rawBody, signatureHeader) {
  if (!signatureHeader) return false;
  // Strip the "sha256=" prefix before comparing
  const sigHex = signatureHeader.replace(/^sha256=/, '');
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(sigHex);
  // timingSafeEqual throws if lengths differ — check first
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Errors

All errors return a JSON body. Validation errors include a per-field errors map:

401 — Unauthenticated

{ "message": "Unauthenticated." }

422 — Validation error

{
  "message": "The sender id field is required.",
  "errors": {
    "sender_id": ["The sender id field is required."]
  }
}

422 — Quota exceeded

{
  "message": "Monthly test quota exceeded. Upgrade your plan to run more tests."
}

Plans & limits

PlanTests / monthAPI accessWebhooksHistory
Free530 days
Pro10090 days
TeamUnlimited365 days

HTTP status codes

CodeMeaning
200Success
201Resource created
401Missing or invalid API key
403Forbidden — resource belongs to another account
422Validation error or monthly quota exceeded
500Server error