Concepts
The data model
Understanding five concepts is enough to use Splithook fluently.
Workspace
└── Endpoint (public URL — one per integration)
├── WebhookRequest (immutable capture of each incoming HTTP request)
└── Destination (target to forward to, with filter + signing)
└── ReplayLog (attempt history — status, latency, response)
Workspace
A workspace is a team container. All members share:
- Endpoints and their captured webhooks
- Signing secrets (stored encrypted)
- Replay history and schemas
- Billing plan and quotas
Members have one of three roles: Owner, Admin, or Member. Roles control who can invite others, delete endpoints, and manage billing. See Workspaces & teams for details.
Endpoint
An endpoint is a public HTTPS URL that accepts webhook HTTP requests from any provider:
https://splithook.com/e/{slug}
The {slug} is an 8-character base32 string generated on creation. You can rotate it at any time — the old slug stops working immediately.
What an endpoint does:
- Accepts any
POSTrequest (no auth, no IP filtering — designed for external providers). - Stores the raw body in Redis with a 24-hour TTL (Free plan) or longer (Pro/Team).
- Persists metadata to the database: method, headers, body hash, IP, timestamp, inferred provider/event type.
- Dispatches async jobs: schema inference, Mercure broadcast, fan-out to destinations.
The capture path is optimized for speed (p99 < 50ms). Your provider gets a 200 OK back before any forwarding happens.
WebhookRequest
A WebhookRequest is the immutable record of a single HTTP call to your endpoint. It contains:
| Field | Description |
|---|---|
method |
HTTP method (always POST in practice) |
headers |
All headers, stored as JSON |
body_hash |
SHA-256 of the raw body — used to detect changes |
provider |
Auto-detected provider (Stripe, GitHub, Shopify…) |
event_type |
Auto-detected event type (charge.failed, push…) |
received_at |
Timestamp at capture |
The body itself lives in Redis, not in the database — keeping the database row small and the hot path fast.
Destination
A destination is a target that receives forwarded webhooks. Each endpoint can have multiple destinations (fan-out). A destination owns:
- URL — where to POST
- Filter — ExpressionLanguage expression; only matching events are forwarded
- Signing mode —
passthrough,strip, orre_sign - Signing secret — used when mode is
re_sign - Retry policy — how many attempts, what backoff
There are two destination types:
| Type | Description |
|---|---|
http |
Plain HTTPS endpoint — your staging server, webhook.site, a queue |
tunnel |
Splithook CLI tunnel — forwards directly to localhost without opening ports |
ReplayLog
Every delivery attempt creates a ReplayLog entry:
- HTTP status code returned by the destination
- Latency in milliseconds
- First 4 KB of the response body (for debugging)
- Whether the payload was re-signed
- Error message if the delivery failed entirely
The replay log is visible per-workspace in the Replay Log tab.
Schema
Every time a webhook arrives, Splithook walks the JSON body recursively and updates an inferred schema for that (endpoint, event_type) pair. The schema records:
- Field paths (e.g.
data.object.amount) - Observed types (
number,string,boolean,null) - Up to 3 sample values per field
- First seen / last seen timestamps
- Observation frequency (how often the field is present)
From this, Splithook generates a TypeScript interface you can copy directly into your codebase. See Schemas & types.