1. Create a project & endpoint
Sign in (a free workspace is provisioned automatically), click Create Project, then open the project’s Endpoints tab and Add Endpoint. Hookie generates a high-entropy webhook URL — the URL itself is the secret, so anyone who has it can send events:
The Overview tab shows the full URL with a copy button and a ready-to-paste curl example.
2. Send an event
POST any JSON to your endpoint URL. The whole payload is stored into the endpoint’s dataset:
You’ll get back the submission id and where it routed:
An endpoint accepts what a browser actually posts, so a plain form needs no JavaScript and no backend of your own — set action to your endpoint URL and you are done:
The fields arrive as an ordinary record, stored whole into the endpoint’s dataset:
Both form encodings work — application/x-www-form-urlencoded and multipart/form-data. A few details worth knowing:
- A field that repeats (checkboxes, a multi-select) becomes an array rather than losing all but one value.
- Field names are kept verbatim and flat. A field called
user.email stays the key user.email — it does not become a nested object — so a mapping rule sees exactly what the form sent.
- A file part is recorded as a descriptor and its bytes are discarded. Hookie stores records, not blobs, so you get proof something was attached without the contents:
- Everything else is still parsed as JSON regardless of its
Content-Type, which is what most webhook providers send.
Because the browser navigates to the endpoint on submit, the visitor lands on Hookie’s JSON response. For a polished flow, submit with fetch and a FormData body — that is also a form encoding, so it needs no preflight — then show your own thank-you state.
3. See it in the console
Open the project’s Datasets tab to see the record as a table, and the Overview dashboard to watch your usage tick up. To see the body exactly as it arrived, before any rule reshaped it, search Submissions on the Observability tab — every id there links to the rest of its chain.
Safe retries
Send an Idempotency-Key and a retry returns the original submission instead of creating a duplicate — and it never counts twice against your quota:
Prefer a key?
Machine-to-machine senders can also use an ingest key (ik_live_…, created on the admin API — POST /admin/api/projects/:id/ingest-keys — stored hashed and returned once):
Route with rules
Everything above routes by address: the URL names the dataset — an endpoint’s own, or the /{dataset} segment on the end — and the payload is stored whole. There is nothing to decide, so rules are not consulted on any of those requests. Adding a rule and then posting to an endpoint URL is the one thing that looks like it should work and doesn’t.
Rules are for the case where the sender can’t say where an event belongs — one stream carrying several kinds of event. They are evaluated by exactly one shape: an ingest key with no dataset in the path.
Every enabled rule in the project is then tried against that payload. A rule’s conditions are exact matches on a path (type equals refund), all of which must hold; its mappings pick fields out into a record of your own shape. Leave conditions empty to match everything, and mappings empty to store the payload whole.
Rules are not first-match: every rule that matches writes its own record, so one event can land in several datasets at once. If none match, the response comes back with "routed": [] and the raw submission is still captured — find it under Observability → Submissions, which is how you tell “no rule matched” from “the event never arrived”.
Next