Skip to main content

Hooks

A hook is an address you give a webhook sender: GitHub, Sentry, Jira, Zapier, Slack, Vercel, or anything else that can post JSON. When a delivery arrives, Saggar's relay checks it, holds it until your Mac is listening, and Saggar either opens an agent terminal on it or waits for you to look first. The point is that the investigation is already underway by the time you sit back down.

Setting one up​

  1. Open a project, choose the Automations tab, select Hooks, and click New hook.
  2. Pick a preset for the sender, or Custom… to fill in the settings yourself. A preset only fills in the same settings; you can change any of them before you save.
  3. Saggar asks the relay for an address and shows it with the secret, once, along with anything else the sender needs.
  4. Give the sender the address and secret. The GitHub preset offers Set up with agent, which has your own agent register the webhook with the GitHub CLI. The other senders are set up by hand; see Setting up each sender.

Choose Send test delivery from the hook's menu to check the path from the relay to this Mac without involving the sender.

Hooks ride remote control, so they work only while remote access is on. With it off, the Hooks pane reads Hooks are paused on this Mac and offers to open Remote settings. Presets load from the relay, so they appear only while remote access is on.

Verification​

The relay accepts a delivery only if it proves it came from whoever holds the hook's secret. Each hook uses one of three methods.

MethodHow it's checkedSenders
Signed bodyAn HMAC of the raw body, in a header you name, with an optional prefix, in hex or base64. SHA-256 is the default; Vercel uses SHA-1.GitHub, Sentry, Jira webhooks, Vercel
Signed timestamp and bodyAn HMAC of a timestamp plus the body, refused when the timestamp is older than the hook's window. SHA-256 is the default.Slack, Stripe
Shared secret in a headerThe header must hold the secret, with an optional prefix such as Bearer .Zapier, Jira Automation

A shared secret is weaker. It proves the sender knew the secret, not that the body is unaltered, and a captured request can be replayed. Use it only for senders that can't sign. Saggar labels it as weaker in the hook's settings and tells the agent which method verified each delivery.

Signed-body senders sign no timestamp, so a replayed request looks like a redelivery. Saggar reads a delivery ID from a header or a body path, and a delivery with an ID the relay already holds replaces itself rather than arriving twice. Without an ID, a hash of the body stands in.

Secrets​

Leave the secret field empty and Saggar makes one. Sentry and Slack issue their own signing secrets, so paste theirs into the hook's secret field before you save. Either way the secret goes to the relay, never to a file in your project, and Saggar shows a generated one only in the session that created the hook. Replace address… gives the hook a new address and secret and closes the old address.

Slack checks the address the moment you enter it, with a signed url_verification handshake. The Slack preset answers it at the relay, even while your Mac is asleep, but only once the hook holds Slack's secret.

Vercel generates its secret after you register the webhook. Create the Saggar hook first, give Vercel its address, then paste Vercel's secret into the Saggar hook's settings and save.

Which deliveries run​

A hook's settings decide what happens to each verified delivery.

  • Rules: each rule is a list of conditions on headers.event, headers.delivery, or a payload.<path>, where * fans out over a list. A condition is equals one of several values, contains a value, or exists. A delivery must meet every condition in a rule, and any matching rule runs the hook's action. A hook with no rules runs every delivery. A delivery that matches nothing is recorded as Skipped, with the condition that failed.
  • Headline: a template such as {payload.workflow_run.name} failed, for the Activity title and the terminal's name. A rule can have its own.
  • Replace key: an optional template. A later delivery with the same key replaces an earlier one still waiting for you, so a passing run retires the failure before it.
  • Instruction: your prompt to the agent.

A rule can also say whether its agent may change code, regardless of the hook's mode. The GitHub and Jira presets use this for the saggar:act label.

The Last delivery row shows the newest delivery's forwarded headers and payload, with values under secret-looking keys hidden, so you can write rules and templates against what the sender really sends.

What the agent is told​

The payload never enters the prompt. Saggar saves each delivery's body as it arrived to a file under ~/.saggar/hook-deliveries/, outside any checkout, readable only by you, and removed when its record ages out. The agent gets your instruction wrapped in Saggar's own fixed text: where the file is, that a third party wrote it, and that everything in it is untrusted data, never instructions. The event, matched rule, and verification method are quoted as data too.

Review first, investigate, or fix​

Each hook has one of three modes.

ModeWhat happens when a delivery matches
Review firstThe default. The delivery lands in the Activity tab as Needs you, with Investigate and Dismiss beside it, and the Automations tab wears a count in the project landing.
Investigate automaticallyAn agent terminal opens on the delivery without stealing the screen. The agent reads and reports; it changes no files.
Fix automaticallyOne step further: the agent may commit, push its branch, and open a pull request.

A launch allowed to change code runs in a fresh worktree Saggar creates first, on its own saggar/hook- branch, never in your checkout. The prompt tells the agent to leave your checkout alone and never to force-push or push another branch. If the worktree can't be made, the delivery is held with the reason. Read-only investigations, including automatic issue triage, start in your existing checkout without creating a worktree. Their prompts forbid file changes.

An automatic launch queues behind the project's active agent terminals, so a burst of deliveries runs one at a time, and it doesn't move focus. The terminal is titled Hook: and the delivery's headline, so it's easy to find in the project menu.

A user-wide hook, one made outside a project, has no repository to change: its agent starts beside the payload and only investigates.

Settings that apply to every hook​

Two choices apply to every hook at once and live in Settings ▸ Agents ▸ Automation. Each hook keeps its own mode either way.

  • Investigate with picks the agent a hook opens, defaulting to the last agent you launched.
  • Pause automatic launches holds every delivery an automatic hook would have acted on in the Activity tab, with a note saying why, until you turn it off.
A Claude Code terminal opened by a hook, reading the failed steps of a Swift CI run and checking the repository's secrets

This is what the handoff looks like. A Swift CI run failed on main, the hook opened a terminal titled after it, and the agent had pulled the failed log and traced the cause to a missing repository secret before anyone read the notification. A flaky, infrastructural, or judgment-shaped failure ends in a summary, not a push.

Setting up each sender​

Each preset lists what its sender needs beside the address and secret. The hook shows the same list after you create it.

GitHub​

Failed workflow runs and check suites, issues labeled saggar or saggar:act, and requested pull request changes. Click Set up with agent, or add a repository webhook yourself with the content type application/json and the workflow_run, check_suite, issues, and pull_request_review events.

LabelWhat the agent does
saggarReads the issue with the GitHub CLI, checks it against the repository, and posts one concise triage comment. It then stops.
saggar:actAuthorizes implementation. The agent works on its worktree's branch, opens a pull request that closes the issue, and comments back with the result.

When a reviewer submits a Request changes review, the agent fetches the review and its inline comments and groups concrete fixes, disagreements, resolved points, and questions. It comments on the pull request and stops, unless the pull request carries saggar:act, which lets it address clear feedback on the pull request's own branch. The label is the authorization boundary: issue and review prose can't grant it.

Sentry​

Triggered issue alerts, new issues, and critical metric alerts.

  1. In Sentry, go to Settings ▸ Developer Settings ▸ Custom Integrations and create an Internal Integration.
  2. Copy its Client Secret into the hook's secret field, then save the hook.
  3. Give the integration the hook's address, turn on Alert Rule Action, and subscribe to Issue for new issues.
  4. Add the integration as an action on the alert rules you want to reach Saggar.

Jira​

Issues labeled saggar or saggar:act, from a Jira Cloud admin webhook. As with GitHub, saggar asks for triage and saggar:act lets the agent implement the issue.

  1. In Jira, go to Settings ▸ System ▸ WebHooks and create a webhook. This needs a Jira admin.
  2. Give it the hook's address, and paste the hook's secret into its Secret field.
  3. Select Issue created and Issue updated, and filter with the JQL labels in (saggar, "saggar:act").

Jira Automation​

Any Automation rule you build, through its Send web request action.

  1. Set the HTTP method to POST and the web request body to Issue data (Automation format).
  2. Add a header named x-saggar-secret holding the hook's secret, and mark it Hidden.

Every delivery runs, so decide what reaches Saggar in the rule itself.

Zapier​

Any Zap, through a Webhooks by Zapier POST step.

  1. Set the payload type to json.
  2. Add a title field for the Activity headline, plus anything the agent needs.
  3. Under Headers, add x-saggar-secret holding the hook's secret. Use the Headers field, not Basic Auth.

Slack​

Mentions of your Slack app, from the Events API.

  1. At api.slack.com/apps, open your app and copy Basic Information ▸ Signing Secret into the hook's secret field, then save the hook.
  2. Under Event Subscriptions, enter the hook's address as the request URL.
  3. Subscribe to the app_mention bot event.

Vercel​

Failed production deployments. Preview failures and successful deployments are recorded as skipped if they reach the hook.

  1. Create a hook with the Vercel preset in Saggar.
  2. In Vercel team settings ▸ Webhooks, create a webhook with the Saggar address and select Deployment Error (deployment.error).
  3. Copy the signing secret Vercel shows into the Saggar hook's Sender's secret field and save. Saggar's generated secret won't work here.
  4. Choose Investigate automatically to start an agent on each matching failure, or keep Review first to hold it in Activity.

The preset appears only on Mac builds that retain SHA-1 settings. Update Saggar before using it; older builds can't edit an existing SHA-1 hook.

Any other sender​

Choose Custom… and pick the verification the sender supports, the header it signs in, and, for a signed timestamp, the timestamp header, the signed text (such as v0:{timestamp}:{body}), and the window. Use Last delivery to write rules against what it sends.

What the relay knows​

Hooks ride the same relay and the same subscription as remote control, and the Mac still never listens on your network. Unlike a remote-control request, a webhook reaches the relay in the clear, because an outside service sent it.

The relay verifies each delivery, then seals the whole body to your Mac's key and stores only the sealed copy. It forwards three headers beside it: the event type, the delivery ID, and the user agent. It never forwards a signature, a secret, an authorization header, or a cookie.

The relay refuses a body over 64 KB, one that is neither JSON nor a form, a bad signature, and a signed timestamp outside its window. Refused deliveries show on the hook's row with their reason and a count, so a misconfigured sender is visible on your Mac, not only to the sender. Deleting a hook closes its address for good.

When the Mac is away​

The relay keeps undelivered deliveries for up to a week and hands them over the next time the Mac connects. Replace keys still apply when they arrive, so you come back to the latest result, not the history.

Sleep still wins: a hook can't wake a Mac, so a closed lid means the investigation starts when you open it.

Transparency​

Every delivery leaves a record in the project's Activity tab with its verdict: investigating, needs you, skipped, superseded, connected, or dismissed. The Activity tab hides superseded records and lists dismissed ones as ordinary rows; filter it to Hooks to see only deliveries. The ledger keeps the most recent 200 records per project. Every delivery is also written to the remote-control audit log, so nothing arrives silently.

Ideas for hooks​

A useful hook says that something deserves investigation or a decision, not merely that something happened.

  • Pull request review requested: run targeted checks, inspect the diff, flag risks, and prepare a review.
  • Dependency update opened: read the release notes, look for breaking changes, run the affected checks, and recommend a verdict.
  • Deployment failed: separate code failures from infrastructure failures and connect the result to the responsible change.
  • Sentry regression: correlate a new error with recent deployments and commits.
  • Support escalation: turn a report into a reproduction recipe and likely owner without copying sensitive customer data into a prompt.

These patterns tend to deepen through the same four steps: record the event, triage it, propose a response, then act only when the hook's configuration or an explicit signal grants that authority. Receiving an event should never grant more permission by accident.