hobhob

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

  1. 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.

  2. In Settings → Webhooks, add an endpoint, name it after the service that will send to it, and choose the automation. Copy its URL.

  3. 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.

  4. 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:

FileContents
request.jsonEvent 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
bodyThe 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:

Deploy on a GitHub push
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 a key=value list such as t=…,v1=….
  • Encoding: hex or base64.
  • Signed content: a template such as {body} or v0:{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.

ServiceMethodsAnswer setup challengesCheck a signature
GitHubPOSTOffSHA-256, header X-Hub-Signature-256, prefix sha256=, hex, {body}
GitLabPOSTOffGitLab sends your secret token in X-Gitlab-Token; compare it in the automation's first step
StripePOSTOffSHA-256, header Stripe-Signature, key list with key v1, hex, {kv:t}.{body}, timestamp kv:t in seconds
ShopifyPOSTOffSHA-256, header X-Shopify-Hmac-Sha256, whole value, base64, {body}
SlackPOSTField challenge in the JSON body, only when type equals url_verificationSHA-256, header X-Slack-Signature, prefix v0=, hex, v0:{header:X-Slack-Request-Timestamp}:{body}, timestamp header:X-Slack-Request-Timestamp
DropboxGET, POSTField challenge in the querySHA-256, header X-Dropbox-Signature, whole value, hex, {body}, with your app secret
Microsoft GraphPOSTField validationToken in the query (the challenge answer is 200 with the token as text/plain); set the fixed response to 202 for notificationsGraph does not sign; check the clientState value in the automation's first step
MetaGET, POSTField hub.challenge in the query, only when hub.verify_token equals your tokenSHA-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.

On this page