API – 5. Fouten (Errors)
De API retourneert fouten consistente JSON met een vaste structuur. Gebruik deze informatie om snel te begrijpen wat er mis ging en hoe je het oplost.
Standaard foutformaat
{
"error": {
"code": "validation_error",
"message": "Controleer de velden.",
"details": {
"invoice.date": "Verplicht (JJJJ-MM-DD).",
"lines.0.unit_price": "Moet ≥ 0 zijn."
}
}
}
Velden
code— korte, machine-lezbare foutcode (stabiel).message— menselijke samenvatting (kan vertaald worden).details— object met veldspecifieke fouten (dot-notatie), alleen aanwezig bij validatieproblemen.
HTTP-statuscodes & betekenissen
| Status | Code | Wanneer | Extra headers (indien van toepassing) |
|---|---|---|---|
| 400 | invalid_json | Body is geen geldige JSON | — |
| 401 | unauthorized | Ontbrekende of ongeldige API key | WWW-Authenticate: Bearer realm="Facturalia" |
| 403 | forbidden | Key geldig, maar vereiste scope ontbreekt | — |
| 404 | not_found | Resource of route bestaat niet | — |
| 405 | method_not_allowed | Verkeerde HTTP-methode (bv. GET i.p.v. POST) | Allow: POST |
| 409 | idempotency_conflict | Zelfde external_id/Idempotency-Key/invoice.number met andere data | — |
| 415 | unsupported_media_type | Content-Type niet application/json | — |
| 422 | validation_error | Data valideert niet | — |
| 429 | rate_limited | Te veel requests binnen het venster | Retry-After, X-RateLimit-* |
| 500 | server_error | Onverwachte fout aan serverzijde | (optioneel) X-Request-Id |
Tip: stuur altijd
Content-Type: application/json; charset=utf-8mee in je request.
Voorbeelden
400 – Ongeldige JSON
{
"error": {
"code": "invalid_json",
"message": "Body is geen geldige JSON."
}
}
401 – Geen of ongeldige key
{
"error": {
"code": "unauthorized",
"message": "Authorization header ontbreekt of ongeldig."
}
}
Header:WWW-Authenticate: Bearer realm="Facturalia", error="invalid_token"
403 – Scope ontbreekt
{
"error": {
"code": "forbidden",
"message": "Scope ontbreekt: invoices:create"
}
}
405 – Methode niet toegestaan
{
"error": {
"code": "method_not_allowed",
"message": "Gebruik POST op dit endpoint."
}
}
Header: Allow: POST
409 – Idempotency-conflict
{
"error": {
"code": "idempotency_conflict",
"message": "Identifier reeds gebruikt voor andere payload."
}
}
Treedt op bij hergebruik van dezelfde external_id, Idempotency-Key of invoice.number met afwijkende inhoud.
415 – Verkeerde Content-Type
{
"error": {
"code": "unsupported_media_type",
"message": "Gebruik Content-Type: application/json."
}
}
422 – Validatiefout (met veldfouten)
{
"error": {
"code": "validation_error",
"message": "Controleer de velden.",
"details": {
"invoice.date": "Verplicht (JJJJ-MM-DD).",
"customer.email": "Ongeldig e-mailadres.",
"lines.1.vat_rate": "Toegestaan: 0, 6, 12, 21."
}
}
}
429 – Te veel requests
Headers:
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 2025-10-26T12:34:56Z
Body:
{
"error": {
"code": "rate_limited",
"message": "Te veel requests. Probeer later opnieuw."
}
}
500 – Serverfout
{
"error": {
"code": "server_error",
"message": "Onverwachte fout",
"details": {
"hint": "Reference: 9f1c2a14" // bv. request-id voor support
}
}
}
Idempotency-headers in responses (indien gebruikt)
- Eerste creatie:
Idempotency-Status: stored - Herspeelde creatie:
Idempotency-Status: replayed
Beste praktijken voor clients
- Lees HTTP-status én
error.codeom te beslissen wat te doen. - Bij
422toon de inhoud vandetailsdirect aan de gebruiker of log ze per veld. - Respecteer
Retry-Afterbij429. - Hergebruik dezelfde idempotency-identifier bij retries van dezelfde creatie (zie hoofdstuk 3).
