Reference

Webhooks

Subscribe to events from §Events. Deliveries are signed (HMAC-SHA256), retried with exponential backoff, and logged for 30 days.

List webhook endpoints

get /v1/webhooks
curl -X GET "https://api.clement.so/v1/webhooks" \
  -H "Authorization: Bearer $CLEMENT_API_KEY"

Query parameters

  • limit integer
  • starting_after string
  • ending_before string

Response200

OK

Property Type Description
object required enum (1)
"list"
data required array<WebhookEndpoint>
has_more required boolean
next_cursor required string | null
prev_cursor required string | null
Example response
{
  "object": "list",
  "data": [
    {
      "id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
      "object": "webhook_endpoint",
      "mode": "live",
      "url": "https://customer.example.com/webhooks/clement",
      "description": "CRM sync — replies, bounces, unsubs",
      "enabled_events": [
        "message.replied",
        "message.bounced",
        "contact.unsubscribed"
      ],
      "secret_last_four": "f4a2",
      "disabled_at": null,
      "consecutive_failures": 0,
      "verified_at": "2026-04-26T11:20:01Z",
      "created_at": "2026-04-26T11:20:00Z",
      "updated_at": "2026-04-26T11:20:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": "string",
  "prev_cursor": "string"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.

Create a webhook endpoint (signing secret returned once)

post /v1/webhooks

The endpoint takes the calling key's mode and receives only events of that mode: create it with a `cl_test_*` key to receive test-mode events. Live endpoints must use `https://` and a public host. Clement immediately sends the endpoint a signed `webhook.verification` event; once it answers with any 2xx, `verified_at` is set and events start flowing. Until then it receives no other events (verification retries on the normal schedule, and a successful `/test` delivery also verifies it).

curl -X POST "https://api.clement.so/v1/webhooks" \
  -H "Authorization: Bearer $CLEMENT_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
      "url": "https://example.com/clement-webhook",
      "enabled_events": [
        "string"
      ],
      "description": "A short human-readable description."
    }'

Request body

Property Type Description
url required string (uri) Must be `https://` for live-mode endpoints. Redirects are not followed.
description string
enabled_events required array<string> Event types to subscribe to (e.g. `["message.replied", "contact.unsubscribed"]`). Use `["*"]` for everything.

Response201

Created

Property Type Description
id required string
Example: "whk_…"
object required enum (1)
"webhook_endpoint"
mode required enum (2) Mode of the key that created the endpoint. An endpoint only receives events of its own mode.
"live""test"
url required string (uri)
description required string | null
enabled_events required array<string> Subscribed event types. Use `*` to subscribe to all events.
secret_last_four required string
disabled_at required string (date-time) | null
consecutive_failures required integer Failed attempts since the last successful delivery. The endpoint is disabled after it has been failing for 7 days.
verified_at required string (date-time) | null When the endpoint first answered with a 2xx. A new endpoint, or one whose `url` changed, receives a signed `webhook.verification` event and no other events until it acknowledges one.
created_at required string (date-time)
updated_at required string (date-time)
secret required string The signing secret. Returned only on create + rotate; never again.
Example response
{
  "id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
  "object": "webhook_endpoint",
  "mode": "live",
  "url": "https://customer.example.com/webhooks/clement",
  "description": "CRM sync — replies, bounces, unsubs",
  "enabled_events": [
    "message.replied",
    "message.bounced",
    "contact.unsubscribed"
  ],
  "secret_last_four": "f4a2",
  "disabled_at": null,
  "consecutive_failures": 0,
  "verified_at": "2026-04-26T11:20:01Z",
  "created_at": "2026-04-26T11:20:00Z",
  "updated_at": "2026-04-26T11:20:00Z"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.

Retrieve a webhook endpoint

get /v1/webhooks/{id}
curl -X GET "https://api.clement.so/v1/webhooks/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1" \
  -H "Authorization: Bearer $CLEMENT_API_KEY"

Path parameters

  • id string required

Response200

OK

Property Type Description
id required string
Example: "whk_…"
object required enum (1)
"webhook_endpoint"
mode required enum (2) Mode of the key that created the endpoint. An endpoint only receives events of its own mode.
"live""test"
url required string (uri)
description required string | null
enabled_events required array<string> Subscribed event types. Use `*` to subscribe to all events.
secret_last_four required string
disabled_at required string (date-time) | null
consecutive_failures required integer Failed attempts since the last successful delivery. The endpoint is disabled after it has been failing for 7 days.
verified_at required string (date-time) | null When the endpoint first answered with a 2xx. A new endpoint, or one whose `url` changed, receives a signed `webhook.verification` event and no other events until it acknowledges one.
created_at required string (date-time)
updated_at required string (date-time)
Example response
{
  "id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
  "object": "webhook_endpoint",
  "mode": "live",
  "url": "https://customer.example.com/webhooks/clement",
  "description": "CRM sync — replies, bounces, unsubs",
  "enabled_events": [
    "message.replied",
    "message.bounced",
    "contact.unsubscribed"
  ],
  "secret_last_four": "f4a2",
  "disabled_at": null,
  "consecutive_failures": 0,
  "verified_at": "2026-04-26T11:20:01Z",
  "created_at": "2026-04-26T11:20:00Z",
  "updated_at": "2026-04-26T11:20:00Z"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.

Update a webhook endpoint

patch /v1/webhooks/{id}
curl -X PATCH "https://api.clement.so/v1/webhooks/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1" \
  -H "Authorization: Bearer $CLEMENT_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
      "url": "https://example.com/clement-webhook",
      "description": "A short human-readable description.",
      "enabled_events": [
        "string"
      ],
      "disabled": false
    }'

Path parameters

  • id string required

Request body

Property Type Description
url string (uri)
description string | null
enabled_events array<string>
disabled boolean Set to `true` to disable; `false` to re-enable a previously-disabled endpoint.

Response200

Updated

Property Type Description
id required string
Example: "whk_…"
object required enum (1)
"webhook_endpoint"
mode required enum (2) Mode of the key that created the endpoint. An endpoint only receives events of its own mode.
"live""test"
url required string (uri)
description required string | null
enabled_events required array<string> Subscribed event types. Use `*` to subscribe to all events.
secret_last_four required string
disabled_at required string (date-time) | null
consecutive_failures required integer Failed attempts since the last successful delivery. The endpoint is disabled after it has been failing for 7 days.
verified_at required string (date-time) | null When the endpoint first answered with a 2xx. A new endpoint, or one whose `url` changed, receives a signed `webhook.verification` event and no other events until it acknowledges one.
created_at required string (date-time)
updated_at required string (date-time)
Example response
{
  "id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
  "object": "webhook_endpoint",
  "mode": "live",
  "url": "https://customer.example.com/webhooks/clement",
  "description": "CRM sync — replies, bounces, unsubs",
  "enabled_events": [
    "message.replied",
    "message.bounced",
    "contact.unsubscribed"
  ],
  "secret_last_four": "f4a2",
  "disabled_at": null,
  "consecutive_failures": 0,
  "verified_at": "2026-04-26T11:20:01Z",
  "created_at": "2026-04-26T11:20:00Z",
  "updated_at": "2026-04-26T11:20:00Z"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.

Delete a webhook endpoint

delete /v1/webhooks/{id}
curl -X DELETE "https://api.clement.so/v1/webhooks/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1" \
  -H "Authorization: Bearer $CLEMENT_API_KEY"

Path parameters

  • id string required

Response200

Deleted

Property Type Description
id required string
Example: "whk_…"
object required enum (1)
"webhook_endpoint"
mode required enum (2) Mode of the key that created the endpoint. An endpoint only receives events of its own mode.
"live""test"
url required string (uri)
description required string | null
enabled_events required array<string> Subscribed event types. Use `*` to subscribe to all events.
secret_last_four required string
disabled_at required string (date-time) | null
consecutive_failures required integer Failed attempts since the last successful delivery. The endpoint is disabled after it has been failing for 7 days.
verified_at required string (date-time) | null When the endpoint first answered with a 2xx. A new endpoint, or one whose `url` changed, receives a signed `webhook.verification` event and no other events until it acknowledges one.
created_at required string (date-time)
updated_at required string (date-time)
Example response
{
  "id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
  "object": "webhook_endpoint",
  "mode": "live",
  "url": "https://customer.example.com/webhooks/clement",
  "description": "CRM sync — replies, bounces, unsubs",
  "enabled_events": [
    "message.replied",
    "message.bounced",
    "contact.unsubscribed"
  ],
  "secret_last_four": "f4a2",
  "disabled_at": null,
  "consecutive_failures": 0,
  "verified_at": "2026-04-26T11:20:01Z",
  "created_at": "2026-04-26T11:20:00Z",
  "updated_at": "2026-04-26T11:20:00Z"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.

List delivery attempts for this endpoint

get /v1/webhooks/{id}/deliveries
curl -X GET "https://api.clement.so/v1/webhooks/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1/deliveries" \
  -H "Authorization: Bearer $CLEMENT_API_KEY"

Path parameters

  • id string required

Query parameters

  • limit integer
  • starting_after string
  • ending_before string
  • outcome string

Response200

OK

Property Type Description
object required enum (1)
"list"
data required array<WebhookDelivery>
has_more required boolean
next_cursor required string | null
prev_cursor required string | null
Example response
{
  "object": "list",
  "data": [
    {
      "id": "whd_01HZ8X3J9K7P2QV6R5N4M3T2LB",
      "object": "webhook_delivery",
      "endpoint_id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
      "event_id": "evt_01HZ8X3J9K7P2QV6R5N4M3T2L9",
      "attempt": 1,
      "outcome": "succeeded",
      "status_code": 200,
      "latency_ms": 124,
      "error": null,
      "delivered_at": "2026-04-29T16:42:14Z",
      "next_retry_at": null,
      "created_at": "2026-04-29T16:42:14Z"
    }
  ],
  "has_more": false,
  "next_cursor": "string",
  "prev_cursor": "string"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.

Retrieve a single delivery attempt

get /v1/webhooks/{id}/deliveries/{delivery_id}
curl -X GET "https://api.clement.so/v1/webhooks/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1/deliveries/del_01HZ8X3J9K7P2QV6R5N4M3T2L1" \
  -H "Authorization: Bearer $CLEMENT_API_KEY"

Path parameters

  • id string required
  • delivery_id string required

Response200

OK

Property Type Description
id required string
object required enum (1)
"webhook_delivery"
endpoint_id required string
event_id required string | null
attempt required integer
outcome required enum (3)
"pending""succeeded""failed"
status_code required integer | null
latency_ms required integer | null
error required string | null
delivered_at required string (date-time) | null
next_retry_at required string (date-time) | null
created_at required string (date-time)
Example response
{
  "id": "whd_01HZ8X3J9K7P2QV6R5N4M3T2LB",
  "object": "webhook_delivery",
  "endpoint_id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
  "event_id": "evt_01HZ8X3J9K7P2QV6R5N4M3T2L9",
  "attempt": 1,
  "outcome": "succeeded",
  "status_code": 200,
  "latency_ms": 124,
  "error": null,
  "delivered_at": "2026-04-29T16:42:14Z",
  "next_retry_at": null,
  "created_at": "2026-04-29T16:42:14Z"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.

Replay a delivery (creates a new attempt with the same payload)

post /v1/webhooks/{id}/deliveries/{delivery_id}/redeliver
curl -X POST "https://api.clement.so/v1/webhooks/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1/deliveries/del_01HZ8X3J9K7P2QV6R5N4M3T2L1/redeliver" \
  -H "Authorization: Bearer $CLEMENT_API_KEY"

Path parameters

  • id string required
  • delivery_id string required

Response202

Redelivery queued

Property Type Description
id required string
object required enum (1)
"webhook_delivery"
endpoint_id required string
event_id required string | null
attempt required integer
outcome required enum (3)
"pending""succeeded""failed"
status_code required integer | null
latency_ms required integer | null
error required string | null
delivered_at required string (date-time) | null
next_retry_at required string (date-time) | null
created_at required string (date-time)
Example response
{
  "id": "whd_01HZ8X3J9K7P2QV6R5N4M3T2LB",
  "object": "webhook_delivery",
  "endpoint_id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
  "event_id": "evt_01HZ8X3J9K7P2QV6R5N4M3T2L9",
  "attempt": 1,
  "outcome": "succeeded",
  "status_code": 200,
  "latency_ms": 124,
  "error": null,
  "delivered_at": "2026-04-29T16:42:14Z",
  "next_retry_at": null,
  "created_at": "2026-04-29T16:42:14Z"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.

Rotate the signing secret

post /v1/webhooks/{id}/rotate

Issues a new `whsec_…` secret. The old one stops working immediately. Update your verifier to the new secret as soon as the rotate response lands.

curl -X POST "https://api.clement.so/v1/webhooks/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1/rotate" \
  -H "Authorization: Bearer $CLEMENT_API_KEY"

Path parameters

  • id string required

Response200

Rotated

Property Type Description
id required string
Example: "whk_…"
object required enum (1)
"webhook_endpoint"
mode required enum (2) Mode of the key that created the endpoint. An endpoint only receives events of its own mode.
"live""test"
url required string (uri)
description required string | null
enabled_events required array<string> Subscribed event types. Use `*` to subscribe to all events.
secret_last_four required string
disabled_at required string (date-time) | null
consecutive_failures required integer Failed attempts since the last successful delivery. The endpoint is disabled after it has been failing for 7 days.
verified_at required string (date-time) | null When the endpoint first answered with a 2xx. A new endpoint, or one whose `url` changed, receives a signed `webhook.verification` event and no other events until it acknowledges one.
created_at required string (date-time)
updated_at required string (date-time)
secret required string The signing secret. Returned only on create + rotate; never again.
Example response
{
  "id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
  "object": "webhook_endpoint",
  "mode": "live",
  "url": "https://customer.example.com/webhooks/clement",
  "description": "CRM sync — replies, bounces, unsubs",
  "enabled_events": [
    "message.replied",
    "message.bounced",
    "contact.unsubscribed"
  ],
  "secret_last_four": "f4a2",
  "disabled_at": null,
  "consecutive_failures": 0,
  "verified_at": "2026-04-26T11:20:01Z",
  "created_at": "2026-04-26T11:20:00Z",
  "updated_at": "2026-04-26T11:20:00Z"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.

Send a synthetic test delivery to this endpoint

post /v1/webhooks/{id}/test

Delivers a `webhook.test` event with `metadata.test = true` so the customer's endpoint can verify wiring without depending on real product activity.

curl -X POST "https://api.clement.so/v1/webhooks/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1/test" \
  -H "Authorization: Bearer $CLEMENT_API_KEY"

Path parameters

  • id string required

Response202

Test queued

Property Type Description
id required string
object required enum (1)
"webhook_delivery"
endpoint_id required string
event_id required string | null
attempt required integer
outcome required enum (3)
"pending""succeeded""failed"
status_code required integer | null
latency_ms required integer | null
error required string | null
delivered_at required string (date-time) | null
next_retry_at required string (date-time) | null
created_at required string (date-time)
Example response
{
  "id": "whd_01HZ8X3J9K7P2QV6R5N4M3T2LB",
  "object": "webhook_delivery",
  "endpoint_id": "whk_01HZ8X3J9K7P2QV6R5N4M3T2LA",
  "event_id": "evt_01HZ8X3J9K7P2QV6R5N4M3T2L9",
  "attempt": 1,
  "outcome": "succeeded",
  "status_code": 200,
  "latency_ms": 124,
  "error": null,
  "delivered_at": "2026-04-29T16:42:14Z",
  "next_retry_at": null,
  "created_at": "2026-04-29T16:42:14Z"
}

Errors

  • 400 Invalid request
  • 401 Authentication failed
  • 403 Insufficient scope
  • 404 Not found
  • 409 Conflict
  • 422 Validation failed
  • 429 Rate limited

See Errors for the full envelope and per-code reference.