Everything the dashboard does is available over HTTP. The base URL is
https://app.webhooker.eu, all paths are under /api/v1, and every request and
response is JSON.
An OpenAPI description of the authenticated surface is served at
/api/openapi.json.
Authentication
Section titled “Authentication”Send an API key as a bearer token:
curl https://app.webhooker.eu/api/v1/sources/ \ -H "Authorization: Bearer $WEBHOOKER_API_KEY"A key is bound to one workspace, so no workspace parameter is needed. Keys act with member permissions: they cannot manage members, roles, plans or other keys.
Requests are metered per workspace per minute by plan. Over budget returns 429
with Retry-After.
Conventions
Section titled “Conventions”- Ids are UUIDs.
- Timestamps are RFC 3339 in UTC.
- Lists return
{"items": [...]}, and paginated lists add"total". Paginated endpoints takepage(1-based) andlimit(default 50, max 200). - PATCH is a partial update: an omitted field is left unchanged. Where
clearing is meaningful, an explicit
nullclears the field. - Secrets come back masked as
***. Submitting the mask is rejected — send the real value to change it, or omit the field to keep it. - Errors use
{"error": {"code": "...", "message": "..."}}. See error codes.
Identity
Section titled “Identity”GET /api/v1/me
Section titled “GET /api/v1/me”The authenticated principal and its active workspace. The cheapest way to check a key.
{ "auth": "api_key", "workspace": { "id": "…", "name": "Acme", "plan": "pro" }}GET /api/v1/plans
Section titled “GET /api/v1/plans”The public plan catalogue with prices, limits and features. No authentication required.
GET /api/v1/status
Section titled “GET /api/v1/status”Service health. No authentication required.
Sources
Section titled “Sources”POST /api/v1/sources/
Section titled “POST /api/v1/sources/”{ "name": "stripe-prod", "color": "#5E8BFF" }name is required; color is optional and assigned from the palette when
omitted. Returns 201:
{ "id": "0190a0b1-c2d3-7e4f-8a9b-0c1d2e3f4a5b", "name": "stripe-prod", "description": null, "color": "#5E8BFF", "token": "6b225n04u5kmyg", "ingest_path": "/in/6b225n04u5kmyg", "verification_config": { "provider": "none" }, "response_config": null, "status": "active", "created_at": "2026-09-19T10:31:00Z"}The ingest URL is the API host plus ingest_path.
GET /api/v1/sources/
Section titled “GET /api/v1/sources/”Paginated. q filters by name. Each item adds event_count.
GET /api/v1/sources/{id}
Section titled “GET /api/v1/sources/{id}”One source.
PATCH /api/v1/sources/{id}
Section titled “PATCH /api/v1/sources/{id}”Any of name, description, color, status (active or paused),
verification_config, response_config.
{ "verification_config": { "provider": "stripe", "secret": "whsec_…" }}Verification variants:
provider | Fields |
|---|---|
none | — |
stripe, github, shopify | secret |
generic_hmac | secret, header, algorithm (sha256/sha1), encoding (hex/base64), optional timestamp_header, optional tolerance_secs (default 300) |
basic_auth | username, password |
api_key | header, key |
response_config takes { "status": 200, "content_type": "json" \| "text", "body": "…" },
where the body may contain {{request_id}}. Send null to restore the default
response. Status must be 100–599.
DELETE /api/v1/sources/{id}
Section titled “DELETE /api/v1/sources/{id}”Moves the source to the trash. 204.
GET /api/v1/sources/trash · POST /api/v1/sources/{id}/restore
Section titled “GET /api/v1/sources/trash · POST /api/v1/sources/{id}/restore”List trashed sources (paginated, q supported) and restore one. Trashed sources
are purged after 7 days. Restoring re-checks your plan’s source cap.
Destinations
Section titled “Destinations”POST /api/v1/destinations/
Section titled “POST /api/v1/destinations/”{ "name": "billing-api", "url": "https://api.acme.dev/hooks/webhooker", "timeout_ms": 10000, "auth_config": { "type": "hmac", "secret": "a-long-random-string" }, "custom_headers": { "X-Environment": "production" }, "retry_policy": { "intervals_seconds": [30, 120, 600, 3600, 14400] }}Only name and url are required. Returns 201 with the destination,
auth_config masked, plus status, circuit_state and circuit_reopen_at.
| Field | Rules |
|---|---|
url | Absolute http/https, public host. Private and internal addresses are rejected. |
timeout_ms | 1–300000. Default 10000. |
auth_config | {"type":"none"}, {"type":"hmac","secret":…}, {"type":"api_key","header":…,"key":…}, {"type":"basic_auth","username":…,"password":…} |
custom_headers | Flat string map, sent verbatim. |
retry_policy | 1–10 intervals, each 1–86400 seconds. Default [30,120,600,3600,14400]. |
GET /api/v1/destinations/ · GET /api/v1/destinations/{id}
Section titled “GET /api/v1/destinations/ · GET /api/v1/destinations/{id}”List or read.
PATCH /api/v1/destinations/{id}
Section titled “PATCH /api/v1/destinations/{id}”Any of name, url, timeout_ms, auth_config, custom_headers,
retry_policy (null restores the default), status (active or paused).
A present auth_config replaces the whole config — send {"type":"none"} to
turn signing off.
DELETE /api/v1/destinations/{id}
Section titled “DELETE /api/v1/destinations/{id}”204. Its connections stop receiving events.
Connections
Section titled “Connections”A connection is the source-to-destination half of a gateway.
POST /api/v1/connections/
Section titled “POST /api/v1/connections/”{ "source_id": "…", "destination_id": "…", "filter_rules": { "operator": "and", "rules": [{ "path": "body.type", "op": "eq", "value": "invoice.paid" }] }, "transformation": { "set_headers": [{ "name": "X-Trace-Id", "value": "{{event.id}}" }], "rename_headers": [{ "from": "Content-Type", "to": "X-Original-Content-Type" }], "remove_headers": ["x-environment"], "merge_body_fields": { "received_via": "webhooker" } }}Both ids must belong to your workspace. A source and a destination can only be
connected once — a duplicate returns 409. Filter and transformation are
optional; syntax is in
filters and transformations.
GET /api/v1/connections/ · GET /api/v1/connections/{id}
Section titled “GET /api/v1/connections/ · GET /api/v1/connections/{id}”List or read.
GET /api/v1/sources/{id}/connections
Section titled “GET /api/v1/sources/{id}/connections”The connections of one source, paginated, each with a destination summary
including its circuit_state. q filters by destination name.
PATCH /api/v1/connections/{id}
Section titled “PATCH /api/v1/connections/{id}”enabled, filter_rules, transformation. An explicit null clears a filter
or a transformation.
DELETE /api/v1/connections/{id}
Section titled “DELETE /api/v1/connections/{id}”204. The destination itself is kept.
Events
Section titled “Events”GET /api/v1/events/
Section titled “GET /api/v1/events/”Paginated, newest first.
| Parameter | Meaning |
|---|---|
source_id | One source. |
verification_status | verified, failed or skipped. |
received_after, received_before | RFC 3339 bounds. |
q | Substring match on the event’s public id. |
page, limit | Pagination. |
Each item carries method, content_type, verification_status, body_size,
received_at and the delivery counters delivery_count, delivered_count,
failed_count, pending_count.
GET /api/v1/events/{id}
Section titled “GET /api/v1/events/{id}”The full event: headers, body (plus body_base64 when the payload is not
valid UTF-8), body_size, received_at, expires_at, and deliveries — each
with its status, attempt_count, next_attempt_at and an attempts array of
attempt_number, request_url, response_status, response_body,
error_message, latency_ms, attempted_at.
Replay
Section titled “Replay”POST /api/v1/events/{id}/resend
Section titled “POST /api/v1/events/{id}/resend”{ "connection_ids": ["…"] }Omit the body to resend to every connection of the event’s source, disabled ones
included. Returns 202 with {"created": n}.
POST /api/v1/deliveries/resend-bulk
Section titled “POST /api/v1/deliveries/resend-bulk”{ "connection_id": "…", "statuses": ["exhausted", "failed"], "since": "2026-09-18T00:00:00Z", "until": "2026-09-19T00:00:00Z"}statuses defaults to ["exhausted"]; since and until are optional.
Returns 202 with {"created": n}.
Dead letters
Section titled “Dead letters”GET /api/v1/sources/{id}/dlq
Section titled “GET /api/v1/sources/{id}/dlq”Paginated exhausted and failed deliveries with destination_name,
attempt_count, last_error, last_response_status and timestamps.
| Parameter | Meaning |
|---|---|
status | Comma-separated terminal statuses. Defaults to exhausted,failed. |
received_after, received_before | RFC 3339 bounds on the event’s arrival time. |
page, limit | Pagination. |
GET /api/v1/sources/{id}/dlq/summary
Section titled “GET /api/v1/sources/{id}/dlq/summary”Per-connection counts of exhausted and failed.
Live streams
Section titled “Live streams”Both endpoints are Server-Sent Events, capped per workspace by plan.
GET /api/v1/sources/{id}/tail
Section titled “GET /api/v1/sources/{id}/tail”Metadata per event: ids, method, received_at, content_type, body_size,
verification_status. Cheap enough to leave open on a dashboard.
GET /api/v1/sources/{id}/stream
Section titled “GET /api/v1/sources/{id}/stream”The full payload per event, including headers and body — what the CLI uses
to replay webhooks against localhost.
curl -N https://app.webhooker.eu/api/v1/sources/$SOURCE_ID/stream \ -H "Authorization: Bearer $WEBHOOKER_API_KEY"Alert channels
Section titled “Alert channels”POST /api/v1/alert-configs/
Section titled “POST /api/v1/alert-configs/”{ "channel": "email", "config": { "to": "ops@acme.dev" }, "source_id": null }channel is email (config requires to) or telegram (requires token and
chat_id). source_id scopes the channel to one source; omit or null for
workspace wide. Returns 201.
GET /api/v1/alert-configs/ · PATCH /api/v1/alert-configs/{id} · DELETE /api/v1/alert-configs/{id}
Section titled “GET /api/v1/alert-configs/ · PATCH /api/v1/alert-configs/{id} · DELETE /api/v1/alert-configs/{id}”List, update (config, enabled) and delete.
GET /api/v1/stats/overview— dashboard metrics for a window. Optionalsource_ids(comma-separated),received_after,received_before. Histogram buckets are hourly up to 48 hours, daily up to 90 days, monthly beyond.GET /api/v1/stats/volume-by-source— event volume per source.GET /api/v1/stats/by-source— per-source delivery breakdown.
API keys and workspaces
Section titled “API keys and workspaces”These endpoints exist, but they are governed by role rather than by the resource rules above, and the ones that change anything are closed to API keys.
Readable with a key:
GET /api/v1/api-keys/— the workspace’s keys by name, prefix, creation date and last use. No secret material is ever returned.GET /api/v1/workspaces— the workspaces the key’s owner belongs to.GET /{id}/members·GET /{id}/invites— admin or owner, evaluated against the key owner’s role in that workspace.
Session only, and closed to keys:
POST /api/v1/api-keys/,DELETE /api/v1/api-keys/{id}— admin or owner. A key cannot mint or revoke keys, including its own.PATCH|DELETE /api/v1/workspaces/{id},POST /{id}/transfer,/{id}/switch,/{id}/leave,PATCH|DELETE /{id}/members/{user_id},POST /{id}/invites,DELETE /{id}/invites/{invite_id},POST /api/v1/invites/accept.
Renaming a workspace needs admin; changing its plan, transferring it or deleting it needs owner. See workspaces and team for the full matrix.