Source: https://docs.debitura.com/developer-concepts/error-handling

# Error Handling

All Debitura APIs return structured error responses with consistent formats. Across the Customer, Collection Partner, and Referral Partner APIs, error responses centre on a canonical `businessErrors[]` array. Most endpoints also populate legacy top-level `error`/`message` fields for backward compatibility (dual-write), including unhandled server errors (`500`), where `error` and `message` are both "An unexpected error occurred. Please try again later." and `businessErrors[0].type` is `InternalError`. Always read `businessErrors[]` for forward-compatible error handling.

<a id="quick-reference"></a>

## Quick Reference

| Status | Meaning | Retryable |
| --- | --- | --- |
| `400` | Validation failed | No — fix request |
| `401` | Invalid or missing API key | No — check credentials |
| `404` | Resource not found | No — verify ID |
| `422` | Business rule violation | No — see `solutionUrl` |
| `500` | Server error | Yes — exponential backoff |

API-specific: `409` (conflicts — Referral Partner client onboarding, in-flight `Idempotency-Key` retries on all three APIs, and Collection Partner state conflicts such as uploads to a merged case), `413`/`415` (file uploads on all three APIs).

<a id="business-errors"></a>

## Business Errors

The `businessErrors[]` array is returned across all endpoints when a request fails for a reason the API can describe with a typed code — most commonly business-rule violations (`422`), but also conflicts (`409`), not-found (`404`), invalid pagination or file uploads (`400`/`413`/`415`), and unexpected server errors (`500`). This format is consistent across all Debitura APIs (Customer, Collection Partner, and Referral Partner).

```json
{
  "error": "Your company's agreement must be signed and current before submitting cases.",
  "message": "Your company's agreement must be signed and current before submitting cases.",
  "businessErrors": [
    {
      "type": "PartnerContractNotValid",
      "message": "Your company's agreement must be signed and current before submitting cases.",
      "solutionUrl": ""
    }
  ]
}
```

Business-rule 422s from Customer API `POST /cases` instead return `businessErrors[]` plus `signingHandoff` (null unless a signing step is missing), with no `error`/`message`.

| Field | Description |
| --- | --- |
| `businessErrors[].type` | Error code (see table below) |
| `businessErrors[].message` | Human-readable description |
| `businessErrors[].solutionUrl` | Link to resolution guidance (when available) |
| `error` | Legacy label — a free-form string, usually the human message (occasionally a distinct label such as `MissingUserIdentifier`). Do not assume it equals `businessErrors[0].type`; read `businessErrors[].type` for the canonical code |
| `message` | Legacy human-readable description (kept for backward compatibility) |

Error messages are safe to display, not to parse

Across all Debitura APIs, `message` (and `businessErrors[].message`) is deliberately generic, human-readable text. For unexpected and internal failures it never contains raw exception text, stack traces, or upstream provider/integration error bodies — those details are logged server-side only. Branch your integration logic on `businessErrors[].type` and the HTTP status code, never on the contents of the message string.

<a id="error-types"></a>

### Error Types

Common types (not exhaustive) — branch on the types you handle and fall back on HTTP status.

| Type | Description | Resolution | `solutionUrl` |
| --- | --- | --- | --- |
| `NoPartnerAvailable` | No collection partner covers the debtor's jurisdiction | Check supported countries or contact support | — |
| `CollectionPartnerNotFound` | An explicitly supplied collectionPartnerId override does not exist or has been deleted | Verify the ID, or omit it to use automatic partner matching | — |
| `CreditorBlocked` | Creditor account is blocked | Contact support | — |
| `MissingDebtCollectionContract` | Debt collection contract not signed | Complete contract signing via URL | ✓ |
| `MissingPowerOfAttorney` | Power of attorney not in place | Complete PoA process via URL | ✓ |
| `MissingKycVerification` | Director identity verification required by collection partner | Complete KYC form via URL | ✓ |
| `CaseNotFound` / `PaymentNotFound` / `WebhookNotFound` / `DivisionNotFound` / `NotFound` | The requested resource was not found or is not accessible — the `type` is usually resource-specific, with generic `NotFound` used for some sub-resources | Verify the ID and your access scope | — |
| `InvalidPagination` | The `page` or `pageSize` parameters are out of range | Adjust pagination parameters — see [Pagination](https://docs.debitura.com/developer-concepts/pagination.md) | — |
| `IdempotencyConflict` | The `Idempotency-Key` was reused with a different request body (returned as `422`; in-flight retries instead get a `409` — see [Idempotency](https://docs.debitura.com/developer-concepts/idempotency.md)) | Use a new key, or replay the original request unchanged | — |
| `Conflict` | The request conflicts with the current state of the resource | Re-fetch the resource and retry | — |
| `FileTooLarge` | The uploaded file exceeds the maximum allowed size | Reduce the file size and retry | — |
| `UnsupportedFileType` | The uploaded file has an unsupported type | Use a supported file format | — |
| `WebhookEventsRequired` | No event types were supplied when creating a webhook subscription | Include at least one valid event type | — |
| `WebhookInvalidEventTypes` | One or more supplied webhook event types are not recognised | Use valid event types — see [Webhooks](https://docs.debitura.com/developer-concepts/webhooks.md) | — |
| `WebhookInvalidUrl` | The webhook subscription URL failed validation (e.g. non-HTTPS) | Provide a reachable HTTPS URL | — |
| `ValidationError` | A generic validation failure without a more specific error type — also returned when the request body itself is not valid UTF-8 | Fix the request and retry; re-encode the request body as UTF-8 if the failure is encoding-related | — |
| `InternalError` | An unexpected server error occurred | Retry with exponential backoff | — |

When `solutionUrl` is provided, it links directly to the portal page where the user can resolve the issue.

<a id="validation-errors-400"></a>

## Validation Errors (400)

Returned for field-level validation failures. Keys are field names; values are arrays of error messages. Keys are field paths in PascalCase (e.g. `Debtor.Email`) — dictionary keys are not camelCased.

```json
{
  "Lines": ["Case total must be a positive amount"],
  "Debtor.Email": ["Invalid email format"]
}
```

<a id="not-found--server-errors-404500"></a>

## Not Found & Server Errors (404/500)

Not-found and unexpected server errors return the same structured envelope as business errors. The not-found `type` is usually resource-specific — `CaseNotFound`, `PaymentNotFound`, `WebhookNotFound`, `DivisionNotFound` — with a generic `NotFound` for some sub-resources; server errors use `InternalError`. Server errors never leak stack traces or internal details.

```json
{
  "error": "No case found with this ID: 3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "message": "No case found with this ID: 3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "businessErrors": [
    {
      "type": "CaseNotFound",
      "message": "No case found with this ID: 3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "solutionUrl": ""
    }
  ]
}
```

<a id="problem-details-401"></a>

## Problem Details (401)

Authentication errors return RFC 7807 Problem Details with `Content-Type: application/problem+json`.

```json
{
  "title": "Unauthorized",
  "status": 401,
  "detail": "API Key was not provided"
}
```

For an unknown key, `detail` is `"Invalid API Key"`.

Framework-level field validation (`[Required]`, `[MinLength]`, etc.) also returns RFC 7807 `ValidationProblemDetails`; see the Validation Errors section above for the controller-level validation shape.

<a id="retry-strategy"></a>

## Retry Strategy

For `500` errors, use exponential backoff with max 3 retries. See [Idempotency](https://docs.debitura.com/developer-concepts/idempotency.md) for safe retry patterns.
