Send a test event to a Webhook subscription
POST /v1/webhooks/{webhook_id}/test
| Property | Value |
|---|---|
| Operation | test_webhook |
| Group | Webhooks |
| Scope | webhooks:write |
| Route class | control |
| Idempotency | Idempotency-Key accepted: a retry with the same key replays the first answer |
| Audit | Audited webhooks.subscription.test (mutation) |
| Released in | 1.63.0 |
| Phase 0 host | Served (the hosted api pins 1.65.0) |
Sends an obviously synthetic webhook.test event to this subscription through the production path, the same queue, signature, egress guard and delivery log, and answers 202 with the delivery pending (1.63.0; api.md section 7). The event reaches no other subscription and no event log. A subscription that is not active is 409 webhook_not_active, its status the detail's reason. From 1.64.0, stated as the api built it: since the test event takes no place in the log, its envelope carries the Organization's latest relayed sequence, at least 1, rather than one of its own, with livemode false and the delivery's event id and instant, so a receiver that discards stale updates by sequence must not treat a test as one.
Request
Set VOCAL_API to the base URL of your API and VOCAL_API_KEY to an API key with the webhooks:write scope (Authentication and keys). Replace each placeholder, such as agt_... or <name>, with your own value.
curl -X POST "$VOCAL_API/v1/webhooks/whk_.../test" \
-H "Authorization: Bearer $VOCAL_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"const response = await fetch(`${process.env.VOCAL_API}/v1/webhooks/whk_.../test`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VOCAL_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
},
});
console.log(response.status, await response.text());import os
import uuid
import requests
response = requests.post(
f"{os.environ['VOCAL_API']}/v1/webhooks/whk_.../test",
headers={
"Authorization": f"Bearer {os.environ['VOCAL_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
)
print(response.status_code, response.text)Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
webhook_id | path | WebhookId | yes | The id of a Webhook subscription: whk_ and the row's UUIDv7 in 26 Crockford base32 characters. Opaque and safe to log. |
idempotency-key | header | string | no | A client-chosen key, kept 24 hours per Organization, environment and route. A replay returns the stored response. |
Responses
| Status | Content type | Shape | Error codes |
|---|---|---|---|
202 | application/json | WebhookDelivery | |
400 | application/json | ErrorEnvelope | invalid_request |
401 | application/json | ErrorEnvelope | unauthenticated |
403 | application/json | ErrorEnvelope | forbidden, org_suspended |
404 | application/json | ErrorEnvelope | not_found |
409 | application/json | ErrorEnvelope | idempotency_in_progress, idempotency_key_reused, webhook_not_active |
429 | application/json | ErrorEnvelope | rate_limited |
500 | application/json | ErrorEnvelope | internal_error |
Fields of the 202 answer
The shape is WebhookDelivery.
| Field | Type | Required | Description |
|---|---|---|---|
attempt | integer | yes | The attempts made so far. |
created_at | Timestamp | yes | |
duration_ms | integer or null | yes | The last attempt's duration, a measurement; null before the first. |
environment | Environment | yes | |
error_code | string or null | yes | Why the last attempt did not succeed, a WebhookDeliveryErrorCode value; null after a success. |
event_id | EventId | yes | |
event_type | string | yes | The type of the event sent. |
id | WebhookDeliveryId | yes | |
kind | WebhookDeliveryKind | yes | |
last_attempt_at | Timestamp or null | yes | |
next_attempt_at | Timestamp or null | yes | When the next attempt is due. |
object | "webhook_delivery" | yes | |
replay_job_id | JobId or null | yes | The replay job; null for any other kind. |
response_status | integer or null | yes | The last attempt's HTTP status; null when no answer came. |
status | WebhookDeliveryStatus | yes | |
webhook_id | WebhookId | yes |