Error Codes
Complete reference of API error responses with recovery steps
Error Response Format
All errors follow a consistent JSON structure. The error field contains a machine-readable code, and message contains a human-readable description.
{
"error": "CASH_LIMIT_EXCEEDED",
"message": "Cash transactions cannot exceed ETB 30,000",
"details": [
{
"field": "payment_method",
"issue": "CASH payment of ETB 45,000 exceeds limit"
}
],
"request_id": "req_abc123def456"
}Always include the request_id when contacting support.
HTTP Status Codes
| Status | Meaning | What to Do |
|---|---|---|
| 400 | Bad Request The request body is malformed or missing required fields. | Check the request payload against the API docs. Ensure all required fields are present and correctly typed. |
| 401 | Unauthorized Missing or invalid API key. | Verify your API key is correct and has not expired. Check the Authorization header format: `Bearer sk_live_...` |
| 403 | Forbidden Your API key does not have the required scope for this action. | Check the scopes assigned to your API key in the dashboard. See the Authentication docs for required scopes per endpoint. |
| 404 | Not Found The requested resource does not exist. | Verify the resource ID. Check for typos in the URL path. Ensure you are using the correct API version (`/v1/`). |
| 409 | Conflict The request conflicts with the current state of the resource. | Common with duplicate receipt submissions. Use idempotency keys to prevent duplicates. |
| 429 | Too Many Requests Rate limit exceeded (API-SEC-3). | Read the `Retry-After` header and wait before retrying. Consider implementing exponential backoff. |
| 500 | Internal Server Error An unexpected error occurred on the server. | Retry with exponential backoff. If persistent, contact support with the `X-Request-Id` header value. |
Business Error Codes
These codes appear in the error field of the response body. They indicate domain-specific issues.
CASH_LIMIT_EXCEEDEDCash payment exceeds the ETB 30,000 per-transaction limit (CASH-1).
Recovery: Split into multiple transactions or use an electronic payment method (ETHQR, bank transfer).
DEVICE_NOT_ACTIVEThe device is not in `active` status. It may be `pending_activation`, `suspended`, or `revoked`.
Recovery: Activate the device via `POST /v1/devices/{id}/activate` before issuing receipts.
DEVICE_QUOTA_EXCEEDEDYour plan does not allow more devices.
Recovery: Upgrade your plan or deactivate unused devices.
ZREPORT_ALREADY_GENERATEDA Z-Report has already been generated for this device today.
Recovery: Z-Reports are once-per-day per device. Use `GET /v1/devices/{id}/reports/x` for real-time summaries.
NO_TRANSACTIONSCannot generate a Z-Report with zero transactions for the period.
Recovery: Issue at least one receipt before generating the Z-Report.
INVALID_ALGORITHMThe signing algorithm is not supported. Only ECDSA-P256 is accepted (SIGN-2).
Recovery: Set `signing_algorithm` to `"ECDSA-P256"` in your device registration request.
INVALID_TINThe provided TIN is not registered with the Ministry of Revenue.
Recovery: Verify the TIN is exactly 10 digits and matches MOR records.
RECEIPT_CHAIN_BROKENThe receipt chain integrity check failed (CHAIN-1).
Recovery: Ensure `previous_receipt_hash` matches the hash of the last receipt issued by this device. Do not skip or reorder receipts.
DUPLICATE_RECEIPTA receipt with this idempotency key already exists.
Recovery: This is expected behavior for retry safety. The original receipt is returned.
VALIDATION_ERROROne or more request fields failed validation.
Recovery: Check the `details` array in the error response for specific field errors.
Rate Limiting
All endpoints enforce rate limits per API key. When exceeded, you receive a 429 response with a Retry-After header indicating seconds to wait.
| Plan | Rate Limit | Burst |
|---|---|---|
| Free / Sandbox | 60 req/min | 10 req/sec |
| Starter | 120 req/min | 20 req/sec |
| Business | 600 req/min | 50 req/sec |
| Enterprise | Custom | Custom |