Skip to main content

Webhook troubleshooting

This recipe shows how to diagnose webhook delivery failures and replay failed deliveries. FVI's webhook system uses HMAC signatures, automatic retries with exponential backoff, and a dead-letter queue for permanently failed deliveries.

Endpoints​

GET /v1/webhooks # List webhooks
GET /v1/webhooks/{id}/deliveries # List recent deliveries
POST /v1/webhooks/{id}/deliveries/{deliveryId}/replay # Replay a failed delivery
GET /v1/webhooks/{id}/dlq # List dead-letter queue entries

Prerequisites​

  • A valid tenantId and bearer JWT.
  • The user must have the tenant_admin role.

Webhook delivery flow​

List recent deliveries​

curl -X GET "https://api.falconveritas.com/v1/webhooks/webhook-001/deliveries?limit=50&status=failed" \
-H "Authorization: Bearer $FVI_JWT" \
-H "X-Tenant-Id: $FVI_TENANT_ID" \
-H "Content-Type: application/json"

Response​

{
"items": [
{
"id": "delivery-001",
"webhook_id": "webhook-001",
"event_type": "exception.sla.breached",
"event_id": "evt-12345",
"attempt_count": 3,
"max_attempts": 3,
"status": "failed",
"last_status_code": 500,
"last_error": "Connection timeout after 30s",
"first_attempted_at": "2026-08-22T15:00:00Z",
"last_attempted_at": "2026-08-22T15:08:30Z",
"next_retry_at": null,
"payload_size_bytes": 1240
}
],
"summary": {
"total": 12450,
"delivered": 12380,
"failed": 50,
"in_flight": 20
}
}

Replay a delivery​

curl -X POST https://api.falconveritas.com/v1/webhooks/webhook-001/deliveries/delivery-001/replay \
-H "Authorization: Bearer $FVI_JWT" \
-H "X-Tenant-Id: $FVI_TENANT_ID" \
-H "Content-Type: application/json" \
-d '{
"idempotency_key": "webhook-replay-fvi-$RANDOM"
}'

The replay creates a new delivery attempt with the same payload as the original, but resets the retry counter and the HMAC signature timestamp.

List dead-letter queue​

curl -X GET "https://api.falconveritas.com/v1/webhooks/webhook-001/dlq" \
-H "Authorization: Bearer $FVI_JWT" \
-H "X-Tenant-Id: $FVI_TENANT_ID" \
-H "Content-Type: application/json"

Common failure causes​

ErrorCauseRemediation
connection_timeoutTarget URL is slow or unreachableIncrease timeout, check firewall rules
tls_handshake_failedInvalid or expired TLS certificate on targetRenew certificate, verify chain
4xx_client_errorTarget rejected the payloadInspect payload format and authentication
5xx_server_errorTarget had an internal errorCheck target server logs
payload_too_largePayload exceeded the target's body limitIncrease target body limit or split events
invalid_signatureTarget rejected the HMAC signatureVerify the signing secret is correct

HMAC signature verification​

FVI signs each webhook payload with HMAC-SHA256. The signature is sent in the X-FVI-Signature header:

X-FVI-Signature: t=1692720000,v1=5257a869e7e3b4d3f9eaa6e1b87c8d9e8b7c8d9e8b7c8d9e8b7c8d9e8b7c8d9e

To verify the signature:

  1. Read the timestamp t and the signature v1 from the header.
  2. Concatenate the timestamp and the raw request body: t.body.
  3. Compute HMAC-SHA256 with your webhook secret.
  4. Compare the computed signature to the v1 value. Use a constant-time comparison to avoid timing attacks.
  5. Reject the request if the timestamp is more than 5 minutes old (replay protection).

Test a webhook​

Use the built-in test event to verify your endpoint is reachable and correctly verifying signatures:

curl -X POST https://api.falconveritas.com/v1/webhooks/webhook-001/test \
-H "Authorization: Bearer $FVI_JWT" \
-H "X-Tenant-Id: $FVI_TENANT_ID" \
-H "Content-Type: application/json" \
-d '{
"event_type": "webhook.test",
"idempotency_key": "webhook-test-fvi-$RANDOM"
}'

Use cases​

  • Diagnosing delivery failures — find out why a webhook is failing.
  • Recovering from target outages — replay deliveries after the target recovers.
  • Verifying signature handling — test your signature verification code.
  • Cleaning up dead-letter queue — replay or discard permanently failed deliveries.

Code samples​

TypeScript​

import { FVI } from '@falconveritas/sdk';

const fvi = new FVI({
baseUrl: 'https://api.falconveritas.com',
bearer: process.env.FVI_JWT!,
tenantId: process.env.FVI_TENANT_ID!,
});

// List failed deliveries
const failed = await fvi.webhooks.listDeliveries('webhook-001', {
status: 'failed',
limit: 50,
});

console.log(`Failed deliveries: ${failed.items.length}`);

// Replay each failed delivery
for (const delivery of failed.items) {
await fvi.webhooks.replayDelivery('webhook-001', delivery.id, {
idempotencyKey: `webhook-replay-fvi-${Date.now()}`,
});
console.log(`Replayed: ${delivery.id}`);
}

Python​

from falconveritas import FVI

client = FVI(
bearer=os.environ["FVI_JWT"],
tenant_id=os.environ["FVI_TENANT_ID"],
)

failed = client.webhooks.list_deliveries("webhook-001", status="failed", limit=50)
print(f"Failed deliveries: {len(failed.items)}")

for delivery in failed.items:
client.webhooks.replay_delivery(
"webhook-001",
delivery.id,
idempotency_key=f"webhook-replay-fvi-{int(time.time())}",
)
print(f"Replayed: {delivery.id}")

Last updated: 2026-08-22 · Page owner: Documentation · Source: src/lib/api/webhooks.ts.