Skip to the page
Sections

Errors

Every error.code: its status, what it means, when it happens and how to fix it.

An error is about your request: the key, the shape, a missing upload, a file. Nothing is recorded. Anything about the consumer's code, phone or receipt is not an error; it is a 200 with a message_key (Outcomes).

The shape of every error
{
  "error": {
    "code": "idempotency_key_required",
    "message": "The Idempotency-Key header is required.",
    "fix": "Mint one key per attempt in the browser (16 to 128 characters) and forward it unchanged.",
    "docs_url": "https://<manager>/docs/errors#idempotency_key_required",
    "request_id": "req_4f9a1c2b7d3e"
  }
}

Branch on code. message and fix are written for a developer, not for the consumer. field is present when one request field is at fault. docs_url points at the entry below. Quote request_id (also in the X-Request-Id header) when asking about a request.

All codes

key_malformedHTTP 401

The API key is not in a recognised format.

When: The header is not Bearer <key>, or the key is not in the utc_test_… / utc_live_… format.

Fix: Keys start with utc_test_ or utc_live_. Check for truncation or whitespace.

Returned by: Every call.

Send the same request again: no, change the request first

key_missingHTTP 401

No API key was sent.

When: No Authorization header was sent.

Fix: Send Authorization: Bearer <key> from your server.

Returned by: Every call.

Send the same request again: no, change the request first

key_unknownHTTP 401

The API key is not recognised.

When: The key is well formed but is not one of ours.

Fix: Check the key was copied in full and belongs to this environment; ask ops for a new one if it was lost.

Returned by: Every call.

Send the same request again: no, change the request first

key_wrong_environmentHTTP 401

A test key was used where a live key is required, or the reverse.

When: A live key was used on a deployment that is not production. Test keys work on every deployment, production included.

Fix: Use a utc_live_ key in production and a utc_test_ key everywhere else.

Returned by: Every call.

Send the same request again: no, change the request first

session_expiredHTTP 401not yet returned

The agent session has expired.

When: A BA agent's session has expired. BA sessions arrive with the BA portal cutover.

Fix: Sign in again with the agent PIN; keep the typed submission and resend it with the same Idempotency-Key.

Returned by: No call yet.

Send the same request again: no, change the request first

campaign_not_assignedHTTP 403not yet returned

This agent is not assigned to that campaign.

When: A BA agent named a campaign they are not assigned to. BA sessions arrive with the BA portal cutover.

Fix: Send a UTC-Campaign header naming one of the campaigns from GET /api/v1/agent/me.

Returned by: No call yet.

Send the same request again: no, change the request first

key_revokedHTTP 403

This API key has been revoked.

When: The key has been revoked.

Fix: Ask ops for a new key, or switch to the campaign's second live key.

Returned by: Every call.

Send the same request again: no, change the request first

not_foundHTTP 404

No such record for this campaign.

When: The id is not a uuid, or names nothing in this key's campaign and environment. Also a receipt_id that does not exist, and a key whose campaign is not in the database.

Fix: Check the id, and that it was created with a key for the same campaign and environment.

Returned by: Every call.

Send the same request again: no, change the request first

idempotency_in_progressHTTP 409

A request with this Idempotency-Key is still being processed.

When: A receipt submission with this Idempotency-Key is still being processed. Wait Retry-After seconds. Past the time budget the stored pending submission is returned instead.

Fix: Wait for the Retry-After seconds, then resend the same request or poll the submission.

Returned by: POST /api/v1/submissions

Send the same request again: yes, it can succeed

file_too_largeHTTP 413

The file is larger than the limit.

When: size_bytes is over 5 MB, or the stored file is.

Fix: Downsize the photo in the browser (longest side 2,000 px, JPEG) before uploading.

Returned by: POST /api/v1/submissions, POST /api/v1/receipts

Send the same request again: no, change the request first

file_wrong_typeHTTP 415

The file is not a JPEG, PNG or WebP image.

When: The stored file is not a JPEG, PNG or WebP. A wrong content_type on POST /api/v1/receipts is 422 validation_failed; this is the file itself, found at submission.

Fix: Send one of the accepted_types from the upload response.

Returned by: POST /api/v1/submissions

Send the same request again: no, change the request first

campaign_mismatchHTTP 422

The UTC-Campaign header does not match the key's campaign.

When: A UTC-Campaign header names a campaign other than the key's.

Fix: Omit the header when using a campaign key.

Returned by: Every call.

Send the same request again: no, change the request first

idempotency_key_invalidHTTP 422

The Idempotency-Key must be 16 to 128 characters.

When: The Idempotency-Key is shorter than 16 or longer than 128 characters.

Fix: Use a UUID or another random string of that length.

Returned by: POST /api/v1/submissions, POST /api/v1/receipts

Send the same request again: no, change the request first

idempotency_key_requiredHTTP 422

The Idempotency-Key header is required.

When: POST /api/v1/submissions was sent without an Idempotency-Key.

Fix: Mint one key per attempt in the browser (16 to 128 characters) and forward it unchanged.

Returned by: POST /api/v1/submissions

Send the same request again: no, change the request first

idempotency_key_reusedHTTP 422

This Idempotency-Key was already used with a different request body.

When: The Idempotency-Key was used before with a different body.

Fix: Mint a new key for a new attempt; resend the same key only with the same body.

Returned by: POST /api/v1/submissions, POST /api/v1/receipts

Send the same request again: no, change the request first

phone_invalidHTTP 422

The phone number is not a valid Tanzanian mobile number.

When: The phone is not a Tanzanian mobile number. Validate in the form first; this is the caller's request, so no row is written.

Fix: Send +255XXXXXXXXX, 255XXXXXXXXX or 0XXXXXXXXX.

Returned by: POST /api/v1/submissions, POST /api/v1/submissions/lookup

Send the same request again: no, change the request first

receipt_already_attachedHTTP 422

This receipt already belongs to another submission.

When: The receipt already belongs to a submission.

Fix: Create a new receipt for a new submission.

Returned by: POST /api/v1/submissions, POST /api/v1/receipts/{id}/upload-url

Send the same request again: no, change the request first

receipt_not_uploadedHTTP 422

The receipt photo has not been uploaded.

When: The receipt's photo is not in storage. After the PUT succeeds, with the same Idempotency-Key.

Fix: PUT the file to the upload URL, or request a fresh one with POST /api/v1/receipts/:id/upload-url.

Returned by: POST /api/v1/submissions

Send the same request again: yes, it can succeed

receipt_requiredHTTP 422

This campaign needs a receipt with every submission.

When: The campaign needs a receipt and the submission has no receipt_id.

Fix: Create one with POST /api/v1/receipts and send its id as receipt_id.

Returned by: POST /api/v1/submissions

Send the same request again: no, change the request first

sentinel_with_live_keyHTTP 422

A test sentinel was sent with a live key.

When: A TEST-0-… code or TEST0… receipt was sent with a live key.

Fix: Sentinel codes and receipts only work with utc_test_ keys.

Returned by: POST /api/v1/codes/check, POST /api/v1/submissions, POST /api/v1/receipts

Send the same request again: no, change the request first

typed_receipt_unsupportedHTTP 422

This campaign does not accept typed receipt codes.

When: A typed receipt was sent to a campaign that takes no receipts.

Fix: Check capabilities.typed_receipt on GET /api/v1/campaign.

Returned by: POST /api/v1/receipts

Send the same request again: no, change the request first

validation_failedHTTP 422

The request body is not valid.

When: The body is not JSON, or a field is missing or has the wrong shape. error.field names the first problem. Also: no code where the campaign needs one; a receipt for a campaign that takes none.

Fix: See field for the first problem and the reference for the expected shape.

Returned by: POST /api/v1/codes/check, POST /api/v1/submissions, POST /api/v1/submissions/lookup, POST /api/v1/receipts, POST /api/v1/receipts/{id}/upload-url

Send the same request again: no, change the request first

rate_limitedHTTP 429

Too many requests.

When: The key is over its requests-per-minute ceiling, or the lookup or code-check limit was reached. After Retry-After seconds.

Fix: Slow down and retry after the Retry-After seconds.

Returned by: Every call.

Send the same request again: yes, it can succeed

internal_errorHTTP 500

Something went wrong on our side.

When: Something failed on our side. With the same Idempotency-Key. Quote the request id if it keeps failing.

Fix: Retry with the same Idempotency-Key, and quote the request id if it keeps failing.

Returned by: Every call.

Send the same request again: yes, it can succeed

ledger_unavailableHTTP 503not yet returned

The submission could not be recorded.

When: The submission could not be recorded. Reserved: no call answers with it today.

Fix: Retry with the same Idempotency-Key.

Returned by: No call yet.

Send the same request again: yes, it can succeed

26 codes.