SendComms
API Reference

Request Log

GET/api/v1/requests

An audit trail of every authenticated API call made with your keys, newest first. The row is written the moment your key is accepted — before anything that can fail: before the rate limit check, before the body is parsed, before we ever talk to a provider. That means it can answer "what happened to my request?" even for calls that never reached a provider at all.

Transactions vs. requests: a transaction only exists once a send has been accepted and priced. A request row exists for every call, including the ones that were rejected, rate limited or crashed. If a call left no transaction behind, this is where you find out why.
REQUESTcURL
# Last 20 calls, newest first
curl -X GET "https://api.sendcomms.com/api/v1/requests?limit=20" \
  -H "Authorization: Bearer sc_live_your_api_key_here"

# Only the calls that failed at the provider
curl -X GET "https://api.sendcomms.com/api/v1/requests?outcome=provider_error&limit=50" \
  -H "Authorization: Bearer sc_live_your_api_key_here"

# Page through everything you sent to one endpoint
curl -X GET "https://api.sendcomms.com/api/v1/requests?endpoint=/api/v1/sms/send&limit=100&offset=100" \
  -H "Authorization: Bearer sc_live_your_api_key_here"

Query Parameters

ParameterTypeRequiredDescription
limitintegerOptionalRows to return. Default 50, maximum 200. Values outside 1–200 are clamped, not rejected.
offsetintegerOptionalRows to skip. Default 0. Combine with limit and total to page.
outcomestringOptionalExact-match filter on one of the outcome values below.
endpointstringOptionalExact-match filter on the path, e.g. /api/v1/sms/send. No prefix or wildcard matching.

Rows are always ordered by started_at descending, and are always scoped to the account that owns the API key — you never see another customer's calls.

Response Codes

200Success401Missing or invalid API key403Account suspended

Response

Success Response

requests holds the page of rows, and total is the number of rows matching your filters — not the number returned.

{
  "success": true,
  "data": {
    "requests": [
      {
        "id": "6f2a1c74-1a0e-4b1f-9b6d-0a2f5e7c9d31",
        "endpoint": "/api/v1/sms/send",
        "method": "POST",
        "service": "sms",
        "sandbox": false,
        "outcome": "provider_error",
        "status_code": 503,
        "error_code": "SMS_SEND_FAILED",
        "error_message": "Failed to send SMS. Please try again in a few minutes.",
        "transaction_id": "sms_mjhw8xkw_6f97ab63ff91",
        "request_summary": {
          "to": "+233540800994",
          "from": "SendComms",
          "reference": "order-12345",
          "message_length": 24
        },
        "duration_ms": 1843,
        "started_at": "2026-08-22T09:14:02.118Z",
        "finished_at": "2026-08-22T09:14:03.961Z"
      },
      {
        "id": "b91d0f30-7c44-4f0e-8a5a-1c3d9f2b6e70",
        "endpoint": "/api/v1/sms/send",
        "method": "POST",
        "service": "sms",
        "sandbox": false,
        "outcome": "rate_limited",
        "status_code": 429,
        "error_code": "RATE_LIMIT_EXCEEDED",
        "error_message": null,
        "transaction_id": null,
        "request_summary": {
          "to": "+233540800994",
          "from": null,
          "reference": null,
          "message_length": 18
        },
        "duration_ms": 12,
        "started_at": "2026-08-22T09:13:58.402Z",
        "finished_at": "2026-08-22T09:13:58.414Z"
      },
      {
        "id": "0c5b8e12-30a7-49d5-b2c1-77e4a1f0b8aa",
        "endpoint": "/api/v1/email/send",
        "method": "POST",
        "service": "email",
        "sandbox": true,
        "outcome": "success",
        "status_code": 200,
        "error_code": null,
        "error_message": null,
        "transaction_id": "email_test_1935f2c0a11_9f3b21c4",
        "request_summary": {
          "to": ["customer@example.com"],
          "subject_length": 27,
          "recipients": 1
        },
        "duration_ms": 96,
        "started_at": "2026-08-22T09:11:44.007Z",
        "finished_at": "2026-08-22T09:11:44.103Z"
      }
    ],
    "total": 1487,
    "limit": 20,
    "offset": 0
  }
}

Request Fields

FieldTypeDescription
iduuidIdentifier for this attempt. Quote it when you contact support.
endpointstringPublic path that was called, e.g. /api/v1/sms/send
methodstringHTTP method
servicestring | nullsms, email, data, airtime, usage or webhooks
sandboxbooleanTrue when the call used an sc_test_ key
outcomestringWhat happened. See the table below.
status_codeinteger | nullHTTP status we returned. Null while the row is still started.
error_codestring | nullOur error code, e.g. INVALID_PHONE_NUMBER
error_messagestring | nullShort sanitised message, truncated to 500 characters
transaction_idstring | nullSet once the call got far enough to create a transaction
request_summaryobjectRedacted summary of what you sent. Never message bodies — see below.
duration_msinteger | nullWall-clock time we spent on the request
started_attimestampWhen your key was accepted. Rows are sorted on this.
finished_attimestamp | nullWhen the response was produced. Null for started rows.

Outcome Values

OutcomeWhat it means
successThe response was a 2xx. The call did what you asked.
client_errorA 4xx we attributed to the request itself — validation failures, missing fields, unknown transaction, insufficient balance. Check error_code.
rate_limitedA 429. You exceeded a per-minute, per-day or per-month limit for that service. Nothing was sent and nothing was charged.
provider_errorA 502/503, or a 5xx carrying a provider error code (SMS_SEND_FAILED, EMAIL_SEND_FAILED, DATA_PURCHASE_FAILED, PROVIDER_ERROR, SERVICE_UNAVAILABLE). We reached the downstream network or carrier and it refused or timed out.
internal_errorA 5xx on our side that was not a provider failure. Safe to retry; if it repeats, quote the id when you contact support.
unauthorizedA 401. The key was rejected mid-request, or the account attached to it was suspended.
startedThe row was opened but never finalised — the request crashed hard or the connection dropped before a response was produced. A row that stays started is the strongest signal to send us.

What request_summary Contains

request_summary is deliberately redacted. It records only what is needed to identify a call — the destination, your reference, and sizes such as message length or recipient count. It never contains message bodies, email HTML, subjects, attachments or credentials. Routes write it before validation runs, so a rejected request is still traceable: you can see who you tried to reach and why it was refused.

"request_summary": {
  "to": "+233540800994",
  "from": "SendComms",
  "reference": "order-12345",
  "message_length": 24
}

The exact keys vary by endpoint. The client IP and user agent of each call are recorded too, but are not returned by this endpoint.

Debugging a 429: filter with outcome=rate_limited to see exactly which calls were shed and when. See Rate Limits for the per-plan ceilings.