Skip to content

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.

Send an API key as a bearer token:

Terminal window
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.

  • Ids are UUIDs.
  • Timestamps are RFC 3339 in UTC.
  • Lists return {"items": [...]}, and paginated lists add "total". Paginated endpoints take page (1-based) and limit (default 50, max 200).
  • PATCH is a partial update: an omitted field is left unchanged. Where clearing is meaningful, an explicit null clears 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.

The authenticated principal and its active workspace. The cheapest way to check a key.

{
"auth": "api_key",
"workspace": { "id": "", "name": "Acme", "plan": "pro" }
}

The public plan catalogue with prices, limits and features. No authentication required.

Service health. No authentication required.

{ "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.

Paginated. q filters by name. Each item adds event_count.

One source.

Any of name, description, color, status (active or paused), verification_config, response_config.

{
"verification_config": { "provider": "stripe", "secret": "whsec_…" }
}

Verification variants:

providerFields
none
stripe, github, shopifysecret
generic_hmacsecret, header, algorithm (sha256/sha1), encoding (hex/base64), optional timestamp_header, optional tolerance_secs (default 300)
basic_authusername, password
api_keyheader, 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.

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.

{
"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.

FieldRules
urlAbsolute http/https, public host. Private and internal addresses are rejected.
timeout_ms1–300000. Default 10000.
auth_config{"type":"none"}, {"type":"hmac","secret":…}, {"type":"api_key","header":…,"key":…}, {"type":"basic_auth","username":…,"password":…}
custom_headersFlat string map, sent verbatim.
retry_policy1–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.

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.

204. Its connections stop receiving events.

A connection is the source-to-destination half of a gateway.

{
"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.

The connections of one source, paginated, each with a destination summary including its circuit_state. q filters by destination name.

enabled, filter_rules, transformation. An explicit null clears a filter or a transformation.

204. The destination itself is kept.

Paginated, newest first.

ParameterMeaning
source_idOne source.
verification_statusverified, failed or skipped.
received_after, received_beforeRFC 3339 bounds.
qSubstring match on the event’s public id.
page, limitPagination.

Each item carries method, content_type, verification_status, body_size, received_at and the delivery counters delivery_count, delivered_count, failed_count, pending_count.

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.

{ "connection_ids": [""] }

Omit the body to resend to every connection of the event’s source, disabled ones included. Returns 202 with {"created": n}.

{
"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}.

Paginated exhausted and failed deliveries with destination_name, attempt_count, last_error, last_response_status and timestamps.

ParameterMeaning
statusComma-separated terminal statuses. Defaults to exhausted,failed.
received_after, received_beforeRFC 3339 bounds on the event’s arrival time.
page, limitPagination.

Per-connection counts of exhausted and failed.

Both endpoints are Server-Sent Events, capped per workspace by plan.

Metadata per event: ids, method, received_at, content_type, body_size, verification_status. Cheap enough to leave open on a dashboard.

The full payload per event, including headers and body — what the CLI uses to replay webhooks against localhost.

Terminal window
curl -N https://app.webhooker.eu/api/v1/sources/$SOURCE_ID/stream \
-H "Authorization: Bearer $WEBHOOKER_API_KEY"
{ "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. Optional source_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.

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.