Notifications and approvals

Outbound webhooks and a Telegram bot for sealed/failed/promoted/needs_approval events, plus /approve and /reject from chat.

Goal#

Get a status ping — never a diff, never file contents — the moment a worker seals or a worktree is promoted, and optionally approve or reject a session from Telegram instead of the CLI. Two independent channels, both opt-in: generic outbound webhooks (any URL — Slack, Discord, n8n, Zapier, your own receiver) and a Telegram long-polling bot.

Steps#

  1. Add one or more webhooks to orch.yaml:

    notify:
      webhooks:
        - url: "https://example.com/orch-hook"
          events: [sealed, failed, promoted, needs_approval] # omit = all
          secret_env: ORCH_WEBHOOK_SECRET
    

    Every delivery is a POST with a JSON body — event, workspace, project, session, agent, status, exit, verdict, guard_lines, usage.in/out, log_path, ts — and, when secret_env names a set environment variable, an X-Orch-Signature header: the hex HMAC-SHA256 of the raw body under that secret. Verify it before trusting the payload.

  2. Send a synthetic event to check the wiring:

    orch notify test [--event sealed|failed|promoted|needs_approval]
    
  3. Add a Telegram bot (talk to @BotFather for a token; message the bot once so getUpdates can see your chat id):

    notify:
      telegram:
        bot_token_env: ORCH_TELEGRAM_TOKEN
        chat_allowlist: [123456789]
        events: [sealed, failed, promoted, needs_approval]
    

    orch daemon starts the long-poll worker automatically when notify.telegram.bot_token_env is set. To run it on its own:

    orch notify telegram run
    

    From an allowlisted chat: /status, /approve <session>, /reject <session> (mark failed + prune the worktree). A message from any other chat id gets orch: unauthorized chat and nothing else runs.

  4. Require approval before a clean seal is eligible to merge:

    promote:
      require_approval: true
    

    A session that seals ok then fires needs_approval instead of being merge-ready; /approve <session> (or orch worktree promote <session>) still does the actual merge.

n8n / Zapier#

Both platforms speak plain HTTP — point their "catch webhook" trigger node at your receiver URL and put that URL in notify.webhooks[].url. No Orchemax-specific node needed: n8n's Webhook node and Zapier's "Catch Hook" trigger both parse the JSON body directly; add a Code/Filter step on event if you only want to react to needs_approval, and verify X-Orch-Signature in that step if the webhook is reachable from outside your network.

What never leaves the machine#

Payloads carry status and summary only — guard verdict lines, file counts, token usage — never a diff or file content. Code stays local; only the event above crosses the wire, and only to the URLs/chats you configured.