PackflipPartner

Errors, idempotency, and retries

Error codes, safe retries, lists, and versioning.

Error shape

Every error response has the same body, so you can branch on error.code:

{ "error": { "code": "insufficient_balance", "message": "Insufficient spendable balance." } }
CodeStatusWhat to do
invalid_request400Fix the request. message names the field. Do not retry unchanged.
unauthorized401Send a valid key as Authorization: Bearer pk_…, for the right environment.
forbidden403The key cannot perform this action.
not_found404The resource does not exist in this account and environment.
conflict409The current state does not allow the action, for example a sealed or already used card. Fetch the resource and decide.
idempotency_conflict409The Idempotency-Key was already used with different data. Use a new key for a new request.
insufficient_balance409Top up, then retry with the same key.
out_of_stock409The vending machine cannot supply the quantity.
service_unavailable503A dependency is not ready. Retry later with backoff.

Unrecognized fields in a request body are rejected with 400, so typos surface immediately. Responses, on the other hand, may gain new fields at any time: ignore fields you do not know.

Idempotency

POST /api/v2/operations requires an Idempotency-Key header of 1 to 255 characters.

  • The first request with a key creates the operation and returns 201.
  • Repeating the same request with the same key returns the original operation with 200 and "replayed": true. Nothing is charged or credited again.
  • Reusing the key with different data returns 409 idempotency_conflict.
  • Keys are scoped to your account and environment and do not expire.

Derive the key from your own record, such as order-{yourOrderId}, and store it before calling the API. If a request times out or fails with a network error, retry with the same key: you will get the operation that was created, or create it now.

Other writes are safe to retry for different reasons:

  • POST /api/v2/customers with an externalUserId returns 409 conflict if the customer exists. Fetch it with GET /api/v2/customers/by-external-id/{externalUserId}.
  • Reveal, refresh, and webhook changes can be repeated without side effects beyond the first.

Retries

Retry network errors, 5xx, and 503 with exponential backoff and jitter, for example 1, 2, 4, 8 seconds, up to a minute. Do not retry 4xx responses other than 409 insufficient_balance after a top-up.

If you receive 429 Too Many Requests, wait for the Retry-After header before retrying. Keep concurrency per account modest; Packflip may apply rate limits to protect the service.

Lists

List endpoints return the newest items first and accept limit:

EndpointDefaultMaximum
GET /api/v2/customers50200
GET /api/v2/customers/{id}/cards50200
GET /api/v2/ledger-entries50200
GET /api/v2/funding-transactions50100
GET /api/v2/funding-intents50100

Keep your own record of operations and customers rather than paging through everything.

Money and time

  • Amounts are decimal strings with six places, such as "12.500000". Use a decimal library; never parse them as floats.
  • Timestamps are ISO 8601 in UTC.
  • IDs are opaque strings with a type prefix: par_, cus_, card_, op_, oco_, ocb_, shp_, wh_, evt_, ftx_. Do not parse them.

Versioning

The API version is a date, currently 2026-09-12, shown in the OpenAPI document and in each webhook's apiVersion. Additive changes can ship at any time. Breaking changes are announced at least 30 days in advance.