Error Handling & Status Codes
When working with the Pennylane API, you may encounter validation issues, missing permissions, or temporary rate limits. This guide explains how errors are returned, what the most common status codes mean, and how to handle them efficiently in your integration.
Pennylane uses standard HTTP status codes and a consistent JSON error schema, making failures predictable and easy to debug.
How Errors Are Structured
All errors returned by the API include:
- an HTTP status code
- a JSON error payload
- optional details to help identify the field or rule causing the failure
Example:
{
"error": "unprocessable_entity",
"message": "Missing required field: customer_id",
"details": {
"field": "customer_id",
"issue": "is required"
}
}Tip: The
detailsobject is the most actionable part of the response for debugging validation errors `(422)``.Most issues can be resolved by inspecting it carefully.
HTTP Status Code Families
Before diving into individual codes, here is how to interpret the main categories:
| Family | Meaning | When it Happens |
|---|---|---|
| 2xx | Success | The request is valid and processed |
| 4xx | Client error | Something must be fixed in the request |
| 5xx | Server error | Temporary issue on Pennylane’s side |
HTTP Status Codes Overview
2xx - Success
| Code | Meaning |
|---|---|
| 200 OK | The request succeeded. |
| 201 Created | A new resource was created (invoice, ledger entry, attachment). |
| 204 No Content | The request succeeded without a response body (e.g., sending an invoice by email). |
4xx - Client Errors
Errors caused by problems in your request: missing token, invalid scope, malformed payload, etc.
400 - Bad Request
Your request payload contains invalid or missing data.
Typical causes:
- invalid JSON format
- wrong field type (e.g., number instead of string)
- unsupported field
- amounts not sent as strings (e.g.,
"120.00")
How to fix: Validate your payload before sending it.
401 - Unauthorized
The access token is missing, invalid, or expired.
How to fix:
- check the
Authorization: Bearer <TOKEN>header - refresh or regenerate the token
- ensure you’re using the correct token type (Company / Firm / OAuth)
403 - Forbidden
Your token is valid but does not include the required scope.
How to fix:
Regenerate a token that includes the missing scope (e.g., customer_invoices:all).
404 - Not Found
The resource does not exist or cannot be accessed.
Typical causes:
- wrong or outdated ID
- incorrect endpoint
- accessing a record belonging to a different company
409 - Conflict
Your request conflicts with existing data.
Common example:
Importing a supplier or customer invoice using a PDF already present in Pennylane.
How to fix:
Avoid reusing the same file_attachment_id.
Only import each document once (idempotency is not enforced except for supplier invoice imports).
422 - Unprocessable Entity
The request is syntactically correct but violates a business validation rule.
Typical causes:
- totals or VAT mismatch
- unbalanced ledger entry lines
- invalid VAT code
- missing required business field
- duplicate references in invoice creation
How to fix:
Inspect the response’s details field and correct the payload.
429 - Too Many Requests
You exceeded the API’s rate limit.
How to fix:
- implement retry logic
- respect the
Retry-Afterheader - reduce request bursts
5xx - Server Errors
These errors are rare and typically temporary.
| Code | Meaning |
|---|---|
| 500 Internal Server Error | Unexpected server failure. |
| 503 Service Unavailable | Temporary outage or maintenance. |
How to fix: Retry with exponential backoff.
Understanding the Error Payload
Most error responses include:
| Field | Description |
|---|---|
error | Machine-readable code (unprocessable_entity, forbidden, etc.) |
message | Human-readable explanation |
details | Additional context (invalid field, totals mismatch, duplicate reference): |
Missing scope
{
"error": "forbidden",
"message": "Missing scope: customer_invoices:all"
}Unbalanced ledger entry
{
"error": "unprocessable_entity",
"message": "Entry lines are not balanced",
"details": {
"debit_total": "100.00",
"credit_total": "80.00"
}
}Duplicate supplier invoice
{
"status": 409,
"error": "A document with ID 2058167880 already exists with such attachment."
}How Pennylane Validates Requests
To ensure data consistency across accounting and invoicing workflows, Pennylane performs:
- schema validation (field types, required objects, JSON structure)
- business validation (totals, VAT rates, date rules, ledger balance)
Most 422 errors originate from the second category.
Handling Errors in Your Integration
1. Validate before submitting
To avoid 400/422 responses:
- check required fields
- validate totals and VAT consistency
- ensure debit/credit lines balance
- use strings for all amount fields (e.g.,
"120.00") - verify that entity IDs exist before referencing them
2. Implement safe retry logic
Retry only when appropriate:
| Should retry? | Status codes | Reason |
|---|---|---|
| ✅ Yes | 429, 500, 503 | Temporary or rate-limited errors |
| ❌ No | 400, 401, 403, 404, 422 | Requires fixing the request |
3. Avoid duplicate POST requests
Pennylane does not enforce idempotency on creation endpoints such as:
/customer_invoices/ledger_entries
If the same request is sent twice, you may create duplicates.
4. Log full error bodies
Always store:
- the status code
errorandmessagedetails(whenever available)
This makes debugging significantly easier.
Updated about 5 hours ago
