Webhooks
Manage webhook subscriptions for real-time event notifications. All endpoints require authentication.
Create a webhook
POST /v1/webhooks
Creates a new webhook subscription. A signing secret is generated automatically.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS endpoint to receive webhook deliveries |
events | array | Yes | Event types to subscribe to |
description | string | No | Human-readable description |
enabled | boolean | No | Whether the webhook is active (default: true) |
Event types
| Event | Description |
|---|---|
fiscalization.completed | A transaction was successfully fiscalized |
fiscalization.dead_letter | A transaction exceeded max retry attempts |
Example
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"
}'
Response 201 Created
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"account_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"url": "https://example.com/webhooks/fiscalapi",
"secret": "whsec_a1b2c3d4e5f6...",
"events": ["fiscalization.completed", "fiscalization.dead_letter"],
"description": "Production webhook",
"enabled": true,
"created_at": "2026-03-09T12:00:00Z"
}
The secret field is returned only on creation (and on secret rotation). Store it securely for verifying webhook signatures.
Errors
| Status | Error | Cause |
|---|---|---|
400 | url is required | Missing URL |
400 | invalid url | Malformed URL |
400 | events is required | Missing events array |
400 | invalid event type | Unrecognized event type |
List webhooks
GET /v1/webhooks
Returns all webhook subscriptions for the authenticated account.
Example
curl https://api.fiscalapi.com/v1/webhooks \
-H "Authorization: Bearer fsk_test_abc123def456..."
Response 200 OK
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://example.com/webhooks/fiscalapi",
"events": ["fiscalization.completed", "fiscalization.dead_letter"],
"description": "Production webhook",
"enabled": true,
"created_at": "2026-03-09T12:00:00Z"
}
]
}
Get a webhook
GET /v1/webhooks/{id}
Returns a single webhook subscription by ID.
Example
curl https://api.fiscalapi.com/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer fsk_test_abc123def456..."
Response 200 OK
Returns a Webhook object (secret is not included).
Errors
| Status | Error |
|---|---|
404 | webhook not found |
Update a webhook
PATCH /v1/webhooks/{id}
Partially updates a webhook subscription. Only supplied fields are modified.
Request body
| Field | Type | Description |
|---|---|---|
url | string | Updated endpoint URL |
events | array | Updated event types |
description | string | Updated description |
enabled | boolean | Enable or disable the webhook |
Example
curl -X PATCH https://api.fiscalapi.com/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer fsk_test_abc123def456..." \
-d '{
"enabled": false
}'
Response 200 OK
Returns the updated Webhook object.
Delete a webhook
DELETE /v1/webhooks/{id}
Deletes a webhook subscription.
Example
curl -X DELETE https://api.fiscalapi.com/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer fsk_test_abc123def456..."
Response 204 No Content
No response body.
Rotate secret
POST /v1/webhooks/{id}/rotate-secret
Generates a new signing secret for the webhook. The old secret is immediately invalidated.
Example
curl -X POST https://api.fiscalapi.com/v1/webhooks/550e8400-e29b-41d4-a716-446655440000/rotate-secret \
-H "Authorization: Bearer fsk_test_abc123def456..."
Response 200 OK
{
"secret": "whsec_f6e5d4c3b2a1..."
}
Webhook object
| Field | Type | Description |
|---|---|---|
id | uuid | Webhook identifier |
account_id | uuid | Owning account identifier |
url | string | Delivery endpoint URL |
events | array | Subscribed event types |
description | string | Human-readable description |
enabled | boolean | Whether the webhook is active |
created_at | datetime | Creation timestamp |
Webhook signatures
Every webhook delivery includes two headers for signature verification:
| Header | Description |
|---|---|
X-Webhook-Timestamp | Unix timestamp of the delivery |
X-Webhook-Signature | sha256=<hex> HMAC-SHA256 signature |
Verifying signatures
The signature is computed as HMAC-SHA256(secret, "{timestamp}.{body}"):
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)
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;
}
Webhook 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"
}
Retry policy
Failed webhook deliveries (non-2xx responses or timeouts) are retried with the following backoff schedule:
| Attempt | Delay |
|---|---|
| 1st retry | 10 seconds |
| 2nd retry | 60 seconds |
| 3rd retry | 300 seconds (5 min) |
| 4th retry | 600 seconds (10 min) |
After all retries are exhausted, a fiscalization.dead_letter event is emitted.
Webhook deliveries have a 10-second timeout per attempt.