Skip to content

Commit 6bc65bd

Browse files
authored
Merge pull request #868 from hookdeck/chore/docs-webhook-event-id-headers
chore(docs): clarify webhook event-id header and idempotency
2 parents 73b6b31 + 96d7005 commit 6bc65bd

7 files changed

Lines changed: 41 additions & 20 deletions

File tree

docs/apis/openapi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1790,7 +1790,7 @@ components:
17901790
properties:
17911791
id:
17921792
type: string
1793-
description: Optional. A unique identifier for the event. If not provided, a UUID will be generated.
1793+
description: Optional. A unique identifier for the event. If not provided, a UUID will be generated. A stable id enables publish idempotency (see `duplicate` in the response) and is the value receivers should use to deduplicate webhook deliveries — by default sent as the `X-Outpost-Event-Id` header (prefix configurable).
17941794
example: "evt_custom_123"
17951795
tenant_id:
17961796
type: string

docs/content/destinations/webhook.mdoc

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,25 @@ Outpost sends an HTTP POST request:
5959
```
6060
POST /webhooks HTTP/1.1
6161
Content-Type: application/json
62-
x-outpost-id: evt_abc123
62+
x-outpost-event-id: evt_abc123
6363
x-outpost-topic: user.created
64-
x-outpost-timestamp: 1704067200
65-
x-outpost-signature: t=1704067200,v0=abc123...
64+
x-outpost-timestamp: 2024-06-01T08:23:36Z
65+
x-outpost-signature: v0=abc123def456...
6666
x-outpost-source: signup-service
6767

6868
{"user_id": "usr_123", "email": "user@example.com"}
6969
```
7070

7171
The request body contains the event's `data` field as JSON. The `metadata` field is translated to headers using the configured prefix.
7272

73+
### Event ID header and idempotency
74+
75+
Webhook delivery is **at-least-once** (see [Event delivery & retries](/docs/outpost/features/event-delivery)). Your handler should deduplicate using the **event id** — the same stable `id` you set when [publishing](/docs/outpost/publishing/events) (Outpost may redeliver on retries).
76+
77+
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`**.
78+
79+
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](https://www.standardwebhooks.com/) specification.
80+
7381
## Signatures
7482

7583
### Default Mode
@@ -171,18 +179,18 @@ PORTAL_ENABLE_WEBHOOK_CUSTOM_HEADERS=true
171179

172180
{% tabs tabGroup="deployment" %}
173181
{% tab label="Managed" %}
174-
Configure webhook operator behavior using these keys in the Config API or in [Hookdeck Destinations settings](https://dashboard.hookdeck.com/settings/project/destinations): `DESTINATIONS_WEBHOOK_HEADER_PREFIX`, `DESTINATIONS_WEBHOOK_DISABLE_EVENT_ID_HEADER`, `DESTINATIONS_WEBHOOK_DISABLE_TIMESTAMP_HEADER`, `DESTINATIONS_WEBHOOK_DISABLE_TOPIC_HEADER`, `DESTINATIONS_WEBHOOK_DISABLE_SIGNATURE_HEADER`, `DESTINATIONS_WEBHOOK_MODE`, `DESTINATIONS_WEBHOOK_SIGNATURE_ALGORITHM`, `DESTINATIONS_WEBHOOK_SIGNATURE_ENCODING`, `DESTINATIONS_WEBHOOK_SIGNATURE_CONTENT_TEMPLATE`, and `DESTINATIONS_WEBHOOK_SIGNATURE_HEADER_TEMPLATE`.
182+
Configure webhook operator behavior using these keys in the Config API or in [Hookdeck Destinations settings](https://dashboard.hookdeck.com/settings/project/destinations): `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`.
175183
{% /tab %}
176184
{% tab label="Self-Hosted" %}
177185
### Header Settings
178186

179187
| Variable | Default | Description |
180188
|----------|---------|-------------|
181-
| `DESTINATIONS_WEBHOOK_HEADER_PREFIX` | `x-outpost-` | Prefix for all webhook headers |
182-
| `DESTINATIONS_WEBHOOK_DISABLE_EVENT_ID_HEADER` | `false` | Disable the event ID header |
183-
| `DESTINATIONS_WEBHOOK_DISABLE_TIMESTAMP_HEADER` | `false` | Disable the timestamp header |
184-
| `DESTINATIONS_WEBHOOK_DISABLE_TOPIC_HEADER` | `false` | Disable the topic header |
185-
| `DESTINATIONS_WEBHOOK_DISABLE_SIGNATURE_HEADER` | `false` | Disable the signature header |
189+
| `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`. |
190+
| `DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_EVENT_ID_HEADER` | `false` | Disable the event ID header |
191+
| `DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_TIMESTAMP_HEADER` | `false` | Disable the timestamp header |
192+
| `DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_TOPIC_HEADER` | `false` | Disable the topic header |
193+
| `DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_SIGNATURE_HEADER` | `false` | Disable the signature header |
186194

187195
### Signature Settings
188196

docs/content/features/event-delivery.mdoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: "Event Delivery & Retries"
33
description: "How Outpost delivers events, tracks attempts, and retries on failure."
44
---
55

6-
Outpost tracks each event, its status, and all delivery attempts. Delivery operates on an **at-least-once guarantee** — events may occasionally be re-delivered. Consumers should use the unique event ID to deduplicate when needed.
6+
Outpost tracks each event, its status, and all delivery attempts. Delivery operates on an **at-least-once guarantee** — events may occasionally be re-delivered. Consumers should deduplicate using the **event id** (the same value as publish `id` when you set one). For how that appears on webhook requests — headers, prefix, Standard Webhooks — see [Event ID header and idempotency](/docs/outpost/destinations/webhook#event-id-header-and-idempotency).
77

88
## Automatic Retries
99

docs/content/guides/migrate-to-outpost.mdoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Webhooks are delivered via HTTP `POST` requests.
5959

6060
### Webhook Event HTTP Headers
6161

62-
- `x-outpost-event-id`: A unique identifier for the event within the Outpost installation
62+
- `x-outpost-event-id`: The event id (same as publish `id` when you provide one). Use it as the **deduplication / idempotency key** for at-least-once delivery; the `x-outpost-` prefix is [configurable](/docs/outpost/destinations/webhook#operator-configuration).
6363
- `x-outpost-signature`: HMAC of the raw body (default `v0=<hex>`; operators may customize templates — see [Webhook destination](/docs/outpost/destinations/webhook#signature)).
6464
- `x-outpost-timestamp`: A Unix timestamp representing the time the event was generated
6565
- `x-outpost-topic`: The topic of the event. For example, `user.created`.

docs/content/publishing/events.mdoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ If you already publish events to a supported message queue, refer to the relevan
4040

4141
| Field | Required | Description |
4242
|-------|----------|-------------|
43-
| `id` | No | Unique event ID. If omitted, generated by hashing topic, data, and timestamp. Providing an ID enables idempotency. |
43+
| `id` | No | Unique event ID. If omitted, Outpost assigns a server-generated id (UUID-style by default; configurable). Providing a stable ID enables **publish idempotency** (duplicate publishes are not re-queued while the key is remembered) and is the value receivers use to **deduplicate deliveries** — for webhooks, see [`X-Outpost-Event-Id` / header prefix](/docs/outpost/destinations/webhook#event-id-header-and-idempotency). |
4444
| `tenant_id` | Yes | The tenant to deliver the event to. Must match an existing tenant. |
4545
| `destination_id` | No | Force delivery to a specific destination, bypassing topic matching. |
4646
| `topic` | No | The event topic. Must match one of the configured topics if set. Assumed to match all topics if omitted. |

docs/content/quickstarts/hookdeck-outpost-curl.mdoc

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ curl --request PUT "$OUTPOST_API_BASE_URL/tenants/$TENANT_ID" \
3838
--header "Authorization: Bearer $OUTPOST_API_KEY"
3939
```
4040

41+
A successful tenant upsert returns **HTTP 200** if that tenant id already existed, or **HTTP 201** if Outpost created it. In shell scripts, treat **200 and 201** as success (do not require exactly `200`—automation that deletes the tenant first will usually see **201**).
42+
4143
## Create a webhook destination
4244

4345
Subscribe the tenant to one or more topics you configured in the dashboard. Set `config.url` to an HTTPS endpoint you control.
@@ -59,6 +61,8 @@ curl --request POST "$OUTPOST_API_BASE_URL/tenants/$TENANT_ID/destinations" \
5961
}'
6062
```
6163

64+
On success, expect **HTTP 2xx** (often **201** the first time this destination is created for the tenant).
65+
6266
To receive every configured topic on this destination, set `"topics": ["*"]` instead.
6367

6468
## Publish a test event
@@ -86,9 +90,18 @@ A `202` response means the event was accepted for delivery.
8690

8791
## Shell scripts: status codes and portability
8892

89-
If you combine API response bodies with `curl --write-out '\n%{http_code}'`:
93+
When a script must **fail on unexpected HTTP status**, capture the code with `curl -o … -w '%{http_code}'` (response body to a file or `/dev/null`). Example for **tenant upsert**—accept **200** or **201** only:
94+
95+
```sh
96+
code="$(curl -sS -o /dev/null -w "%{http_code}" --request PUT "$OUTPOST_API_BASE_URL/tenants/$TENANT_ID" \
97+
--header "Authorization: Bearer $OUTPOST_API_KEY")"
98+
case "$code" in
99+
200|201) ;;
100+
*) echo "tenant upsert failed: HTTP $code" >&2; exit 1 ;;
101+
esac
102+
```
90103

91-
- **Publish** success is **HTTP 202** (not only 200/201). Treat **202** as success in conditional checks.
104+
- **Publish** success is **HTTP 202** (not 200/201). Treat **202** as success in conditional checks.
92105
- **Portability:** GNU `head -n -1` (“all lines but the last”) is **not** available on macOS BSD `head`. Prefer splitting with **`sed '$d'`** (body) and **`tail -n 1`** (status), or another POSIX-friendly approach, so the same script runs on Linux and macOS.
93106

94107
## Verify delivery

docs/content/self-hosting/configuration.mdoc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,11 @@ Choose one for event log persistence:
114114
| Variable | Default | Description |
115115
|----------|---------|-------------|
116116
| `DESTINATIONS_WEBHOOK_MODE` | `default` | Set to `standard` for Standard Webhooks compliance |
117-
| `DESTINATIONS_WEBHOOK_HEADER_PREFIX` | `x-outpost-` | Prefix for all webhook headers |
118-
| `DESTINATIONS_WEBHOOK_DISABLE_EVENT_ID_HEADER` | `false` | Disable the event ID header |
119-
| `DESTINATIONS_WEBHOOK_DISABLE_TIMESTAMP_HEADER` | `false` | Disable the timestamp header |
120-
| `DESTINATIONS_WEBHOOK_DISABLE_TOPIC_HEADER` | `false` | Disable the topic header |
121-
| `DESTINATIONS_WEBHOOK_DISABLE_SIGNATURE_HEADER` | `false` | Disable the signature header |
117+
| `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`. |
118+
| `DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_EVENT_ID_HEADER` | `false` | Disable the event ID header |
119+
| `DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_TIMESTAMP_HEADER` | `false` | Disable the timestamp header |
120+
| `DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_TOPIC_HEADER` | `false` | Disable the topic header |
121+
| `DESTINATIONS_WEBHOOK_DISABLE_DEFAULT_SIGNATURE_HEADER` | `false` | Disable the signature header |
122122
| `DESTINATIONS_WEBHOOK_SIGNATURE_ALGORITHM` | `hmac-sha256` | Signature algorithm |
123123
| `DESTINATIONS_WEBHOOK_SIGNATURE_ENCODING` | `hex` | Encoding: `hex` or `base64` |
124124

0 commit comments

Comments
 (0)