Docs
Callback API
Temporary public webhook endpoints for AI agents, scripts and automated workflows.
Create Callback
Create a callback, give the public callback URL to an external service, then read or wait for the resulting events. No account, API key or subscription is required. Callback creation is paid per request using x402.
Endpoint
POST https://api.exende.dev/v1/callbacks
Without a valid x402 payment, the server responds with HTTP 402 Payment Required. This is the only operation that requires payment; receiving webhooks, reading events, waiting, and deleting the callback do not trigger another x402 payment.
bash
Create callback
curl -i -X POST \
https://api.exende.dev/v1/callbacks \
-H "Content-Type: application/json" \
-d '{}'Payment-required advertises
| x402 version | 2 |
|---|---|
| Network | Base Mainnet |
| CAIP-2 | eip155:8453 |
| Asset | USDC |
| Price | $0.01 |
json
HTTP 201 response
{
"id": "cb_3t2cC09baD4yOvoeVMDK2vGQ",
"callback_url": "https://cb.exende.dev/hooks/cb_3t2cC09baD4yOvoeVMDK2vGQ",
"events_url": "https://api.exende.dev/v1/callbacks/cb_3t2cC09baD4yOvoeVMDK2vGQ/events",
"wait_url": "https://api.exende.dev/v1/callbacks/cb_3t2cC09baD4yOvoeVMDK2vGQ/wait",
"read_token": "ex_cb_sk_...",
"expires_at": "2026-08-22T14:30:00.000Z",
"limits": {
"events": 10,
"payload_bytes": 262144
}
}| Field | Meaning |
|---|---|
| id | Public identifier matching ^cb_[A-Za-z0-9]{24}$ exactly. |
| callback_url | Public send-only webhook URL. Give this URL to the external service. |
| events_url | Authenticated endpoint used to retrieve received events. |
| wait_url | Authenticated long-polling endpoint used to wait for the next event. |
| read_token | Private bearer token used to read, wait for, or delete the callback. |
| expires_at | UTC expiration timestamp. |
| limits.events | Maximum number of events accepted by the callback. |
| limits.payload_bytes | Maximum payload size for each webhook event. |
Security note
The read_token is secret. Never give it to the service sending the webhook. Only callback_url is public.
Send a Webhook
The external service sends an event using POST, PUT, or PATCH on the callback host: https://cb.exende.dev/hooks/{callback_id}.
bash
Webhook event
curl -X POST \
https://cb.exende.dev/hooks/cb_3t2cC09baD4yOvoeVMDK2vGQ \
-H "Content-Type: application/json" \
-d '{"status":"completed","result":"https://example.com/output"}'json
Successful response
{
"received": true,
"event_id": "evt_6ec6e7b4a917d63861a07d9238d4cf6d"
}| Supported payload types | application/json, text/*, application/x-www-form-urlencoded |
|---|---|
| Missing Content-Type | Accepted and stored as text |
| Event cap | 10 events per callback |
| Event ID format | ^evt_[a-f0-9]{32}$ |
| Rate limit | 30 requests per 60 seconds per callback |
JSON bodies are validated before storage. Binary and file uploads are not supported. Only content-type, user-agent, stripe-signature, svix-id, svix-timestamp, svix-signature, x-hub-signature, x-hub-signature-256, webhook-id, webhook-timestamp, webhook-signature, x-signature, and x-webhook-signature headers are retained.
Callback ID validation and per-callback rate limiting run before callback lookup and body parsing. POST, PUT, and PATCH use the same handler and return the same response and error shapes.
| Status | Webhook error |
|---|---|
| 400 | INVALID_CALLBACK_ID |
| 400 | INVALID_JSON |
| 404 | CALLBACK_NOT_FOUND |
| 410 | CALLBACK_EXPIRED |
| 410 | CALLBACK_EVENT_LIMIT_REACHED |
| 413 | PAYLOAD_TOO_LARGE |
| 415 | UNSUPPORTED_MEDIA_TYPE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Wait for an Event
GET https://api.exende.dev/v1/callbacks/{id}/wait
Authentication: Authorization: Bearer ex_cb_sk_...
bash
Long poll
curl \
"https://api.exende.dev/v1/callbacks/cb_3t2cC09baD4yOvoeVMDK2vGQ/wait?timeout=30" \
-H "Authorization: Bearer ex_cb_sk_..."| timeout | Coerced with JavaScript Number. Missing or non-finite values default to 30. Finite values are floored, then clamped to 1–30 seconds. Examples: 1.9 → 1, 0 → 1, 31 → 30, invalid → 30. |
|---|---|
| after | Optional event ID; wait only for the next event after it |
json
Event received
{
"received": true,
"timeout": false,
"event": {
"id": "evt_6ec6e7b4a917d63861a07d9238d4cf6d",
"received_at": "2026-08-22T14:05:17.000Z",
"method": "POST",
"content_type": "application/json",
"headers": {
"content-type": "application/json"
},
"body": {
"status": "completed",
"result": "https://example.com/output"
},
"size_bytes": 60
},
"next_wait_url": "/v1/callbacks/cb_3t2cC09baD4yOvoeVMDK2vGQ/wait?after=evt_6ec6e7b4a917d63861a07d9238d4cf6d&timeout=30"
}json
Timed out
{
"received": false,
"timeout": true,
"after": null
}When an event is received, the response includes received: true, timeout: false, the full event object, and a relative next_wait_url. A timeout returns received: false, timeout: true, and after as the supplied event ID or null. An unknown after event returns HTTP 404 EVENT_NOT_FOUND.
| Status | Wait error |
|---|---|
| 400 | INVALID_CALLBACK_ID |
| 401 | MISSING_AUTHORIZATION |
| 401 | INVALID_AUTHORIZATION |
| 401 | INVALID_TOKEN |
| 404 | CALLBACK_NOT_FOUND |
| 404 | EVENT_NOT_FOUND |
| 410 | CALLBACK_EXPIRED |
| 500 | INTERNAL_ERROR |
Read All Events
GET https://api.exende.dev/v1/callbacks/{id}/events
Authentication: Authorization: Bearer ex_cb_sk_...
bash
Read events
curl \
https://api.exende.dev/v1/callbacks/cb_3t2cC09baD4yOvoeVMDK2vGQ/events \
-H "Authorization: Bearer ex_cb_sk_..."json
Events response
{
"callback_id": "cb_3t2cC09baD4yOvoeVMDK2vGQ",
"status": "active",
"expires_at": "2026-08-22T14:30:00.000Z",
"event_count": 1,
"events": [
{
"id": "evt_6ec6e7b4a917d63861a07d9238d4cf6d",
"received_at": "2026-08-22T14:05:17.000Z",
"method": "POST",
"content_type": "application/json",
"headers": {
"content-type": "application/json"
},
"body": {
"status": "completed",
"result": "https://example.com/output"
},
"size_bytes": 60
}
]
}Returns callback_id, status, expires_at, event_count, and all stored events ordered by received_at ascending. Each event includes id, received_at, method, content_type, filtered headers, body, and size_bytes. JSON bodies are returned as parsed JSON; text and form bodies remain strings. Events with the same second-level received_at timestamp preserve deterministic receipt order. Events are read-only.
| Status | Events error |
|---|---|
| 400 | INVALID_CALLBACK_ID |
| 401 | MISSING_AUTHORIZATION |
| 401 | INVALID_AUTHORIZATION |
| 401 | INVALID_TOKEN |
| 404 | CALLBACK_NOT_FOUND |
| 410 | CALLBACK_EXPIRED |
| 500 | INTERNAL_ERROR |
Delete Callback
DELETE https://api.exende.dev/v1/callbacks/{id}
Authentication: Authorization: Bearer ex_cb_sk_...
bash
Delete callback
curl -X DELETE \
https://api.exende.dev/v1/callbacks/cb_3t2cC09baD4yOvoeVMDK2vGQ \
-H "Authorization: Bearer ex_cb_sk_..."json
Delete response
{
"deleted": true,
"callback_id": "cb_3t2cC09baD4yOvoeVMDK2vGQ"
}Deletes the callback and its stored events before automatic expiration.
| Status | Delete error |
|---|---|
| 400 | INVALID_CALLBACK_ID |
| 401 | MISSING_AUTHORIZATION |
| 401 | INVALID_AUTHORIZATION |
| 401 | INVALID_TOKEN |
| 404 | CALLBACK_NOT_FOUND |
| 410 | CALLBACK_EXPIRED |
| 500 | INTERNAL_ERROR |
Expiration
Callbacks live for 10 minutes. Expired callbacks are automatically cleaned up.
Limits
| API version | 1.0.0 |
|---|---|
| Lifetime | 10 minutes |
| Maximum events | 10 |
| Maximum payload | 262144 bytes (256 KB) per event |
| Webhook methods | POST, PUT, PATCH |
| Wait timeout | 1–30 seconds (default 30) |
| Price | $0.01 USDC |
| Network | Base Mainnet |
| CAIP-2 network | eip155:8453 |
| Payment protocol | x402 v2 |
Errors
These are the standard error codes used across the Callback API. Each endpoint section above lists the subset it can return.
Authenticated routes validate the callback ID first, then the Authorization header and Bearer scheme, callback existence, read token, and expiration in that order.
A missing Authorization header returns MISSING_AUTHORIZATION. A non-Bearer header returns INVALID_AUTHORIZATION. An empty or incorrect Bearer token returns INVALID_TOKEN.
| Status | Code |
|---|---|
| 400 | INVALID_CALLBACK_ID |
| 400 | INVALID_JSON |
| 401 | MISSING_AUTHORIZATION |
| 401 | INVALID_AUTHORIZATION |
| 401 | INVALID_TOKEN |
| 404 | CALLBACK_NOT_FOUND |
| 404 | EVENT_NOT_FOUND |
| 410 | CALLBACK_EXPIRED |
| 410 | CALLBACK_EVENT_LIMIT_REACHED |
| 413 | PAYLOAD_TOO_LARGE |
| 415 | UNSUPPORTED_MEDIA_TYPE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
json
Error response shape
{
"error": {
"code": "INVALID_TOKEN",
"message": "The read token is invalid.",
"request_id": "req_81731247-7585-44a8-8716-d9dda3e0ff61"
}
}Error responses include request_id.
Typical AI Agent Workflow
An AI agent starts an asynchronous video generation job. The video service requires a webhook URL for completion. The agent creates an Exende callback for $0.01, passes callback_url to the video service, and waits using wait_url. When rendering finishes, the service sends the result to Exende and the agent continues automatically.
- asynchronous image/video generation
- long-running scraping jobs
- background processing
- payment notifications
- third-party webhook integrations
- agent workflows without a public server