Destinations

Webhook

Send events via HTTP POST requests to a URL endpoint.

Outpost supports two webhook modes:

  • Default mode - Customizable headers and signature format
  • Standard Webhooks mode - Follows the Standard Webhooks specification

Overview

When you publish an event:

{ "topic": "orders", "data": { "order_id": "123", "status": "created" }, "metadata": { "source": "checkout-service" } }
json

Outpost sends an HTTP POST request with:

  • Body: The event's data field as JSON
  • Headers: System metadata (event-id, topic, timestamp) plus any event metadata, with a configurable prefix

Example request:

POST /webhooks HTTP/1.1 Content-Type: application/json x-outpost-id: evt_123 x-outpost-topic: orders x-outpost-timestamp: 1704067200 x-outpost-signature: t=1704067200,v0=abc123... x-outpost-source: checkout-service {"order_id": "123", "status": "created"}

Configuration

Config

FieldTypeRequiredDescription
config.urlstringYesThe URL to send events to
config.custom_headersstringNoJSON object of custom headers to include

Credentials

FieldTypeRequiredDescription
credentials.secretstringNoSigning secret (auto-generated if not provided)
credentials.previous_secretstringNoPrevious secret during rotation
credentials.previous_secret_invalid_atstringNoRFC3339 timestamp when previous secret expires

If secret is not provided, one will be auto-generated. Tenants cannot modify secrets directly - they can only trigger rotation.

Example

curl --location 'https://<OUTPOST_API_URL>/api/v1/<TENANT_ID>/destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <API_KEY>' \ --data '{ "type": "webhook", "topics": ["orders"], "config": { "url": "https://example.com/webhooks" } }'
sh

Secret Rotation

Secret rotation allows you to change the signing secret without downtime. During the rotation window, both the old and new secrets are valid for signature verification.

To rotate a secret, set rotate_secret to true when updating a destination:

curl --location --request PATCH 'https://<OUTPOST_API_URL>/api/v1/<TENANT_ID>/destinations/<DESTINATION_ID>' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <API_KEY>' \ --data '{ "credentials": { "rotate_secret": "true" } }'
sh

When rotation is triggered:

  1. The current secret becomes previous_secret
  2. A new secret is generated
  3. The previous secret remains valid for 24 hours by default
  4. Both secrets are included in the signature header during the rotation window

To customize the rotation window, set previous_secret_invalid_at:

curl --location --request PATCH 'https://<OUTPOST_API_URL>/api/v1/<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" } }'
sh

Default Mode

In default mode, Outpost provides a customizable webhook implementation.

Headers

Outpost adds headers using the configured prefix (default x-outpost-):

HeaderSourceDescription
x-outpost-idSystemThe unique event ID
x-outpost-timestampSystemEvent timestamp (Unix)
x-outpost-topicSystemThe event topic
x-outpost-signatureSystemHMAC-SHA256 signature
x-outpost-{key}EventAny metadata from the published event's metadata field

Signature

The signature is computed over the timestamp and request body:

signature = HMAC-SHA256(secret, "${timestamp}.${body}")

The signature header value follows the format: t=${timestamp},v0=${signature}

Verifying Signatures

To verify webhook requests:

  1. Extract the timestamp and signature from the x-outpost-signature header
  2. Compute the expected signature using your secret
  3. Compare signatures using a constant-time comparison
  4. Optionally, reject requests with old timestamps to prevent replay attacks

Operator Configuration

Operators can customize webhook behavior via environment variables.

Header Configuration

Environment VariableDefaultDescription
DESTINATIONS_WEBHOOK_HEADER_PREFIXx-outpost-Prefix for all webhook headers
DESTINATIONS_WEBHOOK_DISABLE_EVENT_ID_HEADERfalseDisable the event ID header
DESTINATIONS_WEBHOOK_DISABLE_TIMESTAMP_HEADERfalseDisable the timestamp header
DESTINATIONS_WEBHOOK_DISABLE_TOPIC_HEADERfalseDisable the topic header
DESTINATIONS_WEBHOOK_DISABLE_SIGNATURE_HEADERfalseDisable the signature header

Signature Configuration

Environment VariableDefaultDescription
DESTINATIONS_WEBHOOK_SIGNATURE_ALGORITHMhmac-sha256Signature algorithm
DESTINATIONS_WEBHOOK_SIGNATURE_ENCODINGhexSignature encoding (hex or base64)
DESTINATIONS_WEBHOOK_SIGNATURE_VALUE_TEMPLATE{{.Timestamp.Unix}}.{{.Body}}Template for the value being signed
DESTINATIONS_WEBHOOK_SIGNATURE_HEADER_TEMPLATEt={{.Timestamp.Unix}},v0={{.Signatures | join ","}}Template for signature header value

See the migration guide for common customization patterns.


Standard Webhooks Mode

Standard Webhooks mode follows the Standard Webhooks specification, providing a standardized format for webhook signatures and headers used by many webhook providers.

To enable Standard Webhooks mode, set:

DESTINATIONS_WEBHOOK_MODE=standard

Headers

HeaderSourceDescription
webhook-idSystemThe unique event ID
webhook-timestampSystemEvent timestamp (Unix)
webhook-signatureSystemSignature in v1,<base64> format
webhook-{key}EventAny metadata from the published event's metadata field

Signature

The signature follows the Standard Webhooks specification:

signature = base64(HMAC-SHA256(secret, "${webhook-id}.${timestamp}.${body}"))

Verifying Signatures

Use the official Standard Webhooks SDK to verify signatures. SDKs are available for multiple languages including JavaScript, Python, Go, Ruby, and more.

Secret Format

Standard Webhooks secrets use the format whsec_<base64>, which is automatically generated when creating a destination.

Operator Configuration

Environment VariableDefaultDescription
DESTINATIONS_WEBHOOK_MODEdefaultSet to standard to enable Standard Webhooks mode
DESTINATIONS_WEBHOOK_HEADER_PREFIXwebhook-Prefix for Standard Webhooks headers

Custom Headers

Tenants can add custom HTTP headers to webhook requests. This is useful for authentication, routing, or passing additional metadata to their endpoint.

curl --location 'https://<OUTPOST_API_URL>/api/v1/<TENANT_ID>/destinations' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <API_KEY>' \ --data '{ "type": "webhook", "topics": ["orders"], "config": { "url": "https://example.com/webhooks", "custom_headers": "{\"x-api-key\": \"secret123\", \"x-tenant-id\": \"tenant-abc\"}" } }'
sh

Header Name Requirements

  • Must start with a letter or digit
  • Can contain letters, digits, underscores (_), and hyphens (-)

Reserved Headers

The following headers cannot be overridden:

  • content-type
  • content-length
  • host
  • connection
  • user-agent

Operator Configuration

Custom headers are disabled in the tenant portal by default. To enable, set PORTAL_ENABLE_WEBHOOK_CUSTOM_HEADERS=true.