API reference
REST API for managing webhook sources, destinations, routes, and events in Odyhook. All endpoints require a bearer token issued from Settings → API Tokens.
Base URL: https://odyhook.dev · Spec: /openapi.json (OpenAPI 3.1.0)
Every request authenticates with a bearer token (ody_…) minted at Settings → API Tokens. This page is generated from the OpenAPI spec, so it always matches the live contract. For /api/v1/events/search and /api/v1/fixtures, see the REST API overview.
Sources
GET /api/v1/sources
List sources
Returns a paginated list of webhook sources belonging to the authenticated user.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | integer | no | Maximum number of items to return (1–100, default 25). |
cursor | query | string | no | Opaque pagination cursor. Use the nextCursor value from a previous response. |
| Response | Description | Body |
|---|---|---|
200 | Paginated list of sources. | — |
401 | Missing or invalid API token. | Error |
429 | Too many requests. | Error |
curl -X GET https://odyhook.dev/api/v1/sources \
-H "Authorization: Bearer ody_…"POST /api/v1/sources
Create a source
Creates a new webhook source. A unique ingest slug is generated automatically.
Request body: SourceCreate
| Response | Description | Body |
|---|---|---|
201 | Source created. | Source |
400 | Request body failed schema validation. | Error |
401 | Missing or invalid API token. | Error |
429 | Too many requests. | Error |
curl -X POST https://odyhook.dev/api/v1/sources \
-H "Authorization: Bearer ody_…" \
-H "Content-Type: application/json" \
-d '{ … }' # SourceCreateGET /api/v1/sources/{id}
Get a source
Retrieves a single source by ID.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
| Response | Description | Body |
|---|---|---|
200 | Source found. | Source |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X GET https://odyhook.dev/api/v1/sources/{id} \
-H "Authorization: Bearer ody_…"PATCH /api/v1/sources/{id}
Update a source
Partially updates a source. Only supplied fields are changed.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
Request body: SourceUpdate
| Response | Description | Body |
|---|---|---|
200 | Source updated. | Source |
400 | Request body failed schema validation. | Error |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X PATCH https://odyhook.dev/api/v1/sources/{id} \
-H "Authorization: Bearer ody_…" \
-H "Content-Type: application/json" \
-d '{ … }' # SourceUpdateDELETE /api/v1/sources/{id}
Delete a source
Permanently deletes a source. Associated routes and their event history are also removed.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
| Response | Description | Body |
|---|---|---|
204 | Resource deleted successfully. No response body. | — |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X DELETE https://odyhook.dev/api/v1/sources/{id} \
-H "Authorization: Bearer ody_…"Destinations
GET /api/v1/destinations
List destinations
Returns a paginated list of webhook destinations belonging to the authenticated user.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | integer | no | Maximum number of items to return (1–100, default 25). |
cursor | query | string | no | Opaque pagination cursor. Use the nextCursor value from a previous response. |
| Response | Description | Body |
|---|---|---|
200 | Paginated list of destinations. | — |
401 | Missing or invalid API token. | Error |
429 | Too many requests. | Error |
curl -X GET https://odyhook.dev/api/v1/destinations \
-H "Authorization: Bearer ody_…"POST /api/v1/destinations
Create a destination
Creates a new webhook destination. The URL is checked against SSRF protection rules; private/internal IP ranges are rejected.
Request body: DestinationCreate
| Response | Description | Body |
|---|---|---|
201 | Destination created. | Destination |
400 | Request body failed schema validation. | Error |
401 | Missing or invalid API token. | Error |
429 | Too many requests. | Error |
curl -X POST https://odyhook.dev/api/v1/destinations \
-H "Authorization: Bearer ody_…" \
-H "Content-Type: application/json" \
-d '{ … }' # DestinationCreateGET /api/v1/destinations/{id}
Get a destination
Retrieves a single destination by ID.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
| Response | Description | Body |
|---|---|---|
200 | Destination found. | Destination |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X GET https://odyhook.dev/api/v1/destinations/{id} \
-H "Authorization: Bearer ody_…"PATCH /api/v1/destinations/{id}
Update a destination
Partially updates a destination. Only supplied fields are changed. Setting enabled to true also clears the consecutive failure counter.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
Request body: DestinationUpdate
| Response | Description | Body |
|---|---|---|
200 | Destination updated. | Destination |
400 | Request body failed schema validation. | Error |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X PATCH https://odyhook.dev/api/v1/destinations/{id} \
-H "Authorization: Bearer ody_…" \
-H "Content-Type: application/json" \
-d '{ … }' # DestinationUpdateDELETE /api/v1/destinations/{id}
Delete a destination
Permanently deletes a destination.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
| Response | Description | Body |
|---|---|---|
204 | Resource deleted successfully. No response body. | — |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X DELETE https://odyhook.dev/api/v1/destinations/{id} \
-H "Authorization: Bearer ody_…"Routes
GET /api/v1/routes
List routes
Returns a paginated list of routes belonging to the authenticated user.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | integer | no | Maximum number of items to return (1–100, default 25). |
cursor | query | string | no | Opaque pagination cursor. Use the nextCursor value from a previous response. |
| Response | Description | Body |
|---|---|---|
200 | Paginated list of routes. | — |
401 | Missing or invalid API token. | Error |
429 | Too many requests. | Error |
curl -X GET https://odyhook.dev/api/v1/routes \
-H "Authorization: Bearer ody_…"POST /api/v1/routes
Create a route
Creates a route linking a source to a destination. Both resources must belong to the authenticated user. Returns 409 if a route between this source and destination already exists.
Request body: RouteCreate
| Response | Description | Body |
|---|---|---|
201 | Route created. | Route |
400 | Request body failed schema validation. | Error |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
409 | A duplicate resource already exists. | Error |
429 | Too many requests. | Error |
curl -X POST https://odyhook.dev/api/v1/routes \
-H "Authorization: Bearer ody_…" \
-H "Content-Type: application/json" \
-d '{ … }' # RouteCreateGET /api/v1/routes/{id}
Get a route
Retrieves a single route by ID.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
| Response | Description | Body |
|---|---|---|
200 | Route found. | Route |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X GET https://odyhook.dev/api/v1/routes/{id} \
-H "Authorization: Bearer ody_…"PATCH /api/v1/routes/{id}
Update a route
Partially updates a route. Currently only the enabled flag can be toggled.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
Request body: RouteUpdate
| Response | Description | Body |
|---|---|---|
200 | Route updated. | Route |
400 | Request body failed schema validation. | Error |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X PATCH https://odyhook.dev/api/v1/routes/{id} \
-H "Authorization: Bearer ody_…" \
-H "Content-Type: application/json" \
-d '{ … }' # RouteUpdateDELETE /api/v1/routes/{id}
Delete a route
Permanently deletes a route.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
| Response | Description | Body |
|---|---|---|
204 | Resource deleted successfully. No response body. | — |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X DELETE https://odyhook.dev/api/v1/routes/{id} \
-H "Authorization: Bearer ody_…"Events
GET /api/v1/events
List events
Returns a paginated list of webhook events received by all sources belonging to the authenticated user, ordered newest first.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | integer | no | Maximum number of items to return (1–100, default 25). |
cursor | query | string | no | Opaque pagination cursor. Use the nextCursor value from a previous response. |
| Response | Description | Body |
|---|---|---|
200 | Paginated list of events. | — |
401 | Missing or invalid API token. | Error |
429 | Too many requests. | Error |
curl -X GET https://odyhook.dev/api/v1/events \
-H "Authorization: Bearer ody_…"GET /api/v1/events/{id}
Get an event
Retrieves a single event including its raw body and all delivery records.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Resource identifier. |
| Response | Description | Body |
|---|---|---|
200 | Event found. | EventDetail |
401 | Missing or invalid API token. | Error |
404 | Resource not found or does not belong to the authenticated user. | Error |
429 | Too many requests. | Error |
curl -X GET https://odyhook.dev/api/v1/events/{id} \
-H "Authorization: Bearer ody_…"Schemas
Error
| Field | Type | Required | Description |
|---|---|---|---|
error | object | yes |
Source
A webhook source — an ingest endpoint Odyhook listens on.
| Field | Type | Required | Description |
|---|---|---|---|
id (read-only) | string | yes | Unique identifier (CUID). |
name | string | yes | Display name for the source. |
slug (read-only) | string | yes | URL slug used in the ingest endpoint path. |
verifyStyle | "stripe" | "github" | "generic-sha256" | null | yes | HMAC verification style. null means no signature verification. |
hasSigningSecret (read-only) | boolean | yes | Whether a signing secret is configured. The secret itself is never returned. |
rateLimitPerSec | integer | null | yes | Per-second rate limit override. null means the global default applies. |
rateLimitBurst | integer | null | yes | Burst allowance override. null means the global default applies. |
retentionDays | integer | null | yes | Days events are retained before the daily purge deletes them. null means keep indefinitely. |
createdAt (read-only) | string (date-time) | yes | ISO 8601 creation timestamp. |
SourceCreate
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Display name for the source. |
verifyStyle | "none" | "stripe" | "github" | "generic-sha256" | no | HMAC verification style. Defaults to none (no verification). |
signingSecret | string | no | Signing secret for HMAC verification. Required when verifyStyle is not none. Never returned in responses. |
retentionDays | integer | null | no | Days events are retained before purge. Defaults to 90; set to null to keep indefinitely. |
SourceUpdate
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | Updated display name. |
verifyStyle | "none" | "stripe" | "github" | "generic-sha256" | no | Updated HMAC verification style. |
signingSecret | string | no | New signing secret. Required when changing verifyStyle to a non-none value and no secret is already stored. Never returned in responses. |
rateLimitPerSec | integer | null | no | Per-second rate limit override. Set to null to revert to the global default. |
rateLimitBurst | integer | null | no | Burst allowance override. Set to null to revert to the global default. |
retentionDays | integer | null | no | Days events are retained before purge. Set to null to keep indefinitely. |
Destination
A downstream URL that Odyhook delivers webhook payloads to.
| Field | Type | Required | Description |
|---|---|---|---|
id (read-only) | string | yes | Unique identifier (CUID). |
name | string | yes | Display name for the destination. |
url | string (uri) | yes | HTTPS URL Odyhook POSTs to. |
timeoutMs | integer | yes | Request timeout in milliseconds. |
enabled | boolean | yes | Whether this destination is active. Odyhook auto-disables after consecutive failures. |
hasHeaders (read-only) | boolean | yes | Whether custom outbound headers are configured. Header values are never returned. |
hasOutboundSecret (read-only) | boolean | yes | Whether an outbound signing secret is configured. The secret itself is never returned. |
consecutiveFailures (read-only) | integer | yes | Number of consecutive delivery failures. Reset to 0 when the destination is re-enabled. |
autoDisabledAt (read-only) | string | null (date-time) | yes | ISO 8601 timestamp when the destination was auto-disabled due to repeated failures, or null. |
createdAt (read-only) | string (date-time) | yes | ISO 8601 creation timestamp. |
DestinationCreate
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Display name for the destination. |
url | string (uri) | yes | HTTPS URL to deliver webhooks to. Private/internal IP ranges are rejected (SSRF protection). |
timeoutMs | integer | no | Request timeout in milliseconds. |
headers | string | no | Custom outbound HTTP headers, one per line in 'Name: Value' format. Never returned in responses. |
outboundSecret | string | no | Secret used to sign outbound payloads (HMAC-SHA256 in X-Odyhook-Signature header). Never returned in responses. |
DestinationUpdate
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | Updated display name. |
url | string (uri) | no | Updated delivery URL. Private/internal IP ranges are rejected. |
timeoutMs | integer | no | Updated timeout in milliseconds. |
headers | string | no | Replacement custom outbound headers ('Name: Value' per line). Pass an empty string to clear. Never returned in responses. |
outboundSecret | string | no | Replacement outbound signing secret. Pass an empty string to clear. Never returned in responses. |
enabled | boolean | no | Re-enable or disable the destination. Setting to true also resets consecutiveFailures to 0. |
Route
A link between a source and a destination — determines which sources fan out to which destinations.
| Field | Type | Required | Description |
|---|---|---|---|
id (read-only) | string | yes | Unique identifier (CUID). |
sourceId | string | yes | ID of the source this route belongs to. |
destinationId | string | yes | ID of the destination this route delivers to. |
enabled | boolean | yes | Whether events on this route are delivered. |
hasFilter (read-only) | boolean | yes | Whether a filter expression is configured for this route. |
createdAt (read-only) | string (date-time) | yes | ISO 8601 creation timestamp. |
RouteCreate
| Field | Type | Required | Description |
|---|---|---|---|
sourceId | string | yes | ID of the source to route from. Must belong to the authenticated user. |
destinationId | string | yes | ID of the destination to route to. Must belong to the authenticated user. |
enabled | boolean | no | Whether to enable delivery immediately. |
RouteUpdate
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | no | Enable or disable delivery on this route. |
Event
A webhook event received by an ingest endpoint.
| Field | Type | Required | Description |
|---|---|---|---|
id (read-only) | string | yes | Unique identifier (CUID). |
sourceId (read-only) | string | yes | ID of the source that received this event. |
method (read-only) | string | yes | HTTP method of the inbound request (e.g. POST). |
receivedAt (read-only) | string (date-time) | yes | ISO 8601 timestamp when the event was received. |
remoteIp (read-only) | string | null | yes | IP address of the sender, or null if unavailable. |
idempotencyKey (read-only) | string | null | yes | Idempotency key from the inbound request, or null. |
Delivery
A single delivery attempt record for one route on one event.
| Field | Type | Required | Description |
|---|---|---|---|
id (read-only) | string | yes | Unique identifier (CUID). |
destinationId (read-only) | string | yes | ID of the destination this delivery targets. |
status (read-only) | string | yes | Delivery status (e.g. pending, success, failed, exhausted). |
attemptCount (read-only) | integer | yes | Number of delivery attempts made so far. |
responseCode (read-only) | integer | null | yes | HTTP status code returned by the destination on the last attempt, or null. |
lastError (read-only) | string | null | yes | Error message from the last failed attempt, or null. |
deliveredAt (read-only) | string | null (date-time) | yes | ISO 8601 timestamp of successful delivery, or null. |
createdAt (read-only) | string (date-time) | yes | ISO 8601 timestamp when this delivery record was created. |
EventDetail
| Field | Type | Required | Description |
|---|