BHUTAN SMSC
Reference

API errors

Every failure returns the same three fields. Branch on error — it is a stable string, and it will not be renamed under an integration that was correct when it was written.

{
  "error": "insufficient_credit",
  "message": "Not enough credit to accept this request…",
  "docs_url": "/docs/errors#insufficient_credit"
}

25 codes. The HTTP status is worth reading alongside the code: a 4xx is about your request, a 5xx is about us.

Credentials

The key itself — missing, wrong, retired, or not allowed to do this.

unauthorized HTTP 401 retrying will not help #

No API key was supplied. Send it as `Authorization: Bearer <key>` or `X-Api-Key: <key>`.

What to do Send the key on every request. `Authorization: Bearer <key>` is the usual form; `X-Api-Key: <key>` is accepted identically.
key_invalid HTTP 401 retrying will not help #

That API key is not valid.

What to do Check for a truncated paste. A key is one unbroken string, and a copied line break is the common cause.
key_revoked HTTP 401 retrying will not help #

That API key has been revoked. Issue a new one from your dashboard.

What to do Issue a replacement at /app/keys. Revocation takes effect on the next call, so there is no window to wait out.
key_expired HTTP 401 retrying will not help #

That API key has expired. Issue a new one from your dashboard.

What to do Issue a replacement at /app/keys, and set a longer expiry or none at all if this was not deliberate.
scope_denied HTTP 403 retrying will not help #

This API key does not carry the scope required for this endpoint.

What to do Add the scope the endpoint needs, or use a key that already carries it. Sending needs `send`, reading messages needs `read`, balance and usage need `balance`.
ip_not_allowed HTTP 403 retrying will not help #

This API key is restricted to an IP allowlist that does not include this address.

What to do Add the calling address to the key's allowlist, or clear the allowlist. Check your egress address rather than your office one — a NAT or proxy changes it.
tenant_suspended HTTP 403 retrying will not help #

This account is suspended. Contact support to restore service.

What to do Contact support. Nothing will be accepted until the account is restored, so stop retrying.

Throttling

You are going faster than the account is allowed to.

rate_limited HTTP 429 safe to retry #

You are sending faster than your plan allows. Slow down or upgrade for a higher rate.

What to do Back off and retry. Throughput is per ACCOUNT, so issuing more keys does not raise it — upgrade the plan or ask for an agreed ceiling.

Money

Refused before anything was charged.

insufficient_credit HTTP 402 retrying will not help #

Not enough credit to accept this request. Top up and send it again — nothing was charged.

What to do Top up at /app/billing and send it again. Nothing was charged, so no message is half-sent.

The recipient

Refused at the boundary and never charged. On a multi-recipient send these appear per recipient inside a 200 instead.

invalid_number HTTP 400 retrying will not help #

That is not a phone number.

What to do Check the value reached us as you meant it. Any format is accepted, so this usually means letters, an empty string, or a truncated field.
unroutable_number HTTP 400 retrying will not help #

This gateway carries Bhutanese numbers only (+975), on a prefix an operator claims.

What to do Use a Bhutanese number on a prefix an operator claims: 16 and 17 are B-Mobile, 77 is TashiCell. A foreign number is a real number we do not carry.

The message

The text or its options ask for more than the plan carries. Nothing is ever truncated to fit.

message_too_long HTTP 400 retrying will not help #

This message needs more segments than your plan permits. Shorten it or upgrade — it was not truncated and not charged.

What to do Shorten the body, or upgrade for a higher segment ceiling. POST /api/v1/validate returns the exact count for free before you commit.
unicode_not_allowed HTTP 400 retrying will not help #

This message needs UCS-2 (it contains a character outside the GSM alphabet), which your plan does not include.

What to do Replace the non-GSM characters, or upgrade. A curly apostrophe pasted from a word processor is the usual single culprit.
too_many_recipients HTTP 400 retrying will not help #

More recipients than your plan permits in one request. Split it into smaller batches.

What to do Split the request into smaller batches. The ceiling is per REQUEST, not per second, so several batches back to back are fine.
scheduling_not_allowed HTTP 400 retrying will not help #

Scheduled sending is not included in your plan.

What to do Drop `scheduled_at` and send at the moment you want it, or upgrade to a plan that includes scheduling.
priority_not_allowed HTTP 403 retrying will not help #

Priority sending is not included in your plan. It moves a message ahead of ordinary traffic and is charged at a higher per-segment rate.

What to do Drop `priority`, or upgrade. Priority overtakes ordinary traffic and bills at a higher per-segment rate.

The sender ID

Who the message appears to come from.

sender_invalid HTTP 400 retrying will not help #

That sender ID is not a valid format.

What to do Use up to 11 GSM characters for an alphanumeric sender, or a plain number. Punctuation is the usual rejection.
sender_blocked HTTP 400 retrying will not help #

That sender ID is reserved and cannot be registered.

What to do Choose a different sender ID. This one is reserved and no account can register it.
sender_not_allowed HTTP 403 retrying will not help #

Your plan sends under the platform sender ID and cannot set its own.

What to do Omit `sender` and the platform sender ID is used, or upgrade to a plan that carries your own.
sender_not_approved HTTP 403 retrying will not help #

That sender ID is not approved for your account.

What to do Register the sender ID at /app/senders and wait for approval. Approval is a human review, not an automatic step.

The request itself

The request was understood and still could not be carried out.

not_found HTTP 404 retrying will not help #

No such resource.

What to do Check the id, and that it belongs to the account this key is on. One account cannot read another's messages, and that reads as absent rather than forbidden.
not_cancellable HTTP 409 retrying will not help #

This message has left the queue and can no longer be cancelled.

What to do Nothing to do — it is with a carrier and cannot be recalled. Raise `hold_seconds` if you want a longer undo window next time.
validation_failed HTTP 400 retrying will not help #

The request body is missing a required field or has one of the wrong type.

What to do Read `message` — it names the field. Every accepted field is listed under Send a message.
method_not_allowed HTTP 405 retrying will not help #

That HTTP method is not supported on this endpoint.

What to do Use the method the endpoint documents. The response names the ones it allows.

Ours, not yours

Nothing you can fix, and nothing you were charged for.

internal_error HTTP 500 safe to retry #

Something went wrong on our side. Nothing was charged.

What to do Retry with backoff. Nothing was charged. If it persists, send us the time and your `client_ref`.
Retrying

Only rate_limited and internal_error are worth repeating. Every other code refuses the request as written, so an identical retry gets an identical refusal — and a loop of them is how a clear answer turns into a throttle. There is no Retry-After header on this API; your plan's ceiling is on /api/v1/pricing as max_tps, and the limit is per second, so a short pause clears it.