Docs

API reference

Basics

URL
https://api.webhookadmin.com/v1
Authentication
Authorization: Bearer sk_live_… or sk_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_large above that)
Lists
?cursor=&limit= (limit 1–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>" }
HTTPerrorMeaning
401unauthorizedMissing, wrong, revoked or expired key
402plan_limitPlan limit reached
403forbiddenThe key lacks the scope
404not_foundNot found
409conflictConflicts with existing data
413too_largeBody larger than 1.1MB
422invalidInvalid input
429rate_limitedRate limit exceeded

Rate limits

Requests per minute, counted per environment across all keys.

PlanPer minute
Free600
Starter3,000
Pro12,000
Business60,000
Response headerMeaning
x-ratelimit-limitRequests per minute
x-ratelimit-remainingRemaining in the current minute
x-ratelimit-resetWhen the current minute ends (Unix seconds)
retry-afterOnly on 429. Seconds until you can retry

Endpoints

MethodPathScope
POST/v1/messagesmessages:send
GET/v1/messageslogs:read
GET/v1/messages/{id}logs:read
POST/v1/deliveries/{id}/retrymessages:retry
GET/v1/consumerslogs:read
POST/v1/consumersconsumers:write
GET/v1/endpointslogs:read
POST/v1/endpointsendpoints:write
GET/v1/endpoints/{id}logs:read
PATCH/v1/endpoints/{id}endpoints:write
DELETE/v1/endpoints/{id}endpoints:write
POST/v1/endpoints/{id}/rotate-secretendpoints:write

Send a message

POST /v1/messages

Scope: messages:send

InputTypeDescription
consumerstringThe consumer’s external_id (up to 200 characters)
event_typestringEvent type. Letters, digits and _ . : -, up to 100 characters
payloadJSONThe body to deliver. Up to 1MB
Idempotency-Key (header)stringOptional. 1–256 characters

Returns 202 { id, deliveries }, where deliveries is the number of deliveries created.

  • The same Idempotency-Key returns the same id for 24 hours (the first message, even if the body differs).
  • If no consumer matches consumer, one is created (with no endpoints, so deliveries is 0).
  • The x-usage-month response header holds your organization’s production messages this month (UTC).

List messages

GET /v1/messages

Scope: 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}/retry

Scope: messages:retry

id is a Delivery ID. Returns 202.

List consumers

GET /v1/consumers

Scope: logs:read

Query: cursor, limit. Returns { items: Consumer[], next_cursor }.

Create a consumer

POST /v1/consumers

Scope: consumers:write

InputTypeDescription
external_idstringYour ID for this customer (up to 200 characters)
namestringOptional. Up to 200 characters

Returns 201 Consumer. Returns 409 conflict if the external_id already exists.

List endpoints

GET /v1/endpoints

Scope: logs:read

Query: consumer_id (optional). Returns { items: Endpoint[] }.

Create an endpoint

POST /v1/endpoints

Scope: endpoints:write

InputTypeDescription
consumer_idstringThe consumer id (con_…)
urlstringEndpoint URL (up to 2,000 characters)
event_typesstring[]Optional. Event types to receive. All when omitted
fixed_ipbooleanOptional. Deliver from the static source IP (209.71.107.233)
descriptionstringOptional. 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 paused endpoint are held, and held messages from this month and last month are sent when it is set back to active.
  • An automatically disabled endpoint (disabled) resumes when you set status to active.

Delete an endpoint

DELETE /v1/endpoints/{id}

Scope: endpoints:write

Returns 204.

Rotate the signing secret

POST /v1/endpoints/{id}/rotate-secret

Scope: 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 }