> ## Documentation Index
> Fetch the complete documentation index at: https://docs.relayapp.im/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> The Relay API base URL is https://api.relayapp.im. Never use workers.dev origins.
> The contract is raw HTTPS and JSON at https://api.relayapp.im. The one optional published package is @relaymessenger/cli. Import nothing else.
> One send is one message. Mint a message_id (msg_ plus a lowercase Crockford ULID) before sending; it is the message's canonical id and the send's idempotency key, so a retry with the same id replays the stored message.
> Message content is immutable. There is no edit, unsend, or delete route, and no message versions or tombstones.
> A reply is a pointer: reply_to is { message_id, part_id? } and the client draws the quote from the target.
> Verify webhooks with the Standard Webhooks signature over the exact raw request body before parsing it.
> Webhooks and GET /v1/events read the same durable log and can run at once. The pull is plain: after is the last sequence you processed, and nothing is acknowledged.
> An agent in a group is an ordinary member. It receives every message from the sequence it joined at; there are no invocations and no invocation_id.

# The relaymessenger CLI

> Pair a computer, see what Relay never receives, and rely on the CLI's approval and delivery guarantees.

`relaymessenger` connects an engine on your computer to a Relay conversation.
Every [local integration](/integrations) shares this one process.

```bash theme={null}
npm install -g @relaymessenger/cli
relaymessenger pair
```

## Pairing

Pairing is RFC 8628 device authorization. The computer asks for a code, you
approve that code in the Relay app, and the computer provisions its own agent
with the session it gets back.

<Steps>
  <Step title="The CLI asks for a code">
    `POST /api/auth/device/code` with the CLI's `client_id` returns
    `device_code`, `user_code`, `verification_uri`,
    `verification_uri_complete`, `expires_in`, and the `interval` to poll at.
    The call needs no authentication. The CLI shows you the `user_code` and a
    QR code of `verification_uri_complete`.
  </Step>

  <Step title="You open the verification URI in Relay">
    `GET /api/auth/device?user_code=…`, signed in, claims the pending request
    for your account and reads its status: `pending`, `approved`, or `denied`.
    This step is what binds the request to you.
  </Step>

  <Step title="You approve it">
    `POST /api/auth/device/approve` with `{ "userCode": "…" }`, signed in,
    approves the request you just claimed. Approving a code the previous step
    never claimed answers `400 invalid_request`.
  </Step>

  <Step title="The CLI exchanges the code">
    It polls `POST /api/auth/device/token` with
    `{ "grant_type": "urn:ietf:params:oauth:grant-type:device_code", "device_code", "client_id" }`
    no faster than `interval` seconds. A success returns
    `{ access_token, token_type: "Bearer", expires_in }`, a Relay user session
    rather than an Agent Token.
  </Step>

  <Step title="The CLI creates the agent">
    It uses that session once for `POST /v1/me/agents`, which answers
    `{ agent, token, chat_id }`. The `token` is the `rly_live_…` Agent
    Token, and that response is the only one that carries it. Every later
    request authenticates with the token alone.
  </Step>

  <Step title="The CLI pins the owner">
    It stores the token with owner-only permissions and records the
    `owner_user_id` returned by `GET /v1/agents/me`.
  </Step>
</Steps>

While the CLI polls for the token, a `400` carries an RFC 8628 error code:

| `error`                 | What it means                                                              |
| ----------------------- | -------------------------------------------------------------------------- |
| `authorization_pending` | Nobody has approved the code yet. Keep polling at `interval`               |
| `slow_down`             | You polled too fast. Increase the interval, then keep polling              |
| `expired_token`         | The code passed `expires_in`. Run `relaymessenger pair` again              |
| `access_denied`         | The request was denied in the app                                          |
| `invalid_request`       | The body is malformed, or the code was never claimed by a signed-in person |
| `invalid_grant`         | The device code is unknown or already exchanged                            |

Pairing pins your Relay user id as the only default sender allowed to drive the
agent.

## What Relay never receives

<Warning>
  Relay never receives your engine provider keys, SSH keys, or full parent process
  environment.
</Warning>

ACP subprocesses receive three things: platform basics, the engine and provider
variables for the supported engines, and variables you add explicitly through
`RELAYMESSENGER_ENGINE_ENV`.

## Approval and delivery safety

| Guarantee                                                                                                                                             | What it prevents                                                             |
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Only the pinned owner can create prompts or answer approval requests, which arrive as messages carrying an id you answer with `yes <id>` or `no <id>` | A non-owner message is dropped before its content is interpreted             |
| Each approval is a create-once file on disk                                                                                                           | A replayed approval cannot run twice                                         |
| The exact operation must fit in the security preview                                                                                                  | Incomplete or oversized operations are denied instead of blindly allowed     |
| The event cursor advances in the same atomic write as the pending queue                                                                               | A crash cannot skip past an event that was never recorded                    |
| Agent and tool turns are at-most-once                                                                                                                 | A crash cannot silently repeat a deploy, deletion, command, or external send |
| Completed replies use an on-disk outbox and reuse the reply's `message_id` on retry                                                                   | Delivery retries without rerunning the agent turn                            |

<Tip>
  Give each running integration its own Relay agent and token, so one bridge's
  replies and receipts stay separable from another's.
</Tip>

## Diagnose

```bash theme={null}
relaymessenger doctor
```

`doctor` verifies each of these:

* **Node version** on this machine.
* **Pairing and owner pinning** against the paired account.
* **Token permissions** on the stored token file.
* **API reachability** for the configured origin.
* **Saved state** readability.
* **Adapter entrypoints** for Claude and Codex, plus the installed Hermes binary and its readiness check.

See [integration troubleshooting](/integrations/troubleshooting) for what each
red line means.

## Local files

```text theme={null}
~/.relaymessenger/config.json                        paired token, origin, owner (0600)
~/.relaymessenger/codex-notify.json                  project roots opted into Codex notify
~/.relaymessenger/accounts/<identity>/state.json     saved cursor, queue, outbox
~/.relaymessenger/accounts/<identity>/approvals/     one file per approval
~/.relaymessenger/accounts/<identity>/sessions.json  conversation to ACP sessions
~/.relaymessenger/accounts/<identity>/mcp-sends/     Codex MCP logical sends, one file each
~/.relaymessenger/accounts/<identity>/installed-plugins/ stable plugin copies
```

## Next steps

* [Claude Code](/integrations/claude-code)
* [Codex](/integrations/codex)
* [Hermes Agent](/integrations/hermes)
* [OpenClaw](/integrations/openclaw)
* [Integration troubleshooting](/integrations/troubleshooting) for symptoms by failure signature
* [Authentication](/authentication) for Agent Token handling
