Webhook
Send events via HTTP POST to a URL endpoint. Outpost supports two webhook modes:
- Default mode — Customizable headers and signature format
- Standard Webhooks mode — Follows the Standard Webhooks specification
Creating a Webhook Destination
curl 'https://api.outpost.hookdeck.com/2025-07-01/tenants/<TENANT_ID>/destinations' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data '{
"type": "webhook",
"topics": ["user.created", "user.updated"],
"config": {
"url": "https://example.com/webhooks"
}
}'
Configuration
Config
| Field | Type | Required | Description |
|---|---|---|---|
config.url | string | Yes | The URL to send events to |
config.custom_headers | string | No | JSON object of custom HTTP headers to include |
Credentials
| Field | Type | Required | Description |
|---|---|---|---|
credentials.secret | string | No | Signing secret — auto-generated if not provided |
credentials.previous_secret | string | No | Previous secret during a rotation window |
credentials.previous_secret_invalid_at | string | No | RFC 3339 timestamp when the previous secret expires |
If secret is not provided, one is auto-generated. Tenants can trigger secret rotation but cannot set secrets directly.
Event Format
When you publish an event:
{
"topic": "user.created",
"data": { "user_id": "usr_123", "email": "user@example.com" },
"metadata": { "source": "signup-service" }
}
Outpost sends an HTTP POST request:
POST /webhooks HTTP/1.1
Content-Type: application/json
x-outpost-event-id: evt_abc123
x-outpost-topic: user.created
x-outpost-timestamp: 2024-06-01T08:23:36Z
x-outpost-signature: v0=abc123def456...
x-outpost-source: signup-service
{"user_id": "usr_123", "email": "user@example.com"}
The request body contains the event's data field as JSON. The metadata field is translated to headers using the configured prefix.
Event ID header and idempotency
Webhook delivery is at-least-once (see Event delivery & retries). Your handler should deduplicate using the event id — the same stable id you set when publishing (Outpost may redeliver on retries).
In default mode, that id is sent as the system event-id metadata field. The HTTP header name is {header_prefix}{metadata-key} with no extra separator — the prefix and key are concatenated as-is. So the default prefix x-outpost- plus event-id yields X-Outpost-Event-Id; a prefix without a trailing separator (for example x-acme) would produce x-acme + event-id → X-Acmeevent-Id (Go canonicalizes the wire name). Include a trailing hyphen in the prefix when you want a conventional shape like X-Acme-Event-Id. Change the prefix with DESTINATIONS_WEBHOOK_HEADER_PREFIX, or omit this header with DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_EVENT_ID_HEADER.
In Standard Webhooks mode, the same value is sent as the webhook-id header (default prefix webhook-, so typically Webhook-Id) per the Standard Webhooks specification.
Signatures
Default Mode
The signature is computed over the timestamp and request body:
HMAC-SHA256(secret, "${body}")
The x-outpost-signature header value follows the format: v0=${signature}
To verify:
- Extract the timestamp and signature from the header
- Compute the expected signature using your secret
- Compare signatures using a constant-time comparison
- Optionally reject requests with old timestamps to prevent replay attacks
Standard Webhooks Mode
Follows the Standard Webhooks specification:
base64(HMAC-SHA256(secret, "${webhook-id}.${timestamp}.${body}"))
Use the official Standard Webhooks SDK to verify signatures. Secrets use the whsec_<base64> format.
Enable Standard Webhooks mode by setting DESTINATIONS_WEBHOOK_MODE=standard in the Config API or in Hookdeck Destinations settings.
Enable Standard Webhooks mode:
DESTINATIONS_WEBHOOK_MODE=standard
Secret Rotation
Rotate a webhook secret without downtime. During the rotation window, both the old and new secrets produce valid signatures.
curl --request PATCH \
'https://api.outpost.hookdeck.com/2025-07-01/tenants/<TENANT_ID>/destinations/<DESTINATION_ID>' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data '{
"credentials": {
"rotate_secret": "true",
"previous_secret_invalid_at": "2025-01-15T00:00:00Z"
}
}'
When rotation is triggered:
- The current secret becomes
previous_secret - A new secret is generated
- The previous secret remains valid until
previous_secret_invalid_at(default: 24 hours) - Both secrets appear in the signature header during the rotation window
Custom Headers
Tenants can add custom HTTP headers to webhook requests for authentication or routing:
curl 'https://api.outpost.hookdeck.com/2025-07-01/tenants/<TENANT_ID>/destinations' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data '{
"type": "webhook",
"topics": ["*"],
"config": {
"url": "https://example.com/webhooks",
"custom_headers": "{\"x-api-key\": \"secret123\"}"
}
}'
Header names must start with a letter or digit and may contain letters, digits, underscores, and hyphens. The following headers cannot be overridden: content-type, content-length, host, connection, user-agent.
Custom webhook headers in the tenant portal are disabled by default. Enable them in Hookdeck User Portal settings.
Custom headers are disabled in the tenant portal by default. Enable with:
PORTAL_ENABLE_WEBHOOK_CUSTOM_HEADERS=true
Operator Configuration
Configure webhook operator behavior using these keys in the Config API or in Hookdeck Destinations settings: DESTINATIONS_WEBHOOK_HEADER_PREFIX, DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_EVENT_ID_HEADER, DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_TIMESTAMP_HEADER, DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_TOPIC_HEADER, DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_SIGNATURE_HEADER, DESTINATIONS_WEBHOOK_MODE, DESTINATIONS_WEBHOOK_SIGNATURE_ALGORITHM, DESTINATIONS_WEBHOOK_SIGNATURE_ENCODING, DESTINATIONS_WEBHOOK_SIGNATURE_CONTENT_TEMPLATE, and DESTINATIONS_WEBHOOK_SIGNATURE_HEADER_TEMPLATE.
Header Settings
| Variable | Default | Description |
|---|---|---|
DESTINATIONS_WEBHOOK_HEADER_PREFIX | x-outpost- / webhook- | Prefix for system webhook headers (event id, topic, timestamp, signature). Unless overridden, defaults to x-outpost- when DESTINATIONS_WEBHOOK_MODE is default and webhook- when standard. |
DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_EVENT_ID_HEADER | false | Disable the event ID header |
DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_TIMESTAMP_HEADER | false | Disable the timestamp header |
DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_TOPIC_HEADER | false | Disable the topic header |
DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_SIGNATURE_HEADER | false | Disable the signature header |
Signature Settings
| Variable | Default | Description |
|---|---|---|
DESTINATIONS_WEBHOOK_MODE | default | Set to standard for Standard Webhooks compliance |
DESTINATIONS_WEBHOOK_SIGNATURE_ALGORITHM | hmac-sha256 | Signature algorithm |
DESTINATIONS_WEBHOOK_SIGNATURE_ENCODING | hex | Encoding: hex or base64 |
DESTINATIONS_WEBHOOK_SIGNATURE_CONTENT_TEMPLATE | {{.Body}} | Template for signed content |
DESTINATIONS_WEBHOOK_SIGNATURE_HEADER_TEMPLATE | v0={{.Signatures | join ","}} | Template for signature header value |