REST API v1

API reference

Programmatic access to your endpoints and captured webhook requests. All management endpoints return JSON and require authentication.

Authentication

Management API requests must identify an account. Use an API key (recommended for scripts and CI) or a session cookie from logging in via the web UI.

API key (recommended)
Authorization: Bearer sh_your_api_key

Create and revoke keys in the dashboard under API Keys. Keys are shown once at creation; store them securely.

Session cookie
# Log in via POST /login, then reuse the session cookie
curl -c cookies.txt -X POST https://suavehooks.com/login -d "email=you@example.com&password=..."
curl -b cookies.txt https://suavehooks.com/api/v1/endpoints

Management endpoints

Base URL: https://suavehooks.com · All responses use application/json with camelCase field names.

GET /api/v1/endpoints

List all webhook endpoints for the authenticated account.

[
  {
    "id": "a1b2c3d4e5f6...",
    "name": "stripe-payments",
    "targetUrl": "https://example.com/hook",
    "hasHmacSecret": true,
    "hasTransformRules": false,
    "hasTransformScript": false,
    "transformScriptLanguage": "fsharp",
    "forwardsPaused": false,
    "forwardRateLimit": 10,
    "pollingEnabled": false,
    "mockEnabled": false,
    "requestCount": 42,
    "createdAt": "2026-01-15T10:00:00Z"
  }
]
POST /api/v1/endpoints

Create a new webhook endpoint. Returns 201 with the full endpoint object (same shape as GET /api/v1/endpoints/{id}).

Required: name (letters, numbers, hyphens, underscores; max 64 chars).

Optional fields (all match the edit-endpoint form):

  • targetUrl — legacy primary HTTP forward URL
  • forwardTargets — JSON array of destinations (see forward targets schema)
  • hmacSecret, signingScheme (none, suave, github, both)
  • headerFilterRules, transformRules, transformScript, transformScriptLanguage — see transform scripts
  • forwardsPaused, forwardRateLimit, pollingEnabled
  • mockEnabled, mockStatusCode, mockHeaders, mockBody
  • eventTypeHeader, idempotencyWindowSeconds, slackWebhookUrl
{
  "name": "stripe-payments",
  "targetUrl": "https://example.com/webhooks/stripe",
  "pollingEnabled": true,
  "forwardTargets": [
    {
      "name": "Primary",
      "type": "http",
      "url": "https://example.com/webhooks/stripe",
      "enabled": true,
      "conditions": null
    }
  ]
}

Errors: name_required, invalid_name, name_taken (409), endpoint_limit (429). Validation errors return 400 with an error message.

GET /api/v1/endpoints/{id}

Full endpoint configuration. {id} is the 32-character hex UUID (no dashes).

Includes forwardTargets (with credentialsSet instead of secrets), transform rules, mock response settings, and integration options. hmacSecret is never returned — use hasHmacSecret.

{
  "id": "a1b2c3d4e5f6...",
  "name": "stripe-payments",
  "targetUrl": "https://example.com/hook",
  "forwardTargets": [ { "name": "Primary", "type": "http", "url": "..." } ],
  "hasHmacSecret": true,
  "signingScheme": "suave",
  "forwardsPaused": false,
  "forwardRateLimit": 10,
  "pollingEnabled": true,
  "mockEnabled": false,
  "requestCount": 42,
  "createdAt": "2026-01-15T10:00:00Z"
}
PATCH /api/v1/endpoints/{id}

Update an endpoint. Send only the fields you want to change — omitted fields are left unchanged.

Accepts the same optional fields as create (except name, which is immutable). Set a string field to null to clear it (e.g. remove HMAC secret or Slack webhook).

Replacing forwardTargets replaces the entire target list. Include target id values from a prior GET to preserve stored cloud credentials — see credentials docs.

PATCH https://suavehooks.com/api/v1/endpoints/ENDPOINT_ID
Authorization: Bearer sh_YOUR_KEY
Content-Type: application/json

{
  "pollingEnabled": true,
  "forwardsPaused": false,
  "forwardRateLimit": 20
}

Returns the updated endpoint object. Errors: name_immutable (400), not_found (404).

GET /api/v1/endpoints/{id}/requests

Recent captured requests for an endpoint, newest first.

Query: limit — max items (default 50, max 200).

[
  {
    "id": "f1e2d3c4...",
    "endpointId": "a1b2c3d4...",
    "method": "POST",
    "queryString": "?foo=bar",
    "sourceIp": "203.0.113.10",
    "receivedAt": "2026-03-01T14:22:00Z",
    "bodySize": 1284
  }
]
GET /api/v1/endpoints/{id}/poll

Pull captured events in FIFO order when push webhooks are impractical (firewalls, local dev, batch jobs). Enable per endpoint via the API (pollingEnabled) or under Edit → Enable polling API.

Query: iterator — cursor from the previous response; limit — batch size (default 50, max 100); consumer — optional stable id (letters, numbers, _, -) to track progress server-side without storing the iterator yourself.

The first call without an iterator returns an empty data array and a cursor for new events only. Subsequent calls with that iterator return captures received after the cursor.

{
  "iterator": "v1:eyJyZWNlaXZlZEF0Ijoi...",
  "data": [
    {
      "id": "f1e2d3c4...",
      "endpointId": "a1b2c3d4...",
      "method": "POST",
      "headers": [{ "name": "Content-Type", "value": "application/json" }],
      "body": {"event":"order.created"},
      "receivedAt": "2026-03-01T14:22:00Z"
    }
  ]
}
GET /api/v1/requests/{id}

Full capture detail: headers, body, and forward delivery log.

{
  "id": "f1e2d3c4...",
  "endpointId": "a1b2c3d4...",
  "method": "POST",
  "headers": [{ "name": "Content-Type", "value": "application/json" }],
  "queryString": "",
  "sourceIp": "203.0.113.10",
  "receivedAt": "2026-03-01T14:22:00Z",
  "body": {"event":"payment_intent.succeeded"},
  "forwardDeliveries": [
    {
      "id": "...",
      "targetUrl": "https://example.com/hook",
      "statusCode": 200,
      "attempt": 1,
      "elapsedMs": 145,
      "error": null,
      "deliveredAt": "2026-03-01T14:22:01Z"
    }
  ]
}

Quick example

curl -H "Authorization: Bearer sh_YOUR_KEY" \
  https://suavehooks.com/api/v1/endpoints

curl -X POST -H "Authorization: Bearer sh_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"stripe-hooks","targetUrl":"https://example.com/hook"}' \
  https://suavehooks.com/api/v1/endpoints

curl -H "Authorization: Bearer sh_YOUR_KEY" \
  "https://suavehooks.com/api/v1/endpoints/ENDPOINT_ID/requests?limit=10"

curl -H "Authorization: Bearer sh_YOUR_KEY" \
  "https://suavehooks.com/api/v1/endpoints/ENDPOINT_ID/poll"

curl -H "Authorization: Bearer sh_YOUR_KEY" \
  "https://suavehooks.com/api/v1/endpoints/ENDPOINT_ID/poll?iterator=ITERATOR_FROM_PRIOR_RESPONSE"

Webhook capture (ingress)

Send webhooks to SuaveHooks using your per-endpoint capture URL on the ingest host (https://ingest.suavehooks.com). This is not part of the management API and does not require an API key — configure HMAC verification on the endpoint if you need inbound signing. SuaveHooks adds X-SuaveHooks-* signing headers on outbound forwards; see /docs/signing.

Capture URL
https://ingest.suavehooks.com/u/{userId}/{endpointName}
  • {userId} — your account UUID (32 hex chars, no dashes)
  • {endpointName} — slug you chose when creating the endpoint
  • All HTTP methods are accepted (GET, POST, PUT, etc.)
  • Returns 200 OK with body OK on success
  • 401 if HMAC verification fails · 429 if monthly quota exceeded
curl -X POST https://ingest.suavehooks.com/u/USER_ID/stripe-payments \
  -H "Content-Type: application/json" \
  -d '{"type":"checkout.session.completed"}'

Live tail: connect a WebSocket to /ws/endpoint/{endpointId} (session cookie required) for real-time capture events on the dashboard.

CLI: use suavehooks-cli for send, listen/tunnel, and load-test. Public status: /status (machine-readable: /health). Embed portal: /embed/portal?token=emb_... supports theme, primary, and hide_branding; endpoint detail at /embed/portal/endpoint/{id}. Forward destinations may be HTTP, S3, SQS, Kafka, or Google Pub/Sub — see forward targets.

Rate limits

SuaveHooks applies inbound rate limits to protect the service from abuse. Limits use a sliding window per client IP (or per API key on management routes). When exceeded, the server responds with 429 Too Many Requests and a short plain-text message. A Retry-After header indicates when to retry.

Scope Limit
Global (per IP, most routes) 200/sec
Webhook capture /u/... 50/sec
Login, register, password reset 20/min
Management API /api/v1/* 30/sec

Health (/health), status, static assets, and domain verification paths are excluded from the global cap. Plan-based monthly capture quotas are separate — see billing limits and the errors table.

Errors

Status Meaning
401{"error":"unauthorized"} — missing or invalid credentials
404{"error":"not_found"} — resource missing or wrong ID format
403{"error":"archived"} — endpoint is archived (polling disabled)
410Webhook capture rejected — endpoint is archived
429Monthly capture quota exceeded (ingress), or inbound rate limit exceeded

Health check

Public endpoint for uptime monitoring. No authentication required.

GET /health
{
  "status": "healthy",
  "version": "1.0.0",
  "db_latency_ms": 2,
  "live_tail_subscribers": 0,
  "retention_days": 14,
  "max_requests_per_month": 5000
}

Need an API key? Create a free account then visit API Keys in the dashboard.