Webhooker API
Complete reference for the Webhooker REST API. Base URL: https://webhooker.erk.im
Quick Start
Get webhooks flowing in three steps.
Create a Source
A source gives you a unique ingest URL. Point your webhook provider (Stripe, GitHub, Shopify, etc.) at this URL.
curl -X POST https://webhooker.erk.im/api/sources \
-H "Authorization: Bearer wh_live_..." \
-H "Content-Type: application/json" \
-d '{"name": "stripe-prod"}'
# Response
{
"id": "src_01abc123",
"name": "stripe-prod",
"slug": "stripe-prod-a1b2c3",
"ingest_url": "https://ingest.webhooker.erk.im/stripe-prod-a1b2c3",
"created_at": "2026-05-02T00:00:00Z"
}Create a Destination
A destination is any HTTPS endpoint that should receive the forwarded webhook.
curl -X POST https://webhooker.erk.im/api/destinations \
-H "Authorization: Bearer wh_live_..." \
-H "Content-Type: application/json" \
-d '{"name": "my-api", "url": "https://api.yourapp.com/webhooks"}'
# Response
{
"id": "dst_01xyz456",
"name": "my-api",
"url": "https://api.yourapp.com/webhooks",
"created_at": "2026-05-02T00:00:00Z"
}Create a Connection
A connection links a source to a destination. You can add filters and transformations here too.
curl -X POST https://webhooker.erk.im/api/connections \
-H "Authorization: Bearer wh_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "stripe-to-my-api",
"source_id": "src_01abc123",
"destination_id": "dst_01xyz456"
}'
# Webhooks sent to ingest.webhooker.erk.im/stripe-prod-a1b2c3
# are now delivered to api.yourapp.com/webhooksAuthentication
All API requests require a Bearer token in the Authorization header.
Authorization: Bearer wh_live_xxxxxxxxxxxxxxxxxxxxWhere to find your key: Dashboard Settings API Keys. Keys are prefixed with wh_live_ for production and wh_test_ for test mode.
Core Concepts
Sources
A unique ingest URL for an inbound webhook provider. One source per external service (e.g. one for Stripe, one for GitHub).
Destinations
Your HTTPS endpoints that should receive the forwarded webhook payloads.
Connections
A route linking a source to a destination. Each connection can have independent filters, transformations, and retry rules.
Events
One delivery unit per connection per inbound request. An event tracks the full lifecycle from receipt to successful delivery.
Requests
The raw inbound HTTP request captured at the source. One request may fan out into multiple events across connections.
Attempts
Each time Webhooker tries to deliver an event to a destination. Failed attempts trigger automatic retry logic.
Sources
Sources capture inbound webhooks from external providers. Each source gets a unique ingest URL at https://ingest.webhooker.erk.im/{source_slug}.
/api/sourcesList all sources for the authenticated account.
Response
{
"data": [
{
"id": "src_01abc123",
"name": "stripe-prod",
"slug": "stripe-prod-a1b2c3",
"ingest_url": "https://ingest.webhooker.erk.im/stripe-prod-a1b2c3",
"created_at": "2026-05-02T00:00:00Z"
}
],
"total": 1
}/api/sourcesCreate a new source.
Request
{
"name": "github-webhooks"
}Response
{
"id": "src_01def456",
"name": "github-webhooks",
"slug": "github-webhooks-d4e5f6",
"ingest_url": "https://ingest.webhooker.erk.im/github-webhooks-d4e5f6",
"created_at": "2026-05-02T00:00:00Z"
}/api/sources/:idRetrieve a single source by ID.
/api/sources/:idUpdate a source name.
Request
{ "name": "stripe-prod-v2" }/api/sources/:idDelete a source. All associated connections will also be deleted.
Destinations
Destinations are the HTTPS endpoints that receive forwarded webhook payloads.
/api/destinationsList all destinations.
Response
{
"data": [
{
"id": "dst_01xyz456",
"name": "my-api",
"url": "https://api.yourapp.com/webhooks",
"created_at": "2026-05-02T00:00:00Z"
}
],
"total": 1
}/api/destinationsCreate a new destination.
Request
{
"name": "production-api",
"url": "https://api.yourapp.com/webhooks"
}/api/destinations/:idRetrieve a single destination by ID.
/api/destinations/:idUpdate a destination URL or name.
Request
{
"name": "production-api-v2",
"url": "https://api.yourapp.com/v2/webhooks"
}/api/destinations/:idDelete a destination. Connections referencing this destination will also be removed.
Connections
Connections route webhooks from a source to a destination. Each connection can independently configure filters, transformations, and retry behavior.
/api/connectionsList all connections. Optionally filter with ?source_id= or ?destination_id=.
Response
{
"data": [
{
"id": "con_01ghi789",
"name": "stripe-to-my-api",
"source_id": "src_01abc123",
"destination_id": "dst_01xyz456",
"enabled": true,
"filter_rules": null,
"transformation": null,
"retry_strategy": "exponential",
"created_at": "2026-05-02T00:00:00Z"
}
],
"total": 1
}/api/connectionsCreate a new connection between a source and destination.
Request
{
"name": "stripe-to-my-api",
"source_id": "src_01abc123",
"destination_id": "dst_01xyz456",
"retry_strategy": "exponential"
}/api/connections/:idRetrieve a single connection by ID.
/api/connections/:idReplace a connection's full configuration.
Request
{
"name": "stripe-to-my-api",
"source_id": "src_01abc123",
"destination_id": "dst_01xyz456",
"retry_strategy": "linear",
"filter_rules": { "body.type": { "$eq": "payment.completed" } }
}/api/connections/:idPartially update a connection (e.g. toggle enabled, update filter only).
Request
{ "enabled": false }/api/connections/:idDelete a connection. Existing events for this connection are retained.
Events
Events represent a single delivery of a webhook through a connection. Query events to inspect delivery status or replay failures.
/api/eventsList events. Supports ?connection_id=, ?status=, ?limit=, ?cursor= for pagination.
Response
{
"data": [
{
"id": "evt_01jkl012",
"connection_id": "con_01ghi789",
"status": "delivered",
"attempts": 1,
"created_at": "2026-05-02T00:00:00Z",
"delivered_at": "2026-05-02T00:00:01Z"
}
],
"next_cursor": null
}/api/events/:idRetrieve a single event with full payload and all attempt history.
Response
{
"id": "evt_01jkl012",
"connection_id": "con_01ghi789",
"request_id": "req_01mno345",
"status": "delivered",
"payload": { "event": "payment.completed", "data": { "amount": 4999 } },
"attempts": [
{
"id": "att_01pqr678",
"status": "success",
"response_status": 200,
"duration_ms": 23,
"attempted_at": "2026-05-02T00:00:01Z"
}
]
}/api/events/:id/retryManually trigger a retry for a failed or held event. Returns the new attempt object.
Bookmarks
Bookmarks save a specific webhook payload so you can replay it repeatedly during development or testing without triggering the real provider.
/api/bookmarksList all bookmarks for the account.
Response
{
"data": [
{
"id": "bkm_01stu901",
"name": "stripe-payment-completed",
"event_id": "evt_01jkl012",
"connection_id": "con_01ghi789",
"created_at": "2026-05-02T00:00:00Z"
}
]
}/api/bookmarksCreate a bookmark from an existing event.
Request
{
"name": "stripe-payment-completed",
"event_id": "evt_01jkl012"
}API Keys
Manage programmatic access keys for the account.
/api/api-keysCreate a new API key. The secret value is only returned once at creation time.
Request
{ "name": "ci-deployment" }Response
{
"id": "key_01vwx234",
"name": "ci-deployment",
"key": "wh_live_xxxxxxxxxxxxxxxxxxxx",
"created_at": "2026-05-02T00:00:00Z"
}/api/api-keys/:idRevoke and delete an API key immediately.
Usage
Retrieve event and request counts for billing and monitoring.
/api/usageGet usage metrics for the current billing period.
Response
{
"period_start": "2026-05-01T00:00:00Z",
"period_end": "2026-05-31T23:59:59Z",
"events_total": 8423,
"events_delivered": 8401,
"events_failed": 22,
"requests_total": 8423
}Webhook Payload
When Webhooker delivers an event to your destination, the request body is the original (or transformed) payload. The following headers are added on every delivery:
| Header | Description |
|---|---|
| x-webhooker-event-id | Unique ID of the event (evt_...) |
| x-webhooker-request-id | ID of the originating ingest request (req_...) |
| x-webhooker-attempt-count | Number of delivery attempts so far (starts at 1) |
| x-webhooker-signature | HMAC-SHA256 of the raw body signed with your connection secret |
| x-webhooker-timestamp | Unix timestamp (seconds) of this delivery attempt |
POST https://api.yourapp.com/webhooks
Content-Type: application/json
x-webhooker-event-id: evt_01jkl012
x-webhooker-request-id: req_01mno345
x-webhooker-attempt-count: 1
x-webhooker-signature: sha256=abc123def456...
x-webhooker-timestamp: 1746144000
{
"event": "payment.completed",
"data": { "amount": 4999, "currency": "usd" }
}Signature verification: Compute HMAC-SHA256 over the raw request body using your connection secret. Compare to the value in x-webhooker-signature (strip the sha256= prefix before comparing).
Filters
Filter rules let you selectively route events through a connection. Rules are evaluated against the inbound request using dot-notation paths (body.*, headers.*, query.*).
| Operator | Description | Example |
|---|---|---|
| $eq | Exact equality | { "$eq": "payment.completed" } |
| $ne | Not equal | { "$ne": "payment.refunded" } |
| $contains | String contains substring | { "$contains": "payment" } |
| $exists | Field presence check | { "$exists": true } |
| $gt | Greater than (numeric) | { "$gt": 100 } |
| $lt | Less than (numeric) | { "$lt": 1000 } |
| $in | Value is in array | { "$in": ["usd", "eur"] } |
{
"body.event": { "$eq": "payment.completed" },
"body.data.currency": { "$eq": "usd" },
"body.data.amount": { "$gt": 1000 }
}Transformations
Transformations reshape the payload before delivery. Each mapping copies or renames a field using dot-notation paths. The from path is resolved against the incoming payload; to sets the output field.
{
"mappings": [
{ "from": "body.data.amount", "to": "amount" },
{ "from": "body.data.currency", "to": "currency" },
{ "from": "body.event", "to": "type" }
]
}
// Input payload:
// { "event": "payment.completed", "data": { "amount": 4999, "currency": "usd" } }
// Transformed payload delivered to destination:
// { "amount": 4999, "currency": "usd", "type": "payment.completed" }Set a connection's transformation field when creating or updating the connection. Set to null to pass the payload through unchanged.
Retries
Webhooker automatically retries failed deliveries. A delivery is considered failed when the destination returns a non-2xx status code or times out (>30 s). Choose a strategy per connection.
Linear backoff
Waits a fixed interval between each attempt. Use for destinations where consistent spacing matters.
retry_strategy: "linear"
// Attempts at: +1 m, +2 m, +3 m ... (up to 50)Exponential backoff
Doubles the wait time after each failure with jitter, reducing thundering herd during outages.
retry_strategy: "exponential"
// Attempts at: +30 s, +1 m, +2 m, +4 m ... (up to 50)Set retry_strategy: "none" to disable retries. Events that exhaust all attempts move to status failed. You can always trigger a manual retry via POST /api/events/:id/retry.
Reseller API
Reseller accounts can provision isolated sub-accounts and inspect aggregate usage. Each sub-account is fully isolated with its own sources, destinations, and API keys. Requires a reseller-tier plan.
/api/reseller/sub-accountsList all sub-accounts owned by the reseller.
Response
{
"data": [
{
"id": "sub_01yz5678",
"name": "customer-acme",
"plan": "starter",
"created_at": "2026-05-02T00:00:00Z"
}
]
}/api/reseller/sub-accountsProvision a new sub-account. Returns an API key scoped to that sub-account.
Request
{
"name": "customer-acme",
"plan": "starter"
}Response
{
"id": "sub_01yz5678",
"name": "customer-acme",
"plan": "starter",
"api_key": "wh_live_xxxxxxxxxxxxxxxxx",
"created_at": "2026-05-02T00:00:00Z"
}/api/reseller/sub-accounts/:idUpdate a sub-account's name or plan.
Request
{ "plan": "team" }/api/reseller/sub-accounts/:idDelete a sub-account and all its resources. This action is irreversible.
/api/reseller/usageAggregate usage metrics across all sub-accounts for the current billing period.
Response
{
"period_start": "2026-05-01T00:00:00Z",
"period_end": "2026-05-31T23:59:59Z",
"sub_accounts": 4,
"events_total": 142500,
"events_delivered": 141983,
"events_failed": 517
}MCP Server
Webhooker ships an official Model Context Protocol (MCP) server so AI agents (Claude, Cursor, Copilot, etc.) can manage your webhook infrastructure directly.
Installation
Run the MCP server in a single command — no installation required:
WEBHOOKER_API_KEY=wh_live_... npx @webhooker/mcpClaude Desktop config
Add to your claude_desktop_config.json:
{
"mcpServers": {
"webhooker": {
"command": "npx",
"args": ["@webhooker/mcp"],
"env": {
"WEBHOOKER_API_KEY": "wh_live_..."
}
}
}
}Available tools
The MCP server exposes all major API operations as callable tools:
list_sourcesget_sourcecreate_sourceupdate_sourcedelete_sourcelist_destinationsget_destinationcreate_destinationupdate_destinationdelete_destinationlist_connectionscreate_connectionupdate_connectiondelete_connectionlist_eventsget_eventretry_eventget_usagelist_sub_accountscreate_sub_accountdelete_sub_accountget_reseller_usageError Codes
All errors follow RFC 7807 with a JSON body containing error and message fields.
| Status | Meaning | Common cause |
|---|---|---|
| 400 | Bad Request | Invalid or missing request body fields |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Key lacks permission for the requested resource |
| 404 | Not Found | Resource ID does not exist or belongs to another account |
| 409 | Conflict | Duplicate name or slug; idempotency key conflict |
| 500 | Internal Server Error | Unexpected error — contact support with the request ID |
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"error": "not_found",
"message": "Source src_01abc123 does not exist"
}Ready to integrate?
Create your first source in under 2 minutes. No credit card required.