Docs
API reference
Basics
- URL
https://api.webhookadmin.com/v1- Authentication
Authorization: Bearer sk_live_…orsk_test_…. A key reads and writes only its own environment- Format
- JSON. Times are Unix milliseconds; IDs are prefixed strings
- Body limit
- 1.1MB (413
too_largeabove that) - Lists
?cursor=&limit=(limit1–100, default 50). Returns{ items, next_cursor }
List cursors and message or delivery IDs are accepted only from the oldest month within your retention period to the current month (422 for an out-of-range cursor, 404 for an ID).
Errors
{ "error": "<code>", "message": "<description>" }| HTTP | error | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, wrong, revoked or expired key |
| 402 | plan_limit | Plan limit reached |
| 403 | forbidden | The key lacks the scope |
| 404 | not_found | Not found |
| 409 | conflict | Conflicts with existing data |
| 413 | too_large | Body larger than 1.1MB |
| 422 | invalid | Invalid input |
| 429 | rate_limited | Rate limit exceeded |
Rate limits
Requests per minute, counted per environment across all keys.
| Plan | Per minute |
|---|---|
| Free | 600 |
| Starter | 3,000 |
| Pro | 12,000 |
| Business | 60,000 |
| Response header | Meaning |
|---|---|
x-ratelimit-limit | Requests per minute |
x-ratelimit-remaining | Remaining in the current minute |
x-ratelimit-reset | When the current minute ends (Unix seconds) |
retry-after | Only on 429. Seconds until you can retry |
Endpoints
| Method | Path | Scope |
|---|---|---|
| POST | /v1/messages | messages:send |
| GET | /v1/messages | logs:read |
| GET | /v1/messages/{id} | logs:read |
| POST | /v1/deliveries/{id}/retry | messages:retry |
| GET | /v1/consumers | logs:read |
| POST | /v1/consumers | consumers:write |
| GET | /v1/endpoints | logs:read |
| POST | /v1/endpoints | endpoints:write |
| GET | /v1/endpoints/{id} | logs:read |
| PATCH | /v1/endpoints/{id} | endpoints:write |
| DELETE | /v1/endpoints/{id} | endpoints:write |
| POST | /v1/endpoints/{id}/rotate-secret | endpoints:write |
Send a message
POST /v1/messagesScope: messages:send
| Input | Type | Description |
|---|---|---|
consumer | string | The consumer’s external_id (up to 200 characters) |
event_type | string | Event type. Letters, digits and _ . : -, up to 100 characters |
payload | JSON | The body to deliver. Up to 1MB |
Idempotency-Key (header) | string | Optional. 1–256 characters |
Returns 202 { id, deliveries }, where deliveries is the number of deliveries created.
- The same
Idempotency-Keyreturns the sameidfor 24 hours (the first message, even if the body differs). - If no consumer matches
consumer, one is created (with no endpoints, sodeliveriesis 0). - The
x-usage-monthresponse header holds your organization’s production messages this month (UTC).
List messages
GET /v1/messagesScope: logs:read
Query: status (pending / retrying / success / failed), cursor, limit.
Returns { items: MessageRow[], next_cursor }.
Get a message
GET /v1/messages/{id}Scope: logs:read
Returns MessageDetail (the payload, and deliveries and attempts per endpoint).
Retry a delivery
POST /v1/deliveries/{id}/retryScope: messages:retry
id is a Delivery ID. Returns 202.
List consumers
GET /v1/consumersScope: logs:read
Query: cursor, limit. Returns { items: Consumer[], next_cursor }.
Create a consumer
POST /v1/consumersScope: consumers:write
| Input | Type | Description |
|---|---|---|
external_id | string | Your ID for this customer (up to 200 characters) |
name | string | Optional. Up to 200 characters |
Returns 201 Consumer. Returns 409 conflict if the external_id already exists.
List endpoints
GET /v1/endpointsScope: logs:read
Query: consumer_id (optional). Returns { items: Endpoint[] }.
Create an endpoint
POST /v1/endpointsScope: endpoints:write
| Input | Type | Description |
|---|---|---|
consumer_id | string | The consumer id (con_…) |
url | string | Endpoint URL (up to 2,000 characters) |
event_types | string[] | Optional. Event types to receive. All when omitted |
fixed_ip | boolean | Optional. Deliver from the static source IP (209.71.107.233) |
description | string | Optional. Up to 200 characters |
Returns 201 Endpoint with secret (the signing secret, returned only here).
- URLs use https on port 443 or 8443.
- Raw IP addresses, internal host names, names that resolve to private addresses and names that do not resolve return 422.
Get an endpoint
GET /v1/endpoints/{id}Scope: logs:read
Returns Endpoint with recent: AttemptRow[] and retrying_count.
Update an endpoint
PATCH /v1/endpoints/{id}Scope: endpoints:write
Input (all optional): url, event_types, status (active / paused), description. Returns Endpoint.
- Messages to a
pausedendpoint are held, and held messages from this month and last month are sent when it is set back toactive. - An automatically disabled endpoint (
disabled) resumes when you setstatustoactive.
Delete an endpoint
DELETE /v1/endpoints/{id}Scope: endpoints:write
Returns 204.
Rotate the signing secret
POST /v1/endpoints/{id}/rotate-secretScope: endpoints:write
Returns { secret }. Deliveries carry a signature with the old secret as well for 24 hours.
Types
MessageRow = { id, event_type, consumer: { id, external_id, name }, created_at,
status: 'success' | 'retrying' | 'failed' | 'pending', attempts, endpoint_url }
MessageDetail = { id, event_type, consumer, created_at, idempotency_key, payload, deliveries: Delivery[] }
Delivery = { id, endpoint_id, url, status: 'pending' | 'success' | 'retrying' | 'failed',
attempt_count, next_attempt_at, attempts: AttemptRow[] }
AttemptRow = { id, at, response_status, response_head, duration_ms, error,
via: 'direct' | 'relay', source_ip }
Consumer = { id, external_id, name, created_at, endpoint_count }
Endpoint = { id, consumer_id, url, description, event_types: string[] | null, fixed_ip,
status: 'active' | 'paused' | 'disabled', disabled_reason, failing_since, created_at }