Webhooks allow your external services to be notified automatically when a Job Run reaches a terminal state (e.g., completed, failed, timed_out) or when an Event Trigger is resolved.
Delivery Models
Strait has two webhook delivery models:
Job Run Webhooks
When a run reaches a terminal state, the executor checks if the parent Job has a webhook_url configured and dispatches a signed HTTP POST. Delivery uses in-memory retry with blocking backoff (up to 3 attempts). If the worker process crashes during delivery, in-flight retries are lost.
Event Trigger Webhooks
Event trigger webhooks (configured via notify_url on wait-for-event steps) use a durable delivery model. Deliveries are persisted to the webhook_deliveries table and processed by a background polling worker (apps/strait/internal/webhook/event_notify.go). This survives process restarts — pending deliveries are picked up on the next poll cycle.
Delivery Lifecycle
- Terminal State Reached: When a run finishes, the executor checks if the parent Job has a
webhook_url configured.
- Payload Construction: A
WebhookPayload is assembled containing the run’s final state, result, and error (if any).
- Signing: If a
webhook_secret is configured, the payload is signed using HMAC-SHA256 with a timestamp for replay protection.
- Dispatch: An HTTP POST request is sent to the
webhook_url.
- Retry Logic: If delivery fails, Strait retries based on the response status code.
- Dead Letter: After exhausting retries, deliveries are marked as
dead (event trigger) or silently dropped (job run).
Webhook Payload
The payload sent to your endpoint (defined in apps/strait/internal/worker/webhook.go) includes:
HMAC-SHA256 Signing
To ensure the webhook was sent by Strait, signed deliveries include:
X-Strait-Timestamp: Unix timestamp used in signature generation
X-Strait-Signature: sha256=<signature>
X-Webhook-Signature: compatibility header for existing consumers
Algorithm: HMAC-SHA256 using webhook_secret as key and {timestamp}.{raw-json-body} as the signed payload. The timestamp is included in the signed payload to provide replay protection — receivers should reject deliveries where the timestamp is more than 5 minutes old.
Signed payload format:
Verifying Signatures (Go Example)
Retry Strategy
Job Run Webhooks
Strait attempts to deliver job run webhooks up to 3 times with an exponential backoff:
- Attempt 1: Immediate
- Attempt 2: 1 second delay
- Attempt 3: 5 seconds delay
Event Trigger Webhooks
Event trigger webhooks (configured via notify_url on wait-for-event) use a persistent delivery queue that survives process restarts:
- Max attempts: 5
- Backoff: Exponential — 5s, 25s, 125s, 625s (capped at 30 minutes)
- Delivery worker: Polls the database every 5 seconds for pending deliveries
Retry Rules
- Client Errors (4xx): Delivery is considered a permanent failure; no retries are attempted. The delivery is dead-lettered immediately.
- Server Errors (5xx) / Timeouts: Delivery is retried up to the maximum attempt limit.
- Context Cancellation: If the system is shutting down, delivery may be aborted with a “context canceled” error.
Configuration
Job Webhooks
Webhooks are configured per job:
webhook_url: The destination for the POST request.
webhook_secret: (Optional) The key used for HMAC signing.
Event Trigger Webhooks
Event trigger webhooks are configured per trigger:
notify_url: Set when creating a wait-for-event step or SDK call.
- Delivery status is tracked in the
webhook_deliveries table with event_trigger_id.
Dead Letter Queue (DLQ) Management
Failed webhook deliveries are stored in the database and can be inspected and retried:
The retry endpoint resets the delivery to pending status with zero attempts and immediate next retry time. Only deliveries in failed status can be retried — attempting to retry a pending or delivered delivery returns 409 Conflict.
Always use HTTPS for your webhook URLs to protect the payload and signature in transit.