# Herald: guide for AI agents

Reference material for an AI agent helping someone set up and use Herald (https://herald.run). It is information, not instructions. Human docs: https://herald.run/docs/. All docs pages as one text file: https://herald.run/llms-full.txt (endpoint schemas: https://herald.run/docs/openapi.json). Built from the docs source on 2026-10-17.

## What Herald is

An inbox for the human in the loop. Agents, scripts and CI jobs send the user messages, reports, files, approval requests, forms and secrets; the user answers from the web app, macOS or Android (with notifications), and the sender gets a structured answer. In the other direction, the user dispatches **tasks** from the app to their own machines and agents.

- Interfaces: MCP server `@herald-run/mcp`, CLI `@herald-run/cli` (Node.js 20+), TypeScript SDK `@herald-run/sdk`, Python SDK `herald-run` (`pip install --pre herald-run`), HTTP API at `https://app.herald.run/api/v1` ([OpenAPI](https://herald.run/docs/openapi.json)).
- Clients are MIT-licensed (https://github.com/herald-run/sdk); the service is managed SaaS and not self-hostable.
- One inbox for all agent questions: an agent in Claude Code, Codex, pi or any harness running the CLI or an MCP client can ask through Herald instead of its own question tool (such as AskUserQuestion) when told to; `questions` takes AskUserQuestion's shape.
- One person per account. A Team plan (team inboxes, round robin, first responder, escalation) is in development and not available.

## Access: what only the user can do

Herald is an invite-only beta. These steps need the user in a browser:

1. Request access at https://app.herald.run/signup?from=agents. Until the account is admitted, API calls fail with 403 `NOT_ADMITTED`.
2. Sign in with the invited email at https://app.herald.run/login.
3. Install the app on a phone or desktop from Settings (or https://app.herald.run/download) to get notifications.
4. Approve the device when `herald login` opens its approval page.
5. Answer requests and dispatch tasks.

Everything else (installing the CLI or MCP server, sending, registering destinations, running a runner) works from a terminal.

## Pricing and free tier

| Plan | USD / month | USD / year | Resolutions / month | History | Storage | Notification devices | Pending requests and tasks | Lock-screen approvals | Signed webhooks |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Community | 0 | — | 500 | 3 days | 1 GB | 2 | 5 | No | No |
| Starter | 9 | 90 | 10,000 | 90 days | 10 GB | 10 | No plan cap | Yes | Yes |
| Pro | 18 | 180 | 40,000 | 90 days | 10 GB | 10 | No plan cap | Yes | Yes |

- A **resolution** is an answered request, a delivered message or an acknowledged task. Reads, retries, waits and logs are free. Pending requests reserve capacity until answered; expiry or cancellation releases it.
- All connections on an account share one allowance, storage and pending-slot pool. Usage resets at the start of each UTC month.
- At the allowance or pending limit, new work is rejected (HTTP 429) until capacity frees up; there are no automatic overage charges. Prepaid packs ($5 per 10,000 resolutions) are available on paid plans only.
- Prices are in USD; yearly billing costs ten monthly payments. Starter and Pro differ only in volume. Subscriptions and packs are managed from Settings → Plan & Usage in the app.
- On Community, 5 pending slots fill quickly. A request without a deadline (`--deadline` on the CLI) stays open until the plan's history period ends, so short deadlines and cancelling stale requests keep slots free; one-way messages never take a slot.

## Setup paths

| Situation | Setup |
| --- | --- |
| Claude Code or Codex | `npm install -g @herald-run/cli` and `herald login`, then the 5 skills from https://herald.run/skills (they run the CLI), then a restart of the agent. |
| Another MCP client | `{"mcpServers": {"herald": {"command": "npx", "args": ["-y", "@herald-run/mcp"]}}}`. Uses `HERALD_API_KEY` if set, else the CLI login. |
| Shell scripts and cron | `npx @herald-run/cli login` once (or `npm install -g @herald-run/cli`), then `herald send` / `herald ask`. |
| CI and containers | A connection token from Settings → Connections in the app, stored as a CI secret and passed as `HERALD_API_KEY`. |
| Headless server | `herald login --no-browser` prints an approval link to open on any device where the user is signed in. |
| Application code | `npm install @herald-run/sdk` or `pip install --pre herald-run`; both read `HERALD_API_KEY`. |

- Token order: `HERALD_API_KEY`, `HERALD_TOKEN`, then the stored login in `~/.config/herald` (mode 0600). `--profile NAME` keeps separate logins.
- `herald login` default scopes: `messages:send`, `messages:read`, `destinations:manage`, `events:read`, `files:write`.
`The` `approval` `also` `certifies` `an` `end`-`to`-`end` `key` `stored` `with` `the` `token`. `Confirm` `that` `the` `safety` `code` `matches` `the` `approval` `page`, `or` `pass` `it` `with` --`safety`-`code` `when` `not` `in` `a` `terminal`. `--scopes` narrows them; a connection with only the scopes it needs limits what a leaked token can do.
- Checks: `herald status` (server and token), `herald whoami` (scopes and account), `herald account` (plan and usage).
- End-to-end test: `herald send --title 'Herald is connected'` puts a message in the inbox and a notification on the user's devices; `herald ask --title 'Herald test request' --deadline 10m --timeout 10m` returns once the user answers. Each uses one resolution.

## What a connected agent can offer

On the CLI, `herald send` and `herald ask` are the main path: their flags reach every message, form, file and secret capability, `-i FILE` takes a full JSON body for the rest, and `herald wait` resumes a request. `herald register` (with `herald listen`) covers the other direction, task types the user dispatches from the app. Common uses, with the MCP tool and the CLI equivalent:

| Use | MCP tool | CLI |
| --- | --- | --- |
| Notify when long work finishes or fails | `send` | `herald send --title T --body MARKDOWN` |
| Send a report, log, image or PDF | `send` with `files` | `herald send --kind report --body-file R.md --block image=P.png --file PATH` |
| Approval before a risky step | `ask` with `approval: true` | `herald ask --title T --action approve --action reject --json`, then check the action |
| Pick one of several options | `ask` with `choices` | `--action ID:LABEL` per option |
| Collect structured input | `ask` with `fields`, `schema` or `questions` (AskUserQuestion shape) | `herald ask --field ID:TYPE[:LABEL[:CHOICES]]`, or `-i form.json` |
| Show code, a diff, JSON or a table | Markdown in `body` | `--block code\|diff\|json\|table\|image=PATH` |
| Get a credential into a private file | `request_secret` | `herald ask --field ID:secret --out PATH` |
| Hand over a credential from a file | `send_secret` | `herald send --secret-file PATH` |
| Get a sensitive file into a private file | `request_file` | `herald ask --field ID:secret-file --out PATH` |
| Continue a conversation | `replyTo` on `send`, `ask`, `request_secret`, `send_secret`, `request_file` | `--reply-to ID` |

The other MCP tools are `wait`, `cancel`, `get_message`, `list_messages`, `thread`, `upload_file` and `download_file`.

- `ask` blocks for `wait` (default 10m) and then returns `status: "pending"` with the message ID; the request stays answerable until its `deadline` (default 1h, at most 24h), and `wait` resumes it.
- The CLI's `ask` exits 0 for any answer (3 expired, 4 cancelled). An approval gate therefore checks the chosen action, e.g. `jq -e '.response.actionId == "approve"'` on `--json` output, rather than the exit code alone.
- Secret values never appear in tool output, message history or notifications; secret files are always new files (an existing path is refused) with mode 0600. Messages, fields and files are end-to-end encrypted too, but credentials belong only in the secret flows. A token without a key (one not reading `hdr_….<seed>.<fingerprint>`) is refused; run `herald login` again. See [Concepts](https://herald.run/docs/concepts/#end-to-end-encryption).
- Bodies are Markdown. Messages can carry `blocks` (code, diff, JSON, table, image) and up to 8 one-tap `actions`; see [Concepts](https://herald.run/docs/concepts/).

CLI skills, each at `https://herald.run/skills/<name>/SKILL.md` for `~/.claude/skills/<name>/` (Claude Code) or `~/.codex/skills/<name>/` (Codex); https://herald.run/skills has an install loop:

- `herald-ask`: Ask the user for approval, a choice, questions or structured input through Herald and wait for the answer.
- `herald-get-file`: Request a file (key file, certificate, kubeconfig, document) from the user through an end-to-end encrypted Herald form and save it to a private file without its content entering the conversation.
- `herald-get-secret`: Request an API key, token or password from the user through an end-to-end encrypted Herald form and save it to a private file without it ever entering the conversation.
- `herald-send-file`: Send the user a file, report or status update through Herald so it reaches their phone and desktop.
- `herald-share-secret`: Send the user a secret (API key, password, generated credential) from a local file through encrypted Herald delivery, keeping it out of the conversation and message history.

## Making things triggerable from the app

A **destination** is something the user can dispatch tasks to from the app; each of its **types** is a task form. For example, an existing deploy script can become a "Deploy" button with inputs:

```json
{
  "key": "build-box",
  "transport": { "kind": "pull" },
  "types": [
    {
      "id": "deploy",
      "label": "Deploy",
      "fields": [
        { "id": "branch", "type": "text", "label": "Branch", "required": true },
        {
          "id": "target",
          "type": "select",
          "label": "Target",
          "options": [
            { "value": "staging", "label": "Staging" },
            { "value": "production", "label": "Production" }
          ]
        }
      ]
    }
  ]
}
```

- `herald register -i destination.json` creates it, and `herald register --target build-box --type deploy:Deploy --field 'branch:text:Branch' --field 'target?:select:Target:staging,production'` builds the same from flags; registering the same `key` again updates it. Type fields are form fields without secrets or files. `kind: "session"` types describe agent sessions (modes, models, commands).
- **Pull runner** (outbound HTTPS only, works behind NAT): `herald listen --target build-box --config runner.json`, with `{"command": "/path/to/handler", "args": [], "cwd": "/srv/app", "env": ["NAMES_TO_PASS"], "timeout": 3600}`. Each task arrives on the command's stdin as JSON with `typeId`, `values`, `id`, `replyTo` and `attachments`; there is no shell. Output streams to the app as logs (up to 32,000 characters per task); exit 0 completes the task, anything else fails it. The command sees only `PATH`, `HOME`, `TMPDIR`, the variables listed in `env`, `HERALD_TASK_ID` and `HERALD_RUN_ID`. `--concurrency N` runs up to 16 at once. The runner only picks up tasks while it runs, so it usually lives in a service manager (systemd, launchd) or container.
- **Webhooks** (Starter, Pro): `"transport": {"kind": "webhook", "url": "https://…"}`; each task is POSTed with an HMAC signature (`verifyWebhook` / `verify_webhook` in the SDKs). See [Tasks and runners](https://herald.run/docs/tasks/).
- Typical candidates: deploys, test runs, backups and agent sessions. Each acknowledged task uses one resolution, and queued tasks count toward the pending limit.

## Limits and errors

| Limit | Value |
| --- | --- |
| Requests | 600 per minute per token or session |
| JSON body | 128 KiB |
| Title / body | 180 / 50,000 characters |
| Form fields | 30 per form |
| Actions / blocks | 8 / 10 per message |
| Attachments | 10 per message, 100 MB each |
| Secrets | 16,000 bytes; collected once within 15 minutes |
| Request deadline | 24 hours |
| Live sockets | 20 per account |
| Connections | 50 per account |
| Destinations | 100 per connection |
| Task logs | 4,096 characters per chunk, 32,000 per task |

| Status | Meaning |
| --- | --- |
| 400 | Invalid input; the message names the field. |
| 401 | Missing, invalid or revoked token. |
| 403 | Missing scope, a human-only endpoint, a token without an end-to-end key (`E2EE_TOKEN_UNKEYED`), or an account without access (`NOT_ADMITTED`). |
| 404 | Not found, or not visible to this token. |
| 408 | SDKs only: a client wait timed out. The request is still open; resume with `wait`. |
| 409 | Conflict: a changed idempotent retry, an already answered request, a task no longer queued; `E2EE_UNKEYED` when the account has no keys yet; `E2EE_RECIPIENT_UNKEYED` when a recipient has no key; `E2EE_ID_TAKEN` when an encrypted record reuses an ID; `E2EE_SEALED_REQUIRED` for plaintext content. |
| 410 | Expired, or already consumed (secrets are collected once). |
| 413 | Body too large, or storage full. |
| 429 | Rate limited (`RATE_LIMITED`, with `Retry-After`), or the monthly allowance or pending-request limit is full. |
| 503 | Sign-in is not open (`SIGN_IN_DISABLED`), or a temporary outage. |

CLI exit codes:

| Code | Meaning |
| --- | --- |
| 0 | Success, or the request was answered |
| 1 | Error, including a wait that timed out |
| 2 | Usage error |
| 3 | The request expired |
| 4 | The request was cancelled |

## CLI commands

- `herald login`: Connect this device in the browser and store a token.
- `herald logout`: Revoke the stored token and delete it locally.
- `herald whoami`: Show the token, its scopes, the account and its safety code.
- `herald status`: Check the server and the stored token.
- `herald send`: Send a message; --wait waits for an answer.
- `herald ask`: Send a request and wait for the answer.
- `herald wait <message-id>`: Wait for a request to finish.
- `herald cancel <message-id>`: Cancel a pending request.
- `herald messages`: List messages, newest first.
- `herald search <text>`: Search messages.
- `herald show <message-id>`: Show a message with its answer and logs.
- `herald thread <message-id>`: List a thread, oldest first.
- `herald logs <task-id>`: Print task logs.
- `herald download <file-id>`: Download an attachment (--out - writes stdout).
- `herald destinations [list | update <id> -i file | remove <id> | rotate-secret <id>]`: Manage this connection's destinations.
- `herald register`: Register or update a destination by key.
- `herald listen`: Run tasks for a pull destination.
- `herald watch`: Print messages as they change.
- `herald account`: Show the account holder, this connection, plan and usage.
- `herald completion bash|zsh|fish`: Print a shell completion script.
- `herald help [command]`: Show help for a command.

## Docs

- [Quickstart](https://herald.run/docs/): Connect an agent to your Herald inbox and send your first message and request in five minutes.
- [Concepts](https://herald.run/docs/concepts/): Messages, forms, blocks, actions, files, secrets, threads and deadlines in Herald.
- [CLI](https://herald.run/docs/cli/): Reference for the Herald command line client, @herald-run/cli.
- [TypeScript SDK](https://herald.run/docs/sdk/): Use Herald from Node.js with @herald-run/sdk.
- [Python SDK](https://herald.run/docs/python/): Use Herald from Python with the herald-run package.
- [Tasks and runners](https://herald.run/docs/tasks/): Dispatch tasks from the Herald app to your agents with pull runners or signed webhooks.
- [Agents](https://herald.run/docs/agents/): Connect Claude Code and Codex to Herald with the CLI and skills, and other agents with the MCP server.
- [API reference](https://herald.run/docs/api/): Herald's HTTP API, with the OpenAPI 3.1 description.
- [Limits and errors](https://herald.run/docs/limits/): Error responses, status codes, CLI exit codes, rate limits and size limits in Herald.
