@getpeppr/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +463 -0
- package/dist/core/client.d.ts +523 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +1308 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/code-lists.d.ts +120 -0
- package/dist/core/code-lists.d.ts.map +1 -0
- package/dist/core/code-lists.js +247 -0
- package/dist/core/code-lists.js.map +1 -0
- package/dist/core/country-rules.d.ts +30 -0
- package/dist/core/country-rules.d.ts.map +1 -0
- package/dist/core/country-rules.js +128 -0
- package/dist/core/country-rules.js.map +1 -0
- package/dist/core/schematron.d.ts +59 -0
- package/dist/core/schematron.d.ts.map +1 -0
- package/dist/core/schematron.js +480 -0
- package/dist/core/schematron.js.map +1 -0
- package/dist/core/ubl-builder.d.ts +20 -0
- package/dist/core/ubl-builder.d.ts.map +1 -0
- package/dist/core/ubl-builder.js +530 -0
- package/dist/core/ubl-builder.js.map +1 -0
- package/dist/core/validator.d.ts +16 -0
- package/dist/core/validator.d.ts.map +1 -0
- package/dist/core/validator.js +171 -0
- package/dist/core/validator.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/types/invoice.d.ts +635 -0
- package/dist/types/invoice.d.ts.map +1 -0
- package/dist/types/invoice.js +8 -0
- package/dist/types/invoice.js.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
# @getpeppr/sdk
|
|
2
|
+
|
|
3
|
+
Send Peppol e-invoices in 5 lines of code.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@getpeppr/sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { Peppol } from "@getpeppr/sdk";
|
|
11
|
+
|
|
12
|
+
const peppol = new Peppol({ apiKey: "sk_live_..." });
|
|
13
|
+
|
|
14
|
+
const result = await peppol.invoices.send({
|
|
15
|
+
number: "INV-2026-001",
|
|
16
|
+
to: { name: "Acme Corp", peppolId: "0208:BE9876543210", country: "BE" },
|
|
17
|
+
lines: [
|
|
18
|
+
{ description: "Consulting services", quantity: 10, unitPrice: 150, vatRate: 21 },
|
|
19
|
+
],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
console.log(`Invoice sent! Status: ${result.status}`);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @getpeppr/sdk
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Stripe-like developer experience** -- JSON in, Peppol out
|
|
34
|
+
- **Full Peppol BIS 3.0 compliance** -- UBL 2.1 XML generation handled for you
|
|
35
|
+
- **Client-side + server-side validation** -- catch errors before they reach the network
|
|
36
|
+
- **Create, send, list, acknowledge** invoices
|
|
37
|
+
- **Credit notes** with automatic UBL CreditNote generation
|
|
38
|
+
- **PDF and XML export** -- download invoices in any format
|
|
39
|
+
- **Peppol Directory lookup** -- verify participants before sending
|
|
40
|
+
- **Events API** with async pagination
|
|
41
|
+
- **Batch send** with concurrency control
|
|
42
|
+
- **Webhook signature verification** -- HMAC-SHA256 with replay protection
|
|
43
|
+
- **Built-in retry logic** with exponential backoff and rate limit handling
|
|
44
|
+
- **TypeScript-first** with full type safety
|
|
45
|
+
- **Zero dependencies**
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { Peppol } from "@getpeppr/sdk";
|
|
51
|
+
|
|
52
|
+
const peppol = new Peppol({
|
|
53
|
+
apiKey: "sk_live_...", // Required. Starts with sk_sandbox_ or sk_live_
|
|
54
|
+
baseUrl: "https://...", // Custom gateway URL (default: https://api.getpeppr.dev/v1)
|
|
55
|
+
timeout: 30000, // Request timeout in ms (default: 30s)
|
|
56
|
+
retry: {
|
|
57
|
+
maxRetries: 3, // Max retry attempts (default: 3)
|
|
58
|
+
initialDelayMs: 500, // Initial retry delay (default: 500ms)
|
|
59
|
+
maxDelayMs: 30000, // Max retry delay (default: 30s)
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Sending Invoices
|
|
65
|
+
|
|
66
|
+
### Basic invoice
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const result = await peppol.invoices.send({
|
|
70
|
+
number: "INV-2026-001",
|
|
71
|
+
to: {
|
|
72
|
+
name: "Acme Corp",
|
|
73
|
+
peppolId: "0208:BE9876543210",
|
|
74
|
+
country: "BE",
|
|
75
|
+
},
|
|
76
|
+
lines: [
|
|
77
|
+
{ description: "Consulting services", quantity: 10, unitPrice: 150, vatRate: 21 },
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
console.log(result.id); // "inv-abc123"
|
|
82
|
+
console.log(result.status); // "new" | "sent" | ...
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Full invoice with all options
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const result = await peppol.invoices.send({
|
|
89
|
+
// Required
|
|
90
|
+
number: "INV-2026-001",
|
|
91
|
+
to: {
|
|
92
|
+
name: "Acme Corp",
|
|
93
|
+
peppolId: "0208:BE9876543210",
|
|
94
|
+
vatNumber: "BE9876543210",
|
|
95
|
+
street: "123 Business Ave",
|
|
96
|
+
city: "Brussels",
|
|
97
|
+
postalCode: "1000",
|
|
98
|
+
country: "BE",
|
|
99
|
+
companyId: "0876543210",
|
|
100
|
+
contactName: "Jane Doe",
|
|
101
|
+
phone: "+32 2 123 4567",
|
|
102
|
+
email: "invoices@acme.be",
|
|
103
|
+
},
|
|
104
|
+
lines: [
|
|
105
|
+
{
|
|
106
|
+
description: "Consulting services",
|
|
107
|
+
quantity: 10,
|
|
108
|
+
unit: "hours", // Human-readable: "hours", "days", "kg", or UN/ECE: "HUR", "DAY"
|
|
109
|
+
unitPrice: 150,
|
|
110
|
+
vatRate: 21,
|
|
111
|
+
vatCategory: "S", // S=standard, Z=zero-rated, E=exempt, AE=reverse charge
|
|
112
|
+
itemId: "SRV-001",
|
|
113
|
+
allowances: [{ reason: "Loyalty discount", amount: 50 }],
|
|
114
|
+
charges: [{ reason: "Express delivery", amount: 25 }],
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
|
|
118
|
+
// Optional
|
|
119
|
+
date: "2026-01-15", // Invoice date (default: today)
|
|
120
|
+
dueDate: "2026-02-15", // Due date
|
|
121
|
+
currency: "EUR", // ISO 4217 (default: EUR)
|
|
122
|
+
taxCurrency: "GBP", // Tax reporting currency if different
|
|
123
|
+
taxCurrencyRate: 0.86, // Exchange rate to tax currency
|
|
124
|
+
buyerReference: "PO-2026-042", // Buyer reference (BT-10)
|
|
125
|
+
orderReference: "ORD-789", // Purchase order number
|
|
126
|
+
note: "Thank you for your business",
|
|
127
|
+
paymentTerms: "Net 30 days",
|
|
128
|
+
paymentMeans: 30, // 30=credit transfer, 58=SEPA
|
|
129
|
+
paymentIban: "BE68539007547034",
|
|
130
|
+
paymentBic: "BBRUBEBB",
|
|
131
|
+
paymentReference: "+++090/9337/55493+++",
|
|
132
|
+
|
|
133
|
+
payeeParty: { // When payment recipient differs from seller
|
|
134
|
+
name: "Factoring Co",
|
|
135
|
+
peppolId: "0208:BE1111111111",
|
|
136
|
+
country: "BE",
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
invoicePeriod: { // Billing period (common for SaaS)
|
|
140
|
+
startDate: "2026-01-01",
|
|
141
|
+
endDate: "2026-01-31",
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
delivery: { // Delivery information
|
|
145
|
+
date: "2026-01-20",
|
|
146
|
+
locationId: "LOC-001",
|
|
147
|
+
address: { street: "456 Warehouse Rd", city: "Antwerp", postalCode: "2000", country: "BE" },
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
allowances: [{ reason: "Volume discount", amount: 100, vatRate: 21 }],
|
|
151
|
+
charges: [{ reason: "Handling fee", amount: 25, vatRate: 21 }],
|
|
152
|
+
|
|
153
|
+
attachments: [
|
|
154
|
+
{
|
|
155
|
+
id: "ATT-001",
|
|
156
|
+
description: "Timesheet",
|
|
157
|
+
filename: "timesheet.pdf",
|
|
158
|
+
mimeType: "application/pdf",
|
|
159
|
+
content: "base64-encoded-content...", // Or use `url` for external reference
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Create draft + send later
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const draft = await peppol.invoices.create({
|
|
169
|
+
number: "INV-2026-002",
|
|
170
|
+
to: { name: "Acme Corp", peppolId: "0208:BE9876543210", country: "BE" },
|
|
171
|
+
lines: [{ description: "Consulting", quantity: 5, unitPrice: 200, vatRate: 21 }],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
console.log(draft.id); // "inv-xyz456"
|
|
175
|
+
console.log(draft.status); // "new"
|
|
176
|
+
|
|
177
|
+
// Send when ready
|
|
178
|
+
await peppol.invoices.sendById(draft.id);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Credit notes
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
const result = await peppol.invoices.send({
|
|
185
|
+
number: "CN-2026-001",
|
|
186
|
+
isCreditNote: true,
|
|
187
|
+
invoiceReference: "INV-2026-001", // Required: original invoice number
|
|
188
|
+
to: { name: "Acme Corp", peppolId: "0208:BE9876543210", country: "BE" },
|
|
189
|
+
lines: [
|
|
190
|
+
{ description: "Refund for consulting services", quantity: 2, unitPrice: 150, vatRate: 21 },
|
|
191
|
+
],
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Listing Invoices
|
|
196
|
+
|
|
197
|
+
### Single page
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const page = await peppol.invoices.list({
|
|
201
|
+
limit: 25,
|
|
202
|
+
offset: 0,
|
|
203
|
+
type: "issued", // "issued" | "received"
|
|
204
|
+
includeLines: true, // Include line item details
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
console.log(page.data); // InvoiceSummary[]
|
|
208
|
+
console.log(page.meta); // { totalCount, offset, limit, hasMore }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Auto-paginate all invoices
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
for await (const invoice of peppol.invoices.listAll({ type: "issued" })) {
|
|
215
|
+
console.log(invoice.id, invoice.number, invoice.status);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Invoice Status
|
|
220
|
+
|
|
221
|
+
### Get current status
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const result = await peppol.invoices.getStatus("inv-abc123");
|
|
225
|
+
console.log(result.status); // "sent", "accepted", etc.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Wait for a specific status
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
const result = await peppol.invoices.waitFor("inv-abc123", "accepted", {
|
|
232
|
+
timeout: 60000, // Max wait time in ms (default: 120s)
|
|
233
|
+
interval: 5000, // Polling interval in ms (default: 5s)
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
You can also wait for multiple statuses:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
const result = await peppol.invoices.waitFor("inv-abc123", ["accepted", "paid"]);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Terminal failure statuses (`error`, `refused`, `invalid`) throw immediately without waiting for timeout.
|
|
244
|
+
|
|
245
|
+
### Document status lifecycle
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
new --> sent --> downloaded --> registered --> accepted --> paid --> closed
|
|
249
|
+
\-> refused
|
|
250
|
+
\-> error
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Export PDF / XML
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import { writeFileSync } from "fs";
|
|
257
|
+
|
|
258
|
+
// Export as PDF
|
|
259
|
+
const pdf = await peppol.invoices.getAs("inv-abc123", "pdf");
|
|
260
|
+
writeFileSync("invoice.pdf", Buffer.from(pdf));
|
|
261
|
+
|
|
262
|
+
// Export as UBL XML
|
|
263
|
+
const xml = await peppol.invoices.getAs("inv-abc123", "xml.ubl.invoice.bis3");
|
|
264
|
+
writeFileSync("invoice.xml", Buffer.from(xml));
|
|
265
|
+
|
|
266
|
+
// Available formats: "pdf" | "xml.ubl.invoice.bis3" | "xml.facturae.3.2" | "original"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Acknowledge Received Invoice
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
const result = await peppol.invoices.acknowledge("inv-abc123");
|
|
273
|
+
console.log(result.status); // "accepted"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Batch Send
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
const invoices = [invoice1, invoice2, invoice3, /* ... */];
|
|
280
|
+
|
|
281
|
+
const result = await peppol.invoices.sendBatch(invoices, {
|
|
282
|
+
concurrency: 5, // Max parallel requests (default: 5)
|
|
283
|
+
stopOnError: false, // Continue on failure (default: false)
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
console.log(`${result.succeeded.length} sent, ${result.failed.length} failed`);
|
|
287
|
+
|
|
288
|
+
for (const failure of result.failed) {
|
|
289
|
+
console.error(`Invoice #${failure.index} (${failure.input.number}): ${failure.error.message}`);
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Peppol Directory
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
const entry = await peppol.directory.lookup("0208:BE0123456789");
|
|
297
|
+
|
|
298
|
+
console.log(entry.name); // "My Company NV"
|
|
299
|
+
console.log(entry.country); // "BE"
|
|
300
|
+
console.log(entry.capabilities); // ["invoice", "credit_note"]
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Validation
|
|
304
|
+
|
|
305
|
+
### Client-side validation (instant, offline)
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
const result = peppol.validate({
|
|
309
|
+
number: "INV-001",
|
|
310
|
+
to: { name: "Acme", peppolId: "0208:BE9876543210", country: "BE" },
|
|
311
|
+
lines: [{ description: "Item", quantity: 1, unitPrice: 100, vatRate: 21 }],
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
if (!result.valid) {
|
|
315
|
+
for (const err of result.errors) {
|
|
316
|
+
console.log(`${err.field}: ${err.message}`);
|
|
317
|
+
if (err.suggestion) console.log(` Fix: ${err.suggestion}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
for (const warn of result.warnings) {
|
|
322
|
+
console.log(`Warning: ${warn.field}: ${warn.message}`);
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Server-side validation (XSD + Schematron)
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
const result = await peppol.invoices.validateServer({
|
|
330
|
+
number: "INV-001",
|
|
331
|
+
to: { name: "Acme", peppolId: "0208:BE9876543210", country: "BE" },
|
|
332
|
+
lines: [{ description: "Item", quantity: 1, unitPrice: 100, vatRate: 21 }],
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
console.log(result.valid); // true/false
|
|
336
|
+
console.log(result.xsd.errors); // XSD schema errors
|
|
337
|
+
console.log(result.schematron.errors); // Peppol business rule errors
|
|
338
|
+
console.log(result.schematron.warnings); // Non-blocking warnings
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Events
|
|
342
|
+
|
|
343
|
+
### List events
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
const result = await peppol.events.list({
|
|
347
|
+
limit: 10,
|
|
348
|
+
invoiceId: "inv-abc123", // Filter by invoice
|
|
349
|
+
dateFrom: "2026-01-01", // Filter by date range
|
|
350
|
+
dateTo: "2026-01-31",
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
for (const event of result.data) {
|
|
354
|
+
console.log(event.name, event.text, event.createdAt);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Auto-paginate all events
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
for await (const event of peppol.events.listAll({ invoiceId: "inv-abc123" })) {
|
|
362
|
+
console.log(event.name, event.createdAt);
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Webhooks
|
|
367
|
+
|
|
368
|
+
Verify and parse incoming webhook payloads with HMAC-SHA256 signature verification and replay protection.
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
import { Peppol, webhooks } from "@getpeppr/sdk";
|
|
372
|
+
|
|
373
|
+
// Express example
|
|
374
|
+
app.post("/webhooks/peppol", async (req, res) => {
|
|
375
|
+
try {
|
|
376
|
+
const event = await webhooks.constructEvent(
|
|
377
|
+
req.body, // Raw body string (NOT parsed JSON)
|
|
378
|
+
req.headers["x-b2brouter-signature"], // Signature header
|
|
379
|
+
"whsec_your_webhook_secret", // Your webhook secret
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
switch (event.type) {
|
|
383
|
+
case "invoice.accepted":
|
|
384
|
+
console.log("Invoice accepted:", event.data);
|
|
385
|
+
break;
|
|
386
|
+
case "invoice.refused":
|
|
387
|
+
console.log("Invoice refused:", event.data);
|
|
388
|
+
break;
|
|
389
|
+
case "invoice.received":
|
|
390
|
+
console.log("New invoice received:", event.data);
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
res.sendStatus(200);
|
|
395
|
+
} catch (err) {
|
|
396
|
+
console.error("Webhook verification failed:", err.message);
|
|
397
|
+
res.status(400).send("Webhook verification failed");
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Supported event types:
|
|
403
|
+
|
|
404
|
+
| Event | Description |
|
|
405
|
+
|-------|-------------|
|
|
406
|
+
| `invoice.sent` | Invoice sent to Peppol network |
|
|
407
|
+
| `invoice.accepted` | Recipient accepted the invoice |
|
|
408
|
+
| `invoice.refused` | Recipient refused the invoice |
|
|
409
|
+
| `invoice.error` | Delivery error |
|
|
410
|
+
| `invoice.registered` | Invoice registered by recipient |
|
|
411
|
+
| `invoice.paid` | Invoice marked as paid |
|
|
412
|
+
| `invoice.closed` | Invoice closed |
|
|
413
|
+
| `invoice.received` | New invoice received |
|
|
414
|
+
| `creditnote.sent` | Credit note sent |
|
|
415
|
+
| `creditnote.received` | Credit note received |
|
|
416
|
+
|
|
417
|
+
## UBL XML Generation
|
|
418
|
+
|
|
419
|
+
Generate UBL 2.1 XML without sending (useful for debugging or manual submission):
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
const xml = peppol.toXml({
|
|
423
|
+
number: "INV-001",
|
|
424
|
+
to: { name: "Acme", peppolId: "0208:BE9876543210", country: "BE" },
|
|
425
|
+
lines: [{ description: "Item", quantity: 1, unitPrice: 100, vatRate: 21 }],
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
console.log(xml); // Full UBL 2.1 Invoice XML
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Error Handling
|
|
432
|
+
|
|
433
|
+
The SDK provides three error classes for precise error handling:
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
import { Peppol, PeppolError, PeppolValidationError, PeppolApiError } from "@getpeppr/sdk";
|
|
437
|
+
|
|
438
|
+
try {
|
|
439
|
+
await peppol.invoices.send(input);
|
|
440
|
+
} catch (err) {
|
|
441
|
+
if (err instanceof PeppolValidationError) {
|
|
442
|
+
// Client-side validation failed
|
|
443
|
+
console.log(err.validation.errors); // ValidationError[]
|
|
444
|
+
console.log(err.validation.warnings); // ValidationWarning[]
|
|
445
|
+
} else if (err instanceof PeppolApiError) {
|
|
446
|
+
// API returned an error
|
|
447
|
+
console.log(err.statusCode); // HTTP status code
|
|
448
|
+
console.log(err.responseBody); // Raw response body
|
|
449
|
+
console.log(err.retryAfterMs); // Retry-After delay (on 429)
|
|
450
|
+
} else if (err instanceof PeppolError) {
|
|
451
|
+
// General SDK error (invalid config, timeout, etc.)
|
|
452
|
+
console.log(err.message);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### Automatic retries
|
|
458
|
+
|
|
459
|
+
The SDK automatically retries on transient errors (429, 500, 502, 503, 504) with exponential backoff and jitter. Rate limit responses (429) respect the `Retry-After` header.
|
|
460
|
+
|
|
461
|
+
## License
|
|
462
|
+
|
|
463
|
+
MIT
|