What is a webhook? A form submission, followed end to end
A webhook is an HTTP request one system sends to another when something happens. Instead of your code asking a service "anything new?" every few minutes, the service calls a URL you gave it the moment there's news, with the details in the request body. A website form that posts each submission to your endpoint is a typical example.
One submission, step by step
- You give the form service a URL you control, for example
https://hooks.example.com/website-form. - A visitor sends the contact form.
- The service sends an HTTP
POSTto your URL with a JSON body describing the event: which site, which form, the fields, the time. - Your endpoint checks the request is genuine, does its job (adds a row, creates a contact, posts an alert) and replies with a
2xxstatus. - If your endpoint doesn't answer or returns an error, what happens next depends on the sender: some retry later, some give up.
A typical body looks like this: {"event": "form.submitted", "site": "Ferry Lane Coffee", "form": "contact", "data": {"name": "Sam", "email": "sam@example.com", "message": "Do you do oat milk?"}, "submittedAt": "2026-10-24T09:12:00.000Z"}.
Webhook vs API
| Webhook | API polling | |
|---|---|---|
| Who starts it | The service, when something happens | Your code, on a schedule |
| Delay | Seconds | Up to your polling interval |
| Wasted requests | None | Most polls find nothing new |
| What you need | A public HTTPS endpoint | Credentials to call the API |
| If you're down | You may miss events unless the sender retries | You catch up on the next poll |
Many integrations use both: a webhook to hear about an event quickly, and an API call to fetch the full record or catch up after an outage.
Signing: proving who sent it
Your endpoint is public, so anyone who learns the URL could post fake events to it. Good senders sign each request. The usual method is an HMAC, defined in RFC 2104: the sender and you share a secret, the sender computes a hash of the body with that secret and sends it in a header, and you compute the same hash and compare. A matching value proves the request came from someone holding the secret and wasn't changed.
The Standard Webhooks specification shows the common shape: a message id, a timestamp and a signature header, with the signature computed over the id, timestamp and body. Including the timestamp lets you reject old requests that someone recorded and replays later.
Retries and duplicates
Senders that retry will, sooner or later, deliver the same event twice: your endpoint did the work but its reply was lost, so the sender tried again. The Standard Webhooks spec recommends retries with exponential backoff and treats the message id as an idempotency key. In practice:
- Store the ids of events you've processed and skip repeats.
- Reply with
2xxquickly, then do slow work afterwards, so the sender doesn't time out and retry. - If a sender doesn't retry, keep its own record of events (a dashboard or log) so you can recover anything your endpoint missed.
Test one safely
- Point the webhook at a request inspector you control, or a temporary endpoint on your own machine exposed through a tunnel, and look at real requests before writing any code.
- Use test data. Never paste real customers' details into public request-capture services.
- Write your endpoint to verify the signature first and reject anything that fails, then log the body.
- Send a test event from the service and confirm your endpoint returns
2xx. - Send the same event twice and confirm the second is skipped.
- Switch the URL to production and remove any temporary tunnel.
Common mistakes
- Skipping signature checks because "nobody knows the URL". URLs leak through logs, screenshots and shared docs.
- Comparing signatures with ordinary string equality, which can leak timing information. Use a constant-time comparison.
- Parsing the body before verifying it. Verify the raw body exactly as received; re-serialised JSON won't match.
- Doing all the work before replying, so slow jobs cause timeouts and duplicate deliveries.
Webhooks in Anticode
Anticode uses webhooks in two places. The webhook connector posts each form submission from your published sites to any HTTPS URL you give it, as JSON with the event, site, form, fields and time, as in the example above. Each submission is sent once, without retries, and the submission is always saved and sealed in your project, so it's the record to fall back on.
A site function is a signed webhook that runs while the visitor waits: a custom form posts, Anticode calls your HTTPS endpoint with an anticode-signature header of the form t=<unix seconds>,v1=<HMAC-SHA256 hex> computed over the timestamp and body, and your reply's heading and message appear on the thank-you page. Your endpoint can check the signature and refuse old timestamps. Anticode never runs your code itself. Connect automation tools through the webhook connector, as in sending leads to a CRM.
Questions people ask
Is a webhook URL a secret?
Treat it like one. For services that don't sign requests, such as chat incoming webhooks, the URL is the only credential. Even with signing, a leaked URL invites junk traffic.
Why did I receive the same event twice?
The sender retried, usually because your reply was slow or lost. Use the event's id to skip repeats.
Do I need a server to receive webhooks?
You need an HTTPS endpoint, which can be a small serverless function or an automation service's webhook trigger rather than a server you run.