Skip to content

Guides

Errors

Every failure has the same shape, a stable machine-readable code, and a request id you can quote to support.

The envelope

Failures are wrapped in an error object. Branch on code — never on the message, which is written for humans and may be reworded at any time.

{
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "Wallet balance 0 XOF is below the 23700 XOF required.",
    "retryable": false,
    "detail": {},
    "request_id": "01K3F7XQZ8V2N4M6P8R0T5CJWE"
  }
}
  • code — a stable identifier. This is what your integration should switch on.
  • message — human-readable, and not stable. For internal errors it is always generic: no exception text, no SQL and no stack trace ever crosses the boundary.
  • retryable — whether retrying the identical request could plausibly succeed. Published so you do not have to guess.
  • detail — machine-readable context, always an object. It is empty rather than absent when there is nothing to add.
  • request_id — identifies this exact request in SMSend's logs. Present on every error, including 4xx.

Correlating with support

The same identifier is returned as a header on every response, successful or not. Log it. Quoting it turns a support conversation about a failure that happened yesterday into a single lookup.

X-Request-Id: 01K3F7XQZ8V2N4M6P8R0T5CJWE

What to retry

Only three errors are marked retryable: a request still in flight under the same idempotency key, a rate limit, and an internal error. Everything else will fail again in exactly the same way, and retrying it just spends your rate limit.

Every error code

The complete published vocabulary. New codes can be added, so handle an unrecognised one as a generic failure rather than crashing.

CodeStatusRetryableMeaning
UNAUTHORIZED401NoNo key, a malformed key, a wrong secret, a revoked or expired key, or a suspended account. All indistinguishable by design.
FORBIDDEN403NoThe account or the caller is not permitted to perform this operation.
INSUFFICIENT_SCOPE403NoThe key is valid but does not carry the scope this endpoint requires. The scope is named in the detail.
ACCOUNT_SUSPENDED403NoThe account is suspended and cannot use the API.
VALIDATION_FAILED422NoThe request was malformed: a missing or invalid parameter, a bad idempotency key length, a rejected webhook URL, or an unsupported method or content type.
NOT_FOUND404NoNo such resource — including one that belongs to another account, which is deliberately indistinguishable from one that never existed.
UNSUPPORTED_VERSION400NoThe version in the path is not supported. The supported versions are listed in the detail.
IDEMPOTENCY_KEY_REQUIRED422NoA batch was submitted without an Idempotency-Key header.
REQUEST_IN_PROGRESS409YesAnother request with this idempotency key is still in flight. Wait for the stated interval and retry.
IDEMPOTENCY_KEY_REUSED409NoThis idempotency key was already used for a request with a different body. Use a new key.
INSUFFICIENT_FUNDS402NoThe prepaid wallet cannot cover the quoted amount.
CREDIT_LIMIT_EXCEEDED402NoThe postpaid accrual would exceed the account's credit limit.
WALLET_IN_DEBIT402NoThe wallet is in debit and cannot be spent from.
BATCH_EMPTY422NoThe batch contains no recipients.
BATCH_BODY_EMPTY422NoThe message body is blank once trimmed.
SCHEDULE_INVALID422NoThe scheduled time is in the past, or the timezone is not a valid IANA identifier.
SENDER_NOT_APPROVED422NoThe sender is not approved for this account.
RATE_LIMITED429YesThe key's request limit was exceeded. Wait for the interval in Retry-After.
CONFIGURATION_ERROR500NoPricing or routing is not configured for this request. Contact support; retrying will not help.
INTERNAL_ERROR500YesAn unexpected failure on SMSend's side. Quote the request id to support.

Why other people's resources are 404

Requesting a batch, message or webhook that belongs to another account returns 404, not 403 — the same answer as a resource that has never existed. A 403 would confirm that the identifier is real, which turns the endpoint into a way of testing whether particular ids exist.