Skip to main content

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​

FieldTypeRequiredDescription
urlstringYesHTTPS endpoint to receive webhook deliveries
eventsarrayYesEvent types to subscribe to
descriptionstringNoHuman-readable description
enabledbooleanNoWhether the webhook is active (default: true)

Event types​

EventDescription
fiscalization.completedA transaction was successfully fiscalized
fiscalization.dead_letterA transaction exceeded max retry attempts

Example​

curl -X POST https://api.zyntem.dev/v1/webhooks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"url": "https://example.com/webhooks/zyntem",
"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/zyntem",
"secret": "whsec_a1b2c3d4e5f6...",
"events": ["fiscalization.completed", "fiscalization.dead_letter"],
"description": "Production webhook",
"enabled": true,
"created_at": "2026-03-09T12:00:00Z"
}
Save your webhook secret

The secret field is returned only on creation (and on secret rotation). Store it securely for verifying webhook signatures.

Errors​

StatusErrorCause
400url is requiredMissing URL
400invalid urlMalformed URL
400events is requiredMissing events array
400invalid event typeUnrecognized event type

List webhooks​

GET /v1/webhooks

Returns all webhook subscriptions for the authenticated account.

Example​

curl https://api.zyntem.dev/v1/webhooks \
-H "Authorization: Bearer zyn_test_abc123def456..."

Response 200 OK​

{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://example.com/webhooks/zyntem",
"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.zyntem.dev/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer zyn_test_abc123def456..."

Response 200 OK​

Returns a Webhook object (secret is not included).

Errors​

StatusError
404webhook not found

Update a webhook​

PATCH /v1/webhooks/{id}

Partially updates a webhook subscription. Only supplied fields are modified.

Request body​

FieldTypeDescription
urlstringUpdated endpoint URL
eventsarrayUpdated event types
descriptionstringUpdated description
enabledbooleanEnable or disable the webhook

Example​

curl -X PATCH https://api.zyntem.dev/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_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.zyntem.dev/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer zyn_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.zyntem.dev/v1/webhooks/550e8400-e29b-41d4-a716-446655440000/rotate-secret \
-H "Authorization: Bearer zyn_test_abc123def456..."

Response 200 OK​

{
"secret": "whsec_f6e5d4c3b2a1..."
}

Webhook object​

FieldTypeDescription
iduuidWebhook identifier
account_iduuidOwning account identifier
urlstringDelivery endpoint URL
eventsarraySubscribed event types
descriptionstringHuman-readable description
enabledbooleanWhether the webhook is active
created_atdatetimeCreation timestamp

Webhook signatures​

Every webhook delivery includes two headers for signature verification:

HeaderDescription
X-Webhook-TimestampUnix timestamp of the delivery
X-Webhook-Signaturesha256=<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:

AttemptDelay
1st retry10 seconds
2nd retry60 seconds
3rd retry300 seconds (5 min)
4th retry600 seconds (10 min)

After all retries are exhausted, a fiscalization.dead_letter event is emitted.

Webhook deliveries have a 10-second timeout per attempt.