Lunch
Guides

Errors

Every failure — validation, auth, a missing record, a provider outage, a path that does not exist — answers JSON in one shape:

JSONCode
{ "error": "The request does not match the published schema", "code": "invalid_request" }

Branch on code. It is a stable token and does not change once published. error is prose for a human reading a log, and we reword it freely.

Each operation in the reference lists the codes it can carry. We may add codes as new conditions become distinguishable — we never remove one or repurpose its meaning — so keep a default branch for a value you do not recognise.

Codes any operation can answer

CodeStatusMeaning
invalid_request400the body or path does not match the published schema
unreadable_request400the body is not the media type it claims
unauthorized401missing, malformed or revoked key
payload_too_large413over the body limit
upstream_failed502us, or a provider we depend on
unknown_endpoint404this API serves no such path

unknown_endpoint belongs to no operation, so it appears under none of them in the reference — it is how a mistyped path answers, in the same shape as everything else, rather than as plain text your client cannot parse.

Codes worth handling by name

Not foundbusiness_not_found, invoice_not_found, import_not_found, subscription_not_found. Usually an identifier you have not synced yet rather than one that does not exist.

Refusals on taking an advance, all 409: invoice_not_financeable, business_not_eligible, concentration_exceeded. See financing an invoice.

Conflicts on a replayalready_subscribed, already_imported. Both mean the thing you are creating is already there.

Rejected input we can nameinvalid_routing_number, invalid_invitee, self_dealing (a business cannot finance its own invoice), unsupported_media_type, unreadable_file, too_many_rows (an import is capped at 1000), destination_refused (a webhook URL we will not send to).

Retrying

502 is the only one worth retrying blind, with a backoff. Everything in the 4xx range will answer the same way until the request or the underlying state changes.

A retry after a timeout you never saw the answer to is safe for the writes keyed on your own identifier — syncing a business, syncing an invoice, setting a payout account, taking an advance, an import. Those update rather than duplicate.

Subscribing to a webhook destination is the exception. If the first POST /v1/webhooks committed and you lost the response, the retry answers 409 already_subscribed — and the signing secret was returned once and is never returned again, including by GET /v1/webhooks. The subscription exists and you cannot verify anything it delivers. Recover by deleting it with DELETE /v1/webhooks/{reference} — the listing gives you the reference — and subscribing again, keeping the secret this time.

Last modified on