Reference · 8 operations
Webhooks
Signed deliveries of the event feed to your own endpoints, retried
for 24 hours. A convenience: GET /events is the source of truth.
Last updated from API version 2026-10-06 at (live spec, refreshed hourly). openapi.json
Webhook endpoints
The workspace's webhook endpoints, newest first.
/api/v1/webhook-endpoints- Scope
- write
- Credits
- Free
- Idempotency-Key
- Not used
Never includes the signing secret (only secretHint).
Parameters
- limitqueryinteger
Page size.
default 50min 1max 200 - cursorquerystring
Opaque cursor from
meta.page.nextCursor. Reusing it with other filters is400 CURSOR_MISMATCH.
Responses
200OK.Show schema
WebhookEndpointListResponse
- 0
- 0
Metadata on a paginated success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl "https://app.owlbound.ai/api/v1/webhook-endpoints" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06"Add a webhook endpoint; the signing secret is in this answer only.
/api/v1/webhook-endpoints- Scope
- write
- Credits
- Free
- Idempotency-Key
- optional
url must be https or http on a public host: names are resolved
and private, loopback, link-local and metadata addresses are refused
(422 HOST_NOT_ALLOWED), at create and on every delivery. Redirects are
never followed: answer the POST directly. events lists the event types
to receive (["*"] = all of them, also future ones). The answer carries
secret (owl_whsec_...): store it, it is never shown again (rotate it
with rotate-secret). At most 20 endpoints per workspace.
Each delivery is a POST of the event (the GET /events item) as JSON,
with Owlbound-Event-Id (dedupe on it: an event can arrive more than
once), Owlbound-Delivery-Id and
Owlbound-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, t + "." + body)>
(one v1 per active secret during a rotation). Reject a t more than
5 minutes old. A 2xx answer within 10 seconds is delivered; anything else
is retried with backoff (1 min, 5 min, 30 min, 2 h, 5 h, 8 h, 8 h: 8
attempts over about 24 hours), then failed. An endpoint that fails
every delivery for 3 days is disabled and its owner is emailed. Webhooks
are a convenience: GET /events is the source of truth.
Verify a delivery (Node; rawBody is the body as received, before
parsing):
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the request body exactly as received (a string, before JSON.parse).
export function verifyOwlboundWebhook(rawBody, header, secret, toleranceSeconds = 300) {
const parts = header.split(",").map((p) => p.trim().split("="));
const t = Number(parts.find(([k]) => k === "t")?.[1]);
const signatures = parts.filter(([k]) => k === "v1").map(([, v]) => v);
if (!Number.isInteger(t) || signatures.length === 0) return false;
if (Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false; // replay window
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
return signatures.some((hex) => {
const given = Buffer.from(hex, "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
});
}
// Then dedupe on the Owlbound-Event-Id header: an event can arrive more than once.Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255
Request body (required) · application/json
WebhookEndpointCreateRequest
- urlstringrequiredmax length 2048
- eventsarray of WebhookEventTyperequired*action.startedaction.target.completedaction.target.failedaction.completedaction.pending_approvalapproval.decidedmin items 1
- descriptionstringmax length 500
Responses
200OK.Show schema
WebhookEndpointCreatedResponse
- 0
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/webhook-endpoints" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"events": [
"*"
]
}'One webhook endpoint (without its secret).
/api/v1/webhook-endpoints/{id}- Scope
- write
- Credits
- Free
- Idempotency-Key
- Not used
Parameters
- idpathstringrequired
The webhook endpoint id.
Responses
200OK.Show schema
WebhookEndpointResponse
- 0
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl "https://app.owlbound.ai/api/v1/webhook-endpoints/$ID" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06"Change an endpoint's url, events, description or status.
/api/v1/webhook-endpoints/{id}- Scope
- write
- Credits
- Free
- Idempotency-Key
- optional
A new url is checked like at create. status: "disabled" stops
deliveries (queued ones are canceled when due); status: "enabled"
turns it back on and resets its failure count.
Parameters
- idpathstringrequired
The webhook endpoint id.
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255
Request body (required) · application/json
WebhookEndpointUpdateRequest
- urlstringmax length 2048
- eventsarray of WebhookEventType*action.startedaction.target.completedaction.target.failedaction.completedaction.pending_approvalapproval.decidedmin items 1
- descriptionstring | nullmax length 500
- statusstringenableddisabled
Responses
200OK.Show schema
WebhookEndpointResponse
- 0
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X PATCH "https://app.owlbound.ai/api/v1/webhook-endpoints/$ID" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"events": [
"*"
],
"description": "Created from the API"
}'Delete an endpoint; its queued deliveries are canceled.
/api/v1/webhook-endpoints/{id}- Scope
- write
- Credits
- Free
- Idempotency-Key
- optional
Parameters
- idpathstringrequired
The webhook endpoint id.
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255
Responses
200OK.Show schema
WebhookEndpointDeletedResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- idstringrequired0
- deletedbooleanrequired0
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X DELETE "https://app.owlbound.ai/api/v1/webhook-endpoints/$ID" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)"Make a new signing secret; the previous one keeps signing for 24 hours.
/api/v1/webhook-endpoints/{id}/rotate-secret- Scope
- write
- Credits
- Free
- Idempotency-Key
- optional
The answer carries the new secret (shown once) and
previousSecretExpiresAt. Until then every delivery is signed with both
secrets (two v1 values), so receivers can switch without a gap. A
second rotation within the 24 hours drops the oldest secret.
Parameters
- idpathstringrequired
The webhook endpoint id.
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255
Responses
200OK.Show schema
WebhookEndpointCreatedResponse
- 0
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/webhook-endpoints/$ID/rotate-secret" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)"Deliveries
webhookEndpoints.deliveries.list
An endpoint's deliveries, newest first (kept 7 days).
/api/v1/webhook-endpoints/{id}/deliveries- Scope
- write
- Credits
- Free
- Idempotency-Key
- Not used
Each delivery shows its status, attempts, the last response code and the first 1 KB of the answer.
Parameters
- idpathstringrequired
The webhook endpoint id.
- limitqueryinteger
Page size.
default 50min 1max 200 - cursorquerystring
Opaque cursor from
meta.page.nextCursor. Reusing it with other filters is400 CURSOR_MISMATCH. - statusquerystring
Only deliveries in this status: pending, sending, delivered, failed or canceled.
Responses
200OK.Show schema
WebhookDeliveryListResponse
- 0
- 0
Metadata on a paginated success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl "https://app.owlbound.ai/api/v1/webhook-endpoints/$ID/deliveries" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06"webhookEndpoints.deliveries.redeliver
Send a delivery's event again.
/api/v1/webhook-endpoints/{id}/deliveries/{deliveryId}/redeliver- Scope
- write
- Credits
- Free
- Idempotency-Key
- optional
Queues the same event (same Owlbound-Event-Id, fresh signature) for
this endpoint; it is sent within a minute. While a redelivery of that
event is still pending, the same one is returned. A disabled endpoint is
409 WEBHOOK_ENDPOINT_DISABLED.
Parameters
- idpathstringrequired
The webhook endpoint id.
- deliveryIdpathstringrequired
The delivery id.
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255
Responses
200OK.Show schema
WebhookDeliveryResponse
- 0
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/webhook-endpoints/$ID/deliveries/$DELIVERY_ID/redeliver" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)"Models on this page
Shared objects the operations above link to. Clients must ignore fields they do not know.
WebhookEndpointobject
- idstringrequired0
- urlstringrequired0
- descriptionstring | nullrequired0
- eventsarray of WebhookEventTyperequired*action.startedaction.target.completedaction.target.failedaction.completedaction.pending_approvalapproval.decided
- statusstringrequiredenableddisabled
- disabledReasonstring | nullrequiredmanualfailing
- secretHintstring | nullrequired0
owl_whsec_...and the last 4 characters of the current secret. - previousSecretExpiresAtnumber | nullrequired0
During a rotation: when the previous secret stops signing.
- consecutiveFailuresintegerrequired0
- failingSincenumber | nullrequired0
- lastSuccessAtnumber | nullrequired0
- lastFailureAtnumber | nullrequired0
- createdAtnumberrequired0
- updatedAtnumberrequired0
PagedResponseMetaobject
Metadata on a paginated success response.
- requestIdstringrequired0
- 0
- 0
Cursor pagination.
hasMoreis the only end signal; pages may be short.
CreditsMetaobject
- usednumberrequired0
Credits charged by this request. Reads are free.
- balancenumber0
- estimatednumber0
Dry run only (action operations).
- availablenumber0
Dry run only (action operations).
- reservationIdstring | null0
Async operations only; the job settles this reservation.
PageMetaobject
Cursor pagination. hasMore is the only end signal; pages may be short.
- nextCursorstring | nullrequired0
- hasMorebooleanrequired0
- limitintegerrequiredmin 1max 200
WebhookEndpointWithSecretobject
- secretstringrequired0
The signing secret. Shown only in this answer.
- idstringrequired0
- urlstringrequired0
- descriptionstring | nullrequired0
- eventsarray of WebhookEventTyperequired*action.startedaction.target.completedaction.target.failedaction.completedaction.pending_approvalapproval.decided
- statusstringrequiredenableddisabled
- disabledReasonstring | nullrequiredmanualfailing
- secretHintstring | nullrequired0
owl_whsec_...and the last 4 characters of the current secret. - previousSecretExpiresAtnumber | nullrequired0
During a rotation: when the previous secret stops signing.
- consecutiveFailuresintegerrequired0
- failingSincenumber | nullrequired0
- lastSuccessAtnumber | nullrequired0
- lastFailureAtnumber | nullrequired0
- createdAtnumberrequired0
- updatedAtnumberrequired0
ResponseMetaobject
Metadata on every success response.
- requestIdstringrequired0
- 0
- pagePageMeta0
Cursor pagination.
hasMoreis the only end signal; pages may be short. - dryRunboolean0
True only on a dry-run response (action operations).
WebhookDeliveryobject
- idstringrequired0
- endpointIdstringrequired0
- eventIdstringrequired0
- eventTypestringrequired0
- statusstringrequiredpendingsendingdeliveredfailedcanceled
- attemptsintegerrequired0
- maxAttemptsintegerrequired0
- nextAttemptAtnumber | nullrequired0
- lastAttemptAtnumber | nullrequired0
- responseStatusinteger | nullrequired0
- responseBodystring | nullrequired0
The first 1 KB of the last answer.
- errorstring | nullrequired0
- durationMsnumber | nullrequired0
- redeliveryOfstring | nullrequired0
- deliveredAtnumber | nullrequired0
- createdAtnumberrequired0