Errors

Error response shapes, HTTP status codes, auth failures, and troubleshooting.


Response format

Success

json
{
  "success": true,
  "data": {},
  "message": "Product created successfully"
}

Handler-built error

json
{
  "success": false,
  "error": {
    "type": "UNAUTHORIZED",
    "message": "Invalid API key"
  }
}

Shared middleware error

json
{
  "success": false,
  "error": {
    "type": "validation_bad_input",
    "message": "validation failed"
  }
}

On the middleware shape, type is the last segment of the internal code (validation_bad_input, resource_not_found, forbidden). Trust HTTP status; some not-found/bad-input cases are HTTP 500. Match message as well as status.

Unexpected 500 shape

json
{
  "err": {},
  "message": "Internal Server Error"
}

Log status, error.type, and error.message. Do not log the API key.

HTTP status codes

200

Read, update, delete, product create, bank create, receipt submit, checkout initiate.

201

Customer create, added pricing plan, subscription create, usage create.

400

Malformed JSON, invalid ids/phones, missing required receipt or usage fields.

401

Missing, malformed, or unknown credentials.

403

No payment permission, key/merchant mismatch, or receipt for another merchant's bill.

404

This merchant does not have that product, customer, model, or payment.

500

Unexpected failure, plus ownership/validation cases called out below.

Authentication errors

HTTP`error.type`Message containsMeaning
401`UNAUTHORIZED``Authentication token missing`No API key. Route looked for dashboard login.
401`UNAUTHORIZED``API key is required`Initiate without an API key.
401`UNAUTHORIZED``Invalid API key format`Header is not `public:secret`.
401`UNAUTHORIZED``Invalid API key`Unknown key or wrong secret.
401`UNAUTHORIZED``Invalid authentication token``Authorization: Bearer` is not a valid dashboard login.
401`UNAUTHORIZED``Merchant not found`Key does not resolve to a merchant.
403`FORBIDDEN``not valid for this merchant status`Sandbox/test or active/production pairing failed, or merchant pending/inactive.
403`FORBIDDEN``payment processing permission`Key is not allowed to take payments.

Validation and ownership

SituationHTTP`error.type`Message contains
Product body is not JSON400`INVALID_REQUEST`the parser error
Product create with no pricing plan500`INTERNAL_ERROR``at least one pricing plan is required`
Product id is not a UUID400`INVALID_REQUEST``Invalid product ID`
Product belongs to another merchant404`NOT_FOUND``Product not found`
Customer phone is not 9–15 digits400`validation_bad_input``validation failed`
Customer belongs to another merchant404`resource_not_found`
Deactivate the last active plan500`validation_bad_input``cannot deactivate the last active pricing plan`
Another merchant reads or checks out the subscription500`INTERNAL_ERROR``product not found` or `does not belong to merchant`
Unknown payment reference404`resource_not_found``payment not found`
Usage quantity negative / period does not move forward500`INTERNAL_ERROR``cannot be negative` or `billing period end must be after start`
Usage body not JSON, or `manual_amount` missing400`VALIDATION_ERROR``manual_amount is required` when that field is the problem
Arrears bill with no usage report`usage data not found for billing period`
Manual model with no amount for the period`manual amount not provided for billing period`
Bank or receipt body missing required fields400`BAD_USER_INPUT`
Receipt for another merchant's bill403`forbidden``does not belong to merchant`

Troubleshooting

Common symptoms

401 on every call

Header name is X-API-Key, value is public:secret, and the secret matches the key issued for this merchant.

403 immediately

Merchant is sandbox while the key is production (or the reverse), or the key cannot take payments. Pending merchants cannot call the biller.

The arrears bill never appears

Subscription must be ACTIVE or INCOMPLETE, the period must have ended, and usage or manual_amount for that period must already be stored.

The advance bill appears before the month starts

Expected for FIXED. Due date is period start; the scheduler looks ahead by about one period.

Checkout says the billing window has not started

The bill for that cycle does not exist yet. For Flex, wait until the period ends and gigabytes are posted.

bill:paid arrived twice

Return 200 on the second delivery and do not post the invoice again.