---
updatedAt: 2026-02-06T12:47:54.000Z
agentTools:
  projectIndex: https://pennylane.readme.io/llms.txt
---

# 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:

```json
{
  "error": "unprocessable_entity",
  "message": "Missing required field: customer_id",
  "details": {
    "field": "customer_id",
    "issue": "is required"
  }
}
```

<Callout icon="💡" theme="default">
  **Tip:** The `details` object is the most actionable part of the response for debugging validation errors `(422)``.

  Most issues can be resolved by inspecting it carefully.
</Callout>

# 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.

<Accordion title="400 - Bad Request" icon="fa-info-circle">
  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.
</Accordion>

<Accordion title="401 - Unauthorized" icon="fa-info-circle">
  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)
</Accordion>

<Accordion title="403 - Forbidden" icon="fa-info-circle">
  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`).
</Accordion>

<Accordion title="404 - Not Found" icon="fa-info-circle">
  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
</Accordion>

<Accordion title="409 - Conflict" icon="fa-info-circle">
  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).
</Accordion>

<Accordion title="422 - Unprocessable Entity" icon="fa-info-circle">
  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.
</Accordion>

<Accordion title="429 - Too Many Requests" icon="fa-info-circle">
  You exceeded the API’s rate limit.

  **How to fix:**

  * implement retry logic
  * respect the `retry-after` header
  * reduce request bursts

  <aside>
    ⚠️ **Retry only when rate-limited — not for validation or permission errors.**
  </aside>
</Accordion>

## 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

```json
{
  "error": "forbidden",
  "message": "Missing scope: customer_invoices:all"
}
```

## Unbalanced ledger entry

```json
{
  "error": "unprocessable_entity",
  "message": "Entry lines are not balanced",
  "details": {
    "debit_total": "100.00",
    "credit_total": "80.00"
  }
}
```

## Duplicate supplier invoice

```json
{
  "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
* `error` and `message`
* `details` (whenever available)

This makes debugging significantly easier.