Errors & Rate Limits
Errors & Rate Limits
Error format
API errors use the following structure:
Keys whose value would be null are omitted from the top level of the response, so an error body contains only the fields that apply.
Do not build application logic around the exact text of message. Use the HTTP status and structured error fields instead.
Gateway errors
Some 401 and 403 responses are returned before the request reaches a Penny API — for example, when the access token is missing or invalid, or when the business is inactive. These responses have a single field:
Handle them by HTTP status. They do not include type or trace_id.
HTTP status codes
Individual API operations may document additional error responses or more specific recovery behavior.
Asynchronous operations
Some Penny operations return 202 Accepted because work continues after the initial request completes.
The response represents the current state, not necessarily the final state.
For example, deleting an account returns 202 Accepted with a task that tracks the deletion:
Where the response includes a task, poll GET /tasks/{task_id} until its status is completed, failed, or cancelled.
Otherwise, follow the resource lifecycle documented for that operation.
Poll safely
When polling:
- use a reasonable interval;
- stop when the resource reaches a documented terminal state;
- place a maximum time or attempt limit on polling;
- handle terminal failure states explicitly.
Avoid unbounded loops.
Conflicts
A 409 Conflict means Penny could not safely complete the operation because it conflicts with another change or with a request that is still being processed. Penny returns 409 in two situations:
Sending the same request again unchanged after a concurrent-update 409 fails the same way. Re-read first.
Do not copy an entire GET response into a PATCH request. Read models can contain IDs, status fields, masked values, or derived properties that are not valid update fields.
400 vs 422
Both responses indicate a problem with the request, at different stages.
400 Bad Request
The request is malformed. Penny could not read it as a valid request for the operation.
Typical causes:
- malformed JSON or form data;
- a required field is missing;
- a value has the wrong type or format (for example, a string where a number is expected, or an invalid date);
- an unknown enum value.
The response type is RequestValidationError, and errors lists each problem:
422 Unprocessable Entity
The request is well formed but violates a business rule.
Typical causes:
- mutually exclusive fields were supplied together;
- a referenced resource is in a state that does not permit the operation;
- the operation would breach a limit, such as an insufficient balance;
- an
Idempotency-Keywas reused with a different request body.
The response type is the rule that failed. When more than one rule failed, validation_errors lists each one with its own message and details.
Retry guidance
Not every failed request should be retried.
For create and money-movement requests, retry only when you send the same Idempotency-Key with the same request body, so Penny can return the original result instead of repeating the operation.
Rate limits
Penny returns 429 Too Many Requests when it cannot process a request at the submitted rate. The response includes a Retry-After header with the number of seconds to wait before retrying:
Your integration should:
- wait at least the
Retry-Afterinterval before retrying; - retry using exponential backoff;
- avoid large bursts of unnecessary requests;
- cache resources and access tokens where appropriate.
Troubleshooting
When contacting Penny about an API failure, provide:
- the
trace_id; - the HTTP method and endpoint;
- the approximate request time;
- the environment;
- the resource ID, where relevant.
Do not send client secrets, access tokens, full card details, or other sensitive credentials in support requests.