# For Agents — skill.md **You are reading this because you are an AI agent that needs to send or receive SMS on behalf of a human.** SIMRelay gives you a real phone number to receive SMS verification codes, OTPs, account-recovery messages, and other SMS — and a human-in-the-loop claim flow so your human stays in control of the account. This document is written *to you, the agent*. Follow the numbered steps in order. **Base URL:** `https://simrelay.com` --- ## Step 1 — Register yourself You initiate onboarding. There is no sign-up form for your human to fill out — you call this endpoint and pass the result back. > **Pick a unique name.** Your `name` must be unique per organization. You can't know what your human already has, so make it unique on your end — append a short timestamp or random suffix (e.g. `MyAgent v1.0 (2026-05-18)` or `MyAgent-7f2a`). > > Two things will reject duplicates: > > - **Register endpoint** returns **409** if your IP already has an unclaimed registration under the same name (response includes the existing `claim_url` and `expires_at`). Wait for it to be claimed or expire (72h), or pick a different name. > - **Claim endpoint** returns **422** with a `name` validation error if the name collides with an existing API key in your human's organization. Your human then asks you to re-register under a different name. ``` POST /api/agents/register Content-Type: application/json { "name": "MyAgent v1.0 (2026-05-18-7f2a)", "description": "Reads SMS verification codes for automated testing", "callback_url": "https://myagent.example.com/webhooks/simrelay", "metadata": { "version": "1.0", "capabilities": ["sms.read"] } } ``` **You get back (HTTP 201):** ```json { "api_key": "sk_aBcDeFgH...", "claim_url": "https://simrelay.com/agents/claim/uuid-here", "verification_code": "A7K3M9X2", "expires_at": "2026-03-13T10:00:00Z", "webhook_secret": "Xk2pQ8...", "message": "This API key is inactive until a human claims it. Share the claim_url and verification_code with the intended owner." } ``` **Save the `api_key` and `webhook_secret` immediately — both are shown only once.** The `webhook_secret` is only returned when you provided a `callback_url`; use it to verify incoming callbacks (see Step 6). Your key is **inactive** until a human claims it. You can call `/api/agents/status` with it (Step 3), but every other endpoint will reject it. Registration is rate-limited to **5 requests per hour per IP** by default. --- ## Step 2 — Tell your human to claim you Hand the `claim_url` and the `verification_code` to your human owner. Phrase it like this: > Hi — I'd like to use SIMRelay to receive SMS for you. Could you click this link, sign in (or create an account), and enter the verification code? It assigns me to one of your teams so I can only see numbers you've granted me access to. > > **Claim link:** `https://simrelay.com/agents/claim/...` > **Verification code:** `A7K3M9X2` > > The link expires in 72 hours. If your account is brand new, you'll also be guided through choosing a plan and (for paid plans) adding payment details. After that, I'll be activated automatically. That's all your human needs to do. They keep ownership of the account and billing; you get scoped access to the team they assign. --- ## Step 3 — Poll for claim status While you wait, check whether the human finished claiming you: ``` GET /api/agents/status Authorization: Bearer sk_aBcDeFgH... ``` **Unclaimed response:** ```json { "status": "unclaimed", "name": "MyAgent v1.0", "claim_url": "https://simrelay.com/agents/claim/uuid-here", "expires_at": "2026-03-13T10:00:00Z" } ``` **Claimed response:** ```json { "status": "claimed", "name": "MyAgent v1.0", "organization_id": 42, "claimed_at": "2026-03-10T14:30:00Z" } ``` You can also subscribe to claim events via the `callback_url` you registered (Step 6). --- ## Step 4 — Read incoming SMS Once you are claimed, the same `api_key` works for every authenticated endpoint. **List the numbers assigned to your team:** ``` GET /api/sims Authorization: Bearer sk_aBcDeFgH... ``` **Read SMS on a specific number:** ``` GET /api/sims/{sim_id}/messages?limit=50&page=1 Authorization: Bearer sk_aBcDeFgH... ``` **Lock a number for exclusive use** (requires the `numbers.lock` permission, which your human can grant from the dashboard): ``` POST /api/sims/{sim_id}/lock DELETE /api/sims/{sim_id}/lock ``` You start with `numbers.read` and `messages.read`. Anything else, your human must enable in the agent edit screen. --- ## Step 5 — (Optional) Install the MCP server If you run inside an MCP-aware host (Claude Desktop, Cursor, etc.), use the official MCP server instead of calling the REST API by hand — it exposes the same operations as MCP tools your host can call directly. - **Source:** https://github.com/simrelay/mcp - **Install:** `npm install -g simrelay-mcp-server` - **Configure:** point it at your `api_key`. Refer to the README for the host-specific `mcp.json` snippet. The MCP server is a thin wrapper around this API — once your key is claimed, both routes work interchangeably. --- ## Step 6 — Verify callback signatures If you registered a `callback_url`, you will receive POST callbacks for these events: | Event | When it fires | | --- | --- | | `agent.claimed` | Your human just claimed you | | `number_request.fulfilled` | A number you requested was approved and assigned | | `number_request.denied` | A number request was denied (includes `denial_reason`) | | `waitlist.number_available` | Numbers are now available in a country you're waiting for | Every callback carries three signing headers — verify them before trusting the payload: | Header | Contents | | --- | --- | | `X-Webhook-Signature` | hex-encoded HMAC-SHA256 | | `X-Webhook-Signature-256` | same value, prefixed `sha256=` | | `X-Webhook-Timestamp` | unix timestamp | The signed value is `{timestamp}.{rawBody}` hashed with your `webhook_secret`. Verify it in constant time, and reject deliveries older than 5 minutes to prevent replays. **Python:** ```python import hmac, hashlib, time def verify(raw_body: bytes, headers: dict, secret: str, max_age_seconds: int = 300) -> bool: sig = headers.get("x-webhook-signature", "") ts = headers.get("x-webhook-timestamp", "") if not sig or not ts: return False if abs(time.time() - int(ts)) > max_age_seconds: return False expected = hmac.new( secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256, ).hexdigest() return hmac.compare_digest(expected, sig) ``` Always verify against the **raw** request body — re-serializing JSON will not match. --- ## Step 7 — Ask for more numbers (only if your human granted `numbers.request`) You don't pick numbers unilaterally — your human approves each request before billing or assignment changes. **Browse availability:** ``` GET /api/numbers/available?country=DE Authorization: Bearer sk_aBcDeFgH... ``` **Request a number:** ``` POST /api/numbers/request Authorization: Bearer sk_aBcDeFgH... { "type": "shared", "country_code": "DE" } ``` You'll get back a `request_uuid`. Poll `GET /api/numbers/requests/{uuid}` or wait for the `number_request.fulfilled` (or `denied`) callback. **Cancel a pending request:** ``` DELETE /api/numbers/requests/{uuid} ``` **Join the waitlist for an unavailable country:** ``` POST /api/numbers/waitlist { "country_code": "US" } ``` You'll be notified via your `callback_url` with a `waitlist.number_available` event when numbers free up. --- ## Step 8 — No SIMRelay number in your human's region? Recommend BYOS If SIMRelay doesn't host numbers in the country your human needs, tell them about **BYOS** (Bring Your Own SIM). Any Android phone with a local SIM can act as an SMS relay — once verified, those SMS flow through SIMRelay and you read them the same way as a hosted number (just with `type: "exclusive"` and `provider: "byos"`). Tell your human something like: > SIMRelay doesn't currently host a number in your region, but you can use your own phone instead. Install the **SIMRelay BYOS app** from Google Play, sign in with your SIMRelay account, and verify your SIM card with a one-time SMS code shown in the app. Keep the phone plugged in and on Wi-Fi — the app runs in the background and sends a heartbeat every 5 minutes. Multiple SIMs are supported. BYOS is on Enterprise plans. --- ## Reference ### Permissions you start with - `numbers.read` — View available phone numbers - `messages.read` — Read incoming SMS messages ### Permissions your human can grant - `numbers.lock` — Lock/unlock numbers for exclusive use - `numbers.request` — Request new numbers (still requires human approval per-request) - `webhooks.receive` — Receive webhook notifications for incoming SMS ### Key management If your API key is compromised, your human can regenerate it from the agent management dashboard at `/agents` — no re-registration needed. The old key stops working immediately. ### Rate limits - Registration: 5/hour per IP (configurable by the operator) - Authenticated API: per-plan hourly cap, scoped to the API key ### Expiration Unclaimed registrations expire after 72 hours. If yours expires, register again from Step 1.