Skip to content

Receive a webhook delivery

View as Markdown
POST
/v1/hooks/{tenantId}/{webhookId}
curl --request POST \
--url https://example.com/v1/hooks/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0 \
--header 'Content-Type: */*' \
--data binary

The receive URL a webhook trigger’s sender posts to: no sign-in, and a bearer token, if sent, is never read. The request’s signature, made with the trigger’s secret in its signature scheme over the raw body, is what lets it start the trigger’s flow as the trigger’s owner. Every request that doesn’t prove its sender (an unknown tenant or id, a missing, wrong or stale signature, a secret that can’t be read) is 401 webhook-refused, with the reason only in the trigger’s history and the access audit; the URL isn’t a secret, the signature is. A WooCommerce save-time ping (unsigned webhook_id=<n>) is answered 200 and starts nothing. A closing tenant takes no delivery: a signed one is 409 tenant-closing, and nothing is recorded. A suspended tenant or project takes a signed one without keeping it: 202 with skipped, nothing recorded or started, and the event isn’t kept, so a sender that can redeliver by hand (GitHub) can resend it once it’s restored. The event the flow gets is the body, parsed as JSON for a JSON content type, else its text: data from outside, never instructions. A run’s start isn’t waited for.

tenantId
required
string format: uuid

The trigger’s tenant.

webhookId
required
string format: uuid

The trigger’s routable id (webhookId).

Any bytes, signed as the trigger says; at most the trigger’s bodyLimitBytes.

Media type*/*
string format: binary

A delivery whose dedupe key an earlier fire holds (duplicate: true, that fire’s id): nothing new started. Or a WooCommerce ping (received: ping).

Media typeapplication/json

What the receiver did with a delivery.

object
fireId

The fire recorded (or, with duplicate, the earlier one).

string
duplicate

An earlier fire holds this delivery’s dedupe key: nothing new started.

boolean
skipped

No run started, and the event was dropped. paused: the trigger is paused, and a skipped fire was recorded. tenant-suspended, project-suspended: the trigger’s tenant or project is suspended, and nothing was recorded.

string
Allowed values: paused tenant-suspended project-suspended
received

A WooCommerce save-time ping: answered, nothing started.

string
Allowed values: ping
Example
{
"skipped": "paused",
"received": "ping"
}

Taken: a fire recorded, its run starting as the owner (fireId). Or, on a paused trigger, a skipped fire and no run (skipped: paused). Or, when the trigger’s tenant or project is suspended, nothing recorded and no run (skipped: tenant-suspended, skipped: project-suspended).

Media typeapplication/json

What the receiver did with a delivery.

object
fireId

The fire recorded (or, with duplicate, the earlier one).

string
duplicate

An earlier fire holds this delivery’s dedupe key: nothing new started.

boolean
skipped

No run started, and the event was dropped. paused: the trigger is paused, and a skipped fire was recorded. tenant-suspended, project-suspended: the trigger’s tenant or project is suspended, and nothing was recorded.

string
Allowed values: paused tenant-suspended project-suspended
received

A WooCommerce save-time ping: answered, nothing started.

string
Allowed values: ping
Example
{
"skipped": "paused",
"received": "ping"
}

webhook-body-not-json: a JSON content type whose body does not parse.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

webhook-refused: the request doesn’t prove its sender (the same answer whatever the reason).

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

tenant-closing (details.state): the trigger’s tenant is closing. Nothing was recorded.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

webhook-gone: the trigger was unregistered.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

webhook-body-too-large: past the trigger’s bodyLimitBytes (or 1 MiB, before any lookup).

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

rate-limit-exceeded: past the trigger’s rateLimitPerMinute, or too many refused requests from this client; Retry-After says when.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

tenant-state-unavailable: the tenant’s state can’t be read just now, so nothing was recorded; Retry-After says when to try again.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

See also