Error Handling
Understand error types, HTTP status codes, and implement robust retry strategies.
Error Types
The SDK throws typed errors you can catch and handle precisely. Two main error classes cover all failure modes:
-
PeppolValidationError— Invoice failed local validation. Contains anerrorsarray with field paths and messages. -
PeppolApiError— Network or server error. IncludesstatusCodeandrequestIdfor support.
import { Peppol, PeppolValidationError, PeppolApiError } from "@getpeppr/sdk";
const peppol = new Peppol({ apiKey: "sk_live_..." });
try {
const result = await peppol.invoices.send(invoiceData);
console.log(`Success: ${result.id}`);
} catch (error) {
if (error instanceof PeppolValidationError) {
// Local validation failed — invoice never sent
console.error("Validation errors:");
for (const e of error.errors) {
console.error(` ${e.field}: ${e.message}`);
}
} else if (error instanceof PeppolApiError) {
// API returned an error — network or server issue
console.error(`API error [${error.statusCode}]: ${error.message}`);
console.error(`Request ID: ${error.requestId}`); // for support
} else {
throw error; // unexpected
}
}HTTP Status Codes
The API uses standard HTTP status codes. Here are the ones you'll encounter:
200 Success — request completed 201 Created — invoice sent successfully 400 Bad Request — invalid parameters or validation error 401 Unauthorized — missing or invalid API key 404 Not Found — resource doesn't exist 429 Too Many Requests — rate limit exceeded (see Rate Limits) 500 Server Error — retry with exponential backoff {
"error": "validation_error",
"message": "Invoice validation failed",
"errors": [
{
"field": "from.vatNumber",
"message": "VAT number is required for invoices exceeding EUR 400",
"suggestion": "Add vatNumber to the seller party"
}
],
"requestId": "req_abc123xyz"
}{
"error": "not_found",
"message": "Invoice inv_xyz789 not found",
"statusCode": 404,
"requestId": "req_def456uvw"
}Retry Strategies
For transient failures (5xx errors, network timeouts), implement exponential backoff. Never retry client errors (4xx) — they require fixing the request.
Best Practices
- Retry only 5xx — server errors are transient, client errors need fixes
- Exponential backoff — wait 1s, 2s, 4s between retries
- Max 3 retries — avoid hammering the API
- Include requestId — send it to support for debugging persistent failures
The SDK handles idempotency automatically. It's safe to retry
send() — the same invoice won't be sent twice.
import { Peppol, PeppolApiError } from "@getpeppr/sdk";
const peppol = new Peppol({ apiKey: "sk_live_..." });
async function sendWithRetry(invoice: any, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await peppol.invoices.send(invoice);
} catch (error) {
if (error instanceof PeppolApiError) {
// Don't retry client errors (4xx) — only server errors (5xx)
if (error.statusCode < 500) throw error;
// Don't retry on last attempt
if (attempt === maxRetries) throw error;
// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, attempt - 1) * 1000;
await new Promise((r) => setTimeout(r, delay));
} else {
throw error;
}
}
}
}