Webhook Endpoints
Register, manage, and debug the HTTPS endpoints that receive Stile's signed event notifications.
A webhook endpoint is an HTTPS URL on your server where Stile delivers signed events — session verified, failed, expired, and more. This page covers the management API: creating endpoints, rotating secrets, and inspecting individual deliveries. For signature verification and handler patterns, see the Webhooks guide. You can also manage endpoints in the dashboard.
Examples show cURL, Python, Go, and Node.js. Approved preview users can also use the private-preview Node.js SDK as a typed convenience wrapper.
Live orgs require a webhook endpoint
You cannot create verification sessions until at least one active webhook endpoint exists (the API
returns 400 webhook_required). Sandbox organizations are exempt.
The webhook endpoint object
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier, prefixed we_. |
application_id | string | null | Owning application, or null for a legacy organization-wide endpoint. |
name | string | Optional display name. |
url | string | The HTTPS URL events are delivered to. |
enabled_events | string[] | Subscribed event types. ["*"] means all events. |
status | string | enabled or disabled. Disabled endpoints receive no deliveries. |
description | string | Optional operational notes. |
metadata | object | Key-value string pairs you attach. |
secret | string | The signing secret used to compute Stile-Signature. Returned once — on create and on rotate only. |
Create an endpoint
/v1/webhook_endpointsYour endpoint should be a publicly reachable HTTPS URL and must respond with a 2xx within 30 seconds. Use an HTTPS tunnel for a local handler. Return a 2xx as soon as you've queued the work, then process asynchronously.
| Parameter | Type | Description |
|---|---|---|
name | string | Optional display name, up to 100 characters. |
urlrequired | string | The HTTPS URL to deliver events to. |
enabled_eventsrequired | string[] | Event types to subscribe to. Use ["*"] to receive all events. |
description | string | Operational notes for this endpoint. |
metadata | object | Key-value string pairs to attach to the endpoint. |
curl https://api.stile.id/v1/webhook_endpoints \
-H "Authorization: Bearer stile_sk_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/api/webhooks",
"enabled_events": ["verification_session.verified", "verification_session.failed"]
}'import requests
res = requests.post(
"https://api.stile.id/v1/webhook_endpoints",
headers={"Authorization": "Bearer stile_sk_..."},
json={
"name": "Production verification",
"url": "https://yourapp.com/api/webhooks",
"enabled_events": [
"verification_session.verified",
"verification_session.failed",
],
"description": "Production webhook",
},
)
endpoint = res.json()
# IMPORTANT: save endpoint["secret"] — it's only shown once!
print("Webhook secret:", endpoint["secret"])body := strings.NewReader(`{
"name": "Production verification",
"url": "https://yourapp.com/api/webhooks",
"enabled_events": ["verification_session.verified", "verification_session.failed"],
"description": "Production webhook"
}`)
req, _ := http.NewRequest("POST", "https://api.stile.id/v1/webhook_endpoints", body)
req.Header.Set("Authorization", "Bearer stile_sk_...")
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)const endpoint = await stile.webhookEndpoints.create({
name: "Production verification",
url: "https://yourapp.com/api/webhooks",
enabled_events: [
"verification_session.verified",
"verification_session.failed",
"verification_session.expired",
],
description: "Production webhook",
});
// IMPORTANT: save endpoint.secret — it's only shown once!
console.log("Webhook secret:", endpoint.secret);Store the secret immediately
The response includes the endpoint's signing secret exactly once. Store it in your secret
manager right away — you need it to verify the Stile-Signature header on every delivery. If you
lose it, rotate the secret to get a new one.
Retrieve an endpoint
/v1/webhook_endpoints/:idcurl https://api.stile.id/v1/webhook_endpoints/we_abc123 \
--header "Authorization: Bearer $STILE_API_KEY"The secret is not included — it is only returned on create and rotate.
Update an endpoint
/v1/webhook_endpoints/:id| Parameter | Type | Description |
|---|---|---|
name | string | null | Rename or clear the display name. |
url | string | New delivery URL. |
enabled_events | string[] | Replace the subscribed event types. |
status | "enabled" | "disabled" | Pause or resume delivery without deleting the endpoint. |
description | string | null | Update or clear operational notes. |
metadata | object | Replace all metadata key-value pairs. |
curl https://api.stile.id/v1/webhook_endpoints/we_abc123 \
--request POST \
--header "Authorization: Bearer $STILE_API_KEY" \
--header "Content-Type: application/json" \
--data '{ "enabled_events": ["*"], "status": "disabled" }'Delete an endpoint
/v1/webhook_endpoints/:idcurl https://api.stile.id/v1/webhook_endpoints/we_abc123 \
--request DELETE \
--header "Authorization: Bearer $STILE_API_KEY"List endpoints
/v1/webhook_endpointscurl https://api.stile.id/v1/webhook_endpoints \
-H "Authorization: Bearer stile_sk_..."import requests
res = requests.get(
"https://api.stile.id/v1/webhook_endpoints",
headers={"Authorization": "Bearer stile_sk_..."},
)
data = res.json()req, _ := http.NewRequest("GET", "https://api.stile.id/v1/webhook_endpoints", nil)
req.Header.Set("Authorization", "Bearer stile_sk_...")
res, _ := http.DefaultClient.Do(req)const { data } = await stile.webhookEndpoints.list();Rotate the signing secret
/v1/webhook_endpoints/:id/rotate-secretGenerates a new signing secret for the endpoint and returns it once.
curl -X POST https://api.stile.id/v1/webhook_endpoints/we_abc123/rotate-secret \
-H "Authorization: Bearer stile_sk_..."Rotation takes effect immediately
Update your environment variables as soon as you rotate — deliveries signed with the old secret will stop validating.
List deliveries
/v1/webhook_endpoints/:id/deliveriesReturns a paginated list of delivery records for a specific endpoint, newest first. Stile creates one record for each event-endpoint pair and updates that record on every retry. Each record contains the current attempt number and latest HTTP response; it is not an immutable attempt history. The record ID is also sent as the Stile-Webhook-Id header.
| Parameter | Type | Description |
|---|---|---|
limit | number= 10 | Number of deliveries to return. |
starting_after | string | Return older deliveries that follow this ID in the newest-first list. |
ending_before | string | Return newer deliveries that precede this ID in the newest-first list. |
status | string | Filter by "delivered", "failed", or "pending". |
event_type | string | Filter by an exact event type. |
Use only one cursor per request: starting_after moves toward older deliveries and ending_before moves toward newer deliveries.
curl "https://api.stile.id/v1/webhook_endpoints/we_abc123/deliveries?limit=10" \
--header "Authorization: Bearer $STILE_API_KEY"Retrieve a delivery
/v1/webhook_endpoints/:id/deliveries/:deliveryIdReturns the current detail for one delivery record, including its request payload, current attempt number, and latest response from your endpoint. Earlier retry responses are not retained on this resource.
curl https://api.stile.id/v1/webhook_endpoints/we_abc123/deliveries/cm5x7k9h20000qwerty123456 \
--header "Authorization: Bearer $STILE_API_KEY"Retry a delivery
/v1/webhook_endpoints/:id/deliveries/:deliveryId/retryRe-queues an existing delivery immediately. The same delivery record and Stile-Webhook-Id are reused, and its attempt counter and latest response fields are reset before processing resumes. Use this after fixing an endpoint instead of waiting for the automatic retry schedule (5 min → 30 min → 2 h → 8 h after the initial failure; see the Webhooks guide).
curl -X POST https://api.stile.id/v1/webhook_endpoints/we_abc123/deliveries/cm5x7k9h20000qwerty123456/retry \
-H "Authorization: Bearer stile_sk_..."Send a test event
/v1/webhook_endpoints/:id/testQueue a synthetic event to confirm that the endpoint is reachable and your handler accepts the payload. Omit event_type to send verification_session.verified.
curl https://api.stile.id/v1/webhook_endpoints/we_abc123/test \
--request POST \
--header "Authorization: Bearer $STILE_API_KEY" \
--header "Content-Type: application/json" \
--data '{ "event_type": "verification_session.verified" }'The response contains both the queued event id and its endpoint-specific delivery_id. Retrieve that delivery until it reaches delivered or failed; accepting the test request does not itself prove that your endpoint returned 2xx.
{
"id": "evt_test_123",
"object": "event",
"type": "verification_session.verified",
"created": 1786400000,
"delivery_id": "whd_test_123"
}Automatic and manual retries reuse the original event id and delivery ID. Another subscribed endpoint has its own delivery record for the same event, so deduplicate business work on the event id.
Event types
See the full catalog in the Events API reference — verification session lifecycle (verification_session.*), manual review outcomes (session_review.*), and trust-reuse grants (trust_reuse_grant.*, trust_reuse_consent.*). Use ["*"] in enabled_events to subscribe to everything.
Next steps
Webhooks guide
Delivery lifecycle, retry schedule, and handler best practices.
Verify signatures
Validate the Stile-Signature header in your framework of choice.
Events API
The event object and the complete event-type catalog.
Node.js private preview
Typed webhook endpoint management and signature verification helpers.