Biftpaydocs

Errors, retries and lists

Amounts#

Every amount is an integer in the currency's smallest unit: kobo for NGN (₦1 = 100 kobo) and cents for USD. 1000000 is ₦10,000. There are no decimals anywhere in the API, so nothing gets rounded on the way.

Errors#

Biftpay uses HTTP status codes, and every error has the same body:

json
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "amount: Number must be greater than 0",
    "param": "amount"
  }
}
StatustypeWhat to do
400invalid_request_errorSomething in the request is wrong. param names the field. Fix it; retrying won't help.
401authentication_errorThe API key is missing, wrong or revoked.
403authentication_errorThe key can't do this, or the account can't yet (for example live payments before approval).
404invalid_request_errorNo such object, or it belongs to another account or mode.
409state_errorThe object is in the wrong state, for example canceling a payment that already succeeded, or a payout larger than your available balance (insufficient_funds).
422idempotency_errorThe Idempotency-Key was already used with a different request.
5xxapi_errorSomething went wrong on Biftpay's side. Retry with the same Idempotency-Key.

Use code in your own logic, not message: messages are written for people and may change.

Idempotency: retrying safely#

Networks fail. If a request to create a payment or a payout times out, you can't tell whether it went through. Every POST therefore needs an Idempotency-Key header: a unique value per action, such as a UUID or your order id.

bash
curl https://sandbox.api.biftpay.com/v1/payouts \
  -H "Authorization: Bearer bp_test_..." \
  -H "Idempotency-Key: payout-2026-10-04-vendor-17" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 500000, "currency": "NGN", "bank_account_id": "..." }'
  • Retry with the same key and the same body and you get the original response back, with the header Idempotent-Replayed: true. Nothing happens twice.
  • The same key with a different body is refused with 422 idempotency_key_reused.
  • If the first try failed with a 5xx, nothing was saved, so the retry runs normally.
  • A 4xx answer is saved and replayed too; fix the request and use a new key.

Use a new key for each new action, and the same key only when retrying that action.

Lists and pages#

List endpoints return the newest first:

json
{ "object": "list", "data": [ ... ], "has_more": true }

Ask for up to 100 at a time with limit (default 20). For the next page, pass the id of the last item you got as starting_after:

bash
curl "https://sandbox.api.biftpay.com/v1/payment_intents?limit=100&starting_after=5b0c3f6e-..." \
  -H "Authorization: Bearer bp_test_..."

Stop when has_more is false.

Test and live#

Keys starting bp_test_ see only test data; bp_live_ keys see only live data. Ids from one mode aren't found in the other, and every object has livemode so you can tell them apart.