Not available

Webhooks

Planned: real-time notifications about events in your account. This page describes the intended contract — events, payload, signature and retry behaviour — so an integration can be prepared.

Webhooks do not exist yet: none of the 81 paths in the API specification accepts a webhook registration. No date is set — once there is one it will live in lib/product-status.ts. This page is a preview of the planned contract, not a commitment.

Real-time Events

Planned: instant notification on invoice creation, validation and conversion

Signed

HMAC-SHA256 signatures to verify authenticity

Automatic Retries

Planned: on failure, webhooks are resent automatically

Available Events

EventDescriptionTrigger
invoice.createdA new invoice was successfully createdPOST /api/v1/invoice/de/xrechnung/generate successful
invoice.validatedAn invoice was checked and is conformantPOST /api/v1/invoice/{countryCode}/validate returns `valid: true`
invoice.validation_failedInvoice validation failedE-invoice does not match the schema
conversion.completedFormat conversion completedPOST /api/v1/invoice/convert successful
conversion.failedFormat conversion failedConversion error
peppol.sentE-invoice sent via PeppolPeppol transmission successful
peppol.deliveredDelivery confirmation received from PeppolMDN from recipient
peppol.failedPeppol transmission failedTransmission error

Webhook Payload

Intended format: HTTP POST with a JSON body. The field names follow the invoice schema (`subtotal`, `total`, `taxSummary`) and the generate response (`format`, `filename`, `mimeType`, `hash`):

json
1{
2 "id": "evt_abc123xyz",
3 "type": "invoice.created",
4 "created": "2026-03-01T10:30:00Z",
5 "data": {
6 "object": {
7 "invoiceNumber": "RE-2026-0042",
8 "format": "xrechnung",
9 "filename": "RE-2026-0042.xml",
10 "mimeType": "application/xml",
11 "hash": "9f2c1b7d4e8a03f5c6b9d0e1a2f3c4b5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1",
12 "currency": "EUR",
13 "subtotal": 1500.00,
14 "total": 1785.00,
15 "taxSummary": [
16 { "taxRate": 19, "taxCategoryCode": "S", "netAmount": 1500.00, "taxAmount": 285.00 }
17 ]
18 }
19 }
20}

HTTP Headers

X-Xhub-Signature

HMAC-SHA256 signature of the payload

X-Xhub-Event

Event type (e.g. invoice.created)

X-Xhub-Delivery

Unique delivery ID

X-Xhub-Timestamp

Unix timestamp of signature creation

Signature Verification

Planned: verify the signature to ensure the webhook comes from Invoice-api.xhub:

Node.js / Express

typescript
1import crypto from 'crypto';
2 
3function verifyWebhookSignature(payload, signature, secret) {
4 const expectedSignature = crypto
5 .createHmac('sha256', secret)
6 .update(payload)
7 .digest('hex');
8 
9 const trusted = Buffer.from(`sha256=${expectedSignature}`, 'ascii');
10 const untrusted = Buffer.from(signature, 'ascii');
11 
12 return crypto.timingSafeEqual(trusted, untrusted);
13}
14 
15// Express.js Handler
16app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
17 const signature = req.headers['x-xhub-signature'];
18 const payload = req.body;
19 
20 if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
21 return res.status(401).send('Invalid signature');
22 }
23 
24 const event = JSON.parse(payload);
25 
26 switch (event.type) {
27 case 'invoice.created':
28 console.log('Invoice created:', event.data.object.invoiceNumber);
29 break;
30 case 'invoice.validation_failed':
31 console.log('Validation failed:', event.data.object.errors);
32 break;
33 }
34 
35 res.status(200).send('OK');
36});

Python / Flask

python
1import hmac
2import hashlib
3from flask import Flask, request, abort
4 
5app = Flask(__name__)
6WEBHOOK_SECRET = 'your_webhook_secret'
7 
8def verify_signature(payload, signature, secret):
9 expected = hmac.new(
10 secret.encode(),
11 payload,
12 hashlib.sha256
13 ).hexdigest()
14 return hmac.compare_digest(f'sha256={expected}', signature)
15 
16@app.route('/webhook', methods=['POST'])
17def webhook():
18 signature = request.headers.get('X-Xhub-Signature')
19 payload = request.data
20 
21 if not verify_signature(payload, signature, WEBHOOK_SECRET):
22 abort(401)
23 
24 event = request.json
25 
26 if event['type'] == 'invoice.created':
27 print(f"Invoice created: {event['data']['object']['invoiceNumber']}")
28 elif event['type'] == 'invoice.validation_failed':
29 print(f"Validation failed: {event['data']['object']['errors']}")
30 
31 return 'OK', 200

Retry Policy

Planned: if your endpoint does not respond with 2xx, the webhook is to be resent on this schedule:

AttemptDelay
1Immediately
21 minute
35 minutes
430 minutes
52 hours
612 hours

After 6 failed attempts the webhook is to be marked as failed and an email notification sent.

Best Practices

Respond Quickly

Respond within 5 seconds with 200 OK. Process the webhook asynchronously in the background.

Verify Signature

Always verify the HMAC signature. Reject requests without a valid signature.

Ensure Idempotency

Store the delivery ID and process each webhook only once (duplicates may occur during retries).

Use HTTPS

Your webhook endpoint must support HTTPS. HTTP endpoints are not accepted.

Set Up Webhook

Configuration in the dashboard is planned. It is to allow:

  • • Set endpoint URL
  • • Select events to be sent
  • • View and rotate webhook secret
  • • View delivery logs and failed webhooks