# hookstream — Complete Documentation > The modern event gateway for webhooks that just work. This file contains all hookstream documentation in a single text file, optimized for LLMs. --- # Getting Started # Quickstart Get up and running with hookstream in under 5 minutes. Source: https://hookstream.io/docs/quickstart ## Prerequisites You need a hookstream account. Sign up at hookstream.io — it's free for small projects. You'll also need a way to send HTTP requests (cURL, Postman, or your application). ## Step 1: Create a Source A source is your webhook endpoint. Go to **Dashboard > Sources** and click **Create Source**. Give it a name like "My App Webhooks". hookstream generates a unique ingestion URL for you: `https://hookstream.io/v1/ingest/` Any HTTP request sent to this URL becomes an event in hookstream. ## Step 2: Create a Destination A destination is where events get delivered. Go to **Destinations > Create Destination**. Choose HTTP/Webhook and enter the URL of your application's webhook handler. hookstream supports 8 destination types: HTTP, AWS SQS, AWS S3, AWS EventBridge, GCP Pub/Sub, Kafka, RabbitMQ, and WebSocket. ## Step 3: Create a Connection A connection links a source to a destination. Go to **Connections > Create Connection**. Select your source and destination. Optionally add content-based filters (e.g., only route events where `type` equals `"order.created"`) or JSONata transforms. ## Step 4: Send a Webhook Send a POST request to your source URL: ```shellscript curl -X POST https://hookstream.io/v1/ingest/ \ -H 'Content-Type: application/json' \ -d '{"type": "order.created", "data": {"id": "ord_123", "amount": 4999}}' ``` hookstream receives the event, runs it through any matching connections, and delivers it to each connected destination. ## Step 5: Verify Delivery Go to **Events** in the dashboard to see your event. Click into the event detail to see delivery attempts, status codes, and latency. If delivery fails, hookstream automatically retries with exponential backoff. ## Next Steps You now have a working webhook pipeline. Explore signature verification to secure your sources, content-based filters to route events precisely, and the metrics dashboard to monitor your pipeline's health. ## Code Examples ### Send a test webhook ```bash curl -X POST https://hookstream.io/v1/ingest/ \ -H 'Content-Type: application/json' \ -d '{"type": "order.created", "data": {"id": "ord_123", "amount": 4999}}' ``` ## Related - [first-webhook](https://hookstream.io/docs/first-webhook) - [how-it-works](https://hookstream.io/docs/how-it-works) - [api/authentication](https://hookstream.io/docs/api/authentication) --- # How It Works Understand hookstream's architecture: sources, connections, and destinations. Source: https://hookstream.io/docs/how-it-works ## Overview hookstream is an event gateway that sits between webhook senders and your applications. It receives webhooks (via Sources), routes them (via Connections), and delivers them (to Destinations) — with retries, circuit breakers, and real-time monitoring built in. ## Ingest When a webhook arrives at a source URL, hookstream: 1. Verifies the signature (if configured) using HMAC-SHA256, SHA1, or Standard Webhooks 2. Checks IP allowlist/denylist (if configured) 3. Deduplicates against recent events (KV-backed window) 4. Stores the event in D1 with full headers, body, and metadata 5. Stores large payloads (>10KB) in R2 object storage ## Routing Each connection defines a source-to-destination route with optional: - Content-based filters: 9 operators (eq, neq, contains, gt, gte, lt, lte, exists, in) with dot-notation field paths - JSONata transforms: reshape the payload before delivery - Schema validation: validate against JSON Schema with reject or warn mode An event can match multiple connections, enabling fan-out to many destinations. ## Delivery Each destination has a dedicated Durable Object (DeliveryScheduler) that handles: - Immediate delivery attempt via the configured provider - Automatic retries on failure (exponential, linear, or fixed backoff) - Circuit breaker (open after 5 consecutive failures, half-open probe, auto-close on success) - Dead letter queue for events that exhaust all retries Delivery attempts are tracked with status codes, latency, and error messages. ## Monitoring hookstream provides real-time visibility through: - WebSocket push to the dashboard (EventStream Durable Object per org) - Metrics: event volume, delivery success rate, latency percentiles, error breakdown - Issues: auto-created on delivery failure with severity and affected resources - Alert rules: 5 types (failure rate, latency, volume drop, DLQ, issue opened) - Notification channels: webhook with HMAC signing ## Architecture The flow is: Provider -> Source (ingest URL) -> Pipeline (filter + transform) -> Connection -> DeliveryScheduler DO -> Destination Provider -> Your Endpoint. All backend logic runs on Cloudflare Workers with Durable Objects for stateful operations. Data is stored in D1 (SQLite), KV (cache/dedup), and R2 (large payloads). ## Related - [sources](https://hookstream.io/docs/sources) - [destinations](https://hookstream.io/docs/destinations) - [connections](https://hookstream.io/docs/connections) - [delivery-pipeline](https://hookstream.io/docs/delivery-pipeline) --- # Key Concepts Glossary of core hookstream concepts: orgs, environments, sources, events, and more. Source: https://hookstream.io/docs/concepts ## Organization An organization (org) is the top-level container for all your hookstream resources. When you sign up, an org is auto-provisioned. All sources, destinations, connections, events, and metrics belong to your org. Org ID format: org_. ## Environment Each org has one or more environments (e.g., production, staging). Environments provide data isolation. API keys are scoped to an environment. Environment ID format: env_. ## Source A source is a webhook ingestion endpoint. Each source has a unique URL (`https://hookstream.io/v1/ingest/`) that accepts any HTTP method. Sources can be configured with signature verification, IP filtering, and deduplication. Sources also have templates for common providers (Stripe, GitHub, Shopify, etc.). ## Destination A destination is where events are delivered. hookstream supports 8 provider types: HTTP/Webhook, AWS SQS, AWS S3, AWS EventBridge, GCP Pub/Sub, Kafka (via Confluent REST), RabbitMQ (via HTTP API), and WebSocket. Each destination has its own retry policy, circuit breaker, and dead letter queue. ## Connection A connection routes events from a source to a destination. Connections can have content-based filters (to selectively route events) and JSONata transforms (to reshape payloads before delivery). Multiple connections can reference the same source, enabling fan-out. ## Event An event represents a single webhook request received by a source. Events store the full HTTP method, headers, body, query parameters, and metadata. Events have a lifecycle: received -> processing -> delivered/failed. ## Delivery Attempt A delivery attempt is a single attempt to send an event to a destination. Each attempt records the HTTP status code, response body, latency, and any error. Failed attempts trigger retries according to the destination's retry policy. ## API Key API keys authenticate requests to the hookstream API. Keys are scoped to an environment and have the format `hs_live_<64 hex>` or `hs_test_<64 hex>`. The plaintext key is shown once at creation; only the SHA-256 hash is stored. ## Related - [sources](https://hookstream.io/docs/sources) - [destinations](https://hookstream.io/docs/destinations) - [connections](https://hookstream.io/docs/connections) - [events](https://hookstream.io/docs/events) --- # Your First Webhook Step-by-step tutorial: create a source, send a webhook, and inspect the event. Source: https://hookstream.io/docs/first-webhook ## What You'll Build In this tutorial, you'll create a source, send a webhook from your terminal, watch it appear in the dashboard in real-time, and optionally set up a destination for delivery. Total time: about 3 minutes. ## Step 1: Create a Source Log in to the hookstream dashboard and go to **Sources**. Click **Create Source**. - Name: "Tutorial Source" - Verification: None (for this tutorial) Click Create. You'll see your source's ingestion URL: `https://hookstream.io/v1/ingest/` Copy this URL. ## Step 2: Send a Webhook Open your terminal and send a POST request: ```shellscript curl -X POST https://hookstream.io/v1/ingest/ \ -H 'Content-Type: application/json' \ -d '{"event": "user.created", "user": {"id": "usr_42", "email": "alice@example.com"}}' ``` You should get a `200 OK` response with an `event_id`. ## Step 3: Inspect the Event Go to Events in the dashboard. You'll see your event listed with the method, source, and timestamp. Click on it to see: - Full request headers - Parsed JSON body - Query parameters - Metadata (source IP, content length, etc.) If you have the dashboard open before sending, the event appears in real-time via WebSocket push. ## Step 4: Add a Destination (Optional) To actually deliver events somewhere: 1. Go to Destinations > Create Destination 2. Choose HTTP/Webhook, enter your endpoint URL 3. Go to Connections > Create Connection 4. Select your source and destination 5. Send another webhook — watch it get delivered Check the Events page to see delivery attempts with status codes and latency. ## Alternative: Use the CLI You can also do this entirely from the command line: ```shellscript npm install -g @hookstream/cli hookstream login hookstream sources create "Tutorial Source" hookstream destinations create "My Endpoint" --url https://example.com/webhook hookstream connections create "My Pipeline" --source --destination # Send a webhook to the source URL, then check events: hookstream events list ``` ## Related - [quickstart](https://hookstream.io/docs/quickstart) - [sources](https://hookstream.io/docs/sources) - [api/ingest](https://hookstream.io/docs/api/ingest) --- # Dashboard Tour Visual walkthrough of the hookstream dashboard and its key sections. Source: https://hookstream.io/docs/dashboard-tour ## Overview The Overview page (Dashboard) shows your org's key stats at a glance: total events, delivery success rate, active sources, and active destinations. A volume chart shows event traffic over time. Stat cards animate with real-time WebSocket updates. ## Sources The Sources page lists all your webhook ingestion endpoints. Each source shows its name, ingestion URL, event count, and verification status. Click a source to see its configuration, recent events, and connection graph. Use source templates for one-click setup with Stripe, GitHub, Shopify, and more. ## Destinations The Destinations page shows all delivery targets. Each destination displays its type (HTTP, SQS, S3, etc.), URL, delivery stats, and circuit breaker status. Click a destination to see its retry policy, recent delivery attempts, and circuit breaker history. ## Connections The Connections page shows source-to-destination routes. Each connection shows the source name, destination name, filter rules, and transform status. Click a connection to edit filters, transforms, or view matched events. ## Events The Events page is a searchable, filterable log of all received webhooks. Filter by source, status, date range, or search by body content. Click an event to inspect headers, body, delivery attempts, and retry history. Events update in real-time via WebSocket. ## Metrics The Metrics page provides four charts: event volume over time, delivery success rate, latency percentiles (p50, p95, p99), and error breakdown by type. Time ranges from 1 hour to 30 days. Data is real-time for <=48h and pre-aggregated for older periods. ## Issues & Alerts The Issues page lists auto-detected problems: delivery failures, circuit breaker opens, high error rates. Issues are auto-created and auto-resolved. Alert rules trigger notifications via webhook when conditions are met. Configure notification channels in Settings. ## Topics, Replay & Databases Topics: create pub/sub topics with subscriber destinations and fan-out publish. Replay: replay historical events to any destination with rate limiting. Databases: sync webhook events to queryable collections with auto-schema inference, upsert engine, and CSV/NDJSON export. ## Related - [quickstart](https://hookstream.io/docs/quickstart) - [events](https://hookstream.io/docs/events) - [api/metrics](https://hookstream.io/docs/api/metrics) --- # Core Concepts # Sources Source types, templates, signature verification, deduplication, and IP filtering. Source: https://hookstream.io/docs/sources ## What is a Source? A source is a webhook ingestion endpoint. Each source has a unique URL that accepts any HTTP method. When a request arrives, hookstream creates an event and runs it through the processing pipeline. ## Source URL Every source gets a unique URL: `https://hookstream.io/v1/ingest/` This URL accepts any HTTP method (POST, PUT, PATCH, DELETE, GET). The full request — method, headers, body, and query parameters — is captured as an event. ## Source Templates hookstream includes templates for common webhook providers. Templates pre-configure signature verification, expected headers, and documentation links. Available templates: Stripe, Shopify, GitHub, Slack, Twilio, SendGrid, Intercom, Linear, Clerk, Svix, Paddle, Resend, Postmark, Typeform, and Custom. ## Signature Verification Sources can verify incoming webhook signatures to ensure authenticity. Supported methods: - HMAC-SHA256: Used by Stripe, GitHub, Shopify, and most providers - HMAC-SHA1: Used by some legacy providers - Standard Webhooks: The standardized webhook signature spec Configure the signing secret on your source, and hookstream automatically verifies every incoming request. Requests with invalid signatures are rejected with 401. ## Deduplication Sources can deduplicate events to prevent double-processing. Three strategies: - `payload_hash`: SHA-256 of the request body (default) - `header_field`: use a specific header as the dedup key (e.g., Idempotency-Key) - `body_field`: use a specific JSON body field as the dedup key (e.g., data.id) Dedup uses KV with a configurable window (default: 5 minutes). Duplicate events return 200 but are not processed further. ## IP Filtering Sources can restrict which IP addresses are allowed to send webhooks. Configure an allowlist or denylist of IP addresses or CIDR ranges. Requests from blocked IPs are rejected with 403. ## WebSocket Sources In addition to HTTP ingestion, sources can accept inbound WebSocket connections. A WebSocketSource Durable Object manages per-source connections, enabling bidirectional communication and real-time event streaming from connected clients. ## Related - [api/sources](https://hookstream.io/docs/api/sources) - [guides/signature-verification](https://hookstream.io/docs/guides/signature-verification) - [guides/ip-filtering](https://hookstream.io/docs/guides/ip-filtering) --- # Destinations Destination types, authentication, timeouts, and provider configurations. Source: https://hookstream.io/docs/destinations ## What is a Destination? A destination is where hookstream delivers events. Each destination has a dedicated DeliveryScheduler Durable Object that handles delivery, retries, circuit breaker logic, and dead letter queue management. ## Provider Types hookstream supports 8 destination types: 1. `http` — deliver to any HTTP endpoint 2. `aws_sqs` — push to an SQS queue 3. `aws_s3` — write to an S3 bucket 4. `aws_eventbridge` — publish to an event bus 5. `gcp_pubsub` — publish to a Pub/Sub topic 6. `kafka` — produce via Confluent REST Proxy 7. `rabbitmq` — publish via HTTP Management API 8. `websocket` — broadcast to connected clients ## Retry Policy Each destination has a configurable retry policy: - `max_retries`: maximum retry attempts (default: 5) - `backoff_type`: exponential, linear, or fixed - `intervals`: array of retry delays in seconds (default: [30, 300, 1800, 7200, 86400]) For exponential backoff, each attempt uses the next interval in the array. For linear, the base interval is multiplied by the attempt number. For fixed, the first interval is used every time. Retries are managed by the DeliveryScheduler DO using alarm-based scheduling. ## Circuit Breaker Each destination has a circuit breaker that prevents overwhelming failing endpoints: - Closed: normal operation, deliveries proceed - Open: after 5 consecutive failures, all deliveries are paused (queued) - Half-Open: after a cooldown period, one probe request is sent If the probe succeeds, the circuit closes and queued events are delivered. If it fails, the circuit stays open. You can manually reset the circuit via API or dashboard. ## Destination Templates hookstream provides templates for common destination services: Slack (incoming webhooks), Discord (webhooks), PagerDuty (Events API v2), Datadog (HTTP intake), Zapier (webhooks), and Linear (webhooks). Templates pre-configure URLs, headers, and payload formats. ## Outbound Signing HTTP destinations can sign outbound requests using HMAC-SHA256 or HMAC-SHA1. This allows your receiving endpoint to verify that the webhook came from hookstream. The signature is included in a configurable header (default: X-hookstream-Signature). ## Related - [api/destinations](https://hookstream.io/docs/api/destinations) - [providers/http](https://hookstream.io/docs/providers/http) - [delivery-pipeline](https://hookstream.io/docs/delivery-pipeline) --- # Connections Route events from sources to destinations with filters and transforms. Source: https://hookstream.io/docs/connections ## What is a Connection? A connection is a route that links a source to a destination. When an event arrives at a source, hookstream evaluates all connections for that source. If an event matches a connection's filters, it's delivered to the connection's destination. ## Fan-Out A single source can have multiple connections, each routing to a different destination. This enables fan-out: one webhook can trigger deliveries to multiple endpoints. Each connection can have different filters, so you can route different event types to different destinations. ## Content-Based Filters Connections support content-based filters with 9 operators: - `eq` / `neq`: exact match / inverse match - `contains`: substring match (string only) - `gt` / `gte` / `lt` / `lte`: numeric comparisons - `exists`: field presence check (true = present, false = absent) - `in`: value is in an array Filters use dot-notation paths (e.g., `payload.event.type`) to access nested fields in the event payload. ## JSONata Transforms Connections can transform the event payload before delivery using JSONata expressions. This allows you to reshape, filter fields, add computed values, or convert formats. Transforms are applied after filters and before delivery. Example: `{ "event_type": body.type, "customer_id": body.data.customer.id }` ## Processing Order When an event arrives: 1. All connections for the source are loaded 2. Content-based filters are evaluated for each connection 3. Matching connections apply their transforms 4. Transformed payloads are sent to each destination's DeliveryScheduler DO Connections are evaluated in parallel — there's no ordering guarantee between them. ## Related - [api/connections](https://hookstream.io/docs/api/connections) - [filters-transforms](https://hookstream.io/docs/filters-transforms) - [sources](https://hookstream.io/docs/sources) --- # Events Event lifecycle: received, processed, delivered, and failed states. Source: https://hookstream.io/docs/events ## What is an Event? An event represents a single webhook request received by a source. Events capture the complete HTTP request: method, headers, body, query parameters, source IP, and content type. Each event gets a unique ID and timestamp. ## Event Lifecycle Events go through these states: 1. received: webhook captured, stored in D1 2. processing: running through pipeline (filter, transform, deliver) 3. delivered: successfully delivered to all matched destinations 4. failed: delivery failed after all retries exhausted Events with no matching connections stay in received state. ## Storage Event payloads are stored inline in D1 for bodies under 10KB. Larger payloads are stored in R2 object storage with a reference in D1. Event metadata (headers, method, query params, timestamps) is always in D1. Events older than 30 days are archived to R2 as NDJSON files, partitioned by org and date. ## Searching & Filtering The Events API and dashboard support: - Source filter: show events from a specific source - Status filter: received, delivered, failed - Time range: filter by start and end timestamp - Search: full-text search across event bodies - Cursor pagination: efficient forward/backward traversal ## Retrying Events Failed events can be retried individually or in bulk: - Individual: `POST /v1/events/:id/retry` re-runs the event through the pipeline - Bulk: `POST /v1/issues/:id/retry` retries all events associated with an issue Retried events create new delivery attempts while preserving the original event. ## Related - [api/events](https://hookstream.io/docs/api/events) - [api/delivery-attempts](https://hookstream.io/docs/api/delivery-attempts) - [guides/event-replay](https://hookstream.io/docs/guides/event-replay) --- # Delivery Pipeline Pipeline stages: ingest, filter, transform, deliver, retry, DLQ. Source: https://hookstream.io/docs/delivery-pipeline ## Pipeline Overview The delivery pipeline processes events through these stages: 1. Ingest: receive HTTP request, verify, dedup, store 2. Route: evaluate connections, apply filters 3. Transform: apply JSONata transforms 4. Deliver: send to destination via provider 5. Retry: retry failed deliveries with backoff 6. DLQ: dead-letter events that exhaust all retries ## Ingest Stage The ingest endpoint (`/v1/ingest/:source_id`) accepts any HTTP method. It runs through: signature verification -> IP filter -> dedup check -> event storage -> broadcast to EventStream DO -> pipeline dispatch via `waitUntil()`. ## Delivery Stage Each destination has a DeliveryScheduler Durable Object. When an event is dispatched for delivery: 1. The DO checks the circuit breaker state 2. If closed or half-open, it attempts delivery via the provider 3. On success: updates attempt status, broadcasts delivery event 4. On failure: schedules retry alarm, updates circuit breaker 5. On final failure: moves to DLQ, creates/updates issue ## Retry Logic Retries are alarm-driven in the Durable Object using a configurable intervals array (default: [30, 300, 1800, 7200, 86400] seconds): - Exponential backoff: each attempt uses the next interval in the array (30s, 5m, 30m, 2h, 24h) - Linear backoff: base interval multiplied by attempt number - Fixed backoff: constant delay using the first interval ## Circuit Breaker The circuit breaker protects destinations from cascading failures: - Closed (normal): deliveries proceed. Track consecutive failures. - Open (tripped): after 5 consecutive failures, pause all deliveries. Queue incoming events. Set cooldown alarm. - Half-Open (testing): after cooldown, send one probe delivery. If it succeeds, close the circuit and flush queue. If it fails, re-open. Circuit breaker state is visible in the dashboard and via API. ## Dead Letter Queue Events that exhaust all retries are moved to the dead letter queue. DLQ events are visible in the dashboard and API. You can manually retry DLQ events individually or in bulk. An issue is created automatically for each DLQ event with severity based on failure count. ## Related - [guides/retry-strategies](https://hookstream.io/docs/guides/retry-strategies) - [guides/circuit-breaker](https://hookstream.io/docs/guides/circuit-breaker) - [guides/dead-letter-queue](https://hookstream.io/docs/guides/dead-letter-queue) --- # Filters & Transforms 9 filter operators and JSONata transforms for event routing and payload reshaping. Source: https://hookstream.io/docs/filters-transforms ## Content-Based Filters Filters let you selectively route events based on their content. Each filter has three parts: a field path (dot-notation), an operator, and a value. Multiple filters are AND-combined — all must match for the event to pass. ## Filter Operators 9 available operators: - `eq`: exact string/number match - `neq`: inverse of eq - `contains`: substring match (string only) - `gt`: greater than (numeric comparison) - `gte`: greater than or equal (numeric comparison) - `lt`: less than (numeric comparison) - `lte`: less than or equal (numeric comparison) - `exists`: field is present and not null (true) or absent/null (false) - `in`: value is in an array of allowed values ## Field Paths Field paths use dot-notation to access nested values: - `payload.type` — top-level body field - `payload.data.customer.id` — nested field - `headers.x-github-event` — request header (case-insensitive) - `method` — HTTP method - `content_type` — request content type - `source_ip` — client IP address Paths are case-sensitive (except headers). Missing paths return undefined (useful with the `exists` operator). ## JSONata Transforms Transforms reshape event payloads using JSONata expressions. JSONata is a lightweight query and transformation language for JSON. Transforms are applied after filters and before delivery. ## Transform Examples Flatten a nested payload: { "event": body.type, "customer_id": body.data.customer.id, "amount": body.data.amount / 100 } Convert to Slack message: { "text": "New order " & body.data.order_id & " for $" & $string(body.data.amount / 100) } Filter fields: { "id": body.id, "email": body.email } ## Schema Validation Sources can validate incoming payloads against a JSON Schema. Two modes: - `reject`: invalid payloads return 400 and are not stored - `warn`: invalid payloads are stored with a validation warning flag Schema validation uses @cfworker/json-schema, a Workers-native JSON Schema validator. ## Related - [connections](https://hookstream.io/docs/connections) - [api/connections](https://hookstream.io/docs/api/connections) - [guides/schema-validation](https://hookstream.io/docs/guides/schema-validation) --- # Topics & Pub/Sub Create topics, subscribe destinations, and publish with fan-out delivery. Source: https://hookstream.io/docs/topics-pubsub ## What are Topics? Topics provide a pub/sub messaging pattern on top of hookstream. A topic is a named channel that destinations can subscribe to. When you publish an event to a topic, hookstream delivers it to all subscribed destinations — with optional per-subscription filters and transforms. ## Creating Topics Topics have a name and a slug (URL-safe identifier). Create a topic via the API: `POST /v1/topics` `{ "name": "Order Events", "slug": "order-events" }` The slug is used in the publish URL: `POST /v1/topics/order-events/publish` ## Subscriptions Subscribe a destination to a topic: `POST /v1/topics/:id/subscriptions` `{ "destination_id": "dest_abc" }` Subscriptions can have per-subscription filters (same 9 operators as connections) and JSONata transforms. This allows different subscribers to receive different subsets of events or differently-shaped payloads. ## Publishing Publish an event to a topic: `POST /v1/topics/order-events/publish` `{ "type": "order.created", "data": { "id": "ord_123" } }` hookstream evaluates each subscription's filters, applies transforms, and delivers to each matched destination. This runs alongside the connection-based pipeline, not replacing it. ## Topics vs. Connections Connections are source-to-destination routes: webhooks come in, get routed out. Topics are a publish-subscribe overlay: your application publishes directly to a topic, and subscribers receive it. Use connections for external webhook routing. Use topics for application-initiated fan-out. ## Related - [api/topics](https://hookstream.io/docs/api/topics) - [connections](https://hookstream.io/docs/connections) - [destinations](https://hookstream.io/docs/destinations) --- # Instant Database Collections, upsert engine, schema inference, and data export. Source: https://hookstream.io/docs/instant-database ## What is Instant Database? Instant Database lets you sync webhook events to queryable collections. Each collection is like a table: define a primary key field, and hookstream upserts events as records using a last-write-wins strategy. Query, export, and analyze your webhook data directly. ## Collections A collection is a container for records. Create a collection with: - `name`: human-readable label - `source_id`: which source to sync from - `primary_key_path`: dot-notation path to the unique identifier (e.g., `body.data.id`) Events from the source are automatically upserted as records using the primary key. ## Upsert Engine The upsert engine uses last-write-wins semantics based on `source_event_at` timestamp. When an event arrives: 1. Extract primary key from payload using `primary_key_path` 2. Check if record exists 3. If new: insert record 4. If existing and newer: update record 5. If existing and older: skip (out-of-order protection) This handles out-of-order events without CRDTs. ## Schema Inference hookstream infers the JSON schema of your collection by sampling up to 200 records. The inferred schema shows field names, types, and nesting structure. This is computed on-demand (not stored) to avoid staleness as your data evolves. ## Data Export Export collection records as NDJSON or CSV: `GET /v1/collections/:id/export?format=ndjson` `GET /v1/collections/:id/export?format=csv` CSV export uses json-flatten to convert nested JSON to dot-notation column headers. Large exports stream the response. ## Backfill Backfill re-processes historical events into a collection. Useful when you create a collection after events have already been received. The backfill engine processes events in batches using `waitUntil()`, with the frontend re-triggering batches until complete. ## Related - [api/collections](https://hookstream.io/docs/api/collections) - [filters-transforms](https://hookstream.io/docs/filters-transforms) - [guides/event-replay](https://hookstream.io/docs/guides/event-replay) --- # API Reference # Authentication Overview of hookstream authentication methods: API keys and session cookies. Source: https://hookstream.io/docs/api/authentication ## Overview hookstream supports two authentication methods. Most API endpoints use `combinedAuth`, which tries both methods in order and accepts whichever succeeds first. ## API Key Authentication Send your API key in the `X-API-Key` header. hookstream hashes the key with SHA-256 and looks up the hash in D1. API keys have the format `hs_live_<64 hex chars>` for production or `hs_test_<64 hex chars>` for test environments. Example: ```shellscript curl -H "X-API-Key: hs_live_abc123..." https://hookstream.io/v1/sources ``` ## Session Authentication The hookstream dashboard uses cookie-based session authentication via Better Auth. When you sign in through the web UI (email/password or Google/GitHub OAuth), a session cookie is set automatically. All subsequent API requests from the browser include this cookie. On first login, hookstream auto-provisions an organization and environment for the user. ## Combined Auth Most endpoints use `combinedAuth` middleware, which tries API key authentication first. If no `X-API-Key` header is present or the key is invalid, it falls back to session cookie verification. This allows both programmatic access (API keys) and browser-based access (session cookies). ## Public Endpoints Some endpoints require no authentication: the ingest endpoint (`/v1/ingest/:source_id`), test session endpoints (`/v1/test/*`), health check (`/v1/health`), and tools API (`/v1/tools/*`). These endpoints use IP-based rate limiting instead. ## Related - [api/api-keys](https://hookstream.io/docs/api/api-keys) - [api/errors](https://hookstream.io/docs/api/errors) - [quickstart](https://hookstream.io/docs/quickstart) --- # API Keys Create, list, and revoke API keys for programmatic access. Source: https://hookstream.io/docs/api/api-keys ## Overview API keys provide programmatic access to the hookstream API. Keys are shown in plaintext only once at creation time — hookstream stores a SHA-256 hash internally. API key management requires session authentication (you must be logged in to the dashboard). ## API Endpoints ### `POST /v1/auth/api-keys` Create a new API key. The plaintext key is returned only in this response. **Auth:** session **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `name` | string | Yes | A human-readable label for the key. | **Request Example:** ```json { "name": "Production Backend" } ``` **Response Example:** ```json { "id": "ak_8f3a1b2c4d5e6f7a", "name": "Production Backend", "key": "hs_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2", "key_prefix": "hs_live_a1b2", "scopes": null, "created_at": "2026-03-01T12:00:00Z" } ``` ### `GET /v1/auth/api-keys` List all API keys for the authenticated user. Keys are masked — only the prefix is shown. **Auth:** session **Response Example:** ```json { "api_keys": [ { "id": "ak_8f3a1b2c4d5e6f7a", "name": "Production Backend", "key_prefix": "hs_live_a1b2", "scopes": null, "request_count": 142, "revoked": 0, "created_at": "2026-03-01T12:00:00Z", "last_used_at": "2026-03-01T14:30:00Z" } ] } ``` ### `DELETE /v1/auth/api-keys/:id` Revoke an API key. The key is immediately invalidated and cannot be recovered. **Auth:** session **Response Example:** ```json { "success": true } ``` ## Related - [api/authentication](https://hookstream.io/docs/api/authentication) - [cli/authentication](https://hookstream.io/docs/cli/authentication) --- # Sources Manage webhook sources — your inbound endpoints that receive events. Source: https://hookstream.io/docs/api/sources ## Overview Sources are your webhook ingestion endpoints. Each source gets a unique URL at `/v1/ingest/` where external services send webhooks. Sources support optional signature verification (HMAC-SHA256, HMAC-SHA1, Standard Webhooks), deduplication strategies, and IP allow/deny lists. ## Source Templates hookstream provides 15 pre-configured source templates for popular webhook providers (Stripe, GitHub, Shopify, Twilio, etc.). Use the templates endpoint to list them and pre-fill verification settings when creating a source. ## API Endpoints ### `GET /v1/sources` List all sources for the authenticated organization. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `cursor` | string | No | Cursor for pagination. | | `limit` | number | No | Number of results to return (default 20, max 100). | **Response Example:** ```json { "sources": [ { "id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "name": "Stripe Webhooks", "verification_type": "hmac_sha256", "dedup_strategy": "payload_hash", "ip_allowlist": null, "ip_denylist": null, "event_count": 1542, "created_at": "2026-02-28T10:00:00Z", "updated_at": "2026-03-01T08:30:00Z" } ], "cursor": "eyJpZCI6InNyY18uLi4ifQ", "has_more": false } ``` ### `POST /v1/sources` Create a new source. Returns the source with its ingestion URL. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `name` | string | Yes | Display name for the source. | | `slug` | string | Yes | URL-safe slug for the source (e.g., "my-source"). | | `verification_type` | string | No | Signature verification method: "hmac_sha256", "hmac_sha1", "standard_webhooks", or null. | | `verification_secret` | string | No | Secret used for signature verification. Required if verification_type is set. | | `dedup_strategy` | string | No | Deduplication strategy: "payload_hash", "header_field", "body_field", or null. | | `ip_allowlist` | string[] | No | JSON array of allowed IP addresses or CIDR ranges. | | `ip_denylist` | string[] | No | JSON array of denied IP addresses or CIDR ranges. | **Request Example:** ```json { "name": "Stripe Webhooks", "slug": "stripe-webhooks", "verification_type": "hmac_sha256", "verification_secret": "whsec_abc123def456", "dedup_strategy": "payload_hash" } ``` **Response Example:** ```json { "id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "name": "Stripe Webhooks", "verification_type": "hmac_sha256", "dedup_strategy": "payload_hash", "ip_allowlist": null, "ip_denylist": null, "url": "https://hookstream.io/v1/ingest/src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "created_at": "2026-03-01T12:00:00Z" } ``` ### `GET /v1/sources/:id` Get a single source by ID. **Auth:** combined **Response Example:** ```json { "id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "name": "Stripe Webhooks", "verification_type": "hmac_sha256", "dedup_strategy": "payload_hash", "ip_allowlist": null, "ip_denylist": null, "event_count": 1542, "url": "https://hookstream.io/v1/ingest/src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "created_at": "2026-02-28T10:00:00Z", "updated_at": "2026-03-01T08:30:00Z" } ``` ### `PATCH /v1/sources/:id` Update a source. All fields are optional. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `name` | string | No | Updated display name. | | `verification_type` | string | No | Updated verification method. | | `verification_secret` | string | No | Updated verification secret. | **Request Example:** ```json { "name": "Stripe Production Webhooks" } ``` **Response Example:** ```json { "id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "name": "Stripe Production Webhooks", "verification_type": "hmac_sha256", "dedup_strategy": "payload_hash", "updated_at": "2026-03-01T15:00:00Z" } ``` ### `DELETE /v1/sources/:id` Delete a source. Events and connections referencing this source are not deleted. **Auth:** combined **Response Example:** ```json { "success": true } ``` ### `GET /v1/sources/templates` List pre-configured source templates for popular webhook providers. **Auth:** combined **Response Example:** ```json { "templates": [ { "id": "stripe", "name": "Stripe", "verification_type": "hmac_sha256", "description": "Receive Stripe webhook events with HMAC-SHA256 signature verification." }, { "id": "github", "name": "GitHub", "verification_type": "hmac_sha256", "description": "Receive GitHub webhook events with HMAC-SHA256 signature verification." } ] } ``` ## Related - [sources](https://hookstream.io/docs/sources) - [api/ingest](https://hookstream.io/docs/api/ingest) - [guides/signature-verification](https://hookstream.io/docs/guides/signature-verification) --- # Destinations Manage event destinations — where your webhooks get delivered. Source: https://hookstream.io/docs/api/destinations ## Overview Destinations define where events are delivered. hookstream supports 8 destination types: HTTP/Webhook, AWS SQS, AWS S3, AWS EventBridge, GCP Pub/Sub, Kafka (via Confluent REST Proxy), RabbitMQ (via HTTP Management API), and WebSocket. Each destination has its own retry policy, circuit breaker state, and dead letter queue. ## Circuit Breaker Each destination has an automatic circuit breaker. When consecutive failures exceed the threshold, the circuit opens and deliveries are paused. After a cooldown period, a single test delivery is attempted (half-open state). If it succeeds, the circuit closes and normal delivery resumes. You can check circuit status and manually reset it via the API. ## API Endpoints ### `GET /v1/destinations` List all destinations for the authenticated organization. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `cursor` | string | No | Cursor for pagination. | | `limit` | number | No | Number of results (default 20, max 100). | **Response Example:** ```json { "destinations": [ { "id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "name": "Backend API", "type": "http", "url": "https://api.example.com/webhooks", "retry_max_attempts": 5, "retry_backoff_type": "exponential", "retry_intervals": [ 30, 300, 1800, 7200, 86400 ], "created_at": "2026-02-28T10:00:00Z" } ], "cursor": null, "has_more": false } ``` ### `POST /v1/destinations` Create a new destination with delivery configuration and retry policy. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `name` | string | Yes | Display name for the destination. | | `slug` | string | Yes | URL-safe slug for the destination. | | `type` | string | No | Provider type: "http", "sqs", "s3", "eventbridge", "pubsub", "kafka", "rabbitmq", or "websocket" (default "http"). | | `url` | string | No | Destination URL (required for HTTP destinations). | | `method` | string | No | HTTP method to use for delivery (default "POST"). | | `headers` | object | No | Custom headers to include with deliveries. | | `provider_config` | object | No | Provider-specific configuration. For SQS: { queue_url, region, access_key_id, secret_access_key }. For S3: { bucket, region, ... }. | | `retry_max_attempts` | number | No | Maximum retry attempts, 1-10 (default 5). | | `retry_backoff_type` | string | No | Retry backoff strategy: "exponential", "linear", or "fixed" (default "exponential"). | | `retry_intervals` | number[] | No | Array of retry delay intervals in seconds (default [30, 300, 1800, 7200, 86400]). | **Request Example:** ```json { "name": "Backend API", "slug": "backend-api", "type": "http", "url": "https://api.example.com/webhooks", "headers": { "X-Custom": "value" }, "retry_max_attempts": 5, "retry_backoff_type": "exponential", "retry_intervals": [ 30, 300, 1800, 7200, 86400 ] } ``` **Response Example:** ```json { "id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "name": "Backend API", "slug": "backend-api", "type": "http", "url": "https://api.example.com/webhooks", "retry_max_attempts": 5, "retry_backoff_type": "exponential", "retry_intervals": [ 30, 300, 1800, 7200, 86400 ], "created_at": "2026-03-01T12:00:00Z" } ``` ### `GET /v1/destinations/:id` Get a single destination by ID with its configuration. **Auth:** combined **Response Example:** ```json { "id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "name": "Backend API", "type": "http", "url": "https://api.example.com/webhooks", "retry_max_attempts": 5, "retry_backoff_type": "exponential", "retry_intervals": [ 30, 300, 1800, 7200, 86400 ], "delivery_count": 892, "created_at": "2026-02-28T10:00:00Z", "updated_at": "2026-03-01T08:30:00Z" } ``` ### `PATCH /v1/destinations/:id` Update a destination. All fields are optional. **Auth:** combined **Request Example:** ```json { "name": "Backend API v2", "retry_max_attempts": 10 } ``` **Response Example:** ```json { "id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "name": "Backend API v2", "type": "http", "retry_max_attempts": 10, "updated_at": "2026-03-01T15:00:00Z" } ``` ### `DELETE /v1/destinations/:id` Delete a destination and stop all pending deliveries. **Auth:** combined **Response Example:** ```json { "success": true } ``` ### `GET /v1/destinations/templates` List pre-configured destination templates (Slack, Discord, PagerDuty, etc.). **Auth:** combined **Response Example:** ```json { "templates": [ { "id": "slack", "name": "Slack", "type": "http", "description": "Send webhook events to a Slack channel via Incoming Webhook URL.", "config_template": { "url": "" } }, { "id": "discord", "name": "Discord", "type": "http", "description": "Post webhook events to a Discord channel webhook.", "config_template": { "url": "" } } ] } ``` ### `GET /v1/destinations/:id/circuit` Get the circuit breaker status for a destination (closed, open, or half_open). **Auth:** combined **Response Example:** ```json { "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "state": "closed", "failure_count": 0, "last_failure_at": null, "opened_at": null } ``` ### `POST /v1/destinations/:id/circuit/reset` Manually reset the circuit breaker to closed state, allowing deliveries to resume. **Auth:** combined **Response Example:** ```json { "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "state": "closed", "failure_count": 0 } ``` ## Related - [destinations](https://hookstream.io/docs/destinations) - [guides/retry-strategies](https://hookstream.io/docs/guides/retry-strategies) - [guides/circuit-breaker](https://hookstream.io/docs/guides/circuit-breaker) --- # Connections Link sources to destinations with optional filters and transforms. Source: https://hookstream.io/docs/api/connections ## Overview Connections link a source to a destination. When an event arrives at a source, hookstream evaluates all connections for that source. If the event passes the connection's filters, it is optionally transformed via a JSONata expression and then delivered to the destination. ## Content-Based Filters Filters use 9 operators: `eq`, `neq`, `contains`, `gt`, `gte`, `lt`, `lte`, `exists`, `in`. Filters reference event fields using dot-notation paths (e.g., `payload.data.status`, `headers.x-event-type`, `method`). All filters in a connection must match for the event to be routed (AND logic). ## JSONata Transforms Transforms are JSONata expressions applied to the event payload before delivery. Use transforms to reshape data, extract fields, add computed values, or convert formats. The transform receives the full event body and must return a valid JSON object. ## API Endpoints ### `GET /v1/connections` List all connections for the authenticated organization. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `source_id` | string | No | Filter connections by source. | | `destination_id` | string | No | Filter connections by destination. | | `cursor` | string | No | Cursor for pagination. | | `limit` | number | No | Number of results (default 20, max 100). | **Response Example:** ```json { "connections": [ { "id": "conn_c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.created" } ], "transform_expression": null, "source_name": "Stripe Webhooks", "destination_name": "Backend API", "created_at": "2026-02-28T12:00:00Z" } ], "cursor": null, "has_more": false } ``` ### `POST /v1/connections` Create a connection between a source and a destination. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `source_id` | string | Yes | The source to receive events from. | | `destination_id` | string | Yes | The destination to deliver events to. | | `filter_rules` | array | No | Array of filter rule objects: { field, op, value }. Operators: eq, neq, contains, gt, gte, lt, lte, exists, in. | | `transform_expression` | string | No | JSONata expression to transform the event payload before delivery. | | `transform_enabled` | boolean | No | Whether to apply the transform expression (default false). | **Request Example:** ```json { "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "name": "Stripe to Backend", "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.created" } ], "transform_enabled": true, "transform_expression": "{ \"orderId\": data.id, \"total\": data.amount / 100 }" } ``` **Response Example:** ```json { "id": "conn_c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "name": "Stripe to Backend", "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.created" } ], "transform_enabled": true, "transform_expression": "{ \"orderId\": data.id, \"total\": data.amount / 100 }", "created_at": "2026-03-01T12:00:00Z" } ``` ### `GET /v1/connections/:id` Get a single connection by ID. **Auth:** combined **Response Example:** ```json { "id": "conn_c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "name": "Stripe to Backend", "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.created" } ], "transform_enabled": true, "transform_expression": "{ \"orderId\": data.id, \"total\": data.amount / 100 }", "source_name": "Stripe Webhooks", "destination_name": "Backend API", "created_at": "2026-02-28T12:00:00Z", "updated_at": "2026-03-01T08:00:00Z" } ``` ### `PATCH /v1/connections/:id` Update a connection's filters or transform. **Auth:** combined **Request Example:** ```json { "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.created" }, { "field": "payload.data.amount", "op": "gt", "value": 1000 } ] } ``` **Response Example:** ```json { "id": "conn_c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8", "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.created" }, { "field": "payload.data.amount", "op": "gt", "value": 1000 } ], "updated_at": "2026-03-01T15:00:00Z" } ``` ### `DELETE /v1/connections/:id` Delete a connection. Events already in the pipeline are still delivered. **Auth:** combined **Response Example:** ```json { "success": true } ``` ## Related - [connections](https://hookstream.io/docs/connections) - [filters-transforms](https://hookstream.io/docs/filters-transforms) - [guides/schema-validation](https://hookstream.io/docs/guides/schema-validation) --- # Events Browse, search, and retry webhook events received by your sources. Source: https://hookstream.io/docs/api/events ## Overview Events represent individual webhook payloads received at a source's ingestion URL. Each event stores the HTTP method, headers, body, and metadata. Events support cursor pagination, full-text search, time range filters, and status filtering. You can retry delivery for individual events or in bulk. ## API Endpoints ### `GET /v1/events` List events with optional filters for source, status, time range, and search. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `source_id` | string | No | Filter by source ID. | | `status` | string | No | Filter by event status. | | `cursor` | string | No | Cursor for pagination (base64 encoded). | | `limit` | number | No | Number of results (default 50, max 100). | | `after` | string | No | ISO 8601 lower bound for time range filter. | | `before` | string | No | ISO 8601 upper bound for time range filter. | | `search` | string | No | Search by event ID prefix or payload content. | **Response Example:** ```json { "events": [ { "id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "source_name": "Stripe Webhooks", "method": "POST", "content_type": "application/json", "status": "delivered", "received_at": "2026-03-01T14:30:00Z" } ], "pagination": { "total": 1542, "limit": 50, "has_more": true, "next_cursor": "eyJpZCI6ImV2dF8uLi4ifQ" } } ``` ### `GET /v1/events/:id` Get full event detail including body, headers, and delivery attempts. **Auth:** combined **Response Example:** ```json { "id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "source_name": "Stripe Webhooks", "method": "POST", "headers": { "content-type": "application/json", "stripe-signature": "t=1709..." }, "body": { "type": "invoice.paid", "data": { "id": "inv_123", "amount_paid": 4999 } }, "status": "delivered", "delivery_attempts": [ { "id": "da_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "status": "success", "status_code": 200, "latency_ms": 42, "attempted_at": "2026-03-01T14:30:01Z" } ], "received_at": "2026-03-01T14:30:00Z" } ``` ### `POST /v1/events/:id/retry` Retry delivery for a specific event across all connected destinations. **Auth:** combined **Response Example:** ```json { "retried": [ { "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "status": "queued" } ], "errors": [] } ``` ## Related - [events](https://hookstream.io/docs/events) - [api/delivery-attempts](https://hookstream.io/docs/api/delivery-attempts) - [api/ingest](https://hookstream.io/docs/api/ingest) --- # Delivery Attempts Inspect individual delivery attempts with status codes, latency, and response bodies. Source: https://hookstream.io/docs/api/delivery-attempts ## Overview Delivery attempts track each attempt to deliver an event to a destination. Each attempt records the HTTP status code, response body, latency, and whether it was an initial delivery or a retry. Failed attempts are automatically retried according to the destination's retry policy. ## API Endpoints ### `GET /v1/delivery-attempts` List delivery attempts with optional filters for event, destination, and status. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `event_id` | string | No | Filter by event ID. | | `connection_id` | string | No | Filter by connection ID. | | `destination_id` | string | No | Filter by destination ID. | | `status` | string | No | Filter by attempt status. | | `is_dlq` | string | No | Filter DLQ attempts only. Set to "1" to filter. | | `limit` | number | No | Number of results (default 50, max 100). | | `offset` | number | No | Offset for pagination (default 0). | **Response Example:** ```json { "delivery_attempts": [ { "id": "da_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0", "event_id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", "connection_id": "conn_c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "destination_name": "Backend API", "status": "success", "is_dlq": 0, "created_at": "2026-03-01T14:30:01Z" } ], "pagination": { "total": 892, "limit": 50, "offset": 0, "has_more": true } } ``` ### `GET /v1/delivery-attempts/:id` Get full detail for a delivery attempt including the response body. **Auth:** combined **Response Example:** ```json { "id": "da_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0", "event_id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "destination_name": "Backend API", "status": "success", "status_code": 200, "request_headers": { "content-type": "application/json" }, "response_headers": { "content-type": "application/json" }, "response_body": "{\"ok\":true}", "latency_ms": 42, "attempt_number": 1, "next_retry_at": null, "attempted_at": "2026-03-01T14:30:01Z" } ``` ### `POST /v1/delivery-attempts/:id/retry` Retry a specific failed delivery attempt immediately. **Auth:** combined **Response Example:** ```json { "delivery_attempt": { "id": "da_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0", "event_id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "status": "pending" } } ``` ## Related - [api/events](https://hookstream.io/docs/api/events) - [guides/retry-strategies](https://hookstream.io/docs/guides/retry-strategies) - [guides/dead-letter-queue](https://hookstream.io/docs/guides/dead-letter-queue) --- # Metrics Query event volume, delivery success rates, and latency percentiles. Source: https://hookstream.io/docs/api/metrics ## Overview The metrics API provides aggregated analytics for your webhook traffic. It uses a hybrid approach: queries within the last 48 hours run against raw event data for instant accuracy, while older queries use Cron-driven pre-aggregated hourly and daily rollups for bounded performance. ## Time Intervals Metrics can be aggregated by `hour` or `day` interval. The `start` and `end` parameters accept ISO 8601 timestamps. If omitted, the API defaults to the last 24 hours with hourly intervals. ## API Endpoints ### `GET /v1/metrics/overview` Get summary stats: today's events, deliveries, success rate, and active resource counts. **Auth:** combined **Response Example:** ```json { "events_today": 1542, "events_this_week": 15420, "active_sources": 5, "deliveries_today": 1493, "success_today": 1470, "failed_today": 23, "success_rate_today": 98.46, "dlq_count": 3, "active_destinations": 8, "open_issues": 2, "active_collections": 4 } ``` ### `GET /v1/metrics/volume` Get event volume over time grouped by granularity. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `after` | string | No | ISO 8601 start time. | | `before` | string | No | ISO 8601 end time. | | `source_id` | string | No | Filter by source ID. | | `granularity` | string | No | Aggregation granularity: "hour" or "day" (default "hour"). | **Response Example:** ```json { "series": [ { "bucket": "2026-03-01T00:00:00Z", "source_id": "src_a1b2...", "event_count": 342, "source_name": "Stripe Webhooks" }, { "bucket": "2026-03-01T01:00:00Z", "source_id": "src_a1b2...", "event_count": 289, "source_name": "Stripe Webhooks" } ], "granularity": "hour" } ``` ### `GET /v1/metrics/deliveries` Get delivery success/failure breakdown over time. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `after` | string | No | ISO 8601 start time. | | `before` | string | No | ISO 8601 end time. | | `destination_id` | string | No | Filter by destination ID. | | `granularity` | string | No | Aggregation granularity: "hour" or "day" (default "hour"). | **Response Example:** ```json { "series": [ { "bucket": "2026-03-01T00:00:00Z", "destination_id": "dst_b2c3...", "destination_name": "Backend API", "delivery_count": 342, "success_count": 330, "failed_count": 12, "avg_latency_ms": 45 } ], "granularity": "hour" } ``` ### `GET /v1/metrics/latency` Get delivery latency percentiles (p50, p95, p99) over time. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `after` | string | No | ISO 8601 start time. | | `before` | string | No | ISO 8601 end time. | | `destination_id` | string | No | Filter by destination ID. | **Response Example:** ```json { "series": [ { "bucket": "2026-03-01T00:00:00Z", "destination_id": "dst_b2c3...", "destination_name": "Backend API", "p50_latency_ms": 45, "p95_latency_ms": 230, "p99_latency_ms": 890, "avg_latency_ms": 127 } ] } ``` ## Related - [api/events](https://hookstream.io/docs/api/events) - [guides/monitoring-alerts](https://hookstream.io/docs/guides/monitoring-alerts) --- # Issues Track delivery failures auto-grouped into actionable issues. Source: https://hookstream.io/docs/api/issues ## Overview Issues are automatically created when delivery attempts fail. hookstream groups related failures into a single issue per destination and error type. Issues track occurrence counts, first/last seen timestamps, and resolution status. Issues auto-resolve after 30 minutes without new occurrences. ## API Endpoints ### `GET /v1/issues` List issues with optional status filter. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `status` | string | No | Filter by status: "open", "acknowledged", "resolved", or "all" (default "open"). | | `destination_id` | string | No | Filter by destination ID. | | `limit` | number | No | Number of results (default 50, max 100). | | `offset` | number | No | Offset for pagination (default 0). | **Response Example:** ```json { "issues": [ { "id": "iss_f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "destination_name": "Backend API", "status": "open", "first_seen_at": "2026-03-01T10:00:00Z", "last_seen_at": "2026-03-01T14:30:00Z" } ], "pagination": { "total": 3, "limit": 50, "offset": 0, "has_more": false } } ``` ### `GET /v1/issues/summary` Get issue counts by status. **Auth:** combined **Response Example:** ```json { "open": 3, "acknowledged": 1, "resolved": 45 } ``` ### `GET /v1/issues/:id` Get full issue detail including recent failed delivery attempt IDs. **Auth:** combined **Response Example:** ```json { "id": "iss_f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "destination_name": "Backend API", "error_type": "http_5xx", "message": "Destination returned 503 Service Unavailable", "status": "open", "occurrence_count": 23, "first_seen_at": "2026-03-01T10:00:00Z", "last_seen_at": "2026-03-01T14:30:00Z", "recent_attempt_ids": [ "da_aaa111", "da_bbb222", "da_ccc333" ] } ``` ### `PATCH /v1/issues/:id` Update an issue's status. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `status` | string | No | New status: "open", "acknowledged", or "resolved". | | `snoozed_until` | string | No | ISO 8601 datetime to snooze the issue until, or null to unsnooze. | **Request Example:** ```json { "status": "resolved" } ``` **Response Example:** ```json { "id": "iss_f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1", "status": "resolved", "resolved_at": "2026-03-01T15:00:00Z" } ``` ### `POST /v1/issues/:id/retry` Bulk retry all failed events associated with this issue. **Auth:** combined **Response Example:** ```json { "retried": 23, "total": 23 } ``` ## Related - [api/alert-rules](https://hookstream.io/docs/api/alert-rules) - [api/notification-channels](https://hookstream.io/docs/api/notification-channels) - [guides/monitoring-alerts](https://hookstream.io/docs/guides/monitoring-alerts) --- # Notification Channels Configure where alert notifications are sent (webhook, Slack, Discord, or email). Source: https://hookstream.io/docs/api/notification-channels ## Overview Notification channels define where alerts are delivered. hookstream supports four channel types: webhook (with optional HMAC signing), Slack, Discord, and email. When an alert rule triggers, hookstream sends a notification to each configured channel with the alert details. ## API Endpoints ### `GET /v1/notification-channels` List all notification channels. **Auth:** combined **Response Example:** ```json { "channels": [ { "id": "nc_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2", "name": "Ops Slack", "type": "webhook", "config": { "url": "https://hooks.slack.com/services/T.../B.../xxx" }, "created_at": "2026-02-28T10:00:00Z" } ] } ``` ### `POST /v1/notification-channels` Create a new notification channel. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `name` | string | Yes | Display name for the channel. | | `channel_type` | string | Yes | Channel type: "webhook", "slack", "discord", or "email". | | `config` | object | Yes | Channel configuration. For webhook: { url: string, secret?: string }. If secret is provided, requests are signed with HMAC-SHA256. | **Request Example:** ```json { "name": "Ops Slack", "channel_type": "webhook", "config": { "url": "https://hooks.slack.com/services/T.../B.../xxx" } } ``` **Response Example:** ```json { "id": "nc_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2", "name": "Ops Slack", "type": "webhook", "config": { "url": "https://hooks.slack.com/services/T.../B.../xxx" }, "created_at": "2026-03-01T12:00:00Z" } ``` ### `GET /v1/notification-channels/:id` Get a single notification channel by ID. **Auth:** combined **Response Example:** ```json { "id": "nc_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2", "name": "Ops Slack", "type": "webhook", "config": { "url": "https://hooks.slack.com/services/T.../B.../xxx" }, "created_at": "2026-02-28T10:00:00Z", "updated_at": "2026-03-01T08:00:00Z" } ``` ### `PATCH /v1/notification-channels/:id` Update a notification channel. **Auth:** combined **Request Example:** ```json { "name": "Ops PagerDuty" } ``` **Response Example:** ```json { "id": "nc_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2", "name": "Ops PagerDuty", "type": "webhook", "updated_at": "2026-03-01T15:00:00Z" } ``` ### `DELETE /v1/notification-channels/:id` Delete a notification channel. Alert rules referencing it must be updated first. **Auth:** combined **Response Example:** ```json { "deleted": true } ``` ### `POST /v1/notification-channels/:id/test` Send a test notification to verify the channel is working. **Auth:** combined **Response Example:** ```json { "success": true, "error": null } ``` ## Related - [api/alert-rules](https://hookstream.io/docs/api/alert-rules) - [guides/monitoring-alerts](https://hookstream.io/docs/guides/monitoring-alerts) --- # Alert Rules Define threshold-based alerts for error rates, latency, volume, DLQ, and circuit breakers. Source: https://hookstream.io/docs/api/alert-rules ## Overview Alert rules define conditions that trigger notifications. hookstream evaluates alert rules every 5 minutes via a Cron trigger. When a rule's threshold is exceeded, notifications are sent to configured channels. Rules auto-resolve after 30 minutes if the condition clears. Five alert types are supported: `failure_rate`, `latency`, `volume_drop`, `dlq`, and `issue_opened`. ## API Endpoints ### `GET /v1/alert-rules` List all alert rules. **Auth:** combined **Response Example:** ```json { "alert_rules": [ { "id": "ar_b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3", "name": "High Error Rate", "rule_type": "failure_rate", "rule_config": { "threshold": 0.1 }, "channel_ids": [ "nc_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2" ], "cooldown_minutes": 60, "enabled": true, "last_triggered_at": null, "created_at": "2026-02-28T10:00:00Z" } ] } ``` ### `POST /v1/alert-rules` Create a new alert rule. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `name` | string | Yes | Display name for the rule. | | `rule_type` | string | Yes | Alert type: "failure_rate", "latency", "volume_drop", "dlq", or "issue_opened". | | `rule_config` | object | No | Type-specific configuration. For failure_rate: { threshold }. For latency: { threshold_ms, percentile_target }. For volume_drop: { threshold }. For dlq: { threshold }. | | `channel_ids` | string[] | No | Array of notification channel IDs to send alerts to. | | `cooldown_minutes` | number | No | Minimum minutes between alert triggers (default 60). | **Request Example:** ```json { "name": "High Error Rate", "rule_type": "failure_rate", "rule_config": { "threshold": 0.1 }, "channel_ids": [ "nc_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2" ], "cooldown_minutes": 60 } ``` **Response Example:** ```json { "alert_rule": { "id": "ar_b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3", "name": "High Error Rate", "rule_type": "failure_rate", "rule_config": { "threshold": 0.1 }, "channel_ids": [ "nc_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2" ], "cooldown_minutes": 60, "enabled": true, "created_at": "2026-03-01T12:00:00Z" } } ``` ### `GET /v1/alert-rules/:id` Get a single alert rule with its trigger history. **Auth:** combined **Response Example:** ```json { "alert_rule": { "id": "ar_b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3", "name": "High Error Rate", "rule_type": "failure_rate", "rule_config": { "threshold": 0.1 }, "channel_ids": [ "nc_a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2" ], "cooldown_minutes": 60, "enabled": true, "last_triggered_at": "2026-03-01T10:05:00Z", "created_at": "2026-02-28T10:00:00Z" }, "history": [ { "id": "ah_1a2b3c", "alert_rule_id": "ar_b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3", "triggered_at": "2026-03-01T10:05:00Z", "message": "Failure rate 15% exceeds 10% threshold", "created_at": "2026-03-01T10:05:00Z" } ] } ``` ### `PATCH /v1/alert-rules/:id` Update an alert rule. **Auth:** combined **Request Example:** ```json { "rule_config": { "threshold": 0.05 }, "cooldown_minutes": 30 } ``` **Response Example:** ```json { "alert_rule": { "id": "ar_b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3", "name": "High Error Rate", "rule_type": "failure_rate", "rule_config": { "threshold": 0.05 }, "cooldown_minutes": 30, "updated_at": "2026-03-01T15:00:00Z" } } ``` ### `DELETE /v1/alert-rules/:id` Delete an alert rule. **Auth:** combined **Response Example:** ```json { "deleted": true } ``` ## Related - [api/notification-channels](https://hookstream.io/docs/api/notification-channels) - [api/issues](https://hookstream.io/docs/api/issues) - [guides/monitoring-alerts](https://hookstream.io/docs/guides/monitoring-alerts) --- # Collections Instant Database — sync webhook data into queryable collections with schema inference. Source: https://hookstream.io/docs/api/collections ## Overview Collections are part of hookstream's Instant Database feature. A collection automatically syncs events from a source into a queryable table using a `primary_key_path` for upsert (last-write-wins). Collections support schema inference from record samples, full-text search, NDJSON/CSV export, and batch backfill from existing events. ## API Endpoints ### `GET /v1/collections` List all collections. **Auth:** combined **Response Example:** ```json { "collections": [ { "id": "col_c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4", "name": "Orders", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "primary_key_path": "data.id", "record_count": 1250, "created_at": "2026-02-28T10:00:00Z" } ] } ``` ### `POST /v1/collections` Create a new collection linked to a source. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `name` | string | Yes | Display name for the collection. | | `slug` | string | Yes | URL-safe slug for the collection (e.g., "orders"). | | `source_id` | string | Yes | Source to sync events from. | | `primary_key_path` | string | Yes | Dot-notation path to the primary key field in the event body (e.g., "data.id"). | **Request Example:** ```json { "name": "Orders", "slug": "orders", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "primary_key_path": "data.id" } ``` **Response Example:** ```json { "collection": { "id": "col_c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4", "name": "Orders", "slug": "orders", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "primary_key_path": "data.id", "record_count": 0, "created_at": "2026-03-01T12:00:00Z" } } ``` ### `GET /v1/collections/:id` Get a single collection by ID. **Auth:** combined **Response Example:** ```json { "id": "col_c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4", "name": "Orders", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "primary_key_path": "data.id", "record_count": 1250, "created_at": "2026-02-28T10:00:00Z", "updated_at": "2026-03-01T14:00:00Z" } ``` ### `PATCH /v1/collections/:id` Update a collection's name or description. **Auth:** combined **Request Example:** ```json { "name": "All Orders" } ``` **Response Example:** ```json { "id": "col_c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4", "name": "All Orders", "updated_at": "2026-03-01T15:00:00Z" } ``` ### `DELETE /v1/collections/:id` Delete a collection and all its synced records. **Auth:** combined **Response Example:** ```json { "success": true } ``` ### `GET /v1/collections/:id/records` List records in a collection with optional search and pagination. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `cursor` | string | No | Cursor for pagination. | | `limit` | number | No | Number of results (default 20, max 100). | | `search` | string | No | Full-text search across record data. | **Response Example:** ```json { "records": [ { "id": "rec_001", "primary_key": "ord_abc123", "data": { "type": "order.created", "data": { "id": "ord_abc123", "amount": 4999 } }, "source_event_at": "2026-03-01T14:00:00Z", "synced_at": "2026-03-01T14:00:01Z" } ], "next_cursor": null, "has_more": false } ``` ### `GET /v1/collections/:id/stats` Get collection statistics (record count, size, last sync). **Auth:** combined **Response Example:** ```json { "record_count": 1250, "changelog_entries": 3420, "last_sync_at": "2026-03-01T14:30:00Z" } ``` ### `GET /v1/collections/:id/schema` Infer JSON schema from a sample of up to 200 records in the collection. **Auth:** combined **Response Example:** ```json { "fields": [ { "name": "type", "type": "string", "nullable": false }, { "name": "data.id", "type": "string", "nullable": false }, { "name": "data.amount", "type": "number", "nullable": false } ], "sample_count": 200 } ``` ### `GET /v1/collections/:id/export` Export all collection records as NDJSON or CSV. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `format` | string | No | Export format: "json" (NDJSON, default) or "csv". | **Response Example:** ```json {"primary_key":"ord_abc123","data":{"type":"order.created","data":{"id":"ord_abc123","amount":4999}}} {"primary_key":"ord_def456","data":{"type":"order.updated","data":{"id":"ord_def456","amount":7500}}} ``` ### `GET /v1/collections/:id/records/:key` Get a single record by its record key. **Auth:** combined **Response Example:** ```json { "record": { "id": "rec_001", "collection_id": "col_c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4", "record_key": "ord_abc123", "data": { "type": "order.created", "data": { "id": "ord_abc123", "amount": 4999 } }, "source_event_at": "2026-03-01T14:00:00Z", "synced_at": "2026-03-01T14:00:01Z" } } ``` ### `GET /v1/collections/:id/records/:key/changelog` Get the change history for a specific record. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `limit` | number | No | Number of changelog entries to return (default 50, max 100). | **Response Example:** ```json { "changelog": [ { "id": "cl_001", "collection_id": "col_c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4", "record_key": "ord_abc123", "event_id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", "created_at": "2026-03-01T14:00:01Z" } ] } ``` ### `POST /v1/collections/:id/backfill` Start a backfill job to sync existing events into the collection. **Auth:** combined **Response Example:** ```json { "status": "running", "total": 1250, "message": "Backfill started" } ``` ### `GET /v1/collections/:id/backfill` Check the status and progress of a backfill job. **Auth:** combined **Response Example:** ```json { "status": "running", "progress": 800, "total": 1250, "started_at": "2026-03-01T15:00:00Z", "completed_at": null, "error": null } ``` ## Related - [instant-database](https://hookstream.io/docs/instant-database) - [api/sources](https://hookstream.io/docs/api/sources) --- # Topics Pub/sub topics for fan-out delivery with per-subscription filters and transforms. Source: https://hookstream.io/docs/api/topics ## Overview Topics enable pub/sub-style fan-out delivery. Create a topic with a unique slug, subscribe destinations to it with optional per-subscription filters and transforms, then publish events to the topic. hookstream delivers the event to all matching subscribers. Topics run alongside the connection-based pipeline, not replacing it. ## API Endpoints ### `GET /v1/topics` List all topics. **Auth:** combined **Response Example:** ```json { "topics": [ { "id": "top_d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5", "name": "Order Events", "slug": "order-events", "description": "All order lifecycle events", "subscription_count": 3, "created_at": "2026-02-28T10:00:00Z" } ] } ``` ### `POST /v1/topics` Create a new topic. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `name` | string | Yes | Display name for the topic. | | `slug` | string | Yes | URL-safe slug for the publish endpoint (e.g., "order-events"). | | `description` | string | No | Optional description of the topic. | **Request Example:** ```json { "name": "Order Events", "slug": "order-events", "description": "All order lifecycle events" } ``` **Response Example:** ```json { "id": "top_d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5", "name": "Order Events", "slug": "order-events", "description": "All order lifecycle events", "publish_url": "https://hookstream.io/v1/topics/order-events/publish", "created_at": "2026-03-01T12:00:00Z" } ``` ### `GET /v1/topics/:id` Get a single topic by ID. **Auth:** combined **Response Example:** ```json { "topic": { "id": "top_d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5", "name": "Order Events", "slug": "order-events", "description": "All order lifecycle events", "publish_url": "https://hookstream.io/v1/topics/order-events/publish", "created_at": "2026-02-28T10:00:00Z" }, "subscriptions": [ { "id": "sub_e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6", "topic_id": "top_d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "destination_name": "Backend API", "filter_rules": [], "transform_expression": null, "created_at": "2026-02-28T12:00:00Z" } ] } ``` ### `PATCH /v1/topics/:id` Update a topic's name or description. **Auth:** combined **Request Example:** ```json { "description": "Order lifecycle events (created, updated, cancelled)" } ``` **Response Example:** ```json { "id": "top_d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5", "name": "Order Events", "slug": "order-events", "description": "Order lifecycle events (created, updated, cancelled)", "updated_at": "2026-03-01T15:00:00Z" } ``` ### `DELETE /v1/topics/:id` Delete a topic and all its subscriptions. **Auth:** combined **Response Example:** ```json { "success": true } ``` ### `POST /v1/topics/:id/subscriptions` Subscribe a destination to a topic. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `destination_id` | string | Yes | The destination to receive events. | | `filter_rules` | array | No | Per-subscription content-based filter rules. | | `transform_expression` | string | No | Per-subscription JSONata transform expression. | **Request Example:** ```json { "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.created" } ] } ``` **Response Example:** ```json { "subscription": { "id": "sub_e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6", "topic_id": "top_d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.created" } ], "transform_expression": null, "created_at": "2026-03-01T12:00:00Z" } } ``` ### `PATCH /v1/topics/:id/subscriptions/:sub_id` Update a subscription's filter rules or transform expression. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `filter_rules` | array | No | Updated content-based filter rules. | | `transform_expression` | string | No | Updated JSONata transform expression. | **Request Example:** ```json { "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.updated" } ] } ``` **Response Example:** ```json { "subscription": { "id": "sub_e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6", "topic_id": "top_d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "destination_name": "Backend API", "filter_rules": [ { "field": "payload.type", "op": "eq", "value": "order.updated" } ], "transform_expression": null } } ``` ### `DELETE /v1/topics/:id/subscriptions/:sub_id` Remove a subscription from a topic. **Auth:** combined **Response Example:** ```json { "success": true } ``` ### `POST /v1/topics/:slug/publish` Publish an event to a topic by slug. The event is delivered to all matching subscribers. **Auth:** combined **Request Example:** ```json { "type": "order.created", "data": { "id": "ord_789", "amount": 2500, "currency": "usd" } } ``` **Response Example:** ```json { "event_id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", "topic": "order-events", "received_at": "2026-03-01T14:30:00Z" } ``` ## Related - [topics-pubsub](https://hookstream.io/docs/topics-pubsub) - [api/connections](https://hookstream.io/docs/api/connections) - [api/destinations](https://hookstream.io/docs/api/destinations) --- # Replay Replay historical events to a destination with rate limiting and job tracking. Source: https://hookstream.io/docs/api/replay ## Overview Event replay lets you re-deliver historical events to a destination. This is useful for backfilling a new destination, recovering from outages, or testing. Replay jobs are rate-limited and tracked in KV with auto-expiry. You specify a `source_id`, `destination_id`, and time range. hookstream re-delivers each matching event through the normal delivery pipeline. ## API Endpoints ### `POST /v1/replay` Create a replay job to re-deliver events from a time range. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `destination_id` | string | Yes | Destination to deliver replayed events to. | | `source_id` | string | No | Filter to events from a specific source. | | `from` | string | Yes | ISO 8601 start time for the replay window. | | `to` | string | Yes | ISO 8601 end time for the replay window. | | `rate_limit` | number | No | Maximum events per second (default 10, max 100). | | `max_events` | number | No | Maximum events to replay (default 1000, max 10000). | **Request Example:** ```json { "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "from": "2026-03-01T00:00:00Z", "to": "2026-03-01T12:00:00Z", "rate_limit": 20 } ``` **Response Example:** ```json { "job": { "id": "rpl_f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7", "status": "running", "total": 342 } } ``` ### `GET /v1/replay/:id` Get the status and progress of a replay job. **Auth:** combined **Response Example:** ```json { "job": { "id": "rpl_f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7", "destination_id": "dst_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "from": "2026-03-01T00:00:00Z", "to": "2026-03-01T12:00:00Z", "rate_limit": 20, "max_events": 1000, "status": "completed", "total": 342, "processed": 342, "succeeded": 340, "failed": 2, "started_at": "2026-03-01T15:00:00Z", "completed_at": "2026-03-01T15:00:18Z" } } ``` ## Related - [guides/event-replay](https://hookstream.io/docs/guides/event-replay) - [api/events](https://hookstream.io/docs/api/events) - [api/destinations](https://hookstream.io/docs/api/destinations) --- # Test Sessions Ephemeral webhook testing sessions with inspection, forwarding, and export. Source: https://hookstream.io/docs/api/test-sessions ## Overview Test sessions provide ephemeral webhook endpoints for testing and debugging. Sessions are created without authentication and last for 14 days or 2000 requests. Each session gets a unique ingestion URL and a real-time WebSocket feed. Sessions support custom response configuration, webhook forwarding, event notes, and NDJSON/CSV export. Authenticated users can claim sessions to make them permanent. ## Authentication Most test session endpoints require no authentication — they are designed for quick, frictionless testing. The claim endpoint requires `combinedAuth`, as it associates the session with your organization. Sessions are rate-limited by IP address. ## API Endpoints ### `POST /v1/test/sessions` Create a new ephemeral test session. Returns the session with its ingestion URL. **Auth:** none **Request Example:** ```json {} ``` **Response Example:** ```json { "session_id": "a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8", "url": "https://hookstream.io/v1/ingest/a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8", "expires_at": "2026-03-15T12:00:00Z", "request_limit": 2000, "request_count": 0 } ``` ### `GET /v1/test/sessions/:id` Get a test session with its received events. **Auth:** none **Response Example:** ```json { "session": { "id": "a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8", "url": "https://hookstream.io/v1/ingest/a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8", "expires_at": "2026-03-15T12:00:00Z", "request_limit": 2000, "request_count": 5, "created_at": "2026-03-01T12:00:00Z" }, "events": [ { "id": "evt_001", "source_id": "a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8", "method": "POST", "headers": "{\"content-type\":\"application/json\"}", "content_type": "application/json", "received_at": "2026-03-01T12:05:00Z" } ], "pagination": { "limit": 50, "has_more": false, "next_cursor": null } } ``` ### `POST /v1/test/sessions/:id/claim` Claim a test session, converting it from ephemeral to permanent and associating it with your organization. **Auth:** combined **Response Example:** ```json { "id": "ts_a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8", "claimed": true, "org_id": "org_abc123", "source_id": "src_eph_a3b4c5d6e7f8a9b0" } ``` ### `DELETE /v1/test/sessions/:id` Delete a test session and all its events. **Auth:** none **Response Example:** ```json { "deleted": true } ``` ### `GET /v1/test/sessions/:id/events/:event_id` Get full details for a specific event in a test session, including the resolved payload. **Auth:** none **Response Example:** ```json { "id": "evt_001", "source_id": "a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8", "method": "POST", "headers": "{\"content-type\":\"application/json\"}", "content_type": "application/json", "payload": { "test": true }, "received_at": "2026-03-01T12:05:00Z" } ``` ### `PATCH /v1/test/sessions/:id/response` Configure the custom response returned when webhooks hit this session's ingestion URL. **Auth:** none **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `status_code` | number | No | HTTP status code to return (200-599, default 200). | | `content_type` | string | No | Response content type (default `"application/json"`). | | `body` | string | No | Custom response body (max 4KB). | **Request Example:** ```json { "status_code": 201, "content_type": "application/json", "body": "{\"received\":true}" } ``` **Response Example:** ```json { "ok": true } ``` ### `PATCH /v1/test/sessions/:id/forwarding` Configure webhook forwarding — incoming webhooks are forwarded to the target URL in addition to being recorded. **Auth:** none **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `url` | string | Yes | Target URL to forward webhooks to. Set to null to disable. | **Request Example:** ```json { "url": "http://localhost:3000/webhooks" } ``` **Response Example:** ```json { "ok": true, "forwarding": "http://localhost:3000/webhooks" } ``` ### `PUT /v1/test/sessions/:id/events/:eid/note` Add or update a note on a specific event in a test session. **Auth:** none **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `note` | string | Yes | Note text to attach to the event. | **Request Example:** ```json { "note": "This is the successful payment event" } ``` **Response Example:** ```json { "ok": true } ``` ### `GET /v1/test/sessions/:id/export` Export all events from a test session as NDJSON or CSV. **Auth:** none **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `format` | string | No | Export format: "ndjson" (default) or "csv". | **Response Example:** ```json {"id":"evt_001","method":"POST","body":{"test":true},"received_at":"2026-03-01T12:05:00Z"} {"id":"evt_002","method":"POST","body":{"test":false},"received_at":"2026-03-01T12:06:00Z"} ``` ## Related - [guides/webhook-testing](https://hookstream.io/docs/guides/webhook-testing) - [api/ingest](https://hookstream.io/docs/api/ingest) - [api/websockets](https://hookstream.io/docs/api/websockets) --- # Ingest Public webhook ingestion endpoint — where external services send events. Source: https://hookstream.io/docs/api/ingest ## Overview The ingest endpoint is the public URL where external services send webhook events. Each source gets a unique ingestion URL at `/v1/ingest/`. The endpoint accepts any HTTP method (GET, POST, PUT, PATCH, DELETE) and stores the request method, headers, and body as an event. No authentication is required — security is handled through optional signature verification, deduplication, and IP filtering configured on the source. ## Signature Verification If the source has a verification type configured, hookstream validates the request signature before accepting the event. Supported methods: `hmac_sha256` (Stripe, GitHub, etc.), `hmac_sha1` (legacy providers), and `standard_webhooks` (standardwebhooks.com spec). Failed verification returns 401. ## Deduplication hookstream supports three dedup strategies: `payload_hash` (SHA-256 hash of the request body), `header_field` (uses a specific header as the dedup key, e.g., `Idempotency-Key`), and `body_field` (uses a specific JSON body field as the dedup key). Duplicate events return 200 but are not processed further. ## IP Filtering Sources can define IP allow/deny lists using individual addresses or CIDR ranges. Requests from blocked IPs receive 403 Forbidden. ## API Endpoints ### `ANY /v1/ingest/:source_id` Receive a webhook event at the source's ingestion URL. Accepts any HTTP method. **Auth:** none **Request Example:** ```json { "type": "payment.completed", "data": { "id": "pay_xyz789", "amount": 2999, "currency": "usd" } } ``` **Response Example:** ```json { "event_id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" } ``` ## Related - [api/sources](https://hookstream.io/docs/api/sources) - [guides/signature-verification](https://hookstream.io/docs/guides/signature-verification) - [guides/deduplication](https://hookstream.io/docs/guides/deduplication) --- # Tools API Backend endpoints for free developer tools — health check, scheduler, idempotency tester. Source: https://hookstream.io/docs/api/tools-api ## Overview The Tools API powers hookstream's free developer tools at `/tools`. These endpoints require no authentication but are IP rate-limited. They provide utility functions for webhook development: checking endpoint health, scheduling future webhook deliveries, and testing idempotency behavior. ## API Endpoints ### `POST /v1/tools/health-check` Check the health of a webhook endpoint by sending a test request and measuring the response. **Auth:** none **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `url` | string | Yes | The endpoint URL to check (http or https). | **Request Example:** ```json { "url": "https://api.example.com/webhooks" } ``` **Response Example:** ```json { "url": "https://api.example.com/webhooks", "status_code": 200, "latency_ms": 145, "headers": { "content-type": "application/json" }, "body": "{\"ok\":true}", "healthy": true } ``` ### `POST /v1/tools/schedule` Schedule a webhook to be sent at a future time. **Auth:** none **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `url` | string | Yes | Destination URL for the scheduled webhook (http or https). | | `delay_seconds` | number | Yes | Delay in seconds before sending (1-86400). | | `method` | string | No | HTTP method to use (default "POST"). | | `headers` | object | No | Custom headers to include. | | `body` | string | No | Request body to send. | **Request Example:** ```json { "url": "https://api.example.com/webhooks", "delay_seconds": 3600, "body": "{\"type\": \"reminder\", \"message\": \"Follow up\"}" } ``` **Response Example:** ```json { "schedule_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "fires_at": "2026-03-02T09:00:00Z", "status": "pending" } ``` ### `GET /v1/tools/schedule/:id` Check the status of a scheduled webhook. **Auth:** none **Response Example:** ```json { "url": "https://api.example.com/webhooks", "method": "POST", "fires_at": "2026-03-02T09:00:00Z", "status": "completed", "response_status": 200, "completed_at": "2026-03-02T09:00:01Z" } ``` ### `POST /v1/tools/idempotency-test` Send duplicate requests to test a webhook endpoint's idempotency behavior. **Auth:** none **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `url` | string | Yes | The endpoint URL to test. | | `idempotency_header` | string | Yes | Header name to use for idempotency (e.g., 'Idempotency-Key'). | | `idempotency_value` | string | Yes | Value for the idempotency header. | | `count` | number | Yes | Number of requests to send (2–10). | | `method` | string | No | HTTP method (default POST). | | `headers` | object | No | Additional headers to include. | | `body` | string | No | Request body to send. | | `delay_ms` | number | No | Delay between requests in milliseconds (0–5000). | **Request Example:** ```json { "url": "https://api.example.com/webhooks", "idempotency_header": "Idempotency-Key", "idempotency_value": "order_123_create", "count": 3, "body": "{\"type\": \"order.created\", \"data\": {\"id\": \"ord_123\"}}" } ``` **Response Example:** ```json { "url": "https://api.example.com/webhooks", "idempotency_header": "Idempotency-Key", "idempotency_value": "order_123_create", "total_requests": 3, "unique_responses": 1, "duplicate_responses": 2, "results": [ { "attempt": 1, "status": 200, "latency_ms": 120, "response_body": "{\"ok\":true}", "is_duplicate": false }, { "attempt": 2, "status": 200, "latency_ms": 95, "response_body": "{\"ok\":true}", "is_duplicate": true }, { "attempt": 3, "status": 200, "latency_ms": 88, "response_body": "{\"ok\":true}", "is_duplicate": true } ] } ``` ## Related - [api/ingest](https://hookstream.io/docs/api/ingest) - [api/test-sessions](https://hookstream.io/docs/api/test-sessions) --- # WebSockets Real-time WebSocket connections for live event streaming and inbound sources. Source: https://hookstream.io/docs/api/websockets ## Overview hookstream provides three WebSocket endpoints for real-time communication. All WebSocket connections use Cloudflare Durable Objects with Hibernatable WebSocket API for efficient, stateful connections that survive idle periods without cost. ## Authenticated WebSocket (/v1/ws) The main WebSocket endpoint for authenticated users. For programmatic access, pass your API key as a query parameter: `wss://hookstream.io/v1/ws?token=`. Browser clients use session cookies automatically and do not need the token parameter. After connecting, you receive real-time events for your organization across all sources and destinations. Messages are JSON with a `channel` field for filtering (e.g., `events`, `deliveries`, `issues`). ## Test WebSocket (/v1/ws/test/:source_id) Unauthenticated WebSocket for test sessions. Connects to a per-source EventStream Durable Object (`test:`) and receives real-time events as they arrive at the test session's ingestion URL. Used by the browser-based test inspector. ## Inbound WebSocket Source (/v1/ws/source/:source_id) Authenticated WebSocket for inbound event ingestion. Instead of receiving HTTP webhooks, you can send events over a persistent WebSocket connection at `wss://hookstream.io/v1/ws/source/?token=`. Each message sent on this connection is ingested as an event and routed through the normal delivery pipeline. Useful for high-throughput or bidirectional scenarios. ## Message Format All WebSocket messages are JSON objects with a `type` field indicating the message kind. Server-to-client messages include: `event` (new event received), `delivery` (delivery attempt completed), `issue` (issue created/updated), `sync` (database record synced), and `ping` (heartbeat). Client-to-server messages: `subscribe` (subscribe to a channel) and `pong` (heartbeat response). ## API Endpoints ### `GET /v1/ws` Upgrade to authenticated WebSocket for real-time organization-wide event streaming. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `token` | string | No | API key for authentication (passed as query parameter since WebSocket upgrade cannot use custom headers in browsers). Browser clients use session cookies automatically and can omit this parameter. | **Response Example:** ```json { "type": "event", "channel": "events", "data": { "id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9", "source_id": "src_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "method": "POST", "status": "delivered", "received_at": "2026-03-01T14:30:00Z" } } ``` ### `GET /v1/ws/test/:source_id` Upgrade to unauthenticated WebSocket for a test session's real-time event feed. **Auth:** none **Response Example:** ```json { "type": "event", "channel": "test", "data": { "id": "evt_001", "method": "POST", "body": { "test": true }, "received_at": "2026-03-01T12:05:00Z" } } ``` ### `GET /v1/ws/source/:source_id` Upgrade to authenticated WebSocket for inbound event ingestion. Send JSON messages to create events. **Auth:** combined **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `token` | string | Yes | API key for authentication. | **Request Example:** ```json { "type": "order.created", "data": { "id": "ord_123", "amount": 4999 } } ``` **Response Example:** ```json { "type": "ack", "event_id": "evt_d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9" } ``` ## Related - [guides/real-time-streaming](https://hookstream.io/docs/guides/real-time-streaming) - [api/test-sessions](https://hookstream.io/docs/api/test-sessions) - [api/events](https://hookstream.io/docs/api/events) --- # Errors Standard error response format and common HTTP status codes. Source: https://hookstream.io/docs/api/errors ## Overview All hookstream API errors return a consistent JSON format with an error message, HTTP status code, and a unique `request_id` for debugging. The `request_id` can be provided to hookstream support for investigating issues. ## Error Response Format Every error response includes three fields: `error` (human-readable message), `status` (HTTP status code), and `request_id` (unique identifier for the request). Example: ```json { "error": "Source not found", "status": 404, "request_id": "req_a1b2c3d4e5f6" } ``` ## Common Status Codes **400 Bad Request** — Invalid request body, missing required fields, or malformed parameters. **401 Unauthorized** — Missing or invalid authentication credentials. Check your API key or JWT token. **403 Forbidden** — Valid credentials but insufficient permissions, or IP address blocked by source's deny list. **404 Not Found** — The requested resource does not exist or belongs to a different organization. **409 Conflict** — Duplicate resource. For ingestion, this means the event was deduplicated. **429 Too Many Requests** — Rate limit exceeded. Check the `X-RateLimit-*` response headers and retry after the reset time. **500 Internal Server Error** — An unexpected error occurred on the server. Retry the request or contact support with the `request_id`. ## Related - [api/authentication](https://hookstream.io/docs/api/authentication) - [api/pagination-rate-limits](https://hookstream.io/docs/api/pagination-rate-limits) --- # Pagination & Rate Limits Cursor-based pagination and rate limiting across all API endpoints. Source: https://hookstream.io/docs/api/pagination-rate-limits ## Overview hookstream uses cursor-based pagination for all list endpoints and applies rate limiting to protect the platform. Understanding these patterns helps you build robust integrations that handle large datasets and respect rate limits. ## Cursor-Based Pagination All list endpoints return paginated results. Each endpoint uses a named wrapper key matching the resource type (e.g., `sources`, `destinations`, `events`, `connections`). The response includes pagination metadata. To fetch the next page, pass the cursor value as a query parameter. Cursors are opaque strings — do not parse or construct them. Example: ```shellscript GET /v1/sources?limit=20 ``` → `{ "sources": [...], "cursor": "eyJpZCI6...", "has_more": true }` ```shellscript GET /v1/sources?limit=20&cursor=eyJpZCI6... ``` → `{ "sources": [...], "cursor": null, "has_more": false }` ## Pagination Parameters All list endpoints accept two pagination parameters: - `cursor` (string, optional): Opaque cursor from a previous response. Omit for the first page. - `limit` (number, optional): Number of results per page. Default is 20, maximum is 100. Results are ordered by creation time (newest first) unless otherwise specified. ## Rate Limiting Authenticated endpoints are rate-limited per organization. Unauthenticated endpoints (ingest, test sessions, tools) are rate-limited per IP address. When you exceed the rate limit, the API returns 429 Too Many Requests. ## Rate Limit Headers Every response includes rate limit headers: - `X-RateLimit-Limit`: Maximum requests allowed in the current window. - `X-RateLimit-Remaining`: Requests remaining in the current window. - `X-RateLimit-Reset`: Unix timestamp (seconds) when the window resets. When `X-RateLimit-Remaining` reaches 0, wait until the `X-RateLimit-Reset` time before making additional requests. Example headers: ``` X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 742 X-RateLimit-Reset: 1709312400 ``` ## Best Practices 1. Always check `has_more` before fetching the next page — do not assume fixed page sizes. 2. Implement exponential backoff when receiving 429 responses. 3. Cache the `X-RateLimit-Reset` header and avoid requests until the window resets. 4. Use the `limit` parameter to control page size based on your processing capacity. 5. For high-volume integrations, use WebSocket streaming instead of polling list endpoints. ## Related - [api/events](https://hookstream.io/docs/api/events) - [api/errors](https://hookstream.io/docs/api/errors) --- # Billing Check usage, create checkout sessions, manage subscriptions, and view invoices. Source: https://hookstream.io/docs/api/billing ## Overview The Billing API lets you check your plan's usage, create Stripe Checkout sessions to upgrade, open the Stripe Customer Portal to manage your subscription, view current subscription details, and list invoices. All billing endpoints require authentication (`combinedAuth`). ## Plan Limits Each plan includes specific resource limits: - Free: 50,000 events/month, 10 sources, 5 destinations, 7-day retention - Pro: 1,000,000 events/month, unlimited sources & destinations, 30-day retention - Enterprise: 10,000,000 events/month, unlimited sources & destinations, 90-day retention Paid plans have a soft limit — events are accepted up to 120% before returning 429. Free plan has a hard cutoff at 100%. ## API Endpoints ### `GET /v1/billing/usage` Get current usage vs plan limits for the authenticated organization. **Auth:** combined **Response Example:** ```json { "plan": "pro", "events": { "used": 42531, "limit": 1000000, "percentage": 4 }, "sources": { "used": 3, "limit": null }, "destinations": { "used": 2, "limit": null }, "retention_days": 30 } ``` ### `POST /v1/billing/checkout` Create a Stripe Checkout session. Returns a URL to redirect the user to. **Auth:** combined **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `plan` | string | Yes | Plan to subscribe to: "pro" or "enterprise". | | `interval` | string | No | Billing interval: "monthly" (default) or "annual". | | `coupon` | string | No | Stripe coupon ID to apply. | **Request Example:** ```json { "plan": "pro", "interval": "annual" } ``` **Response Example:** ```json { "checkout_url": "https://checkout.stripe.com/c/pay/..." } ``` ### `POST /v1/billing/portal` Create a Stripe Customer Portal session for subscription management. **Auth:** combined **Response Example:** ```json { "portal_url": "https://billing.stripe.com/p/session/..." } ``` ### `GET /v1/billing/subscription` Get current subscription details. **Auth:** combined **Response Example:** ```json { "subscription": { "id": "sub_abc123", "plan": "pro", "status": "active", "current_period_start": "2026-03-01T00:00:00Z", "current_period_end": "2026-04-01T00:00:00Z", "cancel_at_period_end": 0 } } ``` ### `GET /v1/billing/invoices` List recent invoices from Stripe (up to 12). **Auth:** combined **Response Example:** ```json { "invoices": [ { "id": "in_abc123", "number": "INV-0001", "status": "paid", "amount_due": 1900, "amount_paid": 1900, "currency": "usd", "period_start": "2026-03-01T00:00:00Z", "period_end": "2026-04-01T00:00:00Z", "hosted_invoice_url": "https://invoice.stripe.com/i/...", "created": "2026-03-01T00:00:00Z" } ] } ``` ## Related - [guides/billing](https://hookstream.io/docs/guides/billing) --- # Guides # Signature Verification Verify incoming webhooks with HMAC-SHA256, SHA1, or Standard Webhooks. Source: https://hookstream.io/docs/guides/signature-verification ## Why Verify Signatures? Webhook signatures prove that a request came from the expected sender, not an attacker. The sender creates a hash of the payload using a shared secret. hookstream recomputes the hash and compares — if they don't match, the request is rejected with 401. ## HMAC-SHA256 The most common verification method, used by Stripe, GitHub, Shopify, and others. 1. The sender computes `HMAC-SHA256(secret, request_body)` and includes it in a header 2. Configure your source with `verification_type: "hmac-sha256"` and the signing secret 3. Specify which header contains the signature (e.g., `X-Hub-Signature-256` for GitHub, `Stripe-Signature` for Stripe) 4. hookstream recomputes the HMAC and compares using constant-time comparison Some providers include a prefix (`sha256=`) before the hex signature. hookstream handles this automatically. ## HMAC-SHA1 Some legacy providers use HMAC-SHA1. The process is identical to HMAC-SHA256 but uses the SHA-1 hash algorithm. Configure with `verification_type: "hmac-sha1"`. Note: SHA-1 is considered less secure; prefer SHA-256 when possible. ## Standard Webhooks Standard Webhooks is an open spec for webhook signatures. It uses HMAC-SHA256 with specific header conventions: - `webhook-id`: unique event identifier - `webhook-timestamp`: Unix timestamp - `webhook-signature`: `v1,` The signed content is: `webhook-id.webhook-timestamp.body` Configure with `verification_type: "standard-webhooks"` and your signing secret (base64-encoded). ## Configuring Verification Set up signature verification when creating or updating a source: `POST /v1/sources` ```json { "name": "Stripe Webhooks", "verification_type": "hmac-sha256", "verification_secret": "whsec_...", "verification_header": "stripe-signature" } ``` Or use a source template which pre-configures these fields for common providers. ## Testing Verification Use the free Signature Validator tool at `/tools/signature-validator` to test your verification setup. Paste your secret, headers, and body to see step-by-step verification results. You can also use the HMAC Calculator at `/tools/hmac-calculator` to compute signatures manually. ## Related - [sources](https://hookstream.io/docs/sources) - [api/sources](https://hookstream.io/docs/api/sources) - [guides/ip-filtering](https://hookstream.io/docs/guides/ip-filtering) --- # Retry Strategies Configure exponential, linear, or fixed backoff for delivery retries. Source: https://hookstream.io/docs/guides/retry-strategies ## How Retries Work When a delivery attempt fails (non-2xx status or network error), hookstream automatically retries using the destination's configured retry policy. Retries are managed by the DeliveryScheduler Durable Object using alarm-based scheduling, ensuring reliable execution even across Worker restarts. ## Exponential Backoff The default strategy. Uses a pre-configured intervals array where each retry uses the next value: - Attempt 1: immediate - Retry 1: 30s delay - Retry 2: 5m delay - Retry 3: 30m delay - Retry 4: 2h delay - Retry 5: 24h delay Default intervals: `[30, 300, 1800, 7200, 86400]` seconds. If the attempt exceeds the array length, the last value is used. Exponential backoff gives failing endpoints time to recover while keeping total retry duration bounded. ## Linear Backoff Each retry multiplies the base interval by the attempt number: - Retry 1: base × 1 - Retry 2: base × 2 - Retry 3: base × 3 - Retry 4: base × 4 - Retry 5: base × 5 Delay formula: `intervals[0] × attemptNumber` The base value is the first element of the `retry_intervals` array (default 30s). Linear backoff provides more predictable retry timing. ## Fixed Backoff Every retry uses the same delay: - Retry 1: 30s delay - Retry 2: 30s delay - Retry 3: 30s delay Delay formula: `intervals[0]` (default 30s) Fixed backoff is useful for idempotent endpoints where you want consistent retry intervals. ## Configuration Set retry policy when creating a destination: ```json { "name": "My API", "type": "http", "config": { "url": "https://api.example.com/webhook" }, "retry_max_attempts": 5, "retry_backoff_type": "exponential", "retry_intervals": [30, 300, 1800, 7200, 86400] } ``` Defaults: `retry_max_attempts=5`, `retry_backoff_type=exponential`, `retry_intervals=[30, 300, 1800, 7200, 86400]`. ## Jitter Retry delays are deterministic based on the configured intervals array and backoff type. The `intervals` array defines the exact delays used for exponential backoff, while linear and fixed strategies derive from the first interval value. ## Related - [delivery-pipeline](https://hookstream.io/docs/delivery-pipeline) - [api/destinations](https://hookstream.io/docs/api/destinations) - [guides/dead-letter-queue](https://hookstream.io/docs/guides/dead-letter-queue) --- # Circuit Breaker Understand circuit breaker states, thresholds, and reset behavior. Source: https://hookstream.io/docs/guides/circuit-breaker ## What is the Circuit Breaker? The circuit breaker is a per-destination protection mechanism that prevents hookstream from overwhelming a failing endpoint. When consecutive failures exceed a threshold, the circuit "opens" and pauses delivery — giving the endpoint time to recover. ## Circuit Breaker States Closed (normal): Deliveries proceed normally. Consecutive failure count is tracked. Open (tripped): After 5 consecutive failures, the circuit opens. All new deliveries are queued (not dropped). A cooldown alarm is set. Half-Open (testing): After the cooldown period, one probe delivery is sent. If it succeeds, the circuit closes and queued events are flushed. If it fails, the circuit returns to open. ## Thresholds Default settings: - Failure threshold: 5 consecutive failures to open - Cooldown period: 60 seconds before half-open probe - Success threshold: 1 successful probe to close These values are per-destination and managed by the DeliveryScheduler Durable Object. ## Monitoring Circuit breaker status is visible in: - Dashboard: Destination detail page shows current state (closed/open/half-open) - API: `GET /v1/destinations/:id/circuit` returns state, failure count, and last transition timestamp - Issues: An issue is auto-created when a circuit opens - Alerts: `issue_opened` alert type triggers when circuit opens ## Manual Reset You can manually reset a circuit breaker: `POST /v1/destinations/:id/circuit/reset` This immediately closes the circuit and resets the failure counter. Use this when you've fixed the underlying issue and want to resume delivery without waiting for the cooldown. Also available via the dashboard reset button. ## What Happens to Queued Events? When the circuit is open, new deliveries are queued in the Durable Object. When the circuit closes (after successful probe or manual reset), all queued events are delivered in order. Events are not dropped — only delayed. If events expire their max retry window while queued, they move to DLQ. ## Related - [delivery-pipeline](https://hookstream.io/docs/delivery-pipeline) - [api/destinations](https://hookstream.io/docs/api/destinations) - [guides/retry-strategies](https://hookstream.io/docs/guides/retry-strategies) --- # Dead Letter Queue DLQ flow, manual retry, and bulk retry for permanently failed events. Source: https://hookstream.io/docs/guides/dead-letter-queue ## What is the DLQ? The dead letter queue (DLQ) holds events that have exhausted all retry attempts. Instead of being lost, these events are flagged as permanently failed and available for inspection and manual retry. ## DLQ Flow 1. Delivery attempt fails 2. Retry is scheduled according to backoff policy 3. After `retry_max_attempts` attempts, the delivery is marked as DLQ 4. An issue is created/updated with the failure details 5. The delivery attempt is stored with `is_dlq=true` and `dlq_at` timestamp 6. You can retry the event manually at any time ## Inspecting DLQ Events Find DLQ events in: - Events page: filter by `status="failed"` - Issues page: each issue groups related failures - API: `GET /v1/events?status=failed` - CLI: `hookstream events list --status failed` Each failed event shows all delivery attempts with status codes, error messages, and latency. ## Manual Retry Retry a single event: `POST /v1/events/:id/retry` Retry a single delivery attempt: `POST /v1/delivery-attempts/:id/retry` Bulk retry all events in an issue: `POST /v1/issues/:id/retry` Retried events create new delivery attempts. The original attempts are preserved for audit. ## Best Practices 1. Set up alert rules for DLQ events so you're notified immediately 2. Check circuit breaker status before retrying — if the destination is still down, retries will fail again 3. Use bulk retry via issues to handle batches of related failures 4. Review failure patterns in the Metrics page (error breakdown chart) 5. Consider increasing `max_retries` or adjusting backoff for unreliable destinations ## Related - [delivery-pipeline](https://hookstream.io/docs/delivery-pipeline) - [guides/retry-strategies](https://hookstream.io/docs/guides/retry-strategies) - [api/events](https://hookstream.io/docs/api/events) --- # IP Filtering Configure IP allowlists and denylists with CIDR notation for sources. Source: https://hookstream.io/docs/guides/ip-filtering ## IP Filtering Overview IP filtering adds a network-level security layer to your sources. Restrict which IP addresses can send webhooks by configuring an allowlist (only these IPs) or denylist (block these IPs). Requests from blocked IPs are rejected with 403 Forbidden. ## IP Allowlist An allowlist specifies which IPs are permitted. All other IPs are blocked. Use this when you know the exact IPs your webhook provider sends from. Example: Stripe sends from a known set of IPs listed in their documentation. Configure your source's `ip_allowlist` with those IPs/CIDRs. ## IP Denylist A denylist specifies which IPs are blocked. All other IPs are allowed. Use this to block known bad actors or internal IPs that shouldn't be sending webhooks. ## CIDR Notation Both allowlist and denylist support CIDR notation for IPv4 ranges: - Single IP: `192.168.1.1` - /24 subnet: `192.168.1.0/24` (256 addresses) - /16 subnet: `10.0.0.0/16` (65,536 addresses) Note: IPv6 CIDR is not currently supported. Separate multiple entries with commas. ## Configuration Set IP filtering when creating or updating a source: `POST /v1/sources` ```json { "name": "Stripe Webhooks", "ip_allowlist": "54.187.174.169,54.187.205.235,54.187.216.72" } ``` Or use the dashboard: Source settings > IP Filtering. Comma-separated IPs and CIDR ranges. ## Evaluation Order IP filtering is evaluated early in the ingest pipeline, before signature verification and deduplication. The client IP is read from the `CF-Connecting-IP` header (falling back to `X-Forwarded-For`). Evaluation order: 1. Denylist checked first (403 if IP in deny list) 2. Allowlist checked second (403 if IP not in allow list) 3. Signature verification (401 if invalid) 4. Dedup check 5. Event storage and processing ## Related - [sources](https://hookstream.io/docs/sources) - [api/sources](https://hookstream.io/docs/api/sources) - [guides/signature-verification](https://hookstream.io/docs/guides/signature-verification) --- # Deduplication Prevent duplicate webhook processing with payload hash or field-based dedup. Source: https://hookstream.io/docs/guides/deduplication ## Why Deduplicate? Webhook providers sometimes send the same event multiple times — during retries, infrastructure failovers, or race conditions. Deduplication ensures each unique event is processed only once, preventing double-charges, duplicate notifications, or data corruption. ## Payload Hash Strategy The `payload_hash` strategy. hookstream computes a SHA-256 hash of the full request body and checks it against a KV-stored window. If the hash exists, the event is stored with `dedup_status: "duplicate"` for visibility but is not processed through the delivery pipeline. ## Field-Based Strategy Two field-based strategies extract a specific value as the dedup key: - `header_field`: extracts from a request header (e.g., `X-GitHub-Delivery`) - `body_field`: extracts from the JSON body using dot-notation (e.g., `data.id`) Configure with `dedup_strategy` and `dedup_field` to specify which header or body path to use. ## Dedup Window The dedup window determines how long a hash is remembered. Default: 5 minutes (300 seconds). After the window expires, the same payload could be processed again. Choose a window that exceeds your provider's retry window. Most providers retry within 1-5 minutes, so the default 5-minute window covers most cases. ## KV-Backed Storage Dedup hashes are stored in Cloudflare KV with automatic TTL-based expiry. KV provides globally consistent reads within seconds, low-latency lookups, and automatic cleanup — no maintenance required. ## Configuration Configure dedup when creating a source: ```json { "name": "GitHub Webhooks", "dedup_strategy": "header_field", "dedup_field": "x-github-delivery", "dedup_window_seconds": 600 } ``` Or use payload hash: ```json { "dedup_strategy": "payload_hash", "dedup_window_seconds": 300 } ``` Or use a body field: ```json { "dedup_strategy": "body_field", "dedup_field": "data.id", "dedup_window_seconds": 300 } ``` ## Related - [sources](https://hookstream.io/docs/sources) - [api/sources](https://hookstream.io/docs/api/sources) - [guides/ip-filtering](https://hookstream.io/docs/guides/ip-filtering) --- # Schema Validation Validate incoming payloads against JSON Schema with reject or warn mode. Source: https://hookstream.io/docs/guides/schema-validation ## Schema Validation Overview Schema validation checks incoming webhook payloads against a JSON Schema you define. This catches malformed or unexpected data before it enters your pipeline. Two modes: `reject` (return 422) or `warn` (accept with flag). ## Reject Mode In reject mode, invalid payloads are rejected with HTTP 422 Unprocessable Entity. The response includes validation error details (`validation_errors` array). The event is not stored or processed. Use reject mode when data integrity is critical. ## Warn Mode In warn mode, invalid payloads are accepted and stored with `verification_status` set to `"schema_invalid"`. The event is processed normally through connections. Use warn mode during migration or when you want visibility without blocking. ## Defining a Schema Attach a JSON Schema to your source: `PATCH /v1/sources/:id` ```json { "schema_validation_enabled": true, "json_schema": { "type": "object", "required": ["type", "data"], "properties": { "type": { "type": "string" }, "data": { "type": "object" } } }, "schema_action": "reject" } ``` The schema follows JSON Schema Draft 4, 6, and 7 specifications. ## Implementation Schema validation uses `@cfworker/json-schema`, a Workers-native JSON Schema validator with zero Node.js dependencies. It supports JSON Schema Draft 4, 6, and 7 with most keywords including `$ref`, `allOf`, `anyOf`, `oneOf`, `pattern`, and `format`. ## Schema Inference Don't know your schema yet? Use the Instant Database's schema inference feature to learn it from live data. Create a collection, send some webhooks, then view the inferred schema at `GET /v1/collections/:id/schema`. Copy it to your source configuration. ## Related - [sources](https://hookstream.io/docs/sources) - [api/sources](https://hookstream.io/docs/api/sources) - [filters-transforms](https://hookstream.io/docs/filters-transforms) --- # Event Replay Replay historical events to any destination with rate limiting. Source: https://hookstream.io/docs/guides/event-replay ## What is Event Replay? Event replay lets you re-deliver historical events to any destination. Useful for: re-processing after a bug fix, backfilling a new destination, testing changes against real production data, or recovering from outages. ## Creating a Replay Job `POST /v1/replay` ```json { "destination_id": "dest_xyz", "from": "2025-03-01T00:00:00Z", "to": "2025-03-02T00:00:00Z", "source_id": "src_abc", "rate_limit": 10, "max_events": 1000 } ``` `destination_id`, `from`, and `to` are required. `source_id` is optional (filters to a specific source). This replays matching events to the target destination, at up to 10 events per second. ## Rate Limiting Replay jobs are rate-limited to prevent overwhelming destinations. Set `rate_limit` (events per batch before a 1-second pause) when creating the job. Default is 10, max 100. Set `max_events` to cap total events replayed (default 1000, max 10000). Replayed events use `retryEnabled: false` — failures are recorded but not retried. ## Tracking Progress Check replay job status: `GET /v1/replay/:job_id` Response includes: `status` (running/completed), `total`, `processed`, `succeeded`, `failed`, `started_at`, and `completed_at`. Jobs are stored in KV with 24-hour auto-expiry. ## Best Practices 1. Start with a narrow time range to test before replaying a large window 2. Use a lower rate limit for destinations with rate limits of their own 3. Replay to a test destination first to verify payload handling 4. Monitor the destination's circuit breaker during replay 5. Events are replayed in chronological order ## Related - [api/replay](https://hookstream.io/docs/api/replay) - [events](https://hookstream.io/docs/events) - [delivery-pipeline](https://hookstream.io/docs/delivery-pipeline) --- # Webhook Testing Ephemeral test sessions, graduation, forwarding, and the testing workflow. Source: https://hookstream.io/docs/guides/webhook-testing ## Webhook Testing hookstream provides free webhook testing at `/test` — no signup required. Get a webhook URL instantly, inspect requests in real-time, and optionally graduate the session to your account. ## Creating a Test Session Visit `hookstream.io/test` to auto-create a session. You get: - A unique webhook URL (`https://hookstream.io/v1/ingest/`) - A two-panel inspector UI with real-time updates - 14-day TTL or 2,000 requests, whichever comes first Or via CLI: `hookstream test` ## Inspector UI The inspector shows: - Left panel: list of received events with method, source IP, and timestamp - Right panel: selected event detail with headers, body (syntax-highlighted JSON), query parameters, and metadata - Code snippets in cURL, JavaScript, Python, and Go - Provider auto-detection (Stripe, GitHub, Shopify, etc.) - Search and filter across events - Event notes for annotations ## Custom Response Configure what your test URL returns: `PATCH /v1/test/sessions/:id/response` ```json { "status_code": 201, "headers": { "X-Custom": "value" }, "body": "{\"ok\": true}" } ``` Useful for testing how providers handle different response codes. ## Webhook Forwarding Forward test webhooks to another URL (like your local development server): `PATCH /v1/test/sessions/:id/forwarding` ```json { "url": "https://your-ngrok-url.ngrok.io/webhook", "enabled": true } ``` Forwarding is non-blocking (via `waitUntil`) and includes the forwarding response in the event detail. ## Session Graduation When you sign up, you can "graduate" a test session to your account: `POST /v1/test/sessions/:id/claim` This transfers the source and all events to your org. The webhook URL stays the same, but events now appear in your dashboard with full pipeline support. ## CLI Testing Use the CLI for terminal-based testing: `hookstream test` — create a session and open the inspector URL `hookstream listen` — create a session and stream events to your terminal in real-time via WebSocket The `listen` command shows each event's method, headers, and body as they arrive. ## Related - [api/test-sessions](https://hookstream.io/docs/api/test-sessions) - [first-webhook](https://hookstream.io/docs/first-webhook) - [guides/real-time-streaming](https://hookstream.io/docs/guides/real-time-streaming) --- # Real-Time Streaming WebSocket push, channels, and polling fallback for live updates. Source: https://hookstream.io/docs/guides/real-time-streaming ## Real-Time Push hookstream pushes updates to connected clients in real-time via WebSockets. The EventStream Durable Object manages per-org WebSocket connections using Cloudflare's Hibernatable WebSocket API — zero cost when idle. ## Connecting Connect to the WebSocket endpoint: `wss://hookstream.io/v1/ws?token=` The connection authenticates via the `token` query parameter (API key) or session cookie. On connect, you receive a welcome message with your `org_id`. The connection supports channels for filtering updates. ## Channels Subscribe to specific update channels: - `events`: new events received by any source - `events:`: events for a specific source - `deliveries`: delivery attempts completed - `deliveries:`: deliveries for a specific destination - `issues`: issue created/updated/resolved - `collections:`: database sync updates Send a subscribe message: `{ "type": "subscribe", "channel": "events" }` ## Message Format Messages are JSON with a `type` field: ```json { "type": "event", "channel": "events", "data": { ... } } { "type": "delivery", "channel": "deliveries", "data": { ... } } { "type": "issue", "channel": "issues", "data": { ... } } ``` The `data` field contains the full resource object (event, delivery attempt, or issue). ## Reconnection The dashboard's `useEventStream` hook handles reconnection automatically with exponential backoff. On disconnect, it retries connection with increasing delays (1s, 2s, 4s, up to 30s). Channel subscriptions are re-sent after reconnection. ## Polling Fallback All dashboard pages also poll the API at regular intervals as a fallback. When WebSocket is connected, polling intervals are longer (30s). When disconnected, polling intervals are shorter (5s) to maintain data freshness. The `usePolling` hook manages this automatically. ## Related - [api/websockets](https://hookstream.io/docs/api/websockets) - [events](https://hookstream.io/docs/events) - [guides/webhook-testing](https://hookstream.io/docs/guides/webhook-testing) --- # Monitoring & Alerts Alert rules (5 types), notification channels, and auto-resolve behavior. Source: https://hookstream.io/docs/guides/monitoring-alerts ## Monitoring Overview hookstream provides automated monitoring through issues (auto-detected problems), alert rules (configurable triggers), and notification channels (where alerts are sent). A 5-minute Cron evaluates all rules and sends notifications. ## Issues Issues are auto-created when delivery problems occur: - Delivery failure after all retries exhausted - Circuit breaker opens on a destination - Error rate exceeds threshold Issues track severity, affected resources, failure count, and timeline. Issues auto-resolve after 30 minutes of no new failures. ## Alert Rule Types 5 alert types: 1. `failure_rate`: triggers when delivery failure rate exceeds threshold (percentage) 2. `latency`: triggers when p95 delivery latency exceeds threshold (milliseconds) 3. `volume_drop`: triggers when event volume drops below threshold vs previous window 4. `dlq`: triggers when DLQ event count exceeds threshold 5. `issue_opened`: triggers when new issues are created ## Creating Alert Rules `POST /v1/alert-rules` ```json { "name": "High Failure Rate", "rule_type": "failure_rate", "rule_config": { "threshold": 10 }, "channel_ids": ["ch_abc"], "cooldown_minutes": 60 } ``` This triggers when the failure rate exceeds 10%, sending notifications to the specified channels. Default cooldown is 60 minutes. ## Notification Channels Channels define where alerts are sent. Currently supports webhook channel type: `POST /v1/notification-channels` ```json { "name": "Ops Webhook", "channel_type": "webhook", "config": { "url": "https://ops.example.com/alerts", "secret": "optional_hmac_secret" } } ``` Notifications include HMAC-SHA256 signing (`X-hookstream-Signature` header) if a secret is configured. Test with `POST /v1/notification-channels/:id/test`. ## Auto-Resolve Issues and alert triggers auto-resolve after 30 minutes of no new matching events. The 5-minute Cron checks all open issues and resolves those that have been stable. Manual resolution is also available via `PATCH /v1/issues/:id`. ## Related - [api/alert-rules](https://hookstream.io/docs/api/alert-rules) - [api/notification-channels](https://hookstream.io/docs/api/notification-channels) - [api/issues](https://hookstream.io/docs/api/issues) --- # Outbound Signing Sign outbound webhook requests with HMAC for receiver verification. Source: https://hookstream.io/docs/guides/outbound-signing ## Outbound Signing Outbound signing lets you sign webhook deliveries sent by hookstream, so your receiving endpoints can verify the request is authentic. This mirrors the inbound signature verification that sources use, but applied to outbound requests. ## How It Works When outbound signing is enabled on an HTTP destination: 1. hookstream computes `HMAC(secret, request_body)` using the configured algorithm 2. The signature is included in a header on the outbound request 3. Your receiving endpoint recomputes the HMAC and compares 4. If signatures match, the request is verified as coming from hookstream ## Configuration Configure outbound signing on an HTTP destination's `signing_config`: ```json { "type": "http", "config": { "url": "https://api.example.com/webhook" }, "signing_config": { "secret": "your_secret_here", "algorithm": "sha256", "header": "X-hookstream-Signature", "prefix": "sha256=", "include_timestamp": false, "timestamp_header": "X-hookstream-Timestamp" } } ``` Defaults: `prefix="{algorithm}="`, `include_timestamp=false`. When `include_timestamp` is true, a Unix timestamp is prepended to the signing payload and added as a separate header. ## Supported Algorithms Two algorithms: - `sha256` (recommended): HMAC-SHA256, produces 64-character hex signature - `sha1`: HMAC-SHA1, produces 40-character hex signature The signature is sent as a hex-encoded string in the configured header. ## Receiver Verification Example In your receiving endpoint (Node.js): ```js const crypto = require('crypto'); const signature = req.headers['x-hookstream-signature']; const expected = crypto .createHmac('sha256', process.env.HOOKSTREAM_SECRET) .update(req.rawBody) .digest('hex'); if (signature !== expected) { return res.status(401).json({ error: 'Invalid signature' }); } ``` ## Related - [destinations](https://hookstream.io/docs/destinations) - [api/destinations](https://hookstream.io/docs/api/destinations) - [guides/signature-verification](https://hookstream.io/docs/guides/signature-verification) --- # Billing & Plans Understand hookstream plans, usage limits, and how to manage your subscription. Source: https://hookstream.io/docs/guides/billing ## Plans Overview hookstream offers three plans: - Free: $0/month — 50,000 events/month, 10 sources, 5 destinations, 7-day retention - Pro: $19/month ($190/year) — 1,000,000 events/month, unlimited sources & destinations, 30-day retention - Enterprise: $99/month ($990/year) — 10,000,000 events/month, unlimited sources & destinations, 90-day retention All plans include webhook testing, signature verification, automatic retries, and the full API. ## Usage Tracking Event usage is tracked per organization per calendar month (UTC). The counter resets on the 1st of each month. You can check your current usage via: - Dashboard: Settings > Billing tab - API: `GET /v1/billing/usage` - CLI: `hookstream billing usage` A usage warning banner appears in the dashboard when you reach 80% of your monthly event limit. ## Limit Enforcement When you reach your plan's event limit: - Free plan: Hard cutoff — ingest returns 429 at 100% of the limit - Pro/Enterprise: Soft limit — events are allowed up to 120% of the limit before 429 Source and destination limits are enforced when creating new resources. Existing resources are never deleted when downgrading. The API returns 403 when you attempt to exceed source or destination limits. ## Upgrading Your Plan Upgrade from the dashboard (Settings > Billing) or the Pricing page. hookstream uses Stripe Checkout for secure payment processing — you'll be redirected to Stripe to complete payment. Both monthly and annual billing intervals are available. Annual billing saves ~17%. After checkout completes, your plan upgrades instantly and new limits take effect immediately. ## Managing Your Subscription Use the Stripe Customer Portal to: - Update payment method - Switch between monthly and annual billing - Cancel your subscription - View and download invoices Access the portal from Settings > Billing > Manage Subscription. ## Downgrading & Cancellation When you cancel, your paid plan remains active until the end of the current billing period. After that, you'll revert to the Free plan. Existing sources, destinations, and connections are preserved. If you exceed Free plan limits, you won't be able to create new resources, but existing ones continue to work. Events beyond the Free limit will be rejected (429). ## Coupons & Promotions hookstream supports Stripe promotion codes. Enter a code during checkout to receive a discount. Codes can provide percentage or fixed-amount discounts, and may be limited to specific plans or time periods. Promotion codes are entered on the Stripe Checkout page. ## Related - [api/billing](https://hookstream.io/docs/api/billing) - [cli/commands](https://hookstream.io/docs/cli/commands) --- # Destination Providers # HTTP / Webhook Configure HTTP/webhook destinations with custom headers, auth, and timeouts. Source: https://hookstream.io/docs/providers/http ## Overview The `http` provider delivers events to any HTTP endpoint via POST request. This is the most common destination type, used for webhook delivery to your APIs, Slack, Discord, PagerDuty, and any service that accepts HTTP webhooks. ## Configuration Create an HTTP destination: `POST /v1/destinations` ```json { "name": "My API", "type": "http", "config": { "url": "https://api.example.com/webhook", "method": "POST", "headers": { "Authorization": "Bearer sk_live_...", "X-Custom-Header": "value" }, "timeout": 30 } } ``` ## Config Fields - `url` (required): the destination URL - `method`: HTTP method, default `POST` - `headers`: custom headers merged with the request - `timeout`: request timeout in seconds (default 30) Outbound signing is configured separately on the destination's `signing_config` object (not inside `config`). See the Outbound Signing guide for details. ## Payload The event body is sent as-is in the HTTP request body. `Content-Type` is preserved from the original event. If a JSONata transform is configured on the connection, the transformed payload is sent instead. hookstream adds these headers to every delivery: - `X-hookstream-Event-Id`: the event ID - `X-hookstream-Delivery-Attempt`: the attempt number (1, 2, 3...) - `X-hookstream-Timestamp`: ISO 8601 delivery timestamp ## Authentication Add authentication via headers: - Bearer token: `{ "Authorization": "Bearer sk_..." }` - Basic auth: `{ "Authorization": "Basic " }` - API key: `{ "X-API-Key": "your_key" }` - Custom: any header name/value pair ## Destination Templates Pre-configured templates for common services: - **Slack**: Incoming webhook URL, `Content-Type: application/json` - **Discord**: Webhook URL with `/slack` suffix for Slack-compatible format - **PagerDuty**: Events API v2 with routing key - **Datadog**: HTTP intake with `DD-API-KEY` header - **Zapier**: Webhook trigger URL - **Linear**: Webhook URL with API key header ## Related - [destinations](https://hookstream.io/docs/destinations) - [api/destinations](https://hookstream.io/docs/api/destinations) - [guides/outbound-signing](https://hookstream.io/docs/guides/outbound-signing) --- # AWS SQS Push events to Amazon SQS queues with IAM authentication. Source: https://hookstream.io/docs/providers/aws-sqs ## Overview The `aws_sqs` provider pushes events to an Amazon SQS queue. Authentication uses AWS SigV4 signing computed entirely with `crypto.subtle` (no AWS SDK). Events are sent as SQS messages with the event body as the message body. ## Configuration `POST /v1/destinations` ```json { "name": "Order Queue", "type": "sqs", "config": { "queue_url": "https://sqs.us-east-1.amazonaws.com/123456789/my-queue", "region": "us-east-1", "access_key_id": "AKIA...", "secret_access_key": "wJal..." } } ``` ## Config Fields - `queue_url` (required): full SQS queue URL - `region` (required): AWS region (e.g., `us-east-1`) - `access_key_id` (required): IAM access key - `secret_access_key` (required): IAM secret key (masked in GET responses) - `message_group_id`: for FIFO queues, sets `MessageGroupId` and auto-generates `MessageDeduplicationId` from event ID ## IAM Permissions The IAM user needs `sqs:SendMessage` permission on the queue ARN: ```json { "Effect": "Allow", "Action": "sqs:SendMessage", "Resource": "arn:aws:sqs:us-east-1:123456789:my-queue" } ``` ## SigV4 Signing hookstream signs SQS requests using AWS Signature Version 4, implemented with Web Crypto API (`crypto.subtle`). No AWS SDK is used — the signing is pure JavaScript running on Cloudflare Workers. This ensures compatibility and minimal bundle size. ## Security AWS credentials are stored encrypted and never exposed in API responses. The `secret_access_key` field is masked in GET responses (showing only the last 4 characters). Use an IAM user with minimal permissions scoped to the specific queue. ## Related - [destinations](https://hookstream.io/docs/destinations) - [providers/aws-s3](https://hookstream.io/docs/providers/aws-s3) - [providers/aws-eventbridge](https://hookstream.io/docs/providers/aws-eventbridge) --- # AWS S3 Write events to S3 buckets with configurable key prefixes and partitioning. Source: https://hookstream.io/docs/providers/aws-s3 ## Overview The `aws_s3` provider writes events to an S3 bucket as JSON objects. Each event becomes a separate object with a configurable key pattern. Useful for archiving webhooks, data lake ingestion, or feeding S3-triggered Lambda functions. ## Configuration `POST /v1/destinations` ```json { "name": "Event Archive", "type": "s3", "config": { "bucket": "my-webhook-archive", "region": "us-east-1", "prefix": "webhooks/", "access_key_id": "AKIA...", "secret_access_key": "wJal..." } } ``` ## Config Fields - `bucket` (required): S3 bucket name - `region` (required): AWS region - `access_key_id` (required): IAM access key - `secret_access_key` (required): IAM secret key (masked in GET responses) - `prefix` (required): key prefix (e.g., `"webhooks/"`) - `format`: `"json"` (default) or `"raw"` (`application/octet-stream`) Object keys follow the pattern: `{prefix}{YYYY}/{MM}/{DD}/{event_id}.json` Example: `webhooks/2025/03/01/evt_abc123.json` ## IAM Permissions The IAM user needs `s3:PutObject` permission: ```json { "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::my-webhook-archive/*" } ``` ## Object Format Each S3 object contains the full event as JSON: ```json { "event_id": "evt_abc123", "source_id": "src_xyz", "method": "POST", "headers": { ... }, "body": { ... }, "received_at": "2025-03-01T12:00:00Z" } ``` `Content-Type` is set to `application/json`. ## Related - [destinations](https://hookstream.io/docs/destinations) - [providers/aws-sqs](https://hookstream.io/docs/providers/aws-sqs) - [providers/aws-eventbridge](https://hookstream.io/docs/providers/aws-eventbridge) --- # AWS EventBridge Publish events to EventBridge event buses with custom detail types. Source: https://hookstream.io/docs/providers/aws-eventbridge ## Overview The `aws_eventbridge` provider publishes events to an EventBridge event bus. This enables routing webhooks to any EventBridge target: Lambda, Step Functions, SQS, SNS, and more. Events are published using the `PutEvents` API with SigV4 signing. ## Configuration `POST /v1/destinations` ```json { "name": "Order Events Bus", "type": "eventbridge", "config": { "event_bus_arn": "arn:aws:events:us-east-1:123456789:event-bus/my-bus", "region": "us-east-1", "source": "hookstream", "detail_type": "WebhookEvent", "access_key_id": "AKIA...", "secret_access_key": "wJal..." } } ``` ## Config Fields - `event_bus_arn` (required): ARN of the EventBridge event bus (or `"default"` for the default bus) - `region` (required): AWS region - `source` (required): the `Source` field in the EventBridge event - `detail_type` (required): the `DetailType` field - `access_key_id` (required): IAM access key - `secret_access_key` (required): IAM secret key ## Event Format The webhook body is sent as the `Detail` field. EventBridge events look like: ```json { "Source": "hookstream", "DetailType": "WebhookEvent", "Detail": "{...webhook body...}", "EventBusName": "my-bus" } ``` You can use EventBridge rules to filter and route based on `Detail` content. ## IAM Permissions The IAM user needs `events:PutEvents` permission: ```json { "Effect": "Allow", "Action": "events:PutEvents", "Resource": "arn:aws:events:us-east-1:123456789:event-bus/my-bus" } ``` ## Related - [destinations](https://hookstream.io/docs/destinations) - [providers/aws-sqs](https://hookstream.io/docs/providers/aws-sqs) - [providers/aws-s3](https://hookstream.io/docs/providers/aws-s3) --- # GCP Pub/Sub Publish events to Google Cloud Pub/Sub topics with service account auth. Source: https://hookstream.io/docs/providers/gcp-pubsub ## Overview The `gcp_pubsub` provider publishes events to a Google Cloud Pub/Sub topic. Authentication uses a service account with JWT-based access token generation — no GCP SDK required. ## Configuration `POST /v1/destinations` ```json { "name": "Analytics Topic", "type": "pubsub", "config": { "project_id": "my-gcp-project", "topic_id": "webhook-events", "service_account_json": "{...full service account key JSON...}" } } ``` ## Config Fields - `project_id` (required): GCP project ID - `topic_id` (required): Pub/Sub topic name - `service_account_json` (required): full service account key JSON (contains `client_email`, `private_key`, `project_id`) The private key within the service account JSON is used to sign JWT tokens for authentication. The entire `service_account_json` field is masked in GET responses. ## Authentication hookstream generates short-lived JWT access tokens from the service account JSON: 1. Extract `client_email` and `private_key` from `service_account_json` 2. Create JWT with `iss=client_email`, `sub=client_email`, `scope=pubsub`, `aud=https://oauth2.googleapis.com/token`, `exp=+1hr` 3. Sign with RSA-SHA256 using the private key (`crypto.subtle`) 4. Exchange JWT for access token via Google's token endpoint 5. Use access token in `Authorization: Bearer` header for Pub/Sub publish calls ## IAM Permissions The service account needs the `pubsub.publisher` role (or `pubsub.topics.publish` permission) on the target topic: ```shellscript gcloud pubsub topics add-iam-policy-binding webhook-events \ --member=serviceAccount:hookstream@my-project.iam.gserviceaccount.com \ --role=roles/pubsub.publisher ``` ## Message Format The webhook body is base64-encoded and sent as the Pub/Sub message `data` field. Message attributes include `event_id` and `source_id` for filtering in subscriptions. ## Related - [destinations](https://hookstream.io/docs/destinations) - [topics-pubsub](https://hookstream.io/docs/topics-pubsub) - [providers/kafka](https://hookstream.io/docs/providers/kafka) --- # Kafka Produce events to Kafka topics via Confluent REST Proxy. Source: https://hookstream.io/docs/providers/kafka ## Overview The `kafka` provider produces events to Kafka topics via the Confluent REST Proxy. Since Cloudflare Workers cannot speak the native Kafka protocol (TCP sockets), hookstream uses the HTTP REST API provided by Confluent Cloud or self-hosted Confluent Platform. ## Configuration `POST /v1/destinations` ```json { "name": "Event Stream", "type": "kafka", "config": { "bootstrap_servers": "pkc-xxxxx.us-east-1.aws.confluent.cloud:9092", "cluster_id": "lkc-xxxxx", "topic": "webhook-events", "api_key": "ABCDEFGHIJKLMNOP", "api_secret": "ABCdef123..." } } ``` ## Config Fields - `bootstrap_servers` (required): Confluent bootstrap server (e.g., `pkc-xxxxx.us-east-1.aws.confluent.cloud:9092`) - `cluster_id` (required): Confluent cluster ID (e.g., `lkc-xxxxx`) - `topic` (required): Kafka topic name - `api_key` (required): Confluent API key - `api_secret` (required): Confluent API secret (masked in GET responses) The REST API endpoint is derived from `bootstrap_servers`: converted to HTTPS with the path `/kafka/v3/clusters/{cluster_id}/topics/{topic}/records`. ## Confluent Cloud Setup 1. Create a Kafka cluster in Confluent Cloud 2. Create a topic for your webhook events 3. Create an API key with produce permissions 4. Find your REST endpoint URL in the cluster settings 5. Use these values in the hookstream destination config ## Message Format Events are produced as JSON messages via the Confluent REST API v3. The webhook body becomes the message value: `POST /kafka/v3/clusters/{cluster_id}/topics/{topic}/records` ```json { "value": { "type": "JSON", "data": { ...webhook body... } }, "headers": [ { "name": "hookstream-event-id", "value": "" }, { "name": "hookstream-destination-id", "value": "" } ] } ``` Authentication uses Basic auth with `api_key:api_secret`. ## Related - [destinations](https://hookstream.io/docs/destinations) - [providers/rabbitmq](https://hookstream.io/docs/providers/rabbitmq) - [providers/gcp-pubsub](https://hookstream.io/docs/providers/gcp-pubsub) --- # RabbitMQ Publish events to RabbitMQ exchanges via the HTTP Management API. Source: https://hookstream.io/docs/providers/rabbitmq ## Overview The `rabbitmq` provider publishes events via the RabbitMQ HTTP Management API. Since Workers cannot speak AMQP directly, hookstream uses the `/api/exchanges/{vhost}/{exchange}/publish` endpoint to publish messages. ## Configuration `POST /v1/destinations` ```json { "name": "Message Broker", "type": "rabbitmq", "config": { "url": "https://rabbitmq.example.com:15672", "vhost": "/", "exchange": "webhooks", "routing_key": "events", "username": "hookstream", "password": "secret" } } ``` ## Config Fields - `url` (required): RabbitMQ Management API URL (usually port 15672) - `vhost`: virtual host (default: `/`) - `exchange` (required): exchange name to publish to - `routing_key` (required): routing key for message routing - `username` (required): RabbitMQ username - `password` (required): RabbitMQ password (masked in GET responses) Success requires both an HTTP 2xx response and the response body containing `{"routed": true}`. ## Management API Setup Ensure the RabbitMQ Management plugin is enabled: ```shellscript rabbitmq-plugins enable rabbitmq_management ``` The Management API must be accessible from the internet (or via a tunnel) for hookstream to publish messages. Use HTTPS in production. ## Message Format Messages are published with the webhook body as the payload: ```json { "properties": { "content_type": "application/json", "delivery_mode": 2 }, "routing_key": "events", "payload": "{...webhook body...}", "payload_encoding": "string" } ``` ## Security Credentials are stored encrypted and masked in API responses. Use a dedicated RabbitMQ user with only publish permissions on the target exchange. Always use HTTPS for the Management API connection. ## Related - [destinations](https://hookstream.io/docs/destinations) - [providers/kafka](https://hookstream.io/docs/providers/kafka) - [providers/http](https://hookstream.io/docs/providers/http) --- # WebSocket Broadcast events to connected WebSocket clients. Source: https://hookstream.io/docs/providers/websocket ## Overview The `websocket` provider broadcasts events to all clients connected to the destination's WebSocket endpoint. This enables real-time push delivery to browser dashboards, monitoring tools, or any WebSocket client. ## Configuration `POST /v1/destinations` ```json { "name": "Live Dashboard", "type": "websocket", "config": { "channel_name": "live-orders" } } ``` The WebSocket provider broadcasts events to all clients connected to your org's EventStream Durable Object on the specified channel. ## Connecting Clients connect to the WebSocket endpoint: `wss://hookstream.io/v1/ws?token=` Subscribe to the destination's channel: ```json { "type": "subscribe", "channel": "deliveries:" } ``` Events are pushed as they're delivered. ## Message Format Broadcast messages include the event data: ```json { "type": "delivery", "channel": "deliveries:dest_abc", "data": { "event_id": "evt_123", "status": "delivered", "body": { ...webhook payload... }, "delivered_at": "2025-03-01T12:00:00Z" } } ``` ## Use Cases - Live monitoring dashboards that update without polling - Real-time notification feeds in web applications - Development tools that stream webhooks to the browser - Multi-client broadcast where one webhook reaches many viewers ## Related - [destinations](https://hookstream.io/docs/destinations) - [guides/real-time-streaming](https://hookstream.io/docs/guides/real-time-streaming) - [api/websockets](https://hookstream.io/docs/api/websockets) --- # CLI Reference # CLI Overview What the hookstream CLI does and when to use it. Source: https://hookstream.io/docs/cli/overview ## What is the CLI? The hookstream CLI (`hookstream`) is a command-line tool for managing your webhook infrastructure. It provides full CRUD operations for all resources, webhook testing, and real-time event streaming — all from your terminal. ## Capabilities The CLI supports: - **Authentication**: login (browser or interactive), logout, whoami - **Sources**: list, create, get, delete, templates - **Destinations**: list, create, get, delete, circuit, circuit-reset - **Connections**: list, create, get, delete - **Events**: list, get, replay, retry - **Deliveries**: list, get, retry, dlq - **Metrics**: overview, volume - **Topics**: list, create, get, delete, subscribe, unsubscribe, publish - **Replay**: create, status - **Issues**: list, get, update, retry - **Alerts**: list, create, get, delete - **Channels**: list, create, get, delete, test - **Collections**: list, get, stats, export - **Applications**: list, create, get, delete - **Billing**: usage, subscription - **Testing**: test (send event), listen (real-time stream) ## When to Use the CLI Use the CLI for: - Scripting and automation (CI/CD pipelines, setup scripts) - Quick operations without opening the dashboard - Real-time event streaming to your terminal - Machine-readable output with `--json` flag - Local development and debugging with `hookstream listen` ## Machine-Readable Output Every command supports `--json` for structured output: ```shellscript hookstream --json sources list hookstream --json events get evt_abc ``` ## Related - [cli/installation](https://hookstream.io/docs/cli/installation) - [cli/commands](https://hookstream.io/docs/cli/commands) - [cli/authentication](https://hookstream.io/docs/cli/authentication) --- # Installation Install the CLI via npm, configure settings, and set up environment variables. Source: https://hookstream.io/docs/cli/installation ## Install via npm Install globally via npm: ```shellscript npm install -g @hookstream/cli ``` ## Run without Installing Or use npx for one-off commands: ```shellscript npx @hookstream/cli sources list npx @hookstream/cli listen ``` ## Verify Installation Check that the CLI is installed correctly: ```shellscript hookstream --version # 0.2.1 hookstream --help ``` ## Configuration File The CLI stores configuration in `~/.config/hookstream/config.json` (XDG compliant). The config file stores your API key and base URL. It's created automatically on `hookstream login`. ```json {"api_key": "hs_live_...", "base_url": "https://hookstream.io"} ``` ## Environment Variables You can configure the CLI via environment variables: - `HOOKSTREAM_API_KEY` — your API key (overrides config file) - `HOOKSTREAM_BASE_URL` — API base URL (default: `https://hookstream.io`) Environment variables take precedence over the config file but are overridden by CLI flags. ```shellscript export HOOKSTREAM_API_KEY=hs_live_abc123... hookstream sources list ``` ## Auth Precedence API key resolution order (highest to lowest): 1. `--api-key` CLI flag 2. `HOOKSTREAM_API_KEY` environment variable 3. Config file (`~/.config/hookstream/config.json`) This allows per-command overrides while keeping a default key in config. ## Build from Source Clone the repo and build the CLI locally: ```shellscript git clone https://github.com/seangeng/hookstream cd hookstream pnpm install pnpm build:cli # Run locally node cli/dist/index.js --help ``` ## Related - [cli/overview](https://hookstream.io/docs/cli/overview) - [cli/authentication](https://hookstream.io/docs/cli/authentication) - [cli/commands](https://hookstream.io/docs/cli/commands) --- # Commands Complete command reference with examples for every CLI command. Source: https://hookstream.io/docs/cli/commands ## Authentication Manage CLI authentication: ```shellscript # Browser login (opens hookstream.io/cli-auth) hookstream login # Interactive login (prompts for API key) hookstream login -i # Login with key directly hookstream login --api-key hs_live_abc123... # Show current auth context hookstream whoami # Remove stored credentials hookstream logout ``` ## Sources Manage inbound webhook sources: ```shellscript # List all sources hookstream sources list hookstream sources list --status active # Create a source (with optional template) hookstream sources create "My Webhook" --template stripe hookstream sources create "GitHub Events" --slug github-events --template github # Get source details hookstream sources get # Delete a source (-f skips confirmation) hookstream sources delete -f # List available templates hookstream sources templates ``` ## Destinations Manage delivery destinations: ```shellscript # List destinations hookstream destinations list hookstream destinations list --status active --type http # Create HTTP destination hookstream destinations create "My API" --url https://api.example.com/webhooks --method POST # Create with custom headers and auth hookstream destinations create "Slack" --url https://hooks.slack.com/... \ --header "Authorization:Bearer token" --auth-type bearer_token --timeout 10000 # Create non-HTTP destination (SQS, S3, etc.) hookstream destinations create "My Queue" --type aws_sqs \ --provider-config '{"queue_url":"https://sqs.us-east-1.amazonaws.com/...","region":"us-east-1"}' # Get details hookstream destinations get # Circuit breaker status hookstream destinations circuit hookstream destinations circuit-reset # Delete hookstream destinations delete -f ``` ## Connections Route events from sources to destinations: ```shellscript # List connections hookstream connections list # Create a connection hookstream connections create "My Pipeline" --source --destination # Create with content-based filter hookstream connections create "Payments" --source --destination \ --filter '[{"field":"body.type","operator":"eq","value":"payment.completed"}]' # Get details hookstream connections get # Delete hookstream connections delete -f ``` ## Events Browse and inspect received events: ```shellscript # List recent events hookstream events list hookstream events list --source --method POST --limit 50 # Get event details (headers + payload) hookstream events get # Retry all deliveries for an event hookstream events retry # Replay an event to a URL hookstream events replay --to https://localhost:3000/webhook ``` ## Deliveries Inspect delivery attempts: ```shellscript # List delivery attempts hookstream deliveries list hookstream deliveries list --event --status failed # Get attempt details hookstream deliveries get # Retry a failed delivery hookstream deliveries retry # View dead-letter queue hookstream deliveries dlq ``` ## Metrics View usage metrics: ```shellscript # Overview stats hookstream metrics overview # Event volume over time hookstream metrics volume --granularity hour hookstream metrics volume --granularity day --source ``` ## Topics Manage pub/sub topics: ```shellscript # List topics hookstream topics list # Create a topic hookstream topics create "Orders" --slug orders # Subscribe a destination hookstream topics subscribe # Publish an event hookstream topics publish orders --data '{"type":"order.created","id":"ord_123"}' # Unsubscribe hookstream topics unsubscribe # Delete hookstream topics delete -f ``` ## Replay Replay historical events: ```shellscript # Create a replay job hookstream replay create --destination --from 2026-03-01T00:00:00Z --to 2026-03-07T00:00:00Z # With optional filters hookstream replay create --destination --from --to \ --source --rate-limit 100 --max-events 5000 # Check job status hookstream replay status ``` ## Testing Test webhooks from the command line: ```shellscript # Send a test event to a source hookstream test hookstream test --payload '{"action":"test"}' hookstream test --file payload.json -H "X-Custom:value" hookstream test --method PUT --payload '{"update":true}' # Stream webhooks in real-time (creates ephemeral URL) hookstream listen hookstream listen --forward http://localhost:3000/webhook hookstream listen --full # show full payloads ``` ## Issues View and manage delivery issues: ```shellscript # List open issues hookstream issues list hookstream issues list --status all hookstream issues list --status resolved --limit 20 # Get issue details hookstream issues get # Update status hookstream issues update --status resolved hookstream issues update --status muted # Retry all failed deliveries for an issue hookstream issues retry ``` ## Alerts Manage alert rules: ```shellscript # List alert rules hookstream alerts list # Create an alert rule hookstream alerts create "High Failure Rate" --type failure_rate --channel hookstream alerts create "Latency Alert" --type high_latency --channel \ --threshold 5000 --source # Get alert details hookstream alerts get # Delete an alert rule hookstream alerts delete -f ``` ## Notification Channels Manage notification channels for alerts: ```shellscript # List channels hookstream channels list # Create a webhook channel hookstream channels create "Slack" --type webhook --url https://hooks.slack.com/... hookstream channels create "PagerDuty" --type webhook --url https://events.pagerduty.com/... # Get channel details hookstream channels get # Send a test notification hookstream channels test # Delete hookstream channels delete -f ``` ## Collections Manage Instant Database collections: ```shellscript # List collections hookstream collections list # Get collection details hookstream collections get # View statistics (record count, schema) hookstream collections stats # Export data hookstream collections export hookstream collections export --format csv --output data.csv ``` ## Applications Manage outbound webhook applications: ```shellscript # List applications hookstream applications list # Create an application hookstream applications create "My App" hookstream applications create "Notifications" --uid my-app-001 # Get application details hookstream applications get # Delete hookstream applications delete -f ``` ## Billing View plan and usage: ```shellscript # Check current usage hookstream billing usage # View subscription hookstream billing subscription ``` ## Global Flags Available on every command: - `--api-key ` — use a specific API key for this command - `--base-url ` — override the API base URL - `--json` — output in JSON format (machine-readable) - `--help` — show help for any command - `--version` — show CLI version Global flags must come before the subcommand: `hookstream --json sources list` ## Related - [cli/overview](https://hookstream.io/docs/cli/overview) - [cli/authentication](https://hookstream.io/docs/cli/authentication) - [api/sources](https://hookstream.io/docs/api/sources) --- # CLI Authentication API key auth, --api-key flag, environment variables, and config file. Source: https://hookstream.io/docs/cli/authentication ## Authentication Overview The CLI uses API key authentication. You need an API key to use any authenticated command. The simplest way is `hookstream login` which opens your browser to create and approve a key automatically. ## Browser Login (Recommended) The default login opens your browser to create and approve an API key in one step: ```shellscript hookstream login # Opens browser to hookstream.io/cli-auth # Approve in browser → CLI receives key automatically # ✓ Logged in as hs_live_d82e... ``` ## Interactive Login If the browser doesn't work (e.g. remote server), use interactive mode: ```shellscript hookstream login -i # Enter your API key: hs_live_... # ✓ Logged in as hs_live_d82e... ``` ## Direct Key Login Pass the key directly (useful for scripts): ```shellscript hookstream login --api-key hs_live_abc123... ``` ## Per-Command Flag Override the stored key for a single command: ```shellscript hookstream sources list --api-key hs_live_abc123... ``` ## Environment Variable Set the `HOOKSTREAM_API_KEY` environment variable — ideal for CI/CD pipelines: ```shellscript export HOOKSTREAM_API_KEY=hs_live_abc123... hookstream sources list ``` ## API Key Format hookstream API keys follow this format: - **Live**: `hs_live_<64 hex characters>` - **Test**: `hs_test_<64 hex characters>` The plaintext key is shown only once at creation. hookstream stores only the SHA-256 hash for verification. Create API keys in the dashboard at **Settings > API Keys**. ## Troubleshooting Common issues: - **"Not authenticated"** — no API key found. Run `hookstream login` or set `HOOKSTREAM_API_KEY`. - **"Authentication failed"** — the key is malformed or revoked. Create a new key in the dashboard. - **Unexpected results** — verify your key targets the right environment with `hookstream whoami`. ## Related - [cli/installation](https://hookstream.io/docs/cli/installation) - [api/authentication](https://hookstream.io/docs/api/authentication) - [api/api-keys](https://hookstream.io/docs/api/api-keys) --- # AI & LLM # AI & LLM Integration How to use hookstream with AI tools, LLMs, and coding assistants. Source: https://hookstream.io/docs/ai/overview ## Overview hookstream is designed to be AI-friendly. Every documentation page can be exported as clean markdown, the entire docs are available as a single text file for LLMs, and the test sessions API works as a programmatic AI Agent API. Whether you're pasting docs into Claude, using Cursor to write webhook handlers, or building an autonomous agent that creates and monitors webhook endpoints — hookstream has you covered. ## Markdown Export Every documentation page has a "Copy as Markdown" button in the AI bar at the top. Click it to copy the page content as clean markdown, ready to paste into any AI tool. You can also access any doc page as markdown via URL by appending .md: `https://hookstream.io/docs/quickstart.md` `https://hookstream.io/docs/api/sources.md` `https://hookstream.io/docs/guides/retry-strategies.md` The markdown includes all sections, API endpoint tables, code examples, and related page links. ## llms.txt Discovery hookstream publishes machine-readable discovery files following the llms.txt standard: `https://hookstream.io/llms.txt` — Summary with links to all doc pages `https://hookstream.io/llms-full.txt` — Complete documentation as a single text file AI crawlers and tools can use these files to understand what hookstream does and find relevant documentation. The llms.txt file includes an overview, categorized doc links, API authentication info, and tool links. ## AI Plugin Manifest hookstream serves an OpenAI-compatible plugin manifest at: `https://hookstream.io/.well-known/ai-plugin.json` This file helps AI platforms discover hookstream's capabilities and API endpoints automatically. ## AI Tool Deeplinks The AI bar on each doc page includes buttons to open the page content directly in: - Claude — Opens claude.ai with the page markdown pre-filled - ChatGPT — Opens chat.openai.com with the page markdown pre-filled For Cursor and other IDE-based AI tools, use the Copy as Markdown button and paste into the chat. ## AI Agent API hookstream's test sessions API doubles as an AI Agent API. Any AI agent can create ephemeral webhook endpoints, inspect received events, configure custom responses, and set up forwarding — all without authentication. See the Agent API Reference for the full endpoint list and usage examples. ## Skills File A Claude Code skills file is available at: `https://hookstream.io/hookstream.skills.md` This teaches Claude Code how to use hookstream's API directly. See the Skills & IDE Integration page for installation instructions. ## Related - [ai/skills](https://hookstream.io/docs/ai/skills) - [ai/agent-api](https://hookstream.io/docs/ai/agent-api) - [api/test-sessions](https://hookstream.io/docs/api/test-sessions) --- # Skills & IDE Integration Install hookstream skills in Claude Code, Cursor, and other AI-powered IDEs. Source: https://hookstream.io/docs/ai/skills ## What Are Skills Files? Skills files teach AI coding assistants about specific APIs and tools. When installed, your AI assistant can create sources, send webhooks, configure destinations, and debug delivery issues — using the hookstream API directly from your IDE. ## Claude Code Install the hookstream skills file in your project: ```shellscript mkdir -p .claude/skills curl -o .claude/skills/hookstream.md https://hookstream.io/hookstream.skills.md ``` Once installed, Claude Code can help you: - Create webhook sources and destinations - Set up connections with filters and transforms - Send test webhooks and inspect results - Debug delivery failures - Configure retry strategies and circuit breakers ## Cursor Add the hookstream docs as context in Cursor: 1. Open Cursor Settings > AI > Custom Instructions 2. Add the URL: `https://hookstream.io/llms-full.txt` 3. Or paste the content from the Copy as Markdown button on any doc page Cursor's AI will then have full context about hookstream's API when helping you write webhook integration code. ## Other AI Tools For any AI tool that accepts custom context or system prompts: - Use `https://hookstream.io/llms.txt` for a summary - Use `https://hookstream.io/llms-full.txt` for complete documentation - Use `https://hookstream.io/docs/.md` for specific pages These URLs return plain text/markdown that any AI tool can consume. ## Example Workflows Here are common things to ask your AI assistant after installing the skills file: "Create a hookstream source for Stripe webhooks with signature verification" "Set up a connection that only routes payment_intent.succeeded events" "Test my webhook handler by sending a sample Stripe event" "Why are my deliveries failing? Check the circuit breaker status" "Create a dead letter queue alert that notifies my Slack channel" ## Code Examples ### Install skills file ```bash mkdir -p .claude/skills curl -o .claude/skills/hookstream.md https://hookstream.io/hookstream.skills.md ``` ### Quick test with Claude Code ```bash # After installing, ask Claude Code: # "Create a test webhook endpoint and send a sample event" # It will use the hookstream API to: curl -X POST https://hookstream.io/v1/agent/sessions # Then send a test webhook to the returned URL ``` ## Related - [ai/overview](https://hookstream.io/docs/ai/overview) - [ai/agent-api](https://hookstream.io/docs/ai/agent-api) - [quickstart](https://hookstream.io/docs/quickstart) --- # AI Agent API Reference Full reference for using hookstream test sessions as an AI agent workspace. Source: https://hookstream.io/docs/ai/agent-api ## Overview The AI Agent API provides a clean interface for AI agents to create and manage ephemeral webhook endpoints. It's the same infrastructure as hookstream's free webhook testing tool, exposed with agent-friendly aliases at `/v1/agent/*`. No authentication required. Rate limited to 10 sessions per IP per hour. Sessions last 14 days or 2,000 requests. ## Base URL All endpoints are available at: `https://hookstream.io/v1/agent/sessions` Alternatively, the same endpoints are available at `/v1/test/sessions`. ## Typical Agent Workflow 1. Create a session — `POST /v1/agent/sessions` → returns a webhook URL 2. Configure responses — `PATCH /v1/agent/sessions/:id/response` → set custom status/body 3. Use the webhook URL — Give it to an external service or send test requests 4. Poll for events — `GET /v1/agent/sessions/:id` → list received webhooks 5. Inspect events — check method, headers, body, timing 6. Set up forwarding — `PATCH /v1/agent/sessions/:id/forwarding` → relay to your app 7. Export data — `GET /v1/agent/sessions/:id/export` → NDJSON or CSV 8. Clean up — `DELETE /v1/agent/sessions/:id` ## Filtering Events When fetching events, you can filter with query parameters: `?method=POST` — filter by HTTP method `?status=2xx` — filter by status code range (2xx, 4xx, 5xx) `?q=search_term` — full-text search in headers and body `?limit=50` — results per page (max 100) `?cursor=` — pagination cursor ## Rate Limits - 10 sessions per IP per hour - 2,000 requests per session - 14-day session TTL - Sessions can be deleted early with DELETE For higher limits, create a free hookstream account and use API key authentication. ## Session Graduation Sessions can be graduated to a full hookstream account. When a user signs up and claims a session (`POST /v1/test/sessions/:id/claim` with auth), the session and all its events are moved to the user's organization. The webhook URL continues to work — no data is lost. ## Example: Webhook Debugging Agent An AI agent that helps debug webhook integrations: 1. Agent creates a session and gets a webhook URL 2. User configures their service (e.g., Stripe) to send webhooks to that URL 3. Agent polls for events and analyzes the payloads 4. Agent identifies issues: "Your Stripe webhook is sending checkout.session.completed events but your handler expects payment_intent.succeeded" 5. Agent sets up forwarding to the user's local endpoint 6. Agent monitors delivery status and reports errors ## API Endpoints ### `POST /v1/agent/sessions` Create a new ephemeral webhook endpoint. **Auth:** none **Response Example:** ```json { "session_id": "abc123...", "url": "https://hookstream.io/v1/ingest/abc123...", "expires_at": "2026-03-16T...", "request_limit": 2000, "request_count": 0 } ``` ### `GET /v1/agent/sessions/:id` Get session info and list received events. **Auth:** none **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `method` | string | No | Filter by HTTP method (e.g., POST) | | `status` | string | No | Filter by status range: 2xx, 4xx, 5xx | | `q` | string | No | Search in headers and body | | `limit` | number | No | Results per page (max 100, default 50) | | `cursor` | string | No | Pagination cursor from previous response | **Response Example:** ```json {"session": {"id": "...", "url": "...", "expires_at": "...", "request_count": 5}, "events": [...], "pagination": {"has_more": false}} ``` ### `PATCH /v1/agent/sessions/:id/response` Configure the custom response returned when webhooks hit this endpoint. **Auth:** none **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `status_code` | number | No | HTTP status code (200-599, default 200) | | `content_type` | string | No | Response content type (default "application/json") | | `body` | string | No | Response body (max 4KB) | **Request Example:** ```json { "status_code": 200, "content_type": "application/json", "body": "{\"ok\": true}" } ``` ### `PATCH /v1/agent/sessions/:id/forwarding` Forward received webhooks to another URL in real-time. **Auth:** none **Body Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `url` | string | Yes | Target URL (http/https). Set to null to disable forwarding. | **Request Example:** ```json { "url": "https://my-app.com/webhooks" } ``` ### `GET /v1/agent/sessions/:id/export` Export all session events as NDJSON or CSV. **Auth:** none **Query Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `format` | string | No | "ndjson" (default) or "csv" | ### `DELETE /v1/agent/sessions/:id` Delete session and all events. **Auth:** none ## Code Examples ### Create a session and send a test webhook ```bash # Create session SESSION=$(curl -s -X POST https://hookstream.io/v1/agent/sessions) URL=$(echo $SESSION | jq -r '.url') ID=$(echo $SESSION | jq -r '.session_id') # Send a test webhook curl -X POST $URL \ -H 'Content-Type: application/json' \ -d '{"type": "order.created", "data": {"id": "ord_123"}}' # Check events curl -s "https://hookstream.io/v1/agent/sessions/$ID" | jq '.events' ``` ### Create session with JavaScript ```javascript const res = await fetch("https://hookstream.io/v1/agent/sessions", { method: "POST", }); const { session_id, url } = await res.json(); // Use 'url' as your webhook endpoint console.log("Webhook URL:", url); // Later: check received events const events = await fetch( `https://hookstream.io/v1/agent/sessions/${session_id}` ).then(r => r.json()); console.log("Events:", events.events.length); ``` ## Related - [ai/overview](https://hookstream.io/docs/ai/overview) - [api/test-sessions](https://hookstream.io/docs/api/test-sessions) - [guides/webhook-testing](https://hookstream.io/docs/guides/webhook-testing) ---