# Relay for AI agents

Relay gives an agent a WhatsApp number it can read and write through one API key.
Use the MCP server if your client supports remote MCP, the CLI if you have a shell, or plain HTTP otherwise.

## Connect

### MCP (Claude Code, Codex, Cursor, any streamable-HTTP client)
```
claude mcp add --transport http relay https://relay.tinyclaw.online/mcp --header "Authorization: Bearer $RELAY_API_KEY"
```
Codex `~/.codex/config.toml`:
```
[mcp_servers.relay]
url = "https://relay.tinyclaw.online/mcp"
bearer_token_env_var = "RELAY_API_KEY"
```
Cursor `.cursor/mcp.json`:
```
{ "mcpServers": { "relay": { "url": "https://relay.tinyclaw.online/mcp", "headers": { "Authorization": "Bearer ${env:RELAY_API_KEY}" } } } }
```

### CLI
```
curl -fsSL https://relay.tinyclaw.online/install.sh | bash
relay login            # or export RELAY_API_KEY=rk_...
relay status
```

## How tools work
Every MCP tool takes `{ "action": "...", "params": { ... } }`. Call `action: "help"` on any tool to see its actions and params.
Call `status` with `action: "get"` first: it returns the plan, usage, numbers and suggested next steps.

Tools: status, search_docs, whatsapp_numbers, whatsapp_messages, whatsapp_conversations, whatsapp_contacts,
whatsapp_templates, whatsapp_webhooks, whatsapp_broadcasts, customers, setup_links, sandbox, usage.

Errors come back with `isError: true` and `{ "error": { "code", "message" } }`. Codes: invalid_params, not_found,
conflict, forbidden, insufficient_scope, payment_required, rate_limited, upstream_error.

## Rules agents must follow
1. **Sandbox first.** Build and test on a sandbox number (`kind: "sandbox"`). It costs nothing and messages nobody.
2. **Ask before messaging real people.** Any send from a `live` number, and every broadcast, needs the user's explicit OK for that exact message and audience.
3. **24-hour window.** Free-form messages only within 24h of the customer's last message. Outside it, send an approved template.
4. **No bursts.** Do not loop sends to many numbers; use a broadcast, which is paced. Meta restricts numbers that burst.
5. **Read-only keys** can list and read but not send. If you get `insufficient_scope`, ask the user for a full key.
6. **payment_required** means the plan limit was hit. Tell the user; do not retry.

## Common flows

### Test a reply bot end to end (sandbox)
1. `sandbox.create_number` (or use the number from `status`)
2. `sandbox.inbound` with `from`, `text` – simulates a customer message and opens the 24h window
3. `whatsapp_messages.send_text` to reply; status moves sent → delivered → read
4. `whatsapp_webhooks.create` with your URL to receive `whatsapp.message.received` events

### Onboard a business's own number
1. `customers.create` with the business name
2. `setup_links.create` → send the `url` to the business owner
3. They log in with Facebook and pick their number; it appears in `whatsapp_numbers.list` as `live`

### Send to someone outside the window
1. `whatsapp_templates.list` for the number
2. `whatsapp_messages.send_template` with `name` and `language`

## Webhook signature
Each delivery has `x-relay-signature: <hex>` = HMAC-SHA256(secret, raw body) and `x-relay-event: <event name>`. Compare in constant time.
