Webhooks
Let GitHub, Stripe, Slack, and other services start an automation on your computer, with signatures checked and events kept while it is offline, within the endpoint's maximum age and the relay's retention limits.
A webhook endpoint is a public URL on hob's relay that belongs to one of your computers. When an outside service sends a request to it, hob starts the automation you bound to that endpoint, on that computer, with the request handed to the run. You do not need a public server, a port forward, or a tunnel. Hosted webhooks need hob Pro+; Teams seats include them.
Events wait in the relay while your computer is asleep or offline, and arrive when it reconnects, within the endpoint's maximum age and the relay's retention limits. Stored webhook events are encrypted so that only your computer can read them.
Start an automation from another service
-
Let the automation accept webhooks. Add the trigger to its definition, or choose Webhook in its Add trigger menu:
on: webhook: {}Webhook runs follow the same rules as scheduled runs: no
inputs:and no interactive terminal input. -
In Settings → Webhooks, add an endpoint, name it after the service that will send to it, and choose the automation. Copy its URL.
-
Paste the URL into the service's webhook settings. If the service checks the URL first, set up a challenge answer before you save it there.
-
Send a test from the service, or try the automation locally with
hob webhook test, and read the result in the endpoint's Delivery log.
Give each service its own endpoint. You can then rotate, pause, or delete one without touching the others, and a leaked URL affects only one service.
Read the request in the automation
Each run gets the request in the folder named by HOB_WEBHOOK_DIR:
| File | Contents |
|---|---|
request.json | Event ID, endpoint name, method, the path after the endpoint ID, the query, the full URL, the headers, the time the relay received it, and the signature result |
body | The request body, byte for byte |
The path after the endpoint ID does not choose an automation; it is data. An
automation can branch on it, or on a header such as X-GitHub-Event:
version: 1
name: Deploy on push
on:
webhook: {}
steps:
- id: deploy
name: Deploy
shell: sh
run: |
event=$(jq -r '.headers[] | select(.[0] == "x-github-event") | .[1]' "$HOB_WEBHOOK_DIR/request.json")
[ "$event" = push ] || exit 0
./scripts/deploy.sh "$HOB_WEBHOOK_DIR/body"The folder is temporary and is removed when the run ends. Copy anything the run must keep into your project or an artifact. The delivery log never shows the request.
Check that the sender is real
Anyone who learns an endpoint URL can send to it, so turn on Check a signature for every service that signs its requests. The check is a generic HMAC template:
- Algorithm: HMAC with SHA-256, SHA-1, or SHA-512.
- Header and format: the header that carries the signature, as the whole
value, a value after a prefix such as
sha256=, or one value in akey=valuelist such ast=…,v1=…. - Encoding: hex or base64.
- Signed content: a template such as
{body}orv0:{header:X-Slack-Request-Timestamp}:{body}.{url}is the full URL as the relay received it,{kv:t}is a value from the signature list. - Timestamp: optional. It is compared with the time the relay received the event, so an event that waited while your computer was off still passes.
The secret stays on your computer. It is written once and never shown again. An event that fails the check is logged and skipped. A service whose scheme does not fit the template can be checked in the automation's first step, because the run gets the raw body and headers.
Answer setup challenges
The relay answers the sender at once, before the automation runs. By default it
answers 200 with an empty body; you can set the status, a short body, and its
content type. An automation cannot send a synchronous reply; use the service's
callback URL, such as Slack's response_url, for that.
Some services check a URL before they use it, with a challenge value they expect back. Answer setup challenges returns a named field from the query or the JSON body. It can require another field to equal a value you set. A challenge request is answered and not stored.
Common services
hob has no presets; these settings come from each service's documentation.
| Service | Methods | Answer setup challenges | Check a signature |
|---|---|---|---|
| GitHub | POST | Off | SHA-256, header X-Hub-Signature-256, prefix sha256=, hex, {body} |
| GitLab | POST | Off | GitLab sends your secret token in X-Gitlab-Token; compare it in the automation's first step |
| Stripe | POST | Off | SHA-256, header Stripe-Signature, key list with key v1, hex, {kv:t}.{body}, timestamp kv:t in seconds |
| Shopify | POST | Off | SHA-256, header X-Shopify-Hmac-Sha256, whole value, base64, {body} |
| Slack | POST | Field challenge in the JSON body, only when type equals url_verification | SHA-256, header X-Slack-Signature, prefix v0=, hex, v0:{header:X-Slack-Request-Timestamp}:{body}, timestamp header:X-Slack-Request-Timestamp |
| Dropbox | GET, POST | Field challenge in the query | SHA-256, header X-Dropbox-Signature, whole value, hex, {body}, with your app secret |
| Microsoft Graph | POST | Field validationToken in the query (the challenge answer is 200 with the token as text/plain); set the fixed response to 202 for notifications | Graph does not sign; check the clientState value in the automation's first step |
| Meta | GET, POST | Field hub.challenge in the query, only when hub.verify_token equals your token | SHA-256, header X-Hub-Signature-256, prefix sha256=, hex, {body}, with your app secret |
When an event waits or is skipped
hob holds an event rather than dropping it when the reason can pass, until the endpoint's maximum age (24 hours by default) runs out:
- The project is closed. Automatic runs need an open project, on the desktop or on a headless Host. Keep the project open where webhooks should run.
- A shared automation is not approved for automatic runs. Run it once yourself and allow automatic runs in the trust review.
- The endpoint is paused. Events already received wait until you resume it; new requests are refused.
- Webhooks are not available on this computer, for example after a change to the license.
- A run is already going. The endpoint's overlap setting decides: queue
(the default) runs every event in order, skip drops the new event, and run
beside it starts at once. Running beside needs an automation with
concurrency: allow.
Settings → Webhooks shows held events per endpoint, and the delivery log shows each result, including requests the relay refused for a rate limit, quota, or size.
When a URL leaks
Rotate URL gives the endpoint a new URL at once; the old one stops working. Give the new URL to the service. Pause refuses new requests while you decide. To stop a leaked URL at once, rotate or delete it.
A downgrade or a removed computer also stops its endpoints from accepting requests, but not at once. A downgrade takes effect when one of your computers next renews its license, usually within a day. A removed computer's endpoints keep accepting requests for up to two weeks after its last license renewal. The URLs are kept and work again after an upgrade or a new activation.
Limits
These are the default limits; hob can change them for a license:
- Up to 50 endpoints per computer.
- A request body of up to 1 MB.
- Undelivered events are kept in the relay for 7 days.
- Daily and monthly event quotas apply per license.
- Rate limits apply per endpoint and per license, so more endpoints do not add up to more throughput.
A refused request gets 429 with Retry-After, and the delivery log counts
it. An event that waits longer than its endpoint's maximum age or the relay's
retention is not delivered, and the log says so.
What hob stores
Requests pass through hob's relay, which runs on Cloudflare. The relay encrypts each request to a key that only your computer holds before storing it, deletes it once your computer has received it, and does not log webhook contents. See the privacy policy for details.
For agents
Use hob webhook to set up endpoints when the user asks. Read
hob webhook --help first. Secrets come only from stdin or a file; never put
one on the command line.
hob webhook automations
hob webhook create --name GitHub --automation <automation-id> \
--signature '{"algorithm":"sha256","header":"X-Hub-Signature-256","format":"prefix","prefix":"sha256="}' \
--secret-file ~/.config/github-hook-secret
hob webhook create --name Slack --automation <automation-id> --echo-field challenge --echo-if type=url_verification
hob webhook test <endpoint-id> --body-file payload.json --header "X-GitHub-Event: push"
hob webhook log <endpoint-id>hob webhook test sends a local event through the same path on the Host,
without the relay, so you can develop an automation before the service is
connected. Do not paste endpoint URLs into logs or issues; a URL lets anyone
send events.