# Sentry Relay — Complete Technical Reference for LLMs & Autonomous Agents (`llms-full.txt`) > Canonical single-file documentation for **Sentry Relay** (`https://sentryrelay.com`). > Public Ecosystem Repository: `https://github.com/sentryrelay/sentryrelay` > OpenAPI 3.1 Spec: `https://sentryrelay.com/openapi.json` > MCP Discovery: `https://sentryrelay.com/.well-known/mcp.json` > Agent Skill: `https://sentryrelay.com/skills/sentryrelay/SKILL.md` --- ## 1. Architecture & Core Primitives Sentry Relay is the zero-retention Out-of-Band 2FA, OTP, Magic-Link & Authenticator (TOTP) Relay engineered for unattended AI browser agents, RPA agencies, and autonomous software. - **Dedicated Agent Identities:** Each Identity provisions an isolated `@in.sentryrelay.com` inbound email address, an optional dedicated US local carrier phone number (DID via Telnyx) for SMS shortcodes and voice calls, and a Google Cloud KMS AES-256-GCM envelope-encrypted RFC 6238 TOTP authenticator vault. - **Deterministic `<200ms` Extraction Engine:** A 4-stage deterministic pipeline (Stage 1: RFC 5322/MIME & Forwarding Wrapper Unwrapping; Stage 2: 35+ Provider Templates for Stripe, GitHub, AWS, Google, Okta, Auth0, Clerk, Supabase, Gusto, QuickBooks, etc.; Stage 3: Proximity-Scored Heuristic Regex & Link Unwrapping; Stage 4: Custom Per-Identity Regex Rules) extracts OTPs and magic links without LLM hallucination or latency. - **Zero Raw Message Retention:** Raw email bodies and SMS messages are processed strictly in volatile memory and discarded immediately. Extracted OTP codes and magic links live only in Upstash Redis with a strict **60-second TTL** (`OTP_REDIS_TTL_SECONDS = 60`) and are atomically burned on consumption (`CLAIM_AUTH_EVENT_LUA`). - **Expiring-Window TOTP Protection:** When generating an RFC 6238 TOTP code, if `remainingSeconds <= 4`, the SDK and MCP server automatically pause until the next 30-second window rolls over so a browser agent never types a code that expires mid-submission. - **Human-Fallback Safety Bridge™:** Non-OTP SMS messages (`classification: 'non_auth'`) are forwarded via Resend email or Slack/Discord HTTPS webhooks, and inbound voice verification calls to your Identity's Telnyx DID are bridged live to an operator's phone via signed TeXML ``. --- ## 2. Authentication & Environment Variables Standardize on a single environment variable across all SDKs, CLI commands, and MCP servers: ```bash export SENTRY_RELAY_API_KEY="sr_live_..." # Or for deterministic zero-cost sandbox testing: export SENTRY_RELAY_API_KEY="sr_test_..." ``` - **Production keys (`sr_live_...`):** Authenticate against live `@in.sentryrelay.com` email streams, Telnyx US DIDs, and Cloud KMS TOTP vaults. - **Sandbox keys (`sr_test_...`):** Deterministic test keys for CI/CD pipelines. - **Multi-Client Scoped Keys (`scopedIdentityIds`):** API keys can be restricted to specific Identity IDs (`["id_01J..."]`). Attempting to access any other Identity returns `403 FORBIDDEN`. --- ## 3. Model Context Protocol (MCP) Server (`@sentryrelay/mcp`) ### 3.1 Installation & Client Configuration #### Option A: Claude Code CLI (One-Liner) ```bash claude mcp add sentryrelay \ -e SENTRY_RELAY_API_KEY=sr_live_your_api_key_here \ -- npx -y @sentryrelay/mcp ``` #### Option B: Local `stdio` Config (Claude Desktop / Cursor / Windsurf) ```json { "mcpServers": { "sentryrelay": { "command": "npx", "args": ["-y", "@sentryrelay/mcp"], "env": { "SENTRY_RELAY_API_KEY": "sr_live_your_api_key_here" } } } } ``` #### Option C: Zero-Install Hosted Streamable HTTP Endpoint Connect any Streamable HTTP MCP client directly to: - **Endpoint:** `https://sentryrelay.com/api/mcp` - **Headers:** `Authorization: Bearer sr_live_your_api_key_here` ### 3.2 All 6 Canonical MCP Tools 1. **`wait_for_verification_code`** - **Description:** Long-poll up to `timeoutSeconds` (1..45, default 30) for an incoming verification code (OTP) or magic link sent via Email or SMS to a Sentry Relay Identity. Returns in `<200ms` of arrival and atomically marks the code as consumed. - **Arguments:** - `identity` (`string`, required): Identity ID (`id_...`) or unique slug (e.g. `"checkout-bot"`). - `channel` (`"email" | "sms"`, optional): Filter by delivery channel. - `sender` (`string`, optional): Case-insensitive sender substring filter (e.g. `"stripe.com"`, `"+1203"`). - `subject` (`string`, optional): Case-insensitive email subject substring filter. - `timeoutSeconds` (`number`, optional, default `30`): Max seconds to wait (`1..45`). - `consume` (`boolean`, optional, default `true`): Atomically burn the event on match. 2. **`get_latest_verification_code`** - **Description:** Instant non-blocking check for the most recent unconsumed verification code or magic link received within `lookbackSeconds` (default 60s). - **Arguments:** `identity` (required), `channel` (optional), `sender` (optional), `subject` (optional), `lookbackSeconds` (optional, `1..600`, default `60`), `consume` (optional, default `true`). 3. **`get_totp_code`** - **Description:** Generate a live 6- or 8-digit RFC 6238 TOTP authenticator code from an Identity's Cloud KMS vault. Automatically waits `<4s` for a fresh 30s window if the current token is about to expire. - **Arguments:** - `identity` (`string`, required): Identity ID or slug. - `secretId` (`string`, required): Vaulted TOTP secret ID (e.g. `"github_ops"`). - `autoWaitFreshWindow` (`boolean`, optional, default `true`): Pause when `<=4s` remain so the returned code is guaranteed fresh. 4. **`list_identities`** - **Description:** List accessible Sentry Relay Identities (dedicated agent email addresses on `@in.sentryrelay.com`, Telnyx SMS phone numbers, and vaulted TOTP secret counts). - **Arguments:** `search` (`string`, optional), `limit` (`number`, optional, `1..100`, default `50`). 5. **`create_identity`** - **Description:** Provision a new isolated Sentry Relay Identity inbox (`@in.sentryrelay.com`). - **Arguments:** `name` (`string`, required), `slug` (`string`, optional), `emailHandle` (`string`, optional). 6. **`store_totp_secret`** - **Description:** Vault a new RFC 6238 TOTP authenticator secret (`otpauth://totp/...` URI or raw Base32 secret) under a Sentry Relay Identity using Cloud KMS envelope encryption. - **Arguments:** `identity` (`string`, required), `secretId` (`string`, required), `otpauthUriOrSecret` (`string`, required), `issuer` (`string`, optional), `accountLabel` (`string`, optional). --- ## 4. TypeScript / Node.js SDK (`@sentryrelay/sdk`) ### Installation ```bash npm install @sentryrelay/sdk ``` ### Usage with Playwright / Stagehand ```typescript import { SentryRelay, fillOtpCode } from '@sentryrelay/sdk'; const relay = new SentryRelay({ apiKey: process.env.SENTRY_RELAY_API_KEY!, }); // 1. Trigger login challenge before or while polling const since = Date.now(); await page.fill('input[type="email"]', 'checkout-bot@in.sentryrelay.com'); await page.click('button[type="submit"]'); // 2. Long-poll up to 30s (<200ms push delivery, atomic burn-after-read) const auth = await relay.identities.wait('checkout-bot', { channel: 'email', sender: 'stripe.com', since, timeout: 30, }); if (!auth.matched || !auth.event) { throw new Error(`Timed out after ${auth.waitDurationMs}ms`); } // Works with both single fields and multi-box 6-digit OTP inputs await fillOtpCode(page, 'input[data-testid^="otp-input-"]', auth); // 3. Generate a live RFC 6238 TOTP code (auto-waits if <=4s remain in window) const totp = await relay.totp.get('checkout-bot', 'github_ops'); console.log(totp.code, totp.remainingSeconds); ``` ### Co-Deploying with 1Password & Browserbase Use `@1password/sdk` for static username/password retrieval and `@sentryrelay/sdk` for real-time out-of-band 2FA interception: ```typescript import { createClient } from '@1password/sdk'; import { SentryRelay, fillOtpCode } from '@sentryrelay/sdk'; const op = await createClient({ auth: process.env.OP_SERVICE_ACCOUNT_TOKEN!, integrationName: 'browser-agent-worker', integrationVersion: '1.0.0', }); const relay = new SentryRelay({ apiKey: process.env.SENTRY_RELAY_API_KEY! }); const username = await op.secrets.resolve('op://AgentVault/Stripe-ClientA/username'); const password = await op.secrets.resolve('op://AgentVault/Stripe-ClientA/password'); const since = Date.now(); await page.fill('input[name="email"]', username); await page.fill('input[name="password"]', password); await page.click('button[type="submit"]'); const auth = await relay.identities.wait('client-a-stripe', { sender: 'stripe.com', since }); await fillOtpCode(page, 'input[name="code"]', auth); ``` --- ## 5. Python SDK (`sentryrelay-sdk`) ### Installation ```bash pip install sentryrelay-sdk # Optional framework extras: pip install "sentryrelay-sdk[google-adk,openai-agents,claude-agent,pydantic-ai,langchain,crewai]" ``` ### Usage (Sync & Async) ```python import asyncio from sentryrelay import AsyncSentryRelay, fill_otp_code async def solve_2fa(page): async with AsyncSentryRelay() as relay: # Wait for Email or SMS OTP auth = await relay.wait("checkout-bot", sender="stripe.com", timeout=30) if auth.matched and auth.otp_code: await fill_otp_code(page, 'input[name="otp"]', auth) # Or generate live RFC 6238 TOTP token totp = await relay.totp.get("checkout-bot", "github_ops") print(f"TOTP: {totp.code} ({totp.remaining_seconds}s left)") ``` --- ## 6. CLI (`sentryrelay` / `srelay`) ### Installation ```bash npm install -g sentryrelay ``` ### Commands ```bash # Authenticate (stores key in ~/.config/sentryrelay/credentials.json with 0600 permissions) srelay login --key sr_live_... # List and create Identities srelay identities list srelay identities create --name "Staging Bot" --handle staging-bot # Wait for an OTP or magic link (--raw prints ONLY the code + newline to stdout) CODE=$(srelay wait staging-bot --sender github.com --timeout 30 --raw) # Vault and generate RFC 6238 TOTP tokens srelay totp add staging-bot github_ops "otpauth://totp/GitHub:bot?secret=JBSWY3DPEHPK3PXP&issuer=GitHub" TOTP=$(srelay totp get staging-bot github_ops --raw) ``` --- ## 7. Email Forwarding & Multi-Client Agency Architecture ### Inbound Domain Every Identity receives an instant address at: `@in.sentryrelay.com` (e.g., `checkout-bot@in.sentryrelay.com`). When forwarding existing corporate inboxes (Gmail, Google Workspace, Microsoft 365 / Exchange) to `@in.sentryrelay.com`: 1. Trigger the provider's forwarding confirmation email. 2. Call `srelay wait ` or `relay.identities.wait('')` — Sentry Relay extracts the verification link (`auth.event.magicLink`) or confirmation code (`auth.event.otpCode`) automatically. 3. Stage 1 of the extractor unwraps `---------- Forwarded message ---------`, `X-Forwarded-For`, and `Resent-From` headers so `sender: "stripe.com"` filters match the original upstream sender rather than the forwarding mailbox. ### Multi-Client Agency Isolation (`scopedIdentityIds`) Provision one Identity per client (`client-acme@in.sentryrelay.com`, `client-globex@in.sentryrelay.com`) and issue per-client API keys restricted via `scopedIdentityIds: ["id_01J..."]` so one client's worker container can never read another client's 2FA stream. --- ## 8. REST API Reference (`https://sentryrelay.com/api/v1`) All endpoints require `Authorization: Bearer ` and return `{ "data": ..., "error": null }` on success or `{ "data": null, "error": { "code": "...", "message": "...", "requestId": "..." } }` on error. ### `GET /api/v1/identities` List accessible Identities. Query params: `limit` (`1..100`, default `50`), `cursor` (`string`). ### `POST /api/v1/identities` Create a new Identity. **Request Body (`application/json`):** ```json { "name": "Client Acme — Checkout Bot", "slug": "client-acme", "emailHandle": "client-acme", "fallback": { "enabled": true, "fallbackEmail": "ops@acme-client.com", "fallbackWebhookUrl": "https://hooks.slack.com/services/T000/B000/X000", "voiceForwardToE164": "+14155550199" } } ``` ### `GET /api/v1/identities/{id}` Retrieve Identity details by ID (`id_...`) or `slug`. ### `PATCH /api/v1/identities/{id}` Update Identity display name, status (`"active" | "paused"`), `fallback` configuration, or `customRules` (max 10 regex rules). **Request Body (`application/json`):** ```json { "name": "Client Acme — Production ERP Bot", "status": "active", "fallback": { "enabled": true, "fallbackEmail": "oncall@acme-client.com", "voiceForwardToE164": "+14155550199" } } ``` ### `DELETE /api/v1/identities/{id}` Permanently delete an Identity, release any attached Telnyx SMS DID, and purge vaulted TOTP secrets. ### `GET /api/v1/identities/{id}/wait` Long-poll up to `timeout` seconds (`1..45`, default `30`) for an unconsumed auth event. Query params: `channel` (`"email" | "sms"`), `sender` (`string`), `subject` (`string`), `timeout` (`1..45`), `since` (epoch ms), `consume` (`boolean`, default `true`). ### `GET /api/v1/identities/{id}/latest` Instant non-blocking Redis ZSET lookback check. Query params: `channel` (`"email" | "sms"`), `sender` (`string`), `subject` (`string`), `lookbackSeconds` (`1..600`, default `60`), `consume` (`boolean`, default `true`). ### `GET /api/v1/identities/{id}/totp` List vaulted TOTP secret metadata (`id`, `issuer`, `accountLabel`, `algorithm`, `digits`, `period`, `lastUsedAt`). Raw Base32 seeds are never returned. ### `POST /api/v1/identities/{id}/totp` Vault a new TOTP secret using Cloud KMS AES-256-GCM envelope encryption. Provide **either** `otpauthUri` **or** `secret` + `issuer`. **Request Body (`application/json`):** ```json { "id": "github_ops", "secret": "JBSWY3DPEHPK3PXP", "issuer": "GitHub", "accountLabel": "bot-runner@company.com", "algorithm": "SHA1", "digits": 6, "period": 30 } ``` ### `GET /api/v1/identities/{id}/totp/{secretId}` Generate a live 6- or 8-digit RFC 6238 TOTP code (`code`, `remainingSeconds`, `nextCode`, `period`, `digits`). ### `DELETE /api/v1/identities/{id}/totp/{secretId}` Permanently delete a vaulted TOTP secret. ### `POST /api/v1/identities/{id}/sms/provision` Order and attach a dedicated US local carrier DID via Telnyx. **Request Body (`application/json`):** ```json { "areaCode": "415" } ``` ### `DELETE /api/v1/identities/{id}/sms` Release the Telnyx US phone number attached to the Identity. ### `GET /api/v1/webhooks` & `POST /api/v1/webhooks` & `DELETE /api/v1/webhooks/{webhookId}` Manage HMAC-SHA256 signed (`X-SentryRelay-Signature`) outbound webhooks for `otp.extracted`, `magic_link.extracted`, and `sms.fallback_triggered`. **POST Request Body (`application/json`):** ```json { "targetUrl": "https://api.your-agency.com/webhooks/sentryrelay", "events": ["otp.extracted", "magic_link.extracted", "sms.fallback_triggered"], "scopedIdentityIds": ["id_01J9X7K2M4P8Q1R3S5T7V9W0Y2"] } ``` ### `GET /api/v1/audit-logs` Query sanitized, zero-PII audit logs (`identityId`, `eventType`, `from`, `to`, `limit`, `format=json|csv`).