Guide
Connections Guide
A connection is the route between one FastHook source and one destination. It is the object that turns an accepted source request into a destination-specific event branch.
Use connections when you want routing to stay explicit: one source can fan out to multiple destinations, one destination can receive from multiple sources, and every branch can own its own filters, transforms, retry policy, delay, deduplication window, pause state, and delivery evidence.
This guide matches the dashboard and backend API shape used by FastHook today: connections expose source_id, destination_id, rules, paused_at, disabled_at, embedded source and destination objects, and request/event inspection endpoints.
What A Connection Does
A source accepts or rejects inbound webhook traffic. After a request is accepted, FastHook evaluates the active and paused connections attached to that source. Each matching connection creates its own event for its destination.
That separation is why one provider request can produce several event rows. Each event branch has its own connection_id, destination_id, status, attempts, retry behavior, and replay path.
- Source: public ingress endpoint and source authentication boundary.
- Destination: outbound receiver configuration, including HTTP method, path, auth, and rate limit.
- Connection: route from one source to one destination.
- Event: one destination-specific unit of delivery created for a connection.
- Attempt: one HTTP delivery try for an event.
- Rules: connection-local behavior such as filter, transform, deduplicate, delay, and retry.
Provider request
-> Source src_xxx
-> Request req_xxx
-> Connection connection-to-billing
-> Event evt_xxx
-> Destination des_xxx
-> Attempt att_xxxBackend Resource Shape
The connections API returns the route fields plus embedded source and destination objects. Treat connection ids as opaque strings. Demo data currently includes ids such as web_546d291fd0544d and conn_01, so production code should store and pass the id returned by the API instead of assuming one prefix.
{
"id": "conn_01",
"team_id": "tm_demo_flow_001",
"name": "shopify-orders",
"source_id": "src_demo_shopify_001",
"destination_id": "des_demo_orders_api_001",
"paused_at": null,
"disabled_at": null,
"rules": [
{
"type": "deduplicate",
"window": 5000,
"include_fields": ["body.id"]
},
{
"type": "transform",
"transformation_id": "trs_demo_header_001"
},
{
"type": "delay",
"delay": 1000
},
{
"type": "retry",
"strategy": "linear",
"count": 5,
"interval": 3600000,
"response_status_codes": ["500-599", ">=500", "!501"]
}
],
"source": {
"id": "src_demo_shopify_001",
"name": "my-api",
"url": "https://hook-demosh.fasthook.io/"
},
"destination": {
"id": "des_demo_orders_api_001",
"name": "orders-api",
"type": "HTTP"
}
}Create A Connection In Dashboard
In the dashboard, open Connections and choose New connection. Pick an existing source and destination, then save the route. The structured board shows source nodes, connection nodes, and destination nodes so fan-out branches are visible.
Create one connection per destination branch. If billing, analytics, and audit need the same Stripe source traffic, use three connections instead of hiding all consumers behind one destination URL.
Create With The API
Use POST /v1/connections when both sides already exist. The public API is available at https://api.fasthook.io. The dashboard uses the same backend through /api/control/connections.
The body accepts source_id and destination_id, or nested source.id and destination.id. Rules are optional, but production routes usually add at least filtering, retry, or destination-specific transformation.
- GET /v1/connections lists routes for the active team.
- GET /v1/connections/:id returns one route with source and destination objects.
- PUT /v1/connections upserts by id, otherwise by source_id and name.
- PATCH /v1/connections/:id updates route fields, rules, paused_at, and disabled_at.
- DELETE /v1/connections/:id removes the route.
curl -X POST "https://api.fasthook.io/v1/connections" \
-H "Authorization: Bearer fhp_xxx" \
-H "x-team-id: tm_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "stripe-to-billing",
"description": "Route paid invoice events to billing API",
"source_id": "src_xxx",
"destination_id": "des_xxx",
"paused_at": null,
"disabled_at": null,
"rules": [
{
"type": "filter",
"body": { "type": "invoice.paid" }
},
{
"type": "retry",
"strategy": "exponential",
"interval": 30000,
"count": 5,
"response_status_codes": ["429", "500-599", "!501"]
}
]
}'Supported Rule Types
Connection rules are evaluated for that branch. Keep rules on the destination that needs them; do not make every downstream consumer inherit one receiver's constraints.
FastHook normalizes rules in the order deduplicate, transform, filter, delay, then retry. The API accepts transform-style payloads and returns the dashboard shape as type transform with transformation_id.
- filter: deliver only when headers, body, query, or path match.
- transform: attach a saved transformation_id or create an inline transformation object.
- deduplicate: suppress repeated payloads inside a millisecond window, optionally using include_fields or exclude_fields.
- delay: schedule delivery after a delay in milliseconds.
- retry: retry failed delivery with linear or exponential strategy, interval, count, and response_status_codes.
{
"rules": [
{
"type": "deduplicate",
"window": 300000,
"include_fields": ["body.id"]
},
{
"type": "transform",
"transformation_id": "trs_xxx"
},
{
"type": "filter",
"headers": {
"stripe-signature": { "$exists": true }
},
"body": {
"type": "invoice.paid"
},
"path": {
"$starts_with": "/stripe"
}
},
{
"type": "delay",
"delay": 5000
},
{
"type": "retry",
"strategy": "exponential",
"interval": 30000,
"count": 5,
"response_status_codes": ["429", "500-599", "!501"]
}
]
}Pause, Disable, And Delete
Pause and disable are different operational controls. Pause keeps the route in the topology and holds new routed events for later delivery. Disable stops the route from creating future event branches. Delete removes the route configuration.
- paused_at is null when delivery is running and an ISO timestamp when the route is paused.
- disabled_at is null when routing is enabled and an ISO timestamp when future routing is disabled.
- A disabled connection clears pause state in the dashboard flow.
- Event retry requires the source to be enabled, the connection to be enabled, and the connection to be unpaused.
- Held events are released by unpausing; bulk retry targets failed or cancelled events, not held events.
# dashboard-style update
curl -X PATCH "https://api.fasthook.io/v1/connections/conn_xxx" \
-H "Authorization: Bearer fhp_xxx" \
-H "x-team-id: tm_xxx" \
-H "Content-Type: application/json" \
-d '{ "paused_at": "2026-05-29T17:30:00.000Z" }'
# action endpoints are also available
curl -X PUT "https://api.fasthook.io/v1/connections/conn_xxx/pause" \
-H "Authorization: Bearer fhp_xxx" \
-H "x-team-id: tm_xxx"
curl -X PUT "https://api.fasthook.io/v1/connections/conn_xxx/unpause" \
-H "Authorization: Bearer fhp_xxx" \
-H "x-team-id: tm_xxx"Inspect A Connection
Debug a route from broad to narrow: request, event, then attempt. The connection_id is the bridge between an accepted source request and the destination-specific event row.
- Requests: confirm the source accepted the provider request and see events_count.
- Events: filter by connection_id to see delivered, failed, scheduled, held, and ignored branches.
- Attempts: inspect destination status code, response body, trigger, latency, and failure code.
- Metrics: filter by connection_id, source_id, destination_id, status, and batch_operation_id.
- Latest input: GET /v1/connections/:id/latest-input returns the most recent source input seen by that connection's source.
curl "https://api.fasthook.io/v1/events?connection_id=conn_xxx&limit=20" \
-H "Authorization: Bearer fhp_xxx" \
-H "x-team-id: tm_xxx"
curl "https://api.fasthook.io/v1/connections/conn_xxx/latest-input" \
-H "Authorization: Bearer fhp_xxx" \
-H "x-team-id: tm_xxx"Design Patterns
In the demo workspace, the my-api source fans out through two connections: shopify-orders and shopify-orders-secondary. A single POST to the source creates two event branches with separate destination ids, rules, statuses, and retry behavior. That is the production pattern to copy.
- Name routes as producer-to-consumer, such as stripe-to-billing or github-to-ci.
- Use one connection per downstream owner so failures are isolated.
- Put filters on the branch that does not need all event types.
- Put transforms on the branch whose receiver expects a different payload contract.
- Set destination rate limits before replaying or unpausing large backlogs.
- Treat disabled sources and disabled destinations as separate from connection state.
Source: stripe-production
-> Connection: stripe-to-billing filter invoice.* + retry 429/5xx
-> Connection: stripe-to-analytics transform summary payload
-> Connection: stripe-to-audit raw archive, no transformation
Destination: billing-api
<- Connection: stripe-to-billing
<- Connection: shopify-to-billingCommon Mistakes
Most connection mistakes come from mixing topology and business processing. Keep operational routing in FastHook, but keep domain side effects in the receiver.
- Using one destination URL for several services, which hides per-service failures.
- Assuming one accepted request creates only one event; fan-out creates one event per connection.
- Using disabled when the operator meant paused and wanted to drain held events later.
- Retrying the original request when only one destination branch failed.
- Documenting transformation rules as only type transformation; FastHook normalizes the saved shape to type transform.
- Assuming a connection id prefix instead of treating ids as opaque.
Checklist
- Source is enabled and accepts the provider method.
- Destination is enabled, reachable, and configured with the expected method, path, auth, and rate limit.
- Connection name identifies both producer and consumer.
- Rules use the normalized backend order: deduplicate, transform, filter, delay, and retry.
- paused_at and disabled_at are null for normal delivery.
- Events can be filtered by connection_id after the first test request.
- Receiver stores an idempotency key before operators retry or replay traffic.