{
"error": {
"type": "insufficient_credit",
"code": "insufficient_credit",
"message": "Not enough Jusa Credit: US$3.50 available.",
"param": null,
"doc_url": "https://jusa.localhost.co.zw/developers/docs/errors#insufficient_credit",
"request_id": "req_4f0c…"
}
}
Every error under /dev/v1 is this envelope. code is stable: a code always has the same HTTP
status, and new codes only add. message is ours, written for a person; it never repeats a provider's
words. param names the field at fault. Quote request_id to support. The SDKs raise one
class per type.
Retry or not? A 429, a 5xx and 409 idempotency_in_progress
can be retried with the same Idempotency-Key. A 4xx otherwise is the answer: change the
request. A refusal before anything was written (almost every 4xx) charged nothing.
400 · The request is wrong
| code | type |
amount_out_of_range | invalid_request |
attestation_required | invalid_request |
bundle_not_found | invalid_request |
bundle_required | invalid_request |
challenge_required | invalid_request |
choice_not_allowed | invalid_request |
code_invalid | invalid_request |
code_required | invalid_request |
idempotency_key_invalid | invalid_request |
idempotency_key_required | invalid_request |
invalid_api_version | invalid_request |
invalid_json | invalid_request |
invalid_mode | invalid_request |
invalid_request | invalid_request |
invalid_target | invalid_request |
location_too_imprecise | invalid_request |
meter_blocked | invalid_request |
meter_not_found | invalid_request |
network_mismatch | invalid_request |
parameter_invalid | invalid_request |
parameter_missing | invalid_request |
parameter_unknown | invalid_request |
product_unavailable | invalid_request |
quote_expired | invalid_request |
quote_mismatch | invalid_request |
referral_invalid | invalid_request |
token_phone_required | invalid_request |
topup_method_unavailable | invalid_request |
url_invalid | invalid_request |
url_not_allowed | invalid_request |
url_not_https | invalid_request |
webhook_endpoint_limit | invalid_request |
401 · No key, or not a valid one
| code | type |
invalid_api_key | authentication |
invalid_signature | authentication |
legacy_signature_retired | authentication |
missing_api_key | authentication |
signature_expired | authentication |
402 · Not enough Jusa Credit
| code | type |
insufficient_credit | insufficient_credit |
403 · Not allowed
| code | type |
account_frozen | permission |
agent_live_mode_off | permission |
approval_required | permission |
approval_requires_another_person | permission |
cap_exceeded | permission |
download_link_expired | permission |
feature_not_enabled | permission |
geofence_window_closed | permission |
insufficient_scope | permission |
ip_not_allowed | permission |
key_controls_escalation | permission |
key_controls_not_enforced | permission |
key_not_for_browsers | permission |
key_spend_cap_reached | permission |
live_mode_not_allowed | permission |
live_mode_only | permission |
not_a_developer_org | permission |
not_allowed_for_agent_keys | permission |
origin_not_allowed | permission |
outside_geofence | permission |
phone_not_allowed | permission |
pin_not_set | permission |
pin_required | permission |
product_not_allowed | permission |
recipient_blocked | permission |
role_not_allowed | permission |
sensitive_data_not_enabled | permission |
source_not_allowed | permission |
target_blocked | permission |
terms_not_accepted | permission |
test_mode_only | permission |
404 · Not found
| code | type |
download_link_invalid | not_found |
resource_missing | not_found |
unknown_route | not_found |
405 · Method not allowed
| code | type |
method_not_allowed | invalid_request |
409 · The state does not allow it
| code | type |
activity_id_conflict | conflict |
already_cancelled | conflict |
already_member | conflict |
already_referred | conflict |
already_rewarded | conflict |
already_sent | conflict |
approval_not_pending | conflict |
batch_not_awaiting_approval | conflict |
batch_not_cancellable | conflict |
batch_not_terminal | conflict |
blocklist_entry_exists | conflict |
checkin_required | conflict |
checkout_session_not_held | conflict |
checkout_session_not_open | conflict |
client_reference_conflict | conflict |
completion_id_conflict | conflict |
conflict | conflict |
cost_center_taken | conflict |
dispute_exists | conflict |
erasure_deferred | conflict |
funding_mismatch | conflict |
group_in_use | conflict |
idempotency_in_progress | idempotency |
insufficient_program_funds | conflict |
invite_exists | conflict |
invite_expired | conflict |
key_inactive | conflict |
last_owner | conflict |
link_claimed | conflict |
link_expired | conflict |
link_revoked | conflict |
meter_not_editable | conflict |
name_taken | conflict |
no_geofence | conflict |
nothing_to_advance | conflict |
nothing_to_retry | conflict |
nothing_to_send | conflict |
occurrence_already_fired | conflict |
occurrence_not_runnable | conflict |
program_cap_reached | conflict |
program_closed | conflict |
program_exhausted | conflict |
program_inactive | conflict |
program_transition_invalid | conflict |
receipt_not_available | conflict |
recipient_conflict | conflict |
recipient_erased | conflict |
referral_not_configured | conflict |
referral_not_pending | conflict |
review_not_open | conflict |
reward_not_releasable | conflict |
reward_not_voidable | conflict |
schedule_inactive | conflict |
send_delivered_late | conflict |
send_not_disputable | conflict |
send_not_retryable | conflict |
send_transition_invalid | conflict |
token_link_used | conflict |
token_not_available | conflict |
webhook_endpoint_deleted | conflict |
webhook_endpoint_disabled | conflict |
webhook_endpoint_legacy | conflict |
415 · Not JSON
| code | type |
unsupported_media_type | invalid_request |
422 · Idempotency key reused with another body
| code | type |
idempotency_mismatch | idempotency |
423 · Locked
| code | type |
pin_locked | permission |
429 · Too many requests
| code | type |
lookup_quota_exceeded | rate_limit |
otp_limited | rate_limit |
rate_limited | rate_limit |
token_resend_limited | rate_limit |
500 · Our fault
| code | type |
internal_error | api_error |
501 · Not available yet
| code | type |
not_yet_available | invalid_request |
503 · A provider cannot answer, or live sends are paused
| code | type |
live_api_disabled | api_error |
live_dispatch_disabled | api_error |
provider_capacity_exceeded | provider_unavailable |
provider_unavailable | provider_unavailable |
zesa_unavailable | provider_unavailable |
Why a send failed: failure_code
A send, reward or batch line that did not go through says why in failure_code, with our words for it in
failure_message. These are states of an object, not errors of a request.
| failure_code | Means |
amount_out_of_range | The amount is outside the product's range. |
cancelled | The send was cancelled before it went out. |
insufficient_credit | There was not enough Jusa Credit. |
invalid_target | The number or account is not valid for this product. |
meter_blocked | This meter is blocked or inactive at ZESA. |
meter_not_found | ZESA does not recognise this meter number. |
network_mismatch | The number belongs to a different network from the product. |
product_unavailable | This product cannot be sent right now. |
provider_float_empty | Held while the provider tops up its float. It will be retried. |
provider_outage_window_expired | The provider was down for the whole retry window. |
provider_rejected | The network declined the send. |
reviewed_failed | Jusa reviewed the send and confirmed it was not delivered. |
zesa_hold_expired | ZESA stayed down past the send's max_hold. |
A batch line that never became a send
Refused before anything was charged, for a reason a single send answers as an error.
| failure_code | Means |
account_frozen | Sends are frozen on this account. |
bundle_not_found | That data bundle is not offered. |
bundle_required | A data send names its bundle. |
cap_exceeded | It would take the recipient past one of their caps. |
duplicate_client_reference | Its client_reference was already used by another send. |
internal_error | Something went wrong on our side before it was sent. Nothing was charged. |
key_spend_cap_reached | It would take the API key past one of its spend caps. |
live_dispatch_disabled | Live sends are switched off here. |
product_not_allowed | The API key may not send this product or network. |
recipient_blocked | The recipient is blocked, or being erased. |
target_blocked | The number or meter is on a blocklist. |
token_phone_required | A ZESA send needs a Zimbabwean mobile for the token. |
zesa_unavailable | ZESA could not confirm the meter, so nothing was sent or charged. |