SAPI-SK: error codes
All errors on /sapi use a uniform envelope with a structured error object: a machine-readable code and category, an explicit retryable flag, and a correlation_id for tracing in logs and support.
Error shape
{
"error": {
"category": "VALIDATION",
"code": "SAPI-VAL-001",
"message": "Invalid send request",
"details": [
{ "field": "metadata.documentId", "issue": "Required" }
],
"retryable": false,
"correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}
}The details field is optional (e.g. field-level validation). category takes values AUTH, VALIDATION, PROCESSING, TEMPORARY, and PERMANENT.
Code catalog
| HTTP | Code | Category | Retryable | Meaning |
|---|---|---|---|---|
| 401 | SAPI-AUTH-001 | AUTH | no | Invalid client credentials. |
| 401 | SAPI-AUTH-002 | AUTH | no | The access token is invalid or expired. |
| 403 | SAPI-AUTH-003 | AUTH | no | The client has no access to the given organization / Peppol participant. |
| 403 | SAPI-AUTH-004 | AUTH | no | The IP address is not on the client's allowlist. |
| 423 | SAPI-AUTH-005 | AUTH | yes | The client is temporarily locked after 5 failed attempts (15 min). |
| 400 | SAPI-VAL-001 | VALIDATION | no | Malformed request: bad JSON, missing field, or missing required header (Idempotency-Key, X-Peppol-Participant-Id). |
| 400 | SAPI-VAL-002 | VALIDATION | no | X-Peppol-Participant-Id does not match metadata.senderParticipantId. |
| 400 | SAPI-VAL-003 | VALIDATION | no | The SHA-256 checksum does not match the payload. |
| 400 | SAPI-VAL-004 | VALIDATION | no | The payload exceeds the 10 MB limit. |
| 400 | SAPI-VAL-005 | VALIDATION | no | The document failed Peppol validation, or its number collides with a natively issued invoice (nothing was sent). |
| 404 | SAPI-VAL-006 | VALIDATION | no | The document does not exist or you don't have access to it. |
| 409 | SAPI-PROC-001 | PROCESSING | no | The Idempotency-Key was already used with a different request body. |
| 422 | SAPI-PROC-002 | PERMANENT | no | Insufficient credit to send. |
| 500 | SAPI-PROC-003 | PROCESSING | no | Internal processing error. |
| 429 | SAPI-TMP-001 | TEMPORARY | yes | Rate limit exceeded: respect Retry-After. |
| 502 | SAPI-TMP-002 | TEMPORARY | yes | The Peppol network is temporarily unavailable: retry with exponential backoff. |
Only retry when
retryable: true (categories TEMPORARY and the SAPI-AUTH-005 lockout), and always with exponential backoff. On 429, respect the Retry-After header.Recommendations
Don't retry 4xx errors (other than 429 and the 423 lockout) without changing the request: the retryable field is false. On 423, wait 15 minutes and retry the same request. When sending, store the providerDocumentId and use a stable Idempotency-Key to avoid duplicates on retry. Include the correlation_id when contacting support.