Skip to content
CaptiFi Connect

Developer docs

Errors and error codes

Every error comes back in the same shape with a stable code, so your code can branch on the code and show people the message.

What an error looks like

A 4xx or 5xx status and an error object. code never changes for a given problem, so that's the thing to check. message is written for people and may be reworded. details carries specifics when there are any, and is null otherwise.

HTTP/1.1 402 Payment Required

{
  "error": {
    "code": "insufficient_credit",
    "message": "This send needs 12 credits and the balance is 3.",
    "details": { "needed": 12, "balance": 3 }
  }
}

Error codes

StatusCodeWhat it means
400invalid_jsonThe body is not valid JSON.
401unauthorizedThe API key is missing, mistyped or revoked.
402insufficient_creditNot enough credit for the whole send. details.needed and details.balance say by how much; nothing was sent.
403account_pendingYour account has not been approved yet. Test keys still work.
403account_suspendedThe account is suspended. Email support.
403test_mode_not_supportedNot possible with a test key: sending or cancelling a campaign, or cancelling a live text.
404not_foundNo such resource in your account. Another account's ids read as not found too.
409idempotency_conflictThis Idempotency-Key was used before with a different body. Use a new key for a new request.
409not_cancellableThe text or campaign has already gone (or finished), so it can no longer be cancelled.
409not_editableOnly a draft campaign can be edited.
409not_deletableOnly a draft campaign can be deleted. Cancel it instead.
409already_launchedThe campaign has already been sent or scheduled.
409launchingThe campaign is being sent right now. Try again in a moment.
409contact_existsAnother contact already has that number.
409sender_existsYou have already asked for that sender name.
413body_too_largeThe body is over 1 MB.
422validation_failedThe request isn't valid. details lists the fields and what's wrong with each.
422no_valid_recipientsEvery recipient was refused. details.rejected gives the reason for each.
422body_too_longThe text is longer than 10 SMS parts.
422sender_not_approvedThe sender name is not approved for your account.
422invalid_urlA webhook URL is not a valid https URL, or contains a username or password.
422unresolvable_hostA webhook URL's host could not be found.
422private_addressA webhook URL points at a private or local network address.
422too_many_webhooksAn account can have up to 10 webhook endpoints.
429rate_limitedToo many requests. Wait for the number of seconds in the Retry-After header.
500internal_errorSomething went wrong on our side. Safe to retry with the same Idempotency-Key.

Rejected numbers

A send with some bad numbers still goes to the good ones. The refused numbers come back in data.rejected, each with a reason, and none of them are charged. Only when every number is refused does the whole request fail, with no_valid_recipients.

ReasonWhy the number was refused
invalid_numberNot a phone number we can read.
unsupported_destinationA number outside the UK, Guernsey, Jersey and the Isle of Man.
not_mobileA UK landline or other non-mobile number.
duplicateThe same number appeared more than once in the request; it gets one text.
opted_outThe number is on your opt-out list.
too_longWith merge fields filled in, the text for this person ran past 10 parts.
empty_bodyWith merge fields filled in, the text for this person was empty.

When to retry

Retry 429 after the Retry-After wait, and 500 or a dropped connection after a short pause. Send the same Idempotency-Key each time: if the first attempt did get through, you get its original answer back (with meta.replayed set to true) instead of a second send.

Don't retry other 4xx errors as they are; something in the request or the account needs changing first. The API reference lists which errors each endpoint can return.