Webhooks
Webhooks allow your application to receive real-time notifications when fiscalization events occur. Instead of polling for transaction status, configure a webhook endpoint and FiscalAPI will send you HTTP POST requests with event details.
Overview
When a transaction completes fiscalization (or permanently fails), FiscalAPI sends an HTTP POST to your configured webhook URL with the event payload.
Setting up webhooks
1. Create a webhook endpoint
First, create a webhook subscription with the events you want to receive:
curl -X POST https://api.fiscalapi.com/v1/webhooks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer fsk_test_abc123def456..." \
-d '{
"url": "https://example.com/webhooks/fiscalapi",
"events": ["fiscalization.completed", "fiscalization.dead_letter"],
"description": "Production webhook"
}'
The response includes a secret (prefixed with whsec_) that you'll use to verify signatures. Store it securely -- it's only shown on creation and when rotated.
2. Handle webhook deliveries
Your endpoint receives POST requests with a JSON payload:
{
"event": "fiscalization.completed",
"entry_id": "550e8400-e29b-41d4-a716-446655440000",
"transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"account_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "completed",
"attempt": 1,
"max_attempts": 50,
"last_error": "",
"timestamp": "2026-03-09T14:30:05Z"
}
3. Verify signatures
Every delivery includes two headers for HMAC-SHA256 signature verification:
| Header | Description |
|---|---|
X-Webhook-Timestamp | Unix timestamp of the delivery |
X-Webhook-Signature | sha256=<hex> HMAC-SHA256 signature |
The signature is computed over "{timestamp}.{body}" using your webhook secret:
Python:
import hmac
import hashlib
def verify_webhook(secret, timestamp, body, signature):
expected = hmac.new(
secret.encode(),
f"{timestamp}.{body}".encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
Node.js:
const crypto = require("crypto");
function verifyWebhook(secret, timestamp, body, signature) {
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest("hex");
return `sha256=${expected}` === signature;
}
Events
| Event | Description |
|---|---|
fiscalization.completed | A transaction was successfully fiscalized |
fiscalization.dead_letter | A transaction exceeded max retry attempts and was moved to dead letter |
Retry policy
Failed deliveries (non-2xx responses or timeouts) are retried with the following backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 10 seconds |
| 2nd retry | 60 seconds |
| 3rd retry | 300 seconds (5 min) |
| 4th retry | 600 seconds (10 min) |
Webhook deliveries have a 10-second timeout per attempt.
Managing webhooks
See the Webhooks API reference for full CRUD operations:
- Create a webhook
- List webhooks
- Update a webhook (change URL, events, or disable)
- Delete a webhook
- Rotate secret
Best practices
- Respond quickly -- return a
200status within 10 seconds - Process asynchronously -- queue webhook payloads for background processing
- Always verify signatures -- use the
X-Webhook-TimestampandX-Webhook-Signatureheaders - Handle duplicates -- use the
entry_idfield to deduplicate events - Rotate secrets periodically -- use the rotate-secret endpoint to generate new signing keys