# AgentCell - Append .md to any doc URL for markdown - MCP: https://mcp.agentcell.store/mcp - Docs MCP: https://agentcell.store/docs/_mcp/server - Onboarding: https://agentcell.store/docs/agent-onboarding.md - API: https://api.agentcell.store/v1 ## Docs - [Docs home](https://agentcell.store/docs): Human vs agent paths, popular guides, and machine-readable exports. - [Welcome](https://agentcell.store/docs/welcome): Product vision, org → pods → cells, and how AgentCell differs from CPaaS. - [Quickstart](https://agentcell.store/docs/quickstart): Sign up, verify, pay, create a cell, attach a webhook, send the first SMS. - [Agent onboarding](https://agentcell.store/docs/agent-onboarding): Canonical playbook for an AI agent from zero to first send and receive. - [Cells](https://agentcell.store/docs/guides/cells): Fleet CRUD for dedicated phone identities: create, list, health, multi-cell. - [Messages](https://agentcell.store/docs/guides/messages): Send and receive SMS with idempotency and labels. MMS is not available yet. - [Conversations](https://agentcell.store/docs/guides/conversations): Threading model, conversationState, and between-number lookups. - [Drafts](https://agentcell.store/docs/guides/drafts): Human-in-the-loop create and send workflow for outbound SMS. - [Labels](https://agentcell.store/docs/guides/labels): Message state: unread, spam, and campaign tags. - [Lists](https://agentcell.store/docs/guides/lists): Allow and block policy by direction and list type. - [Ignore lists](https://agentcell.store/docs/guides/ignore-lists): STOP / opt-out at cell or pod scope with auto_ignore_on_stop. - [Pod cycle send](https://agentcell.store/docs/guides/pod-cycle-send): Round-robin campaigns across a pod at 1 message per second per cell. - [Pods](https://agentcell.store/docs/guides/pods): Multi-tenant isolation, nested resources, and pod operations. - [Webhooks](https://agentcell.store/docs/guides/webhooks): Signed event delivery, retries, and event types. - [WebSockets](https://agentcell.store/docs/guides/websockets): WSS subscribe to the real-time event stream. - [Billing](https://agentcell.store/docs/guides/billing): Tiers, invoices, Stripe checkout, and console pay. - [Machine payments](https://agentcell.store/docs/guides/machine-payments): x402 and MPP for agent-autonomous payment and prepaid balance. - [Console login](https://agentcell.store/docs/guides/console-login): Human magic link and phone OTP. Console is observe + pay + connect. - [Agent activity](https://agentcell.store/docs/guides/agent-activity): Status, activity log, and connections in the owner console. - [Compliance](https://agentcell.store/docs/guides/compliance): User-owned 10DLC/TCPA, quiet hours, STOP handling, and first-message rules. - [MCP](https://agentcell.store/docs/integrations/mcp): Hosted MCP URL (primary) or local npx -y @agentcell/mcp, OAuth or API key, tool catalog. - [CLI](https://agentcell.store/docs/integrations/cli): npm i -g @agentcell/cli — Go binary shim and command tree. - [SDKs](https://agentcell.store/docs/integrations/sdk): TypeScript @agentcell/sdk and Python agentcell for the agent REST API. - [Skills](https://agentcell.store/docs/integrations/skills): Cursor and Claude SKILL.md install plus copy blocks. - [LangChain](https://agentcell.store/docs/integrations/langchain): Phase 2 tool wrappers around the TypeScript and Python SDKs. - [Replit](https://agentcell.store/docs/integrations/replit): Phase 2 one-click template. Use the SDK or CLI today. - [Sim.ai](https://agentcell.store/docs/integrations/sim-ai): Phase 2/3 workflow nodes. Use MCP and webhooks today. - [API](https://agentcell.store/docs/reference/api): REST base URL, bearer auth, and resource index. - [OpenAPI](https://agentcell.store/docs/reference/openapi): Machine-readable OpenAPI 3.1 spec download. - [Errors](https://agentcell.store/docs/reference/errors): Stable error envelope and codes such as PAYMENT_REQUIRED and TCPA_QUIET_HOURS. - [Rate limits](https://agentcell.store/docs/reference/rate-limits): Per-cell, conversation streak, and pod cycle throttling. - [Pagination](https://agentcell.store/docs/reference/pagination): limit + page_token (10/25/50) on lists; message history time cursors. - [Idempotency](https://agentcell.store/docs/reference/idempotency): client_id on pods/drafts/cycle; Idempotency-Key on sends. Not on cell create. - [WebSockets](https://agentcell.store/docs/reference/websockets): AsyncAPI event types for WSS /v1/events/stream. - [FAQ](https://agentcell.store/docs/faq): Pricing, ownership, 10DLC, console vs API, admin vs console login. - [Changelog](https://agentcell.store/docs/changelog): Reverse-chronological releases and API breaking changes. ## Exports - llms.txt: https://agentcell.store/docs/llms.txt - llms-full.txt: https://agentcell.store/docs/llms-full.txt - OpenAPI JSON: https://agentcell.store/docs/openapi.json - OpenAPI YAML: https://agentcell.store/docs/openapi.yaml - AsyncAPI: https://agentcell.store/docs/asyncapi.json - Docs MCP: https://agentcell.store/docs/_mcp/server --- # Welcome (/docs/welcome) # Welcome to AgentCell Give every agent a real phone identity it can use to reach people, receive context, and act independently. ## Vision Email gave software an inbox. AgentCell gives it a phone. A cell is more than an endpoint: it is an identity, a durable context, and a safe boundary for real-world SMS. ## Architecture Organization owns the work. Pods group related policy. Cells give individual agents dedicated numbers and message histories. | Layer | Role | | --- | --- | | Organization | Tenant, billing, API keys | | Pod | Isolation, compliance, cycle send | | Cell | Phone + SIM + number | ## Vs CPaaS | Dimension | Typical CPaaS | AgentCell | | --- | --- | --- | | Identity | Rented number | Owned cell (phone + SIM + number) | | Pricing | Usage credits | $229 setup / $79 monthly; SMS included | | Agent signup | Rare | First-class | | 10DLC | Often in-product | User-managed | > **Next** Pay, create a cell, wait for the handset to claim (1–72h), then send. > > /docs/quickstart ## Coming later This release is SMS. These channels and controls are planned; the API rejects them with explicit codes so clients do not pretend they work. | Later | Today | | --- | --- | | MMS | SMS only — 400/404 MMS_NOT_SUPPORTED | | Voice | Not available | | iMessage | Not available | | WhatsApp | Not available | | Remote restart / ADB control | 501 RESTART_NOT_SUPPORTED; ADB stored for operators only | ## Copy for Cursor / Claude ``` AgentCell gives AI agents hosted cellphones. - Cell = phone + SIM + E.164 after the handset claims (typically 1–72h) - POST /cells does not return a number. No client_id on create. - $229 setup / $79 monthly. SMS included. No per-message fee. - Later: MMS, voice, iMessage, WhatsApp, remote restart. - Org → pods → cells - API https://api.agentcell.store/v1 - MCP https://mcp.agentcell.store/mcp - Docs https://agentcell.store/docs ``` --- # Agent onboarding (/docs/agent-onboarding) # AgentCell — Agent Onboarding Guide > End-to-end playbook for an **AI agent** (or agent-assisted developer) to go from zero to sending and receiving **SMS** (MMS later) — without human console access except OTP. **Payment** supports fully autonomous **x402** and **MPP**; Stripe Checkout remains available when a human pays by card. When the human **does** need the UI, the agent can issue a **console login link** or they can sign in at `/login` on `agentcell.store` (redirects to `/user`) with their sign-up phone number. > Platform details: [AGENTCELL_PLATFORM_SPEC.md](./AGENTCELL_PLATFORM_SPEC.md) | Product concept: [AGENTCELL_CONCEPT.md](./AGENTCELL_CONCEPT.md) --- ## Table of Contents 1. [Overview](#1-overview) 2. [Prerequisites](#2-prerequisites) 3. [Onboarding Phases](#3-onboarding-phases) 4. [Receive Path Options](#4-receive-path-options) 5. [Payment Flow](#5-payment-flow) 6. [Human Console Login](#6-human-console-login) 7. [Compliance & First Message Rules (User Responsibility)](#7-compliance--first-message-rules-user-responsibility) 8. [Error Handling Reference](#8-error-handling-reference) 9. [Copy Blocks (Full Flow)](#9-copy-blocks-full-flow) 10. [Production Checklist](#10-production-checklist) --- ## 1. Overview This guide walks an AI agent through **9 platform phases** plus **user-owned compliance** (10DLC/A2P is **not** handled by AgentCell). The recommended path for Cursor/Claude is **MCP** (`agentcell-mcp`); CI and scripts use **CLI**; custom integrations use **REST**. **Principal–agent assumption:** The AI agent uses the API **on behalf of the account owner** (human on file). The **owner** is responsible for telecom compliance; AgentCell provides hosting and messaging only. ```mermaid sequenceDiagram participant Agent as AI Agent participant MCP as agentcell-mcp participant API as AgentCell API participant Human as Human participant Stripe as Stripe participant Carrier as Carrier Agent->>MCP: agent_sign_up MCP->>API: POST /agent/sign-up API->>Human: OTP via SMS + email Human->>Agent: OTP code Agent->>MCP: agent_verify MCP->>API: POST /agent/verify Agent->>MCP: pay (checkout | x402 | mpp) alt Stripe Checkout MCP->>API: POST /billing/checkout API->>Stripe: Checkout session Stripe->>Human: Payment UI Human->>Stripe: Pay setup + first month Stripe->>API: billing.payment_succeeded else x402 or MPP MCP->>API: POST /cells or /billing/mpp/pay API-->>Agent: 402 Payment Required Agent->>API: Retry with PAYMENT-SIGNATURE or MPP Credential API->>API: billing.x402.settled or billing.mpp.settled end Agent->>MCP: create_cell MCP->>API: POST /cells API->>Carrier: Provision device + number Note over Agent,Human: User registers 10DLC/A2P externally (not AgentCell) Agent->>MCP: set_webhook Agent->>MCP: send_message Carrier->>API: Inbound SMS API->>Agent: webhook cell.message.received Agent->>MCP: reply_message ``` ### Phase summary | # | Phase | Human required? | |---|-------|-----------------| | 1 | Discover | No | | 2 | Sign up | No (agent calls API) | | 3 | Verify | Yes (OTP from human) | | 4 | Pay | Yes (Stripe Checkout) | | 5 | Provision cell | No | | 6 | User compliance (10DLC/A2P, external) | Yes (owner registers with carrier/TCR) | | 7 | Configure events | No | | 8 | First send | No | | 9 | First receive & reply | No | | 10 | Operate | No | --- ## 2. Prerequisites Before starting, the agent should: - [ ] Load documentation index: `https://agentcell.store/docs/llms.txt` - [ ] Have MCP configured (hosted URL **or** `npx -y @agentcell/mcp`) **or** CLI (`npm i -g @agentcell/cli`) **or** SDK (`@agentcell/sdk` / `agentcell` PyPI) + API access - [ ] Know the human's **phone** (E.164) and **email** for sign-up OTP - [ ] Have a **webhook URL** (HTTPS) ready, **or** plan to use WebSockets (no public URL) - [ ] Understand [pricing](/pricing): org $29 + cell setup $150 + cell $50 = $229 / $79. SMS included. - [ ] For US outbound SMS: owner must register **10DLC/A2P externally** — AgentCell does not file registrations ([§10 concept](./AGENTCELL_CONCEPT.md#10-compliance-user-responsibility--carrier-handling)) **Reference platforms:** - Agent sign-up pattern: [AgentMail Agent Onboarding](https://docs.agentmail.to/agent-onboarding.md) - Webhook/security patterns: [AgentPhone Webhooks](https://docs.agentphone.ai/documentation/guides/webhooks.md) **Install CLI or MCP (npm downloads Go binary):** ```bash npm i -g @agentcell/cli # exposes `agentcell` command npx -y @agentcell/mcp # optional local stdio MCP ``` **Or build from source (Go 1.23+):** ```bash go build -o agentcell ./cmd/cli go build -o agentcell-mcp ./cmd/mcp ``` --- ## 3. Onboarding Phases ### Phase 1: Discover **Goal:** Orient to account state and documentation. **MCP:** ``` account_overview # after auth only; skip pre-sign-up ``` **Pre-sign-up:** Fetch `https://agentcell.store/docs/llms.txt` and read `agent-onboarding.md`. **CLI:** ```bash # No auth yet — read docs curl -s https://agentcell.store/docs/llms.txt | head -50 ``` --- ### Phase 2: Sign up **Goal:** Obtain `api_key` and `organization_id`. **MCP tool:** `agent_sign_up` | Parameter | Required | Example | |-----------|----------|---------| | `human_phone` | Yes | `+15551234567` | | `human_email` | Yes | `developer@example.com` | | `username` | Yes | `my-agent` | **REST:** ```bash curl -X POST https://api.agentcell.store/v1/agent/sign-up \ -H "Content-Type: application/json" \ -d '{ "human_phone": "+15551234567", "human_email": "developer@example.com", "username": "my-agent" }' ``` **Response:** ```json { "api_key": "ac_xxxxxxxx", "organization_id": "org_xxxxxxxx", "otp_sent_to": ["+15551234567", "developer@example.com"] } ``` **CLI:** ```bash agentcell agent sign-up \ --human-phone +15551234567 \ --human-email developer@example.com \ --username my-agent ``` **Store securely:** `api_key`, `organization_id`. Set `AGENTCELL_API_KEY=ac_...`. **Notes:** - Idempotent: same email/phone rotates key and resends OTP if expired - Blocklisted domains (e.g. `example.com`) rejected — use real email - Pattern from [AgentMail sign-up](https://docs.agentmail.to/quickstart.md) **Failures:** | Error | Fix | |-------|-----| | 400 validation | Check E.164 phone format | | 409 already exists | Call sign-up again to rotate key | --- ### Phase 3: Verify **Goal:** Unlock outbound messaging to external numbers. **Human action:** Read 6-digit OTP from SMS and email; provide to agent. **MCP tool:** `agent_verify` with `otp_code` **REST:** ```bash curl -X POST https://api.agentcell.store/v1/agent/verify \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -d '{"otp_code": "123456"}' ``` **CLI:** ```bash export AGENTCELL_API_KEY=ac_... agentcell agent verify --otp-code 123456 ``` **Until verified:** - Inbound SMS to assigned numbers works after cell creation - Outbound to **only** the signup phone/email allowed - Other sends return **403** `NOT_VERIFIED` --- ### Phase 3b: Human console access (optional) **Goal:** Let the human owner open **`/user`** on `agentcell.store` to **view** account status, **pay** bills, and **copy API keys / MCP config** — not to manage cells, webhooks, or messaging (agent handles that via API/MCP/CLI). **When:** Anytime after Phase 3 verify. Often used before Phase 4 (Stripe checkout) or when the agent needs the human to review billing. #### Option A — Agent sends magic login link (recommended) **MCP tool:** `create_console_login_link` ```bash curl -X POST https://api.agentcell.store/v1/auth/login-links \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "redirect_path": "/user/billing", "delivery": "sms", "label": "Complete setup payment" }' ``` **Response:** `login_url` — human opens once → **automatic login** (15 min default TTL). Deliver to human via: - `delivery: "sms"` — sent to org `human_phone` - `delivery: "email"` — sent to org `human_email` - `delivery: "none"` — agent pastes URL in chat ```bash agentcell console login-link create --redirect-path /user/billing --delivery sms ``` #### Option B — Human logs in with sign-up phone Human visits **`https://console.agentcell.store/login`**: 1. Enter **`human_phone`** (same E.164 used at sign-up) 2. Receive SMS OTP 3. Enter code → console session No API key required for this path. **Not admin:** This is the **customer console**. Platform operators use **`console.agentcell.store/admin`** with a phone enrolled in the `admins` table — see [AGENTCELL_ADMIN.md](./AGENTCELL_ADMIN.md). --- ### Phase 4: Pay **Goal:** Payment method or prepaid balance on file; account funded for setup + first month. Ongoing monthly hosting and service fees keep your cell active. If payment fails, service suspends; reinstatement costs **$150 USD** within 30 days, or ownership is forfeited. See [AGENTCELL_OWNERSHIP.md](./AGENTCELL_OWNERSHIP.md). Choose **one** payment path: #### Path A — Stripe Checkout (human card) **MCP tool:** `create_setup_checkout` | Parameter | Description | |-----------|-------------| | `tier` | `starter`, `pro`, or `enterprise` | | `success_url` | Redirect after payment | | `cancel_url` | Redirect on cancel | ```bash curl -X POST https://api.agentcell.store/v1/billing/checkout \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tier": "pro", "success_url": "https://my-app.com/billing/success", "cancel_url": "https://my-app.com/billing/cancel" }' ``` **Human action:** Open `checkout_url` in browser; pay setup fee + first month. #### Path B — x402 (agent wallet, no browser) **MCP tools:** `get_payment_options`, `pay_with_x402` 1. Agent calls `GET /billing/x402/requirements?intent=setup&tier=pro` (or `POST /cells` and handle `402`). 2. API returns **`402`** + `PAYMENT-REQUIRED` header (USDC amount, network, destination). 3. Agent signs with wallet; retries with `PAYMENT-SIGNATURE` header. 4. On settle → subscription active + prepaid credited if applicable; webhook `billing.x402.settled`. ```bash # SDK handles 402 loop automatically: agentcell billing pay x402 --intent setup --tier pro ``` **Requirements:** Agent wallet with USDC on supported network (Base, Solana). Optional: `POST /billing/x402/wallets` to register org wallet. #### Path C — MPP (Machine Payments Protocol) **MCP tools:** `get_payment_options`, `pay_with_mpp` 1. Discovery: `GET https://api.agentcell.store/.well-known/mpp` 2. `POST /billing/mpp/pay` with `intent=setup`, `tier=pro` → **`402`** Challenge. 3. Agent authorizes via **Stripe SPT** (`method=stripe`) or **Tempo stablecoin** (`method=tempo`). 4. Retry with MPP Credential → Receipt + webhook `billing.mpp.settled`. ```bash agentcell billing pay mpp --intent setup --tier pro --method tempo # or MCP: pay_with_mpp(intent="setup", tier="pro", method="stripe") ``` **After any path:** Poll billing status: ```bash agentcell billing status # or MCP: get_billing_status / get_payment_options ``` **Failures:** | Error | Fix | |-------|-----| | 402 `PAYMENT_REQUIRED` | Complete checkout, x402, or MPP before `create_cell` | | `X402_SETTLEMENT_FAILED` | Retry with fresh signature; check wallet balance | | `MPP_CHALLENGE_EXPIRED` | Re-request challenge via `POST /billing/mpp/pay` | | `MPP_INVALID_CREDENTIAL` | Regenerate SPT or Tempo proof | | Checkout expired | Create new checkout session (Stripe path only) | **Pricing charged:** - **$229 + tax** due at setup (org $29 + cell setup $150 + first cell month $50) - **$79 / month** after - SMS is included in the cell plan (no per-message fee) See [pricing](/pricing) and [AGENTCELL_CONCEPT.md §6](./AGENTCELL_CONCEPT.md#6-pricing-model). --- ### Phase 5: Provision cell **Goal:** A waiting cell, then a numbered radio after the handset claims. `create_cell` after payment creates `pending_provision` plus a `phone_orders` snapshot (**$229 / $79**). It does **not** return an E.164. Typically **1–72 hours**. An operator enters tracking **or** a target E.164 and sends `AGENTCELL-SETUP v1` / `c:` from the **connected admin handset**. The default-SMS app (sideload / operator image — **not** Play Store) claims; the API issues `pk_live_` (never in SMS) and the phone stays on `WSS /v1/device/ws`. GET cell then includes public `phone` (no ADB) and public `order`. **MCP tool:** `create_cell` (no `client_id`; MCP has no setup-SMS / claim tools) | Parameter | Description | |-----------|-------------| | `display_name` | Friendly name (**required**) | | `area_code` | Preferred US area code (best-effort, optional) | | `country` | Default `US` | **REST:** ```bash curl -X POST https://api.agentcell.store/v1/cells \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "display_name": "My Agent Cell", "area_code": "415" }' ``` **Create response:** `cell_id`, empty `phone_number`, `status: pending_provision`. GET later may include `order` and `phone`. **Wait until numbered:** ```bash agentcell cells get --cell-id cell_abc123 agentcell cells health --cell-id cell_abc123 # health is online when phones.ws_connected ``` **CLI:** ```bash agentcell cells create --display-name "My Agent Cell" --area-code 415 ``` **Notes:** - Do **not** pass `client_id` on cell create (CLI has no `--client-id` on this command). - **Additional phones:** Call `create_cell` again — each cell is another $150 + $50/mo on top of org $29. - Send before an E.164 → **409 `CELL_NOT_READY`**. Send with no connected radio → **503 `CARRIER_UNAVAILABLE`**. - Outbound US SMS still requires **your** external 10DLC/A2P registration (Phase 6). **Failures:** | Error | Fix | |-------|-----| | 402 | Complete payment (Phase 4) | | 409 `CELL_NOT_READY` | Wait for claim / E.164 | | 503 `CARRIER_UNAVAILABLE` | Handset must stay on the device WebSocket | --- ### Phase 6: User compliance (10DLC/A2P — external) **Goal:** Enable outbound SMS to US mobile numbers per **carrier rules**. **Not handled by AgentCell.** You (the account owner) must register 10DLC/A2P with your carrier, The Campaign Registry (TCR), or your compliance vendor and associate registration with each number/cell you use for outbound A2P SMS. **AgentCell assumption:** The AI agent sends messages **on behalf of the owner**, who accepts legal and regulatory liability. | Step | Who | Action | |------|-----|--------| | 1 | Owner | Register brand and campaign externally | | 2 | Owner | Link campaign to AgentCell-provisioned number(s) per carrier process | | 3 | Agent | Record external IDs via `PATCH /pods/{pod_id}/compliance` or cell `metadata` | | 4 | Agent | Test outbound; handle carrier rejections in your error logic | **Inbound:** Works when the number is active — no AgentCell registration step (same as AgentPhone inbound pattern). **While unregistered externally:** Outbound to US mobiles may fail at the carrier; API may return success but message never delivers. See [carrier limits](./AGENTPHONE_BREAKDOWN.md#5-rate-limits--carrier-constraints). **Reference:** [AgentPhone 10DLC guide](https://docs.agentphone.ai/documentation/guides/phone-numbers.md) for **background on carrier requirements** — AgentPhone registers in-product; **AgentCell does not**. Use that doc to understand rules, then complete registration through **your** chosen provider. **No AgentCell API for registration** — there is no `POST /registration/a2p`, no `submit_a2p_registration` MCP tool, and no console wizard. --- ### Phase 7: Configure events **Goal:** Receive inbound messages in real time. #### Option A: Webhooks (recommended for servers) **MCP tool:** `set_webhook` ```bash curl -X POST https://api.agentcell.store/v1/webhooks \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/webhook", "event_types": ["cell.message.received", "cell.message.delivered"], "context_limit": 10 }' ``` Save `secret` from response for signature verification. **Handler requirements:** 1. Return **200 OK** immediately 2. Process payload asynchronously 3. Verify HMAC: `{timestamp}.{raw_body}` with `secret` 4. Reject timestamps > 5 minutes old 5. Dedupe with `X-Webhook-ID` / `event_id` **Per-cell webhook:** Use `set_cell_webhook` to scope to one cell. #### Option B: WebSockets (no public URL) Connect to `wss://api.agentcell.store/v1/events/stream` with Bearer token. Subscribe to `cell.message.received`. Pattern from [AgentMail WebSockets](https://docs.agentmail.to/websockets.md). #### Option C: Polling (fallback) ```bash agentcell cells messages list --cell-id cell_abc123 --labels unread ``` Mark processed messages with `--add-labels read --remove-labels unread`. **Test webhook:** ```bash agentcell webhooks test ``` --- ### Phase 8: First send **Goal:** Send first outbound SMS/MMS. **First-message compliance (US):** Include brand name, opt-in acknowledgment, and STOP instructions: ``` Hi! This is Acme Corp. You're receiving this because you signed up for updates. Reply STOP at any time to unsubscribe. ``` **MCP tool:** `send_message` ```bash curl -X POST https://api.agentcell.store/v1/cells/cell_abc123/messages/send \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: first-message-v1" \ -d '{ "to": "+15559876543", "body": "Hi! This is Acme Corp. You opted in for updates. Reply STOP to unsubscribe." }' ``` **MMS:** ```json { "to": "+15559876543", "body": "Here is your photo.", "media_urls": ["https://example.com/photo.jpg"] } ``` **CLI:** ```bash agentcell cells messages send \ --cell-id cell_abc123 \ --to +15559876543 \ --body "Hi! This is Acme Corp. Reply STOP to unsubscribe." ``` **Failures:** | Code | Meaning | Action | |------|---------|--------| | 403 `NOT_VERIFIED` | Phase 3 incomplete | Verify OTP | | 402 | Insufficient balance | Top up / check subscription | | 422 `CARRIER_REJECTED` | Outbound blocked by carrier (often missing external 10DLC) | Complete Phase 6 with your carrier/TCR | | 429 `OUTBOUND_LIMIT_REACHED` | Daily cap | Wait for reset or scale cells | --- ### Phase 9: First receive & reply **Goal:** Handle inbound message and reply in thread. **Inbound webhook payload:** ```json { "event_type": "cell.message.received", "event_id": "evt_abc123", "cell": { "cell_id": "cell_abc123", "phone_number": "+14155551234" }, "message": { "message_id": "msg_xyz789", "conversation_id": "conv_def456", "from": "+15559876543", "to": "+14155551234", "body": "When will my order arrive?", "extracted_body": "When will my order arrive?", "channel": "sms", "labels": ["received", "unread"] }, "conversation_state": null, "recent_history": [] } ``` **Handler steps:** 1. Verify signature 2. Check idempotency (`event_id` not seen before) 3. Process `message.body` or `extracted_body` 4. Mark read: `PATCH .../messages/{id}` with `add_labels: ["read"], remove_labels: ["unread"]` 5. Reply: **MCP:** `reply_message` ```bash curl -X POST https://api.agentcell.store/v1/cells/cell_abc123/messages/msg_xyz789/reply \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -d '{"body": "Your order arrives Thursday. Tracking: 1Z999..."}' ``` **Update conversation state** (optional, for multi-turn context): ```bash curl -X PATCH https://api.agentcell.store/v1/conversations/conv_def456 \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -d '{"metadata": {"order_id": "ORD-4521", "customer_name": "Jane"}}' ``` **Full thread by number pair** (line ↔ contact, with stats): ```bash curl "https://api.agentcell.store/v1/conversations/between?from=%2B14155551234&to=%2B15559876543" \ -H "Authorization: Bearer $AGENTCELL_API_KEY" # Stats only — outbound/inbound counts, segments, characters curl "https://api.agentcell.store/v1/conversations/between?cell_id=cell_support&contact=%2B15559876543&include_messages=false" \ -H "Authorization: Bearer $AGENTCELL_API_KEY" ``` **MCP:** `get_conversation_between` | **CLI:** `agentcell conversations between --from ... --to ...` Future webhooks include `conversation_state` automatically (AgentPhone pattern). --- ### Phase 10: Operate **Goal:** Production-ready agent operation. **Recommended setup:** | Feature | Purpose | |---------|---------| | **Messaging settings** | `include_stop_footer`, `auto_ignore_on_stop`, `ignore_list_enabled` on cell or pod | | **TCPA quiet hours** | Pod-only `tcpa_quiet_hours` — gates pod-scoped sends; direct cell send without `pod_id` bypasses | | **Ignore lists** | Suppress inbound webhooks and block outbound to opted-out numbers (cell and/or pod scope) | | **Pod cycle send** | Blast a recipient list across pod phones, 1 msg/sec/cell, `single_message` or `per_recipient` | | **Lists** | Block spam numbers; allow-only VIP senders | | **Drafts** | Human-in-the-loop before sensitive sends | | **Pods** | Multi-tenant SaaS (one pod per customer) | | **Labels** | Campaign tracking, triage, read/unread | | **Usage monitoring** | `get_usage` daily; watch segment counts vs your registered campaign tier | **Enable STOP footer + auto-ignore on a pod:** ```bash curl -X PATCH https://api.agentcell.store/v1/pods/pod_acme \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "messaging_settings": { "include_stop_footer": true, "auto_ignore_on_stop": true, "ignore_list_enabled": true, "tcpa_quiet_hours": { "enabled": true, "window_start": "08:00", "window_end": "21:00", "timezone_mode": "recipient_local", "queue_until_open": true } } }' ``` **Direct reply bypasses pod quiet hours** (omit `pod_id`): ```bash curl -X POST https://api.agentcell.store/v1/cells/cell_abc123/messages/send \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"to": "+15559876543", "body": "Your ticket was updated."}' ``` **Pod cycle send (single message, many recipients):** ```bash curl -X POST https://api.agentcell.store/v1/pods/pod_acme/messages/cycle \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{ "client_id": "promo-v1", "mode": "single_message", "body": "Flash sale ends tonight!", "recipients": ["+15551111111", "+15552222222", "+15553333333"] }' ``` **MCP:** `get_pod_overview`, `list_pod_messages`, `get_conversation_between`, `get_pod_conversation_between`, `get_pod_compliance`, `list_pod_stop_list`, `update_pod_settings`, `pod_cycle_send`, `check_quiet_hours` **MCP maintenance prompts:** - *"Show usage for the last 7 days."* - *"List unread messages on my support cell."* - *"Show all outbound messages from pod acme today."* - *"Get the full conversation between our support line and +15559876543."* - *"How many SMS segments have we sent to +15559876543?"* - *"What's on the stop list for pod acme?"* - *"Get 10DLC compliance info for pod acme."* - *"Add +15551112222 to the receive block list."* - *"Rotate a promo message through my sales pod to these 20 numbers."* - *"Enable STOP footer and auto-ignore on pod acme."* - *"Is it TCPA-safe to text +15559876543 from pod acme right now?"* **CLI:** ```bash agentcell usage daily --days 7 agentcell cells messages list --cell-id cell_abc123 --labels unread ``` --- ## 4. Receive Path Options | Method | Best for | Public URL? | AgentMail | AgentPhone | |--------|----------|-------------|-----------|------------| | **Webhooks** | Server deployments | Yes (HTTPS) | Yes | Yes | | **WebSockets** | Local/dev agents | No | Yes | — | | **Polling** | Debugging only | No | Fallback | Fallback | ### Webhook verification (Node.js) ```javascript const crypto = require("crypto"); function verifyWebhook(rawBody, signature, timestamp, secret) { if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) return false; const signed = timestamp + "." + rawBody; const expected = crypto.createHmac("sha256", secret).update(signed).digest("hex"); return signature === `sha256=${expected}`; } ``` Return 200 immediately; process async. Dedupe on `event_id`. --- ## 5. Payment Flow AgentCell billing differs from AgentMail (plan tiers) and AgentPhone (pay-as-you-go credits). **Three rails:** Stripe, **x402**, **MPP**. ```mermaid flowchart TD SignUp[Agent sign-up] --> Verify[OTP verify] Verify --> PayChoice{Payment rail} PayChoice --> Checkout[Stripe Checkout] PayChoice --> X402[x402 PAYMENT-SIGNATURE] PayChoice --> MPP[MPP Challenge/Credential] Checkout --> Funded[Account funded] X402 --> Funded MPP --> Funded Funded --> CreateCell[create_cell] CreateCell --> SetupFee[Setup fee applied if not in checkout] CreateCell --> Monthly[Monthly hosting + service subscription] Monthly --> Usage[Daily usage meter: volume only, SMS included] Usage --> Invoice[Monthly invoice: org + cell fees] ``` | Charge | When | Amount | |--------|------|--------| | Setup fee | Checkout, x402, MPP, or first `create_cell` 402 | **$229** (org + setup + first month) | | Hosting + org | Monthly subscription or prepaid renewal | **$79**/mo | | SMS | Included | $0 | ### x402 details | Item | Value | |------|-------| | Protocol | [x402 v2](https://github.com/x402-foundation/x402) — HTTP 402 + `PAYMENT-REQUIRED` / `PAYMENT-SIGNATURE` / `PAYMENT-RESPONSE` | | Default asset | USDC (Base, Solana) | | Schemes | `exact`, `upto`, optional `batch-settlement` | | Facilitator | AgentCell-hosted `/billing/x402/facilitator/verify` + `/settle` | | MCP | `pay_with_x402`, `get_payment_options` | ### MPP details | Item | Value | |------|-------| | Protocol | [Machine Payments Protocol](https://mpp.dev/protocol) — co-authored Stripe + Tempo | | Discovery | `GET https://api.agentcell.store/.well-known/mpp` | | Methods | `stripe` (SPT / Link), `tempo` (stablecoin, min ~$0.01) | | Server | `mppx` integration; Stripe `PaymentIntent` with `machine_payment: true` | | MCP | `pay_with_mpp`, `get_payment_options` | ### Prepaid & hybrid billing Agents can maintain a **prepaid balance** (x402/MPP deposits) instead of card-on-file: ```bash agentcell billing prepaid deposit --amount-usd 200 --rail mpp agentcell billing prepaid balance ``` Configure auto top-up: `PATCH /billing/preferences` or MCP `set_billing_preferences`. **Grace period:** 30 days on failed renewal before number release (Stripe dunning or prepaid depletion). **Check status anytime:** ```bash agentcell billing status agentcell billing payment-options curl https://api.agentcell.store/v1/billing/status -H "Authorization: Bearer $AGENTCELL_API_KEY" ``` --- ## 6. Human Console Login The human owner uses **`https://console.agentcell.store/user`** to **observe**, **pay**, and **connect the agent** — not to operate the platform. | Method | Initiator | Summary | |--------|-----------|---------| | **Magic login link** | Agent | `create_console_login_link` → human opens URL → read-only dashboard + billing pay + API keys | | **Phone OTP** | Human | `/login` → sign-up `human_phone` → SMS code | | In console | Via agent (API/MCP/CLI) | |------------|-------------------------| | View cells, messages, usage, **agent status & activity**, webhook delivery log | Create cells, send SMS, configure webhooks | | Pay invoices, reinstatement (Stripe) | x402/MPP machine payments | | Create/copy API keys, MCP/CLI setup snippets | Scoped keys, permissions, all management | See [Phase 3b](#phase-3b-human-console-access-optional). Full spec: [AGENTCELL_PLATFORM_SPEC.md §4.2b](./AGENTCELL_PLATFORM_SPEC.md#42b-agent-observability-console--api), [§9.3](./AGENTCELL_PLATFORM_SPEC.md#93-console-policy-summary). **Page-level UI breakdown:** [AGENTCELL_FRONTEND_CONSOLE.md](./AGENTCELL_FRONTEND_CONSOLE.md). ### Agent observability (`/user/agent`) Read-only panels for the human owner: | Panel | Shows | |-------|--------| | **Status** | `active` / `idle` / `offline` / `never_connected`; time since last API/MCP/WebSocket activity | | **Last connection** | Client type (MCP stdio/hosted, CLI, SDK, REST, WebSocket), API key name, `last_seen_at` | | **Live sessions** | Whether MCP and/or WebSocket streams are connected now | | **24h stats** | API requests, logged actions, messages sent, errors | | **Activity feed** | Recent agent actions with summaries (`messages.send`, `cells.create`, `webhooks.set`, …) | | **Connections** | Per API key: client version, first/last seen, request count | **Typical agent prompt:** *"Check `/user/agent` — you'll see I'm active and what I've sent in the last hour."* **Security:** - Magic links: single-use, 15 min default TTL, revocable via `revoke_console_login_link` - Phone login: verified org `human_phone` only - Console cannot send SMS or change webhooks/pods/cells --- ## 7. Compliance & First Message Rules (User Responsibility) AgentCell does **not** register 10DLC/A2P. The account owner completes compliance **externally** (Phase 6). The agent operates on the owner's behalf. ### 10DLC (US outbound) - Required by US carriers for A2P SMS from 10-digit numbers - **You** register with carrier/TCR/compliance vendor — not via AgentCell API - Daily segment limits depend on **your** external campaign tier — see [AGENTPHONE_BREAKDOWN.md §5](./AGENTPHONE_BREAKDOWN.md#5-rate-limits--carrier-constraints) ### First outbound to new contact Must include: 1. **Brand name** 2. **Opt-in confirmation** 3. **Opt-out** (Reply STOP) Non-compliant messages may show as sent but never deliver. ### MMS rules - Media URLs: public HTTPS - Max 5 MB for carrier MMS - Types: JPEG, PNG, GIF, MP3, M4A, MP4, PDF, vCard, etc. ### STOP handling & ignore lists When [messaging settings](./AGENTCELL_CONCEPT.md#39-messaging-settings-cell--pod) are enabled: | Setting | Behavior | |---------|----------| | `include_stop_footer` | Outbound SMS append STOP instructions | | `auto_ignore_on_stop` | STOP keyword adds sender to pod/cell ignore list | | `ignore_list_enabled` | Ignored numbers: no inbound webhook; outbound returns `IGNORED_RECIPIENT` | Pod-level ignore applies to **all cells in the pod** when the pod has `ignore_list_enabled: true`. Cell-level ignore adds entries for that line only. Effective ignored set = **union** of both when each scope is enabled. If settings are off, you handle STOP and opt-out manually (Lists API or your store). ### TCPA quiet hours (pod) Configure on the **pod** only. When `tcpa_quiet_hours.enabled` is `true`: - **Pod cycle send** and any send with `pod_id` in the request are blocked or deferred outside **8 AM–9 PM recipient local time** (configurable). - **`POST /cells/{id}/messages/send` without `pod_id`** bypasses quiet hours — use for replies and transactional one-offs you authorize. - Pods without the setting, and org-level cells not in a pod, are **unrestricted** by this feature. Preflight: `GET /pods/{pod_id}/quiet-hours/check?to=+1...` or MCP `check_quiet_hours`. --- ## 8. Error Handling Reference | HTTP | Code | Retriable? | Phase | Fix | |------|------|------------|-------|-----| | 402 | `PAYMENT_REQUIRED` | No | 4, 5 | Stripe checkout, `pay_with_x402`, or `pay_with_mpp` | | 402 | `X402_SETTLEMENT_FAILED` | Maybe | 5 | Retry x402 with fresh signature | | 402 | `MPP_SETTLEMENT_FAILED` | Maybe | 5 | New MPP challenge | | 403 | `NOT_VERIFIED` | No | 3 | Verify OTP | | 403 | `FORBIDDEN` | No | — | Check API key permissions | | 409 | `IDEMPOTENCY_CONFLICT` | No | 5, 8 | Same key, different body | | 422 | `TCPA_QUIET_HOURS` | No | 10 | Wait for window, enable `queue_until_open`, or direct cell send without `pod_id` | | 422 | `IGNORED_RECIPIENT` | No | 10 | Remove from recipients or delete ignore entry | | 422 | `CARRIER_REJECTED` | No | 6 | Complete external 10DLC/A2P for this number | | 422 | `CELL_NOT_READY` | Yes | 5 | Poll health until online | | 401 | `CONSOLE_LINK_INVALID` | No | 3b, 6 | Request new login link from agent | | 429 | `CONSOLE_CHALLENGE_RATE_LIMITED` | Yes | 6 | Wait before retrying phone OTP | | 429 | `CONVERSATION_STREAK_LIMIT` | No | 8 | Wait for contact reply | | 429 | `OUTBOUND_LIMIT_REACHED` | No | 8 | Daily reset; add cells | | 502 | `CARRIER_ERROR` | Maybe | 8 | Confirm delivery before retry | **Idempotency:** - Creates: reuse same `client_id` on pods, drafts, and cycle jobs — **not** on cell create - Sends: reuse same `Idempotency-Key` within 24h **No idempotency on AgentPhone sends** — AgentCell adds `Idempotency-Key` (AgentMail pattern). Always confirm failed sends didn't deliver before retry. --- ## 9. Copy Blocks (Full Flow) ### Python ```python """ AgentCell full onboarding — copy into Cursor/Claude. Phases: sign-up → verify → checkout → create cell → webhook → send → reply. Requires: pip install agentcell; human provides OTP; payment via Stripe OR x402 OR MPP wallet. """ import os import time from agentcell import AgentCell # Phase 2: Sign up (no API key) client = AgentCell() resp = client.agent.sign_up( human_phone="+15551234567", human_email="developer@example.com", username="my-agent", ) api_key = resp.api_key print("OTP sent to:", resp.otp_sent_to) # Phase 3: Verify (human provides OTP) client = AgentCell(api_key=api_key) client.agent.verify(otp_code="123456") # replace with real OTP # Optional: send human a console login link (billing UI, fleet view) # link = client.auth.create_console_login_link(redirect_path="/user/billing", delivery="sms") # print("Console login sent:", link.login_url) # Phase 4: Payment — choose one path # Path A: Stripe (human opens checkout_url) checkout = client.billing.create_checkout( tier="pro", success_url="https://example.com/success", cancel_url="https://example.com/cancel", ) print("Pay at:", checkout.checkout_url) input("Press Enter after payment completes...") # Path B: x402 (autonomous — requires wallet env) # client = AgentCell(api_key=api_key, payment_mode="x402", wallet_key=os.environ["WALLET_KEY"]) # client.billing.pay_with_x402(intent="setup", tier="pro") # Path C: MPP (autonomous — SPT or Tempo) # client.billing.pay_with_mpp(intent="setup", tier="pro", method="tempo") # Phase 5: Create cell (no number yet; no client_id) cell = client.cells.create( display_name="My Agent Cell", area_code="415", ) print("Cell:", cell.cell_id, cell.phone_number) # phone_number empty until claim while True: got = client.cells.get(cell.cell_id) if got.get("phone_number"): break time.sleep(30) # Phase 6: User compliance (external — not AgentCell API) # Owner registers 10DLC/A2P with carrier/TCR; record metadata on the pod: client.pods.update_compliance( "pod_acme", a2p_10dlc={ "brand_id": "BXXXXXX", "campaign_id": "CXXXXXX", "status": "approved", "status_source": "user_declared", }, ) # Phase 7: Webhook wh = client.webhooks.create( url="https://your-server.com/webhook", event_types=["cell.message.received"], ) print("Webhook secret:", wh.secret) # Phase 8: First send client.cells.messages.send( cell.cell_id, to="+15559876543", body="Hi! This is Acme Corp. You opted in for updates. Reply STOP to unsubscribe.", request_options={"additional_headers": {"Idempotency-Key": "first-msg-v1"}}, ) # Phase 9: Reply (when message received via webhook or poll) messages = client.cells.messages.list(cell.cell_id, labels=["unread"]) if messages.messages: msg = messages.messages[0] client.cells.messages.reply(cell.cell_id, msg.message_id, body="Thanks! We'll help shortly.") client.cells.messages.update(cell.cell_id, msg.message_id, add_labels=["read"], remove_labels=["unread"]) ``` ### TypeScript ```typescript /** * AgentCell full onboarding — copy into Cursor/Claude. */ import { AgentCellClient } from "@agentcell/sdk"; async function onboard() { const anon = new AgentCellClient({}); const resp = await anon.agent.signUp({ humanPhone: "+15551234567", humanEmail: "developer@example.com", username: "my-agent", }); const client = new AgentCellClient({ apiKey: resp.apiKey }); await client.agent.verify({ otpCode: "123456" }); // Phase 4: Payment — Stripe OR x402 OR MPP // Stripe (human): const checkout = await client.billing.createCheckout({ tier: "pro", ... }); // x402 (autonomous): // await client.billing.payWithX402({ intent: "setup", tier: "pro" }); // MPP (autonomous): // await client.billing.payWithMpp({ intent: "setup", tier: "pro", method: "tempo" }); const cell = await client.cells.create({ displayName: "My Agent Cell", areaCode: "415", clientId: "my-agent-cell-v1", }); await client.webhooks.create({ url: "https://your-server.com/webhook", eventTypes: ["cell.message.received"], }); await client.cells.messages.send( cell.cellId, { to: "+15559876543", body: "Hi! This is Acme Corp. Reply STOP to unsubscribe.", }, { headers: { "Idempotency-Key": "first-msg-v1" } } ); } onboard(); ``` ### Bash (CLI) ```bash #!/bin/bash set -e # Phase 2–3 agentcell agent sign-up \ --human-phone +15551234567 \ --human-email developer@example.com \ --username my-agent export AGENTCELL_API_KEY="ac_..." # from sign-up output agentcell agent verify --otp-code 123456 # Phase 4 CHECKOUT=$(agentcell billing checkout --tier pro --json) echo "Open checkout URL from JSON output, then press Enter" read # Phase 5–8 (complete external 10DLC/A2P with your carrier before US outbound) CELL=$(agentcell cells create --display-name "My Agent" --area-code 415 --client-id cell-v1 --json) CELL_ID=$(echo "$CELL" | jq -r .cell_id) agentcell webhooks create --url https://your-server.com/webhook --event-types cell.message.received agentcell cells messages send \ --cell-id "$CELL_ID" \ --to +15559876543 \ --body "Hi! This is Acme Corp. Reply STOP to unsubscribe." ``` --- ## 10. Production Checklist Before going live: - [ ] OTP verified (Phase 3) - [ ] Payment active — Stripe subscription, prepaid balance, or x402/MPP-funded setup (Phase 4) - [ ] Human knows how to reach console (magic link or `/login`) — can view **agent status & activity** - [ ] Cell online (`device_status: online`) - [ ] **Your** external 10DLC/A2P covers each US outbound cell (Phase 6 — not AgentCell) - [ ] Webhook verified with test event OR WebSocket connected - [ ] Webhook signature verification implemented - [ ] Idempotency on sends (`Idempotency-Key`) - [ ] Read/unread labels to prevent duplicate processing - [ ] Messaging settings configured if using STOP footer / ignore lists (Phase 10) - [ ] Pod TCPA quiet hours configured for campaign pods (Phase 10); direct replies omit `pod_id` when bypass intended - [ ] Pod cycle jobs monitored via `get_pod_cycle_job` or `pod.cycle.*` webhooks - [ ] Lists configured (block known spam, allow test numbers) - [ ] First-message template includes brand + opt-in + STOP - [ ] Usage alerts configured (`get_usage` / console) - [ ] Error handling for 402, 403, 429 codes - [ ] Human-in-the-loop drafts for sensitive replies (optional) --- ## Related documents | Document | Purpose | |----------|---------| | [AGENTCELL_PLATFORM_SPEC.md](./AGENTCELL_PLATFORM_SPEC.md) | API, MCP, CLI, PostgreSQL/GORM, DigitalOcean | | [AGENTCELL_FRONTEND_CONSOLE.md](./AGENTCELL_FRONTEND_CONSOLE.md) | Console portal pages and wireframes | | [AGENTCELL_CONCEPT.md](./AGENTCELL_CONCEPT.md) | Product vision and pricing | | [AGENTCELL_ADMIN.md](./AGENTCELL_ADMIN.md) | Operator portal, phone keys, physical device provisioning | | [AGENTCELL_OWNERSHIP.md](./AGENTCELL_OWNERSHIP.md) | Phone/SIM/number ownership, shipping, reinstatement, forfeiture | | [AGENTMAIL_BREAKDOWN.md](./AGENTMAIL_BREAKDOWN.md) | AgentMail patterns (sign-up, idempotency) | | [AGENTPHONE_BREAKDOWN.md](./AGENTPHONE_BREAKDOWN.md) | Carrier limits and webhook patterns (10DLC background; AgentPhone registers in-product, AgentCell does not) | --- # Cells (/docs/guides/cells) # Cells A cell is a dedicated phone identity for your agent: one phone, one SIM, one E.164 number, with its own message history. ## Create a cell A cell is a hosted phone identity (phone + SIM + number) after a handset claims. area_code is a preference string, not a shoppable inventory. POST /cells returns pending_provision and a phone order ($229 / $79). phone_number is empty until linked. Do not pass client_id. You cannot bring your own number or handset. ```curl curl -X POST https://api.agentcell.store/v1/cells \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Content-Type: application/json" \ -d '{"display_name":"support-agent","area_code":"415"}' ``` ```typescript const cell = await agentcell.cells.create({ displayName: "support-agent", areaCode: "415", }); // phoneNumber is empty until the handset claims ``` ```python cell = client.cells.create( display_name="support-agent", area_code="415", ) # cell.phone_number is empty until the handset claims ``` ```cli agentcell cells create --display-name support-agent --area-code 415 ``` ## List and health List cells for the organization or a pod. GET a cell may include public phone (no ADB) and public order (tracking, 1–72h, 229/79). Health is online when phones.ws_connected. - GET /cells — fleet list with limit (10/25/50) and page_token - GET /cells/{cell_id} — single cell + public phone + order - GET /cells/{cell_id}/health — radio WebSocket / heartbeat - GET /cells/{cell_id}/ownership — phone, SIM, number bundle | Status | Meaning | | --- | --- | | pending_provision | Waiting for a handset to claim (typically 1–72h). | | Active | Linked number; send if the radio is connected. | | Suspended | Billing needs attention; 30-day reinstatement window. | | Forfeited | The reinstatement window ended; number is gone. | ## Multi-cell fleets One organization can own many cells. Group them into pods for campaigns, routing, and shared ignore/compliance policy. Additional cells charge another $150 setup + $50/mo on top of the $29 org monthly. > **Ownership matters** Cells are yours while active. Learn about the 30-day suspension and $150 reinstatement policy. > > /ownership ## Common errors - `PAYMENT_REQUIRED` — Setup or hosting has not been paid. - `CELL_NOT_READY` — No E.164 yet; wait for the handset to claim. - `VALIDATION_ERROR` — Missing display_name. area_code is optional. client_id is not a cell field. ## Copy for Cursor / Claude ``` Create a cell, wait for the handset, then send. curl -X POST https://api.agentcell.store/v1/cells \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"display_name":"support-agent","area_code":"415"}' agentcell cells get --cell-id $CELL_ID agentcell cells health --cell-id $CELL_ID ``` --- # Messages (/docs/guides/messages) # Messages Send and receive SMS on a cell. MMS is not available yet (media_urls returns MMS_NOT_SUPPORTED). ## Send Use a stable Idempotency-Key (or client_id) whenever a request could be retried. AgentCell returns the original message instead of sending a duplicate. ```curl curl -X POST https://api.agentcell.store/v1/cells/$CELL_ID/messages/send \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Idempotency-Key: send-hello-1" \ -d '{"to":"+14155550144","body":"Hello from my agent"}' ``` ```typescript await client.messages.send({ cellId: cell.id, to: "+14155550144", body: "Hello from my agent", idempotencyKey: "send-hello-1", }); ``` ```python client.messages.send( cell_id=cell.id, to="+14155550144", body="Hello from my agent", idempotency_key="send-hello-1", ) ``` ```cli agentcell cells messages send --cell-id $CELL_ID --to +14155550144 --body "Hello from my agent" ``` ## SMS only This release accepts SMS bodies only. Non-empty media_urls returns 400 MMS_NOT_SUPPORTED. GET …/media/{index} is not available. Voice, iMessage, and WhatsApp are later, like MMS. Send requires a numbered cell on the workspace (and on the pod when pod_id is set). No connected handset on the device WebSocket (and not sim) returns 503 CARRIER_UNAVAILABLE — simulation is e2e-only. - Required: to (E.164) and body - MMS, voice, iMessage, WhatsApp: later - CELL_NOT_READY / PHONE_NOT_ASSOCIATED if no number is assigned - CARRIER_UNAVAILABLE if no handset is on the device WebSocket ## Inbound and reply Subscribe to cell.message.received. Reply with POST /cells/{id}/messages/{message_id}/reply so the thread stays intact. > **Idempotency** Pods, drafts, and cycle jobs use client_id. Cell create does not. Sends use the Idempotency-Key header (24h scope). > > /docs/reference/idempotency ## Common errors - `IGNORED_RECIPIENT` — Number is on the effective ignore list. - `TCPA_QUIET_HOURS` — Pod quiet hours blocked or deferred the send. - `CELL_NOT_READY` — Cell has no E.164 yet. - `PHONE_NOT_ASSOCIATED` — No phone on this workspace or pod. - `CARRIER_UNAVAILABLE` — Production fail-closed without a carrier. - `CARRIER_REJECTED` — The carrier refused the message. - `RATE_LIMITED` — Honor Retry-After and back off. ## Copy for Cursor / Claude ``` Send with an idempotency key, then listen for cell.message.received. curl -X POST https://api.agentcell.store/v1/cells/$CELL_ID/messages/send \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Idempotency-Key: send-hello-1" \ -d '{"to":"+14155550144","body":"Hello from my agent"}' ``` --- # Conversations (/docs/guides/conversations) # Conversations A conversation is the thread between a cell and a contact (or between two numbers). conversationState tells your agent whether to wait, reply, or start a new thread. ## List and between List conversations for a cell, fetch a thread, or look up the pair of numbers with GET /conversations/between. ```curl curl "https://api.agentcell.store/v1/conversations/between?a=+14155550192&b=+14155550144" \ -H "Authorization: Bearer $AGENTCELL_API_KEY" ``` ```typescript const thread = await agentcell.conversations.between({ a: "+14155550192", b: "+14155550144", }); ``` ```python thread = client.conversations.between(a="+14155550192", b="+14155550144") ``` ```cli agentcell cells conversations with --cell-id $CELL_ID --contact +14155550144 ``` ## conversationState Use state to decide the next action: active, awaiting_reply, closed, or limited by streak rules. | State | Agent action | | --- | --- | | active | Continue the thread. | | awaiting_reply | Do not start a new outbound until the contact replies. | | streak_limited | CONVERSATION_STREAK_LIMIT — wait for inbound. | ## Common errors - `CONVERSATION_STREAK_LIMIT` — The contact must reply before more outbound. ## Copy for Cursor / Claude ``` List conversations for a cell, then fetch the thread with a contact. agentcell cells conversations list --cell-id $CELL_ID agentcell cells conversations with --cell-id $CELL_ID --contact +14155550144 ``` --- # Drafts (/docs/guides/drafts) # Drafts Drafts are the human-in-the-loop path: the agent proposes an SMS, a human approves, then the agent sends. ## Create and send Create a draft on a cell, optionally in reply to a message. Sending a draft is a separate call so approval can happen in the console or via API. ```curl curl -X POST https://api.agentcell.store/v1/cells/$CELL_ID/drafts \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"to":"+14155550144","body":"Need a human to approve this.","client_id":"draft-1"}' ``` ```typescript const draft = await agentcell.drafts.create({ cellId, to: "+14155550144", body: "Need a human to approve this.", clientId: "draft-1", }); await agentcell.drafts.send({ cellId, draftId: draft.id }); ``` ```python draft = client.drafts.create(cell_id=cell_id, to="+14155550144", body="Need a human to approve this.", client_id="draft-1") client.drafts.send(cell_id=cell_id, draft_id=draft.id) ``` ```cli agentcell cells drafts create --cell-id $CELL_ID --to +14155550144 --body "Need a human to approve this." ``` > **Console is read-only** Humans observe drafts in the console; sending still happens through API, MCP, or CLI. > > /docs/guides/console-login ## Common errors - `VALIDATION_ERROR` — Draft body or recipient missing. ## Copy for Cursor / Claude ``` Create a draft, wait for approval, then send. agentcell cells drafts create --cell-id $CELL_ID --to +14155550144 --body "Need approval" agentcell cells drafts send --cell-id $CELL_ID --draft-id $DRAFT_ID ``` --- # Labels (/docs/guides/labels) # Labels Labels mark message state and campaign tags: unread, spam, trash, or your own keys. ## Built-in and custom Apply labels on send or with a PATCH. Filter lists with ?labels=. Spam, blocked, and trash visibility depend on API key permissions. | Label | Use | | --- | --- | | unread | Inbound not yet processed. | | spam | Suspected junk; requires label_spam_read. | | ignored | Inbound from an ignore-list number. | ```cli agentcell cells messages update --cell-id $CELL_ID --message-id $MSG_ID --add-labels unread ``` ```curl curl -X PATCH https://api.agentcell.store/v1/cells/$CELL_ID/messages/$MSG_ID \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"add_labels":["unread"]}' ``` ```typescript await agentcell.messages.update({ cellId, messageId, addLabels: ["unread"] }); ``` ```python client.messages.update(cell_id=cell_id, message_id=msg_id, add_labels=["unread"]) ``` ## Common errors - `VALIDATION_ERROR` — Unknown label or missing permission. ## Copy for Cursor / Claude ``` Tag inbound as unread, then filter. curl "https://api.agentcell.store/v1/cells/$CELL_ID/messages?labels=unread" -H "Authorization: Bearer $AGENTCELL_API_KEY" ``` --- # Lists (/docs/guides/lists) # Lists Allow and block lists control who a cell may message, by direction and list type. ## Direction × type Each list is a matrix of inbound/outbound and allow/block. Org-level lists can apply across cells; cell lists are local. | Direction | Allow | Block | | --- | --- | --- | | Inbound | Only listed numbers can reach the cell | Listed numbers are rejected | | Outbound | Agent may only send to listed numbers | Listed numbers cannot be sent to | ```curl curl -X POST https://api.agentcell.store/v1/cells/$CELL_ID/lists \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"direction":"outbound","type":"allow","phone_number":"+14155550144"}' ``` ```typescript await agentcell.lists.add({ cellId, direction: "outbound", type: "allow", phoneNumber: "+14155550144" }); ``` ```python client.lists.add(cell_id=cell_id, direction="outbound", type="allow", phone_number="+14155550144") ``` ```cli agentcell cells lists add --cell-id $CELL_ID --direction outbound --type allow --phone +14155550144 ``` > **Ignore lists are separate** STOP / opt-out uses ignore lists, not allow/block lists. > > /docs/guides/ignore-lists ## Common errors - `VALIDATION_ERROR` — Invalid direction or type. ## Copy for Cursor / Claude ``` Allow outbound only to a known number. curl -X POST https://api.agentcell.store/v1/cells/$CELL_ID/lists \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"direction":"outbound","type":"allow","phone_number":"+14155550144"}' ``` --- # Ignore lists (/docs/guides/ignore-lists) # Ignore lists Ignored E.164 numbers are silently dropped from agent workflows. STOP handling can add them automatically. ## What ignore does | Direction | When ignored | | --- | --- | | Inbound | Stored with label ignored; no cell.message.received unless you subscribe to cell.message.ignored | | Outbound | Rejected with 422 IGNORED_RECIPIENT before carrier handoff | ## Scope source is manual, auto_stop, or import. auto_ignore_on_stop writes STOP senders into the enabled scopes. | Scope | Applies when | | --- | --- | | Pod | ignore_list_enabled on the pod — every cell in the pod | | Cell | ignore_list_enabled on the cell | | Effective set | Union of pod + cell entries | ```curl curl -X POST https://api.agentcell.store/v1/cells/$CELL_ID/ignore-list \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"phone_number":"+15559876543","reason":"STOP keyword"}' ``` ```typescript await agentcell.ignoreList.add({ scope: "cell", scopeId: cellId, phoneNumber: "+15559876543" }); ``` ```python client.ignore_list.add(scope="cell", scope_id=cell_id, phone_number="+15559876543") ``` ```cli agentcell cells ignore-list add --cell-id $CELL_ID --phone +15559876543 --reason STOP ``` ## Common errors - `IGNORED_RECIPIENT` — Remove the entry or pick another recipient. ## Copy for Cursor / Claude ``` Enable ignore lists and auto-STOP, then add a number. agentcell cells settings --cell-id $CELL_ID --ignore-list-enabled --auto-ignore-on-stop curl -X POST https://api.agentcell.store/v1/cells/$CELL_ID/ignore-list -d '{"phone_number":"+15559876543"}' ``` --- # Pod cycle send (/docs/guides/pod-cycle-send) # Pod cycle send Distribute outbound messages across every cell in a pod by round-robin, at most one send per second per cell. ## Modes | Mode | Body | Use | | --- | --- | --- | | single_message | One body (+ media) to every recipient | Same blast copy | | per_recipient | messages[] with to/body per row | Personalized copy | ```curl curl -X POST https://api.agentcell.store/v1/pods/pod_acme/messages/cycle \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"client_id":"q1-blast-v1","mode":"single_message","body":"Your appointment is tomorrow at 10am.","recipients":["+15551111111","+15552222222"]}' ``` ```typescript await agentcell.pods.cycleSend({ podId: "pod_acme", clientId: "q1-blast-v1", mode: "single_message", body: "Your appointment is tomorrow at 10am.", recipients: ["+15551111111", "+15552222222"], }); ``` ```python client.pods.cycle_send(pod_id="pod_acme", client_id="q1-blast-v1", mode="single_message", body="Your appointment is tomorrow at 10am.", recipients=["+15551111111"]) ``` ```cli agentcell pods cycle send --pod-id pod_acme --mode single_message --body "Your appointment is tomorrow at 10am." ``` ## Scheduling The job assigns each recipient to the next cell in stable cell_id order. Before sending from cell C it waits until C's last send was ≥ 1 second ago. Respects pod TCPA quiet hours and the effective ignore list. - POST /pods/{id}/messages/cycle — start (idempotent client_id) - GET /pods/{id}/messages/cycle/{job_id} — status - POST .../cancel — cancel queued (not in-flight) sends ## Common errors - `CYCLE_JOB_NOT_FOUND` — Unknown job id. - `TCPA_QUIET_HOURS` — Recipient deferred or skipped per pod config. - `IGNORED_RECIPIENT` — Logged on the job row, send skipped. ## Copy for Cursor / Claude ``` Start a round-robin blast. curl -X POST https://api.agentcell.store/v1/pods/pod_acme/messages/cycle \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"client_id":"q1-blast-v1","mode":"single_message","body":"Hello fleet.","recipients":["+15551111111"]}' ``` --- # Pods (/docs/guides/pods) # Pods Pods isolate multi-tenant work: a group of cells with shared policy, compliance metadata, and cycle send. ## CRUD and nested reads Create pods, attach cells, then use overview, messages, conversations, compliance, stop-list, and usage endpoints. | Read | Path | | --- | --- | | Overview | GET /pods/{id}/overview | | Messages | GET /pods/{id}/messages | | Compliance | GET /pods/{id}/compliance | | Stop list | GET /pods/{id}/stop-list | ```curl curl -X POST https://api.agentcell.store/v1/pods \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"name":"acme","client_id":"pod-acme-v1"}' ``` ```typescript const pod = await agentcell.pods.create({ name: "acme", clientId: "pod-acme-v1" }); ``` ```python pod = client.pods.create(name="acme", client_id="pod-acme-v1") ``` ```cli agentcell pods create --name acme --client-id pod-acme-v1 ``` ## Common errors - `VALIDATION_ERROR` — Pod name or client_id missing. ## Copy for Cursor / Claude ``` Create a pod, then attach cells and enable ignore lists. curl -X POST https://api.agentcell.store/v1/pods -H "Authorization: Bearer $AGENTCELL_API_KEY" -d '{"name":"acme","client_id":"pod-acme-v1"}' ``` --- # Webhooks (/docs/guides/webhooks) # Webhooks Signed HTTP callbacks for inbound messages, delivery, billing, and cycle jobs. Verify the signature before you act. ## Event types | Event | When | | --- | --- | | cell.message.received | Inbound SMS | | cell.message.sent | Outbound accepted | | cell.message.delivered | Carrier delivery receipt | | cell.message.failed | Delivery failure | | cell.message.ignored | Inbound from ignored number (opt-in) | | pod.cycle.completed | Cycle job finished | | billing.payment_succeeded | Stripe payment completed | | billing.x402.settled | x402 payment credited | ## Set a webhook Retries are exponential. Failed deliveries appear in the console webhook log (read-only). ```curl curl -X POST https://api.agentcell.store/v1/webhooks \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"url":"https://your-server.com/webhook","events":["cell.message.received"]}' ``` ```typescript await agentcell.webhooks.create({ url: "https://your-server.com/webhook", events: ["cell.message.received"] }); ``` ```python client.webhooks.create(url="https://your-server.com/webhook", events=["cell.message.received"]) ``` ```cli agentcell webhooks set --url https://your-server.com/webhook --events cell.message.received ``` ## Common errors - `VALIDATION_ERROR` — URL must be HTTPS. ## Copy for Cursor / Claude ``` POST https://api.agentcell.store/v1/webhooks {"url":"https://your-server.com/webhook","events":["cell.message.received","cell.message.delivered"]} Verify the signature before processing. ``` --- # WebSockets (/docs/guides/websockets) # WebSockets Subscribe to WSS /v1/events/stream when you cannot expose a public webhook URL. ## Connect Authenticate with the same API key. Active subscriptions show up on GET /agent/status as connections.websocket_active. ```typescript const ws = new WebSocket("wss://api.agentcell.store/v1/events/stream", { headers: { Authorization: `Bearer ${process.env.AGENTCELL_API_KEY}` }, }); ws.onmessage = (event) => console.log(JSON.parse(event.data)); ``` ```python import websockets, os, json, asyncio async def listen(): async with websockets.connect( "wss://api.agentcell.store/v1/events/stream", extra_headers={"Authorization": f"Bearer {os.environ['AGENTCELL_API_KEY']}"}, ) as ws: async for raw in ws: print(json.loads(raw)) asyncio.run(listen()) ``` ```curl # WebSockets are not curl. Use MCP, SDK, or a WSS client. ``` ```cli agentcell events stream ``` > **AsyncAPI** Download the machine-readable event catalog. > > /docs/asyncapi.json ## Common errors - `NOT_VERIFIED` — API key is not verified. ## Copy for Cursor / Claude ``` Connect to wss://api.agentcell.store/v1/events/stream with Authorization: Bearer $AGENTCELL_API_KEY. See /docs/asyncapi.json for event payloads. ``` --- # Billing (/docs/guides/billing) # Billing Modular cell plan: org $29 + cell setup $150 + cell $50 = $229 setup / $79 monthly. SMS is included. Agents pay with x402 or MPP; Stripe is optional for the human. ## Cell plan | Line | USD | | --- | --- | | Org / account monthly | 29 | | Cell one-time setup | 150 | | Cell monthly | 50 | | Due at setup | 229 + tax (live GET /v1/billing/plan) | | Monthly after | 79 + tax | | SMS | included | | Reinstate | 150 / 30 days | ## Stripe checkout ```curl curl -X POST https://api.agentcell.store/v1/billing/checkout \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"success_url":"https://example.com/ok","cancel_url":"https://example.com/cancel"}' ``` ```typescript const checkout = await agentcell.billing.createCheckout({}); console.log(checkout.checkoutUrl); ``` ```python checkout = client.billing.create_checkout() print(checkout.checkout_url) ``` ```cli agentcell billing checkout ``` > **Human pay path** Stripe Checkout charges the live setup fee plus exclusive tax. GET /v1/billing/plan for current amounts. > > https://console.agentcell.store/user/billing ## Common errors - `PAYMENT_REQUIRED` — 402 — pay via Stripe, x402, or MPP. - `PREPAID_INSUFFICIENT` — Top up prepaid before sending. ## Copy for Cursor / Claude ``` POST https://api.agentcell.store/v1/billing/checkout {"tier":"pro","success_url":"https://example.com/ok","cancel_url":"https://example.com/cancel"} Or send the owner https://console.agentcell.store/user/billing ``` --- # Machine payments (/docs/guides/machine-payments) # Machine payments Agents can pay without a browser using x402 (wallet) or MPP (Machine Payments Protocol). Prepaid balance covers later usage. ## x402 flow - Call a paid endpoint (create cell, checkout intent). - API returns 402 PAYMENT_REQUIRED with payment details. - Agent retries with PAYMENT-SIGNATURE. - Webhook billing.x402.settled credits the org. ## MPP flow POST /billing/mpp/pay with a credential. Settlement creates a Stripe PaymentIntent. billing.mpp.settled fires on success. ```typescript const client = new AgentCell({ apiKey, paymentMode: "x402", walletKey: process.env.WALLET_KEY }); await client.billing.payWithX402({ intent: "setup", tier: "pro" }); ``` ```python client.billing.pay_with_x402(intent="setup", tier="pro") ``` ```curl curl -X POST https://api.agentcell.store/v1/cells -H "Authorization: Bearer $AGENTCELL_API_KEY" # 402 + payment details → retry with PAYMENT-SIGNATURE ``` ```cli agentcell billing pay x402 --intent setup --tier pro ``` ## Common errors - `X402_INVALID_PAYMENT` — Signature did not verify. - `MPP_CHALLENGE_EXPIRED` — Request a new challenge. - `PREPAID_INSUFFICIENT` — Prepaid balance too low. ## Copy for Cursor / Claude ``` On 402 PAYMENT_REQUIRED, settle via x402 PAYMENT-SIGNATURE or MPP credential, then retry the original request. Events: billing.x402.settled, billing.mpp.settled. ``` --- # Console login (/docs/guides/console-login) # Console login Humans sign in at console.agentcell.store/login with a magic link from the agent or phone OTP. The console is observe + pay + connect — not a management UI. ## Two ways in | Option | Who starts it | | --- | --- | | Magic link | Agent POST /v1/auth/login-links (recommended) | | Phone OTP | Human enters the sign-up phone at /login | ```curl curl -X POST https://api.agentcell.store/v1/auth/login-links \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"redirect_path":"/user/billing","delivery":"sms"}' ``` ```typescript const link = await agentcell.auth.createConsoleLoginLink({ redirectPath: "/user/billing", delivery: "sms" }); ``` ```python link = client.auth.create_console_login_link(redirect_path="/user/billing", delivery="sms") ``` ```cli agentcell console login-link create --redirect-path /user/billing --delivery sms ``` ## Read-only policy Humans can view fleet, usage, activity, and webhook deliveries; they can pay, reinstate, and copy API keys. Creating cells, sending SMS, and configuring webhooks stay on the agent. > **No public admin link** Operators use the same /login. If the phone is in the admins table they see Admin Portal. Landing never links /admin. > > https://console.agentcell.store/login ## Common errors - `CONSOLE_LINK_EXPIRED` — Create a new login link. - `CONSOLE_PHONE_NOT_FOUND` — Phone is not the sign-up human_phone. ## Copy for Cursor / Claude ``` agentcell console login-link create --redirect-path /user/billing --delivery sms Human opens https://console.agentcell.store/login with the sign-up phone. ``` --- # Agent activity (/docs/guides/agent-activity) # Agent activity Owners watch agent status, the activity log, and live connections in the console. Agents read the same data over the API. ## Status badges | Badge | Meaning | | --- | --- | | Active | Green — agent online recently | | Idle | Amber — 5m–24h inactive | | Offline | Gray — >24h inactive | | Suspended | Red — billing failure | | Forfeited | Dark red — ownership rescinded | ## API - GET /agent/status — live status and websocket_active - GET /agent/activity — filterable log (limit, offset, action, client, since/until) - GET /agent/connections — per API key client type and request counts ```curl curl https://api.agentcell.store/v1/agent/status -H "Authorization: Bearer $AGENTCELL_API_KEY" ``` ```typescript const status = await agentcell.agent.status(); ``` ```python status = client.agent.status() ``` ```cli agentcell auth whoami --json ``` > **Try in console** Observability only — no management controls. > > https://console.agentcell.store/user/agent ## Common errors - `NOT_VERIFIED` — Verify the agent before status is meaningful. ## Copy for Cursor / Claude ``` GET https://api.agentcell.store/v1/agent/status GET https://api.agentcell.store/v1/agent/activity?client=mcp_hosted&limit=50 Console: https://console.agentcell.store/user/agent ``` --- # Compliance (/docs/guides/compliance) # Compliance 10DLC/A2P and TCPA are the owner's responsibility. AgentCell hosts and routes; it does not register campaigns for you. ## User-owned 10DLC Register brand and campaign with your carrier/TCR, then record metadata on the pod (status_source: user_declared). AgentCell does not submit 10DLC for you. - Register brand + campaign externally - PATCH pod compliance with brand_id, campaign_id, status - Keep quiet hours, STOP footer, and ignore lists enabled ## First outbound and STOP | Setting | Behavior | | --- | --- | | include_stop_footer | Appends STOP instructions to outbound SMS | | auto_ignore_on_stop | Adds sender to pod and/or cell ignore list | | tcpa_quiet_hours | Pod-only; default 8:00 AM – 9:00 PM recipient local time | > **Quiet hours** Pod-scoped outbound outside the window returns TCPA_QUIET_HOURS or defers when queue_until_open is set. > > /docs/guides/ignore-lists ## Common errors - `TCPA_QUIET_HOURS` — Wait for the window or use a lawful direct cell send. ## Copy for Cursor / Claude ``` Owner registers 10DLC externally, then: PATCH /pods/{id}/compliance {"a2p_10dlc":{"brand_id":"BXXXXXX","campaign_id":"CXXXXXX","status":"approved","status_source":"user_declared"}} Enable include_stop_footer, auto_ignore_on_stop, ignore_list_enabled, tcpa_quiet_hours. ``` --- # MCP (/docs/integrations/mcp) # MCP The hosted Model Context Protocol server is the primary path for Cursor, Claude, and other agents. Stdio via npx is available for local tooling. ## Hosted URL Production MCP is https://mcp.agentcell.store/mcp. agent_sign_up works without a key; every other tool needs the API key from sign-up (x-api-key header) or OAuth. ```typescript { "mcpServers": { "agentcell": { "url": "https://mcp.agentcell.store/mcp", "headers": { "x-api-key": "ac_..." } } } } ``` ```cli npx -y @agentcell/mcp ``` ```python # Use the hosted URL from any MCP client. # Env: AGENTCELL_API_KEY=ac_... ``` ```curl # MCP is not REST. Point your MCP client at https://mcp.agentcell.store/mcp ``` ## Stdio Hosted MCP with OAuth or x-api-key is primary. For local agents that skip hosted OAuth: npx -y @agentcell/mcp with AGENTCELL_API_KEY and optional AGENTCELL_BASE_URL. ## Tool catalog (subset) | Tool | REST | | --- | --- | | agent_sign_up | POST /v1/agent/sign-up | | agent_verify | POST /v1/agent/verify | | create_cell | POST /v1/cells | | send_message | POST /v1/cells/{id}/messages/send | | create_setup_checkout | POST /v1/billing/checkout | | create_console_login_link | POST /v1/auth/login-links | > **Quickstart** Sign up through first send using MCP tools. > > /docs/quickstart ## Copy for Cursor / Claude ``` { "mcpServers": { "agentcell": { "url": "https://mcp.agentcell.store/mcp", "headers": { "x-api-key": "${AGENTCELL_API_KEY}" } } } } ``` --- # CLI (/docs/integrations/cli) # CLI Headless CLI for CI and terminals. npm i -g @agentcell/cli downloads the Go binary. ## Install | Env | Purpose | | --- | --- | | AGENTCELL_API_KEY | Bearer token | | AGENTCELL_BASE_URL | Default https://api.agentcell.store | | AGENTCELL_CLI_BINARY | Dev override to a local binary | ```cli npm i -g @agentcell/cli export AGENTCELL_API_KEY=ac_... agentcell --help ``` ```curl npm i -g @agentcell/cli ``` ```typescript # Same binary. Use --json in scripts. ``` ```python # Same binary. Use --json in scripts. ``` ## Command tree agent / auth / console login-link / cells (create, list, health, messages, conversations, drafts, lists) / billing / webhooks. Global --json for machine output. Exit 0 success, 1 error, 2 auth required. > **Quickstart** Use the CLI path in the first-send guide. > > /docs/quickstart ## Copy for Cursor / Claude ``` npm i -g @agentcell/cli export AGENTCELL_API_KEY=ac_... agentcell agent sign-up --human-phone +15551234567 --human-email you@example.com --username my-agent agentcell cells create --display-name support --area-code 415 --client-id support-v1 ``` --- # SDKs (/docs/integrations/sdk) # SDKs Typed HTTP clients for the agent REST API. TypeScript @agentcell/sdk and Python agentcell. No admin routes, no JWT, SMS only. ## Install ```typescript npm i @agentcell/sdk import { AgentCell } from "@agentcell/sdk"; const client = new AgentCell({ apiKey: process.env.AGENTCELL_API_KEY }); ``` ```python pip install agentcell from agentcell import AgentCell client = AgentCell(api_key=os.environ["AGENTCELL_API_KEY"]) ``` ```cli Use the CLI or MCP if you do not want an SDK. ``` ```curl REST is at https://api.agentcell.store/v1 ``` ## First send Pay, create a cell (no client_id), wait until it has an E.164 (CELL_NOT_READY until then), then POST …/messages/send. No connected handset returns 503 CARRIER_UNAVAILABLE. Simulation is e2e-only, not a product mode. ```typescript const cell = await client.cells.create({ displayName: "support", areaCode: "415", }); await client.messages.send({ cellId: cell.cell_id ?? cell.id, to: "+14155550144", body: "Hello from my agent", idempotencyKey: "hello-1", }); ``` ```python cell = client.cells.create(display_name="support", area_code="415") client.messages.send(cell_id=cell["cell_id"], to="+14155550144", body="Hello from my agent", idempotency_key="hello-1") ``` ```cli agentcell cells messages send --cell-id $CELL_ID --to +14155550144 --body "Hello from my agent" ``` ```curl curl -X POST https://api.agentcell.store/v1/cells/$CELL_ID/messages/send \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -H "Idempotency-Key: hello-1" \ -d '{"to":"+14155550144","body":"Hello from my agent"}' ``` > **MCP** Prefer hosted MCP when the agent can use OAuth. Use the SDK when you want a library. > > /docs/integrations/mcp ## Common errors - `PAYMENT_REQUIRED` — Pay before create-cell or send. - `CELL_NOT_READY` — No number assigned yet. - `CARRIER_UNAVAILABLE` — Production fail-closed without a carrier. - `FLEET_REQUIRED` — Usage/analytics locked on new unpaid orgs. ## Copy for Cursor / Claude ``` npm i @agentcell/sdk pip install agentcell export AGENTCELL_API_KEY=ac_... # POST /v1/cells/{id}/messages/send # Errors: PAYMENT_REQUIRED, CELL_NOT_READY, CARRIER_UNAVAILABLE, FLEET_REQUIRED ``` --- # Skills (/docs/integrations/skills) # Skills Cursor and Claude skills for onboarding, messaging, webhooks, billing, MCP, and CLI. The AgentCell plugin syncs SKILL.md files into the editor. ## Install - Cursor: /add-plugin agentcell or copy skills from agentcell-skills - Plugin sync: cd agentcell-plugin && sh scripts/sync-skills.sh - Each skill is a SKILL.md under skills// | Skill | When to use | | --- | --- | | agentcell-onboard | Sign-up through first cell | | agentcell-mcp | MCP + OAuth setup | | agentcell-messaging | Send, reply, conversations | | agentcell-webhooks | Webhook CRUD and delivery | | agentcell-billing | Payment rails | | agentcell-cli | CLI commands | > **Onboarding skill** Pair with the full agent onboarding playbook. > > /docs/agent-onboarding ## Copy for Cursor / Claude ``` # AgentCell skills # Copy this folder into your Cursor/Claude skills path, or install the AgentCell plugin. # Skills: onboard, mcp, messaging, webhooks, billing, cli # Docs: https://agentcell.store/docs/agent-onboarding ``` --- # LangChain (/docs/integrations/langchain) # LangChain Phase 2: tool wrappers around @agentcell/sdk and the Python agentcell package. Not shipped — use MCP or the SDKs today. > Phase 2 — not shipped. Use MCP, CLI, or the SDKs today. ## Intended shape LangChain tools will wrap create_cell, send_message, list_conversations, and billing status with AGENTCELL_API_KEY. Until then, call the TypeScript or Python SDK from a custom tool. ```python # Phase 2 (not shipped) # from langchain_agentcell import AgentCellToolkit # toolkit = AgentCellToolkit(api_key=os.environ["AGENTCELL_API_KEY"]) # Today: from agentcell import AgentCell client = AgentCell(api_key=os.environ["AGENTCELL_API_KEY"]) ``` ```typescript // Phase 2 (not shipped) // import { AgentCellToolkit } from "@agentcell/langchain"; // Today: import { AgentCell } from "@agentcell/sdk"; const client = new AgentCell({ apiKey: process.env.AGENTCELL_API_KEY }); ``` ```cli Use MCP instead of LangChain tools until Phase 2 ships. ``` ```curl Use REST until LangChain wrappers ship. ``` > **Use MCP today** Hosted MCP already exposes the tool catalog LangChain would wrap. > > /docs/integrations/mcp ## Copy for Cursor / Claude ``` # Phase 2 — LangChain toolkit is not shipped. # Use MCP (https://mcp.agentcell.store/mcp) or: pip install agentcell export AGENTCELL_API_KEY=ac_... ``` --- # Replit (/docs/integrations/replit) # Replit Phase 2: one-click Replit template. Until it ships, clone a Node or Python repl, set AGENTCELL_API_KEY, and follow quickstart. > Phase 2 — not shipped. Use MCP, CLI, or the SDKs today. ## Today - Create a Repl (Node or Python) - Set secret AGENTCELL_API_KEY - npm install @agentcell/sdk or pip install agentcell - Follow /docs/quickstart > **Quickstart** The first-send path is identical on Replit. > > /docs/quickstart ## Copy for Cursor / Claude ``` # Phase 2 Replit template is not shipped. export AGENTCELL_API_KEY=ac_... npm install @agentcell/sdk # then follow https://agentcell.store/docs/quickstart ``` --- # Sim.ai (/docs/integrations/sim-ai) # Sim.ai Phase 2/3: Sim.ai workflow nodes for cells and messages. Until nodes ship, call MCP tools or webhooks from your Sim graph. > Phase 2 — not shipped. Use MCP, CLI, or the SDKs today. ## Current path Use hosted MCP as the tool surface and webhooks for inbound events. Sim.ai nodes will wrap the same REST operations later. > **Webhooks + MCP** Inbound events already stream to any HTTPS URL. > > /docs/guides/webhooks ## Copy for Cursor / Claude ``` # Sim.ai nodes are not shipped. # Point Sim HTTP tools at https://api.agentcell.store/v1 # or MCP at https://mcp.agentcell.store/mcp ``` --- # API (/docs/reference/api) # API REST API at https://api.agentcell.store/v1. Authenticate with Authorization: Bearer ac_... ## Base URL and auth All customer routes are under /v1. Device API uses X-Phone-Key. Admin API is /v1/admin and is not available to agent keys. ```curl curl https://api.agentcell.store/v1/auth/whoami \ -H "Authorization: Bearer $AGENTCELL_API_KEY" ``` ```typescript const me = await fetch("https://api.agentcell.store/v1/auth/whoami", { headers: { Authorization: `Bearer ${process.env.AGENTCELL_API_KEY}` }, }); ``` ```python client = AgentCell(api_key=os.environ["AGENTCELL_API_KEY"]) print(client.auth.whoami()) ``` ```cli agentcell auth whoami --json ``` ## Resource index | Resource | Endpoints | | --- | --- | | Agent auth | POST /agent/sign-up, POST /agent/verify | | Cells | GET/POST /cells, GET/PATCH/DELETE /cells/{id} | | Messages | POST /cells/{id}/messages, reply, list | | Conversations | GET /conversations/between, cell threads | | Drafts / lists / ignore | Cell and pod nested routes | | Pods / cycle | CRUD + POST /pods/{id}/messages/cycle | | Webhooks / WSS | CRUD + WSS /v1/events/stream | | Billing / usage | /billing/*, /usage/* | > **OpenAPI** Download the machine-readable spec. > > /docs/reference/openapi ## Copy for Cursor / Claude ``` Base: https://api.agentcell.store/v1 Auth: Authorization: Bearer ac_... Whoami: GET /auth/whoami OpenAPI: https://agentcell.store/docs/openapi.yaml ``` --- # OpenAPI (/docs/reference/openapi) # OpenAPI OpenAPI 3.1 for the AgentCell REST API. Use it to generate clients or inspect routes. ## Downloads - JSON — /docs/openapi.json - YAML — /docs/openapi.yaml - Canonical source in the spec hub: docs/openapi.yaml > **Download YAML** Raw OpenAPI 3.1 document. > > /docs/openapi.yaml ## Copy for Cursor / Claude ``` curl -O https://agentcell.store/docs/openapi.yaml # or https://agentcell.store/docs/openapi.json ``` --- # Errors (/docs/reference/errors) # Errors Stable error envelope. Codes are the contract your agent should switch on — not the human message. ## Envelope ```typescript { "error": { "message": "Human-readable message", "code": "PAYMENT_REQUIRED", "type": "validation_error", "details": [] } } ``` ```python # response.status_code == 402 # response.json()["error"]["code"] == "PAYMENT_REQUIRED" ``` ```curl # Look at error.code, not the HTTP reason phrase. ``` ```cli agentcell ... --json # errors include code ``` ## Key codes | Code | Typical HTTP | Retry? | | --- | --- | --- | | VALIDATION_ERROR | 400 | No | | PAYMENT_REQUIRED | 402 | After pay | | X402_INVALID_PAYMENT | 402 | No | | X402_SETTLEMENT_FAILED | 402 | Maybe | | MPP_CHALLENGE_EXPIRED | 402 | Yes | | MPP_INVALID_CREDENTIAL | 402 | No | | MPP_SETTLEMENT_FAILED | 402 | Maybe | | PREPAID_INSUFFICIENT | 402 | After top-up | | NOT_VERIFIED | 403 | No | | CONSOLE_LINK_INVALID | 401 | No | | CONSOLE_LINK_EXPIRED | 401 | No | | CONSOLE_CHALLENGE_RATE_LIMITED | 429 | Yes | | CONSOLE_PHONE_NOT_FOUND | 404 | No | | RATE_LIMITED | 429 | Yes | | CONVERSATION_STREAK_LIMIT | 429 | No | | OUTBOUND_LIMIT_REACHED | 429 | No | | CARRIER_REJECTED | 422 | No | | CARRIER_UNAVAILABLE | 503 | No | | IGNORED_RECIPIENT | 422 | No | | TCPA_QUIET_HOURS | 422 | Later | | CELL_NOT_READY | 409 | Yes | | PHONE_NOT_ASSOCIATED | 409 | No | | FLEET_REQUIRED | 403 | After pay | | MMS_NOT_SUPPORTED | 400 | No | | RESTART_NOT_SUPPORTED | 501 | No | | FACILITATOR_UNAVAILABLE | 503 | No | | CYCLE_JOB_NOT_FOUND | 404 | No | | PHONE_KEY_EXPIRED | 401 | No | | INVALID_CONFIRMATION | 400 | No | | PROVISION_FAILED | 500 | Maybe | ## Copy for Cursor / Claude ``` VALIDATION_ERROR — Fix the request body. PAYMENT_REQUIRED — Settle Stripe, x402, or MPP. X402_INVALID_PAYMENT — Signature did not verify. X402_SETTLEMENT_FAILED — Retry settlement. MPP_CHALLENGE_EXPIRED — Request a new challenge. MPP_INVALID_CREDENTIAL — Credential rejected. MPP_SETTLEMENT_FAILED — Retry MPP settlement. PREPAID_INSUFFICIENT — Prepaid balance too low. NOT_VERIFIED — Complete agent verify. CONSOLE_LINK_INVALID — Create a new login link. CONSOLE_LINK_EXPIRED — Create a new login link. CONSOLE_CHALLENGE_RATE_LIMITED — Slow OTP requests. CONSOLE_PHONE_NOT_FOUND — Must be sign-up human_phone. RATE_LIMITED — Honor Retry-After. CONVERSATION_STREAK_LIMIT — Wait for a reply. OUTBOUND_LIMIT_REACHED — Daily cap. CARRIER_REJECTED — Carrier refused the message. CARRIER_UNAVAILABLE — No carrier configured; production send is fail-closed. IGNORED_RECIPIENT — Number is ignored. TCPA_QUIET_HOURS — Outside pod quiet hours. CELL_NOT_READY — Cell has no E.164 yet; poll health. PHONE_NOT_ASSOCIATED — No phone on this workspace or pod. FLEET_REQUIRED — New accounts cannot read usage/analytics until purchase or a cell exists. MMS_NOT_SUPPORTED — SMS only; MMS is later. RESTART_NOT_SUPPORTED — Remote restart/ADB is later, like MMS. FACILITATOR_UNAVAILABLE — x402 facilitator not configured. CYCLE_JOB_NOT_FOUND — Unknown cycle job. PHONE_KEY_EXPIRED — Device API key expired. INVALID_CONFIRMATION — Provisioning confirm failed. PROVISION_FAILED — Device provisioning failed. ``` --- # Rate limits (/docs/reference/rate-limits) # Rate limits Carrier campaign tier sets daily segments and MPS. AgentCell also enforces conversation streak, outbound caps, ignore lists, quiet hours, and 1 msg/sec/cell on pod cycle send. ## Campaign planning (your 10DLC tier) Throughput is roughly 2.25–225 MPS depending on campaign tier. | Campaign | Est. daily segments | | --- | --- | | Sole proprietor | ~3,000 | | Low volume | ~6,000 | | High volume standard | ~30,000 | | High volume highest trust | ~600,000 | ## API 429 / 422 codes | Code | Retriable | | --- | --- | | RATE_LIMITED | Yes — honor Retry-After | | CONVERSATION_STREAK_LIMIT | No — contact must reply | | OUTBOUND_LIMIT_REACHED | No — daily reset | | IGNORED_RECIPIENT | No | | TCPA_QUIET_HOURS | No — wait or queue_until_open | ## Common errors - `RATE_LIMITED` — Back off. - `CONVERSATION_STREAK_LIMIT` — Wait for inbound. ## Copy for Cursor / Claude ``` On RATE_LIMITED, read Retry-After and retry. Pod cycle send: max 1 message / second / cell. Quiet hours: TCPA_QUIET_HOURS on pod-scoped outbound. ``` --- # Pagination (/docs/reference/pagination) # Pagination Lists use HMAC page_token with limit in {10, 25, 50} (default 25). Offset is ignored. Message history may also use before/after time filters. ## Styles | Resource | Style | | --- | --- | | Cells, pods, conversations, activity, invoices | limit + page_token | | Messages | limit + page_token, optional before/after ISO 8601 | ```curl curl "https://api.agentcell.store/v1/cells?limit=25" -H "Authorization: Bearer $AGENTCELL_API_KEY" curl "https://api.agentcell.store/v1/cells?limit=25&page_token=TOKEN" -H "Authorization: Bearer $AGENTCELL_API_KEY" ``` ```typescript await client.cells.list({ limit: 25, pageToken: token }); ``` ```python client.cells.list(limit=25, page_token=token) ``` ```cli agentcell cells list --limit 25 --page-token TOKEN ``` ## Copy for Cursor / Claude ``` GET /cells?limit=25 GET /cells?limit=25&page_token=... Limits: 10, 25 (default), 50. Offset is not used. ``` --- # Idempotency (/docs/reference/idempotency) # Idempotency Pods, drafts, and cycle jobs use client_id in the JSON body. Cell create does not. Sends use the Idempotency-Key header (24h scope). ## Rules | Operation | Mechanism | | --- | --- | | Cell create | No client_id. display_name required. | | Creates (pods, webhooks, drafts) | client_id in body | | Sends (messages, cycle jobs) | Idempotency-Key header; cycle also accepts client_id | ```curl curl -X POST https://api.agentcell.store/v1/pods \ -H "Authorization: Bearer $AGENTCELL_API_KEY" \ -d '{"name":"acme","client_id":"pod-acme-v1"}' ``` ```typescript await agentcell.pods.create({ name: "acme", clientId: "pod-acme-v1" }); ``` ```python client.pods.create(name="acme", client_id="pod-acme-v1") ``` ```cli agentcell pods create --name acme --client-id pod-acme-v1 ``` ## Copy for Cursor / Claude ``` Do not pass client_id on POST /cells. Use client_id on pods, drafts, and cycle jobs. Use Idempotency-Key on sends. ``` --- # WebSockets (/docs/reference/websockets) # WebSockets AsyncAPI for WSS /v1/events/stream. Same event names as webhooks. ## Stream Connect to wss://api.agentcell.store/v1/events/stream with Authorization: Bearer. Download /docs/asyncapi.json for payload shapes. > **Download AsyncAPI** Machine-readable WebSocket catalog. > > /docs/asyncapi.json ## Copy for Cursor / Claude ``` wss://api.agentcell.store/v1/events/stream Authorization: Bearer $AGENTCELL_API_KEY Spec: https://agentcell.store/docs/asyncapi.json ``` --- # Changelog (/docs/changelog) # Changelog Reverse-chronological releases. Breaking API changes are marked. ## 0.1.0 — platform preview Initial public API, hosted MCP, CLI, SMS device radio, and this documentation site. - REST /v1: agent signup, cells, messages, conversations, drafts, lists, ignore lists, pods, cycle send, webhooks, billing, usage - SMS radio: POST /v1/device/claim, WSS /v1/device/ws, AGENTCELL-SETUP from the admin handset - Cell plan $229 setup / $79 monthly snapshotted on phone_orders; typically 1–72h wait - MCP at https://mcp.agentcell.store/mcp (agent-scoped; no admin radio tools) - CLI via npm i -g @agentcell/cli - SDKs: @agentcell/sdk and Python agentcell - Optional local MCP: npx -y @agentcell/mcp - Machine exports: llms.txt, OpenAPI, AsyncAPI | Area | Notes | | --- | --- | | Auth | Bearer API keys; console magic link + OTP | | Payments | Stripe, x402, MPP, prepaid | | Breaking | None yet — v0.1.0 is the baseline | ## Copy for Cursor / Claude ``` # AgentCell 0.1.0 REST https://api.agentcell.store/v1 MCP https://mcp.agentcell.store/mcp OpenAPI https://agentcell.store/docs/openapi.yaml ```