Skip to content

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 an errors array with field paths and messages.
  • PeppolApiError — Network or server error. Includes statusCode and requestId for 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
400 Validation Error
{
  "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"
}
404 Not Found
{
  "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.
retry.ts
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;
      }
    }
  }
}