API Reference
CatchHook provides a REST API for programmatic access to endpoints, ingress events, and tunnel connections.
Base URL: https://catchhook.app/api/v1
Authentication
All API requests require a Bearer token in the Authorization header:
Authorization: Bearer <your-api-token>
Create API tokens in Account Settings. Tokens have two dimensions of access control:
Token level
| Level | Access | Created from |
|---|---|---|
| Account | All workspaces and resources in the account | Account Settings |
| Workspace | Only the specific workspace's resources | Workspace management page |
CLI tokens (created during catchhook login) are always account-scoped.
Permission scopes
| Scope | Access |
|---|---|
read |
List and view endpoints, events, forwarding targets, and activity |
write |
Create, update, delete resources; replay events |
tunnel |
Connect to tunnel WebSocket and report deliveries |
Endpoints
Verify authentication
GET /api/v1/auth/verify
Returns the current user and account information. Useful for verifying your token is valid.
Response:
{
"user": { "id": 1, "email": "you@example.com" },
"account": { "id": 1, "name": "Acme Corp" }
}
List endpoints
GET /api/v1/endpoints
Returns all endpoints accessible to the authenticated user, including both webhook and email endpoints.
Response:
{
"data": [
{
"id": "ep_abc123",
"name": "GitHub Webhooks",
"kind": "webhook",
"custom_id": null,
"provider": "github",
"provider_config": {},
"webhook_url": "https://listen.catchhook.app/hooks/ep_abc123",
"email_address": null,
"tunnel_active": false,
"created_at": "2026-05-01T12:00:00Z",
"updated_at": "2026-05-01T12:00:00Z"
},
{
"id": "ep_def456",
"name": "Transactional Emails",
"kind": "email",
"custom_id": null,
"provider": null,
"provider_config": {},
"webhook_url": null,
"email_address": "billing@in.catchhook.app",
"tunnel_active": false,
"created_at": "2026-05-01T12:00:00Z",
"updated_at": "2026-05-01T12:00:00Z"
}
]
}
Create an endpoint
POST /api/v1/endpoints
Body (webhook endpoint):
{
"endpoint": {
"name": "My Endpoint",
"kind": "webhook",
"provider": "github"
}
}
Body (email endpoint):
{
"endpoint": {
"name": "Transactional Emails",
"kind": "email",
"email_local_part": "billing"
}
}
| Parameter | Description |
|---|---|
name |
Human-readable label (required) |
kind |
Endpoint type: webhook (default) or email |
provider |
Webhook only. One of github, stripe, shopify, or twilio. Stores the intended webhook source for this endpoint. Only CLI preset providers (github, stripe) enable the CLI --provider signature config workflow on free plans; shopify and twilio are detection-only. |
email_local_part |
Email only. The local part of the generated email address (before the @). Must be lowercase alphanumeric with dots, hyphens, or underscores (2–50 characters). If omitted, a random local part is generated. |
Provider detection of incoming webhooks happens automatically at ingest for all webhook endpoints, regardless of whether provider is set. Setting provider stores your intended webhook source. For CLI preset providers (github, stripe), it also enables the --provider workflow for automatic signature config creation.
For email endpoints, provider is not applicable — email provider detection happens automatically based on email headers.
Get an endpoint
GET /api/v1/endpoints/:id
Returns details for a single endpoint. The response includes kind, webhook_url (webhook endpoints), and email_address (email endpoints).
List events for an endpoint
GET /api/v1/endpoints/:endpoint_id/requests
Returns ingress events (both webhook requests and email events) received by the specified endpoint. Events are returned in reverse chronological order.
Query parameters:
| Parameter | Description | Default |
|---|---|---|
limit |
Number of events (1–100) | 25 |
offset |
Number of events to skip | 0 |
Response:
{
"data": [
{
"id": "req_abc123",
"type": "webhook",
"method": "POST",
"path": "/hooks/checkout",
"status": 200,
"content_type": "application/json",
"ip_address": "1.2.3.4",
"size": 1024,
"requested_at": "2026-05-01T12:00:00Z",
"detected_provider": "stripe",
"provider_event_data": { "event_type": "invoice.paid" }
},
{
"id": "eml_def456",
"type": "email",
"method": "EMAIL",
"path": "billing@in.catchhook.app",
"status": null,
"content_type": null,
"ip_address": null,
"size": 4096,
"requested_at": "2026-05-01T11:30:00Z",
"from": "sender@example.com",
"to": "billing@in.catchhook.app",
"subject": "Order Confirmation",
"detected_provider": "sendgrid",
"authentication": { "spf": "PASS", "dkim": "PASS", "dmarc": "PASS" }
}
],
"meta": {
"total": 42,
"limit": 25,
"offset": 0
}
}
Get a single event
GET /api/v1/endpoints/:endpoint_id/requests/:id
Returns full details for a single event, including the complete body. For email events, the full response includes text_body, html_body, email_headers, attachment_metadata, and authentication_results.
Delete an event
DELETE /api/v1/endpoints/:endpoint_id/requests/:id
Scope required: write
Permanently deletes a single ingress event.
Delete events in bulk
DELETE /api/v1/endpoints/:endpoint_id/requests/destroy_all
Scope required: write
Deletes all events for the endpoint. Optional query parameters:
| Parameter | Description |
|---|---|
before |
Delete events received before this ISO 8601 timestamp |
after |
Delete events received after this ISO 8601 timestamp |
Replay an event
POST /api/v1/endpoints/:endpoint_id/requests/:id/replay
Scope required: write
Re-sends an event to a target URL.
For webhook events, the API currently supports exact replay only — the original method, headers, and body are forwarded as-is with optional identity and signature handling. Request modification parameters (body editing, header/query overrides) are available in the web UI and will be added to the API in a future release.
For email events, the replay sends the normalized JSON payload as a POST request to the target URL.
Body:
{
"target_url": "https://your-app.com/webhooks",
"identity_mode": "original",
"signature_mode": "strip"
}
| Parameter | Options | Default | Description |
|---|---|---|---|
target_url |
Any valid URL | (required) | Destination for the replayed event |
identity_mode |
original, regenerate |
original |
regenerate appends unique suffixes to ID fields (JSON bodies only, requires payment method). Webhook events only. |
signature_mode |
strip, resign |
strip |
resign computes a fresh signature using the endpoint's signing secret (requires payment method and a supported signature config). Webhook events only. |
CatchHook adds replay metadata headers to every outbound request: X-Catchhook-Replay, X-Catchhook-Original-Request, and X-Catchhook-Replay-Time.
Delete an endpoint
DELETE /api/v1/endpoints/:id
Scope required: write
Permanently deletes an endpoint and all its events.
Signature Configs
Create a signature config
POST /api/v1/endpoints/:endpoint_id/signature_configs
Scope required: write
Creates a signature verification config for a webhook endpoint. On Pro/Business plans, any provider is allowed. On free plans, this is restricted to provider-mode endpoints where the sig config provider matches the endpoint's provider.
Not applicable to email endpoints — email authentication (SPF, DKIM, DMARC) is handled at the transport level by AWS SES.
Body:
{
"signature_config": {
"provider": "github",
"secret": "whsec_your_secret_here",
"enabled": true
}
}
| Parameter | Description |
|---|---|
provider |
github, stripe, shopify, twilio, or generic_hmac |
secret |
The webhook signing secret |
enabled |
Whether verification is active (default true) |
Conflict handling: If a config for the same provider already exists, the API returns 409 Conflict. Pass ?force=true to overwrite the existing config.
Response (201):
{
"data": {
"id": 1,
"provider": "github",
"enabled": true
}
}
Forwarding Targets
List forwarding targets
GET /api/v1/endpoints/:endpoint_id/forwarding_targets
Returns all forwarding targets for the specified endpoint. Works for both webhook and email endpoints.
Get a forwarding target
GET /api/v1/endpoints/:endpoint_id/forwarding_targets/:id
Create a forwarding target
POST /api/v1/endpoints/:endpoint_id/forwarding_targets
Scope required: write
Body:
{
"forwarding_target": {
"name": "Production API",
"url": "https://api.example.com/webhooks",
"enabled": true,
"timeout_seconds": 30,
"retry_count": 3
}
}
For email endpoints, forwarding delivers a normalized JSON POST containing the email's from, to, subject, text/HTML bodies, headers, attachments metadata, and authentication results.
Update a forwarding target
PATCH /api/v1/endpoints/:endpoint_id/forwarding_targets/:id
Scope required: write
Delete a forwarding target
DELETE /api/v1/endpoints/:endpoint_id/forwarding_targets/:id
Scope required: write
Events
List recent events
GET /api/v1/events
Scope required: read
Returns recent activity events for the account, including both webhook and email ingress. Useful for building integrations that react to CatchHook activity.
Query parameters:
| Parameter | Description | Default |
|---|---|---|
limit |
Number of events (1–200) | 50 |
since |
ISO 8601 timestamp | 24 hours ago |
Event types:
| Type | Description |
|---|---|
webhook.received |
A webhook was received at a webhook endpoint |
email.received |
An email was received at an email endpoint |
forwarding.failed |
A forwarding attempt failed |
alert.triggered |
An alert condition was detected |
Response:
{
"data": [
{
"type": "webhook.received",
"occurred_at": "2026-05-01T12:00:00Z",
"endpoint_id": "ep_abc123",
"request_id": "req_def456",
"data": {
"method": "POST",
"path": "/hooks/stripe",
"content_type": "application/json",
"size": 1024
}
},
{
"type": "email.received",
"occurred_at": "2026-05-01T11:30:00Z",
"endpoint_id": "ep_ghi789",
"request_id": "eml_jkl012",
"data": {
"from": "sender@example.com",
"to": "billing@in.catchhook.app",
"subject": "Order Confirmation",
"size": 4096
}
}
],
"meta": { "count": 2 }
}
Tunnel
Connect (authenticated)
POST /api/v1/tunnel/connect
Scope required: tunnel
Body:
{
"endpoint_id": "ep_abc123"
}
Works for both webhook and email endpoints. Exchanges your API token for a one-time WebSocket ticket (valid for 30 seconds).
Response:
{
"ticket": "a1b2c3...",
"expires_in": 30
}
Connect (anonymous / temporary endpoint)
POST /api/v1/tunnel/connect_anonymous
No authentication required. Uses the endpoint's tunnel_key instead.
Body:
{
"tunnel_key": "tk_xyz789"
}
Response:
{
"ticket": "a1b2c3...",
"expires_in": 30,
"endpoint_id": "ep_abc123"
}
Report delivery
POST /api/v1/tunnel/delivery_reports
Scope required: tunnel
Reports the result of a local tunnel delivery back to CatchHook. Used by the CLI to power tunnel health metrics and alerts.
Body:
{
"endpoint_id": "ep_abc123",
"webhook_request_id": "req_def456",
"target_url": "http://localhost:3000/webhooks",
"status_code": 200,
"response_message": "OK",
"response_time_ms": 42
}
The webhook_request_id field accepts both webhook (req_*) and email (eml_*) event IDs.
Rate limits
API endpoints are rate-limited to prevent abuse:
| Endpoint | Limit |
|---|---|
| Tunnel connect (authenticated) | 30 requests / 60 seconds |
| Tunnel connect (anonymous) | 10 requests / 60 seconds |
When rate-limited, the API returns 429 Too Many Requests with a retry_after field.
Error responses
All errors follow a consistent format:
{
"error": "not_found"
}
Common status codes:
| Code | Meaning |
|---|---|
| 401 | Invalid or missing API token |
| 403 | Token doesn't have the required scope |
| 404 | Resource not found |
| 422 | Validation error |
| 429 | Rate limited |