Webhooks
Methods in this section: 8. All methods
GET /webhooks Available
#List subscriptions
The subscriptions of this integration (at most 10).
- Scopes
webhooks:manage
- Rate limit
- read
- Idempotency
- not required
- Successful response
200 A page of subscriptions.
Response body
| Field | Type | Description |
|---|
data | array<Webhook> required | |
next_cursor | string required, may be null | Cursor of the next page; null — the list is exhausted. Valid for 24 hours. |
Examples
Request
curl -X GET 'https://wfacademy.id/api/public/v1/webhooks' \
-H 'Authorization: Bearer '"$API_KEY"
Response 200
{
"data": [
{
"id": "EXAMPLE_ID",
"url": "https://example.com/hooks/sapport",
"events": [
null
],
"include_pii": false,
"description": null,
"status": "active",
"created_at": "2026-10-11T09:00:00Z",
"updated_at": "2026-10-11T09:00:00Z"
}
],
"next_cursor": "EXAMPLE_CURSOR"
}
Error example
{
"type": "https://wfacademy.id/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing
POST /webhooks Available
#Create a subscription
Creates an event subscription. A subscription has no secret: every delivery is signed with the platform key (JWS EdDSA with a detached payload; headers Webhook-Event-Id, Webhook-Timestamp, Webhook-Signature); verify it with the public key from GET /.well-known/jwks.json. The URL goes through SSRF protection; include_pii: true is allowed only for lead.*, contact.* and conversation.* events.
- Scopes
webhooks:manage
- Rate limit
- write
- Idempotency
- Idempotency-Key is required
- Successful response
201 The subscription.
Parameters
| Field | Type | Description |
|---|
Idempotency-Key | string required | In: header. A unique string (usually a UUID v4), new for every new operation. A retry with the same key and body yields the same result. |
Request body Subscription parameters.
| Field | Type | Description |
|---|
url | string required | Receiving URL: https, port 443, a host name (not an IP), no redirects. |
events | array<string> required | Event types: an exact type or a family pattern "family.*". Available types: conversation.created, message.created, conversation.handoff_requested, conversation.summary_ready, lead.created, lead.updated, lead.scored, lead.status_changed, lead.action_requested, task.created, task.completed, contact.merged, crm.mode_changed, article.ready, article.published, article.generation.failed, strategist.report.ready, strategist.turn.completed, operation.completed, api_key.expiring. |
include_pii | boolean optional | Full payload with personal data; only for lead.*, contact.* and conversation.* events. Default false. |
description | string optional, may be null | A note for yourself, up to 200 characters. |
Response body
| Field | Type | Description |
|---|
id | string required | Subscription id. |
url | string required | Receiving URL: https, port 443, a host name (not an IP), no redirects. |
events | array<string> required | Event types: an exact type or a family pattern "family.*". Available types: conversation.created, message.created, conversation.handoff_requested, conversation.summary_ready, lead.created, lead.updated, lead.scored, lead.status_changed, lead.action_requested, task.created, task.completed, contact.merged, crm.mode_changed, article.ready, article.published, article.generation.failed, strategist.report.ready, strategist.turn.completed, operation.completed, api_key.expiring. |
include_pii | boolean required | Full payload with personal data; only for lead.*, contact.* and conversation.* events. Default false. |
description | string required, may be null | A note for yourself, up to 200 characters. |
status | string required | active — deliveries flow; paused — suspended. Values: active paused |
created_at | string required | ISO 8601 UTC. |
updated_at | string required | ISO 8601 UTC. |
Examples
Request
curl -X POST 'https://wfacademy.id/api/public/v1/webhooks' \
-H 'Authorization: Bearer '"$API_KEY" \
-H 'Idempotency-Key: '"$(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com/hooks/sapport",
"events": [
"string"
],
"include_pii": false,
"description": "string"
}'
Response 201
{
"id": "EXAMPLE_ID",
"url": "https://example.com/hooks/sapport",
"events": [
"string"
],
"include_pii": false,
"description": "string",
"status": "active",
"created_at": "2026-10-11T09:00:00Z",
"updated_at": "2026-10-11T09:00:00Z"
}
Error example
{
"type": "https://wfacademy.id/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing pii_transfer_not_allowed bad_request validation_failed payload_too_large idempotency_key_required idempotency_conflict operation_in_progress invalid_state
GET /webhooks/{id} Available
#Get a subscription
The subscription.
- Scopes
webhooks:manage
- Rate limit
- read
- Idempotency
- not required
- Successful response
200 The subscription.
Parameters
| Field | Type | Description |
|---|
id | string required | In: path. Subscription id. |
Response body
| Field | Type | Description |
|---|
id | string required | Subscription id. |
url | string required | Receiving URL: https, port 443, a host name (not an IP), no redirects. |
events | array<string> required | Event types: an exact type or a family pattern "family.*". Available types: conversation.created, message.created, conversation.handoff_requested, conversation.summary_ready, lead.created, lead.updated, lead.scored, lead.status_changed, lead.action_requested, task.created, task.completed, contact.merged, crm.mode_changed, article.ready, article.published, article.generation.failed, strategist.report.ready, strategist.turn.completed, operation.completed, api_key.expiring. |
include_pii | boolean required | Full payload with personal data; only for lead.*, contact.* and conversation.* events. Default false. |
description | string required, may be null | A note for yourself, up to 200 characters. |
status | string required | active — deliveries flow; paused — suspended. Values: active paused |
created_at | string required | ISO 8601 UTC. |
updated_at | string required | ISO 8601 UTC. |
Examples
Request
curl -X GET 'https://wfacademy.id/api/public/v1/webhooks/EXAMPLE_ID' \
-H 'Authorization: Bearer '"$API_KEY"
Response 200
{
"id": "EXAMPLE_ID",
"url": "https://example.com/hooks/sapport",
"events": [
"string"
],
"include_pii": false,
"description": "string",
"status": "active",
"created_at": "2026-10-11T09:00:00Z",
"updated_at": "2026-10-11T09:00:00Z"
}
Error example
{
"type": "https://wfacademy.id/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing not_found
PATCH /webhooks/{id} Available
#Update a subscription
Changes the URL, events, note, status (active or paused) and include_pii. Pass at least one field.
- Scopes
webhooks:manage
- Rate limit
- write
- Idempotency
- not required
- Successful response
200 The subscription after the update.
Parameters
| Field | Type | Description |
|---|
id | string required | In: path. Subscription id. |
Request body The fields to change.
| Field | Type | Description |
|---|
url | string optional | Receiving URL: https, port 443, a host name (not an IP), no redirects. |
events | array<string> optional | Event types: an exact type or a family pattern "family.*". Available types: conversation.created, message.created, conversation.handoff_requested, conversation.summary_ready, lead.created, lead.updated, lead.scored, lead.status_changed, lead.action_requested, task.created, task.completed, contact.merged, crm.mode_changed, article.ready, article.published, article.generation.failed, strategist.report.ready, strategist.turn.completed, operation.completed, api_key.expiring. |
include_pii | boolean optional | Full payload with personal data; only for lead.*, contact.* and conversation.* events. Default false. |
description | string optional, may be null | A note for yourself, up to 200 characters. |
status | string optional | Pause or resume deliveries. Values: active paused |
Response body
| Field | Type | Description |
|---|
id | string required | Subscription id. |
url | string required | Receiving URL: https, port 443, a host name (not an IP), no redirects. |
events | array<string> required | Event types: an exact type or a family pattern "family.*". Available types: conversation.created, message.created, conversation.handoff_requested, conversation.summary_ready, lead.created, lead.updated, lead.scored, lead.status_changed, lead.action_requested, task.created, task.completed, contact.merged, crm.mode_changed, article.ready, article.published, article.generation.failed, strategist.report.ready, strategist.turn.completed, operation.completed, api_key.expiring. |
include_pii | boolean required | Full payload with personal data; only for lead.*, contact.* and conversation.* events. Default false. |
description | string required, may be null | A note for yourself, up to 200 characters. |
status | string required | active — deliveries flow; paused — suspended. Values: active paused |
created_at | string required | ISO 8601 UTC. |
updated_at | string required | ISO 8601 UTC. |
Examples
Request
curl -X PATCH 'https://wfacademy.id/api/public/v1/webhooks/EXAMPLE_ID' \
-H 'Authorization: Bearer '"$API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com/hooks/sapport",
"events": [
"string"
],
"include_pii": false,
"description": "string",
"status": "active"
}'
Response 200
{
"id": "EXAMPLE_ID",
"url": "https://example.com/hooks/sapport",
"events": [
"string"
],
"include_pii": false,
"description": "string",
"status": "active",
"created_at": "2026-10-11T09:00:00Z",
"updated_at": "2026-10-11T09:00:00Z"
}
Error example
{
"type": "https://wfacademy.id/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing pii_transfer_not_allowed not_found bad_request validation_failed payload_too_large
DELETE /webhooks/{id} Available
#Delete a subscription
Deletes the subscription together with its delivery log. Deleting again returns not_found.
- Scopes
webhooks:manage
- Rate limit
- write
- Idempotency
- not required
- Successful response
204 The subscription is deleted; no body.
Parameters
| Field | Type | Description |
|---|
id | string required | In: path. Subscription id. |
Examples
Request
curl -X DELETE 'https://wfacademy.id/api/public/v1/webhooks/EXAMPLE_ID' \
-H 'Authorization: Bearer '"$API_KEY"
Error example
{
"type": "https://wfacademy.id/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing not_found
POST /webhooks/{id}/test Available
#Send a test event
Queues a webhook.test event (livemode: false) for this subscription only; it is signed exactly like live events. The subscription must be active.
- Scopes
webhooks:manage
- Rate limit
- write
- Idempotency
- Idempotency-Key is required
- Successful response
202 The event is queued.
Parameters
| Field | Type | Description |
|---|
id | string required | In: path. Subscription id. |
Idempotency-Key | string required | In: header. A unique string (usually a UUID v4), new for every new operation. A retry with the same key and body yields the same result. |
Response body
| Field | Type | Description |
|---|
event_id | string required, may be null | The test event id. |
type | string required | |
status | string required | The event is queued; it goes out on the next delivery run (within a minute). |
Examples
Request
curl -X POST 'https://wfacademy.id/api/public/v1/webhooks/EXAMPLE_ID/test' \
-H 'Authorization: Bearer '"$API_KEY" \
-H 'Idempotency-Key: '"$(uuidgen)"
Response 202
{
"event_id": "EXAMPLE_ID",
"type": "string",
"status": "string"
}
Error example
{
"type": "https://wfacademy.id/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing not_found invalid_state idempotency_key_required idempotency_conflict operation_in_progress
GET /webhooks/{id}/deliveries Available
#Delivery log
Delivery attempts of the subscription, newest first; kept for 30 days.
- Scopes
webhooks:manage
- Rate limit
- read
- Idempotency
- not required
- Successful response
200 A page of the log.
Parameters
| Field | Type | Description |
|---|
id | string required | In: path. Subscription id. |
limit | integer optional | In: query. Page size: 1 to 100, default 25. |
cursor | string optional | In: query. The next_cursor value from the previous response. Valid for 24 hours; start over when filters change. |
state | string optional | In: query. Filter by delivery state. |
Response body
| Field | Type | Description |
|---|
data | array<WebhookDelivery> required | |
next_cursor | string required, may be null | Cursor of the next page; null — the list is exhausted. Valid for 24 hours. |
Examples
Request
curl -X GET 'https://wfacademy.id/api/public/v1/webhooks/EXAMPLE_ID/deliveries' \
-H 'Authorization: Bearer '"$API_KEY"
Response 200
{
"data": [
{
"id": "EXAMPLE_ID",
"event_id": "EXAMPLE_ID",
"state": "pending",
"attempt": 0,
"status_code": null,
"duration_ms": null,
"error": null,
"next_attempt_at": null,
"delivered_at": null,
"created_at": "2026-10-11T09:00:00Z"
}
],
"next_cursor": "EXAMPLE_CURSOR"
}
Error example
{
"type": "https://wfacademy.id/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing not_found validation_failed
GET /.well-known/jwks.json Available
#Webhook signing public keys
The cell public key set (JWKS) for verifying the Webhook-Signature header. No authentication; cacheable (Cache-Control: max-age=300); on an unknown kid, fetch the set again. The signed payload is the string "<Webhook-Event-Id>.<Webhook-Timestamp>.<request body>"; the signature is a compact JWS with an empty middle part (RFC 7515, Appendix F). Reject deliveries whose Webhook-Timestamp is older than 5 minutes.
- Scopes
- none required, a valid key is enough
- Rate limit
- read
- Idempotency
- not required
- Successful response
200 The key set.
Response body
| Field | Type | Description |
|---|
keys | array<WebhookJwk> required | The active key and keys still valid after a rotation. |
Examples
Request
curl -X GET 'https://wfacademy.id/api/public/v1/.well-known/jwks.json' \
-H 'Authorization: Bearer '"$API_KEY"
Response 200
{
"keys": [
{
"kty": "string",
"crv": "string",
"x": "string",
"kid": "string",
"alg": "string",
"use": "string"
}
]
}
Error example
{
"type": "https://wfacademy.id/developers/errors#service_unavailable",
"title": "Service unavailable",
"status": 503,
"code": "service_unavailable",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
service_unavailable internal_error