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.
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
| Event | Description | Trigger |
|---|---|---|
invoice.created | A new invoice was successfully created | POST /api/v1/invoice/de/xrechnung/generate successful |
invoice.validated | An invoice was checked and is conformant | POST /api/v1/invoice/{countryCode}/validate returns `valid: true` |
invoice.validation_failed | Invoice validation failed | E-invoice does not match the schema |
conversion.completed | Format conversion completed | POST /api/v1/invoice/convert successful |
conversion.failed | Format conversion failed | Conversion error |
peppol.sent | E-invoice sent via Peppol | Peppol transmission successful |
peppol.delivered | Delivery confirmation received from Peppol | MDN from recipient |
peppol.failed | Peppol transmission failed | Transmission 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`):
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-SignatureHMAC-SHA256 signature of the payload
X-Xhub-EventEvent type (e.g. invoice.created)
X-Xhub-DeliveryUnique delivery ID
X-Xhub-TimestampUnix timestamp of signature creation
Signature Verification
Planned: verify the signature to ensure the webhook comes from Invoice-api.xhub:
Node.js / Express
1import crypto from 'crypto';2 3function verifyWebhookSignature(payload, signature, secret) {4 const expectedSignature = crypto5 .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 Handler16app.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
1import hmac2import hashlib3from flask import Flask, request, abort4 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.sha25613 ).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.data20 21 if not verify_signature(payload, signature, WEBHOOK_SECRET):22 abort(401)23 24 event = request.json25 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', 200Retry Policy
Planned: if your endpoint does not respond with 2xx, the webhook is to be resent on this schedule:
| Attempt | Delay |
|---|---|
| 1 | Immediately |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 12 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