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
/loginonagentcell.store(redirects to/user) with their sign-up phone number.
Platform details: AGENTCELL_PLATFORM_SPEC.md | Product concept: AGENTCELL_CONCEPT.md
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.
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 | 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 |
Before starting, the agent should:
https://agentcell.store/docs/llms.txtnpx -y @agentcell/mcp) or CLI (npm i -g @agentcell/cli) or SDK (@agentcell/sdk / agentcell PyPI) + API accessReference platforms:
Install CLI or MCP (npm downloads Go binary):
npm i -g @agentcell/cli # exposes `agentcell` command
npx -y @agentcell/mcp # optional local stdio MCP
Or build from source (Go 1.23+):
go build -o agentcell ./cmd/cli
go build -o agentcell-mcp ./cmd/mcp
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:
# No auth yet — read docs
curl -s https://agentcell.store/docs/llms.txt | head -50
Goal: Obtain api_key and organization_id.
MCP tool: agent_sign_up
| Parameter | Required | Example |
|-----------|----------|---------|
| human_phone | Yes | +15551234567 |
| human_email | Yes | [email protected] |
| username | Yes | my-agent |
REST:
curl -X POST https://api.agentcell.store/v1/agent/sign-up \
-H "Content-Type: application/json" \
-d '{
"human_phone": "+15551234567",
"human_email": "[email protected]",
"username": "my-agent"
}'
Response:
{
"api_key": "ac_xxxxxxxx",
"organization_id": "org_xxxxxxxx",
"otp_sent_to": ["+15551234567", "[email protected]"]
}
CLI:
agentcell agent sign-up \
--human-phone +15551234567 \
--human-email [email protected] \
--username my-agent
Store securely: api_key, organization_id. Set AGENTCELL_API_KEY=ac_....
Notes:
example.com) rejected — use real emailFailures:
| Error | Fix | |-------|-----| | 400 validation | Check E.164 phone format | | 409 already exists | Call sign-up again to rotate key |
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:
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:
export AGENTCELL_API_KEY=ac_...
agentcell agent verify --otp-code 123456
Until verified:
NOT_VERIFIEDGoal: 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.
MCP tool: create_console_login_link
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_phonedelivery: "email" — sent to org human_emaildelivery: "none" — agent pastes URL in chatagentcell console login-link create --redirect-path /user/billing --delivery sms
Human visits https://console.agentcell.store/login:
human_phone (same E.164 used at sign-up)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.
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.
Choose one payment path:
MCP tool: create_setup_checkout
| Parameter | Description |
|-----------|-------------|
| tier | starter, pro, or enterprise |
| success_url | Redirect after payment |
| cancel_url | Redirect on cancel |
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.
MCP tools: get_payment_options, pay_with_x402
GET /billing/x402/requirements?intent=setup&tier=pro (or POST /cells and handle 402).402 + PAYMENT-REQUIRED header (USDC amount, network, destination).PAYMENT-SIGNATURE header.billing.x402.settled.# 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.
MCP tools: get_payment_options, pay_with_mpp
GET https://api.agentcell.store/.well-known/mppPOST /billing/mpp/pay with intent=setup, tier=pro → 402 Challenge.method=stripe) or Tempo stablecoin (method=tempo).billing.mpp.settled.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:
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:
See pricing and AGENTCELL_CONCEPT.md §6.
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:<claim> 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:
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:
agentcell cells get --cell-id cell_abc123
agentcell cells health --cell-id cell_abc123
# health is online when phones.ws_connected
CLI:
agentcell cells create --display-name "My Agent Cell" --area-code 415
Notes:
client_id on cell create (CLI has no --client-id on this command).create_cell again — each cell is another $150 + $50/mo on top of org $29.CELL_NOT_READY. Send with no connected radio → 503 CARRIER_UNAVAILABLE.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 |
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.
Reference: AgentPhone 10DLC guide 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.
Goal: Receive inbound messages in real time.
MCP tool: set_webhook
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:
{timestamp}.{raw_body} with secretX-Webhook-ID / event_idPer-cell webhook: Use set_cell_webhook to scope to one cell.
Connect to wss://api.agentcell.store/v1/events/stream with Bearer token.
Subscribe to cell.message.received. Pattern from AgentMail WebSockets.
agentcell cells messages list --cell-id cell_abc123 --labels unread
Mark processed messages with --add-labels read --remove-labels unread.
Test webhook:
agentcell webhooks test
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
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:
{
"to": "+15559876543",
"body": "Here is your photo.",
"media_urls": ["https://example.com/photo.jpg"]
}
CLI:
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 |
Goal: Handle inbound message and reply in thread.
Inbound webhook payload:
{
"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:
event_id not seen before)message.body or extracted_bodyPATCH .../messages/{id} with add_labels: ["read"], remove_labels: ["unread"]MCP: reply_message
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):
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):
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).
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:
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):
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):
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:
CLI:
agentcell usage daily --days 7
agentcell cells messages list --cell-id cell_abc123 --labels unread
| 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 |
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.
AgentCell billing differs from AgentMail (plan tiers) and AgentPhone (pay-as-you-go credits). Three rails: Stripe, x402, MPP.
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 |
| Item | Value |
|------|-------|
| Protocol | x402 v2 — 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 |
| Item | Value |
|------|-------|
| Protocol | Machine Payments 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 |
Agents can maintain a prepaid balance (x402/MPP deposits) instead of card-on-file:
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:
agentcell billing status
agentcell billing payment-options
curl https://api.agentcell.store/v1/billing/status -H "Authorization: Bearer $AGENTCELL_API_KEY"
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. Full spec: AGENTCELL_PLATFORM_SPEC.md §4.2b, §9.3. Page-level UI breakdown: AGENTCELL_FRONTEND_CONSOLE.md.
/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:
revoke_console_login_linkhuman_phone onlyAgentCell does not register 10DLC/A2P. The account owner completes compliance externally (Phase 6). The agent operates on the owner's behalf.
Must include:
Non-compliant messages may show as sent but never deliver.
When messaging settings 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).
Configure on the pod only. When tcpa_quiet_hours.enabled is true:
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.Preflight: GET /pods/{pod_id}/quiet-hours/check?to=+1... or MCP check_quiet_hours.
| 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:
client_id on pods, drafts, and cycle jobs — not on cell createIdempotency-Key within 24hNo idempotency on AgentPhone sends — AgentCell adds Idempotency-Key (AgentMail pattern). Always confirm failed sends didn't deliver before retry.
"""
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="[email protected]",
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"])
/**
* 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: "[email protected]",
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();
#!/bin/bash
set -e
# Phase 2–3
agentcell agent sign-up \
--human-phone +15551234567 \
--human-email [email protected] \
--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."
Before going live:
/login) — can view agent status & activitydevice_status: online)Idempotency-Key)pod_id when bypass intendedget_pod_cycle_job or pod.cycle.* webhooksget_usage / console)| Document | Purpose | |----------|---------| | AGENTCELL_PLATFORM_SPEC.md | API, MCP, CLI, PostgreSQL/GORM, DigitalOcean | | AGENTCELL_FRONTEND_CONSOLE.md | Console portal pages and wireframes | | AGENTCELL_CONCEPT.md | Product vision and pricing | | AGENTCELL_ADMIN.md | Operator portal, phone keys, physical device provisioning | | AGENTCELL_OWNERSHIP.md | Phone/SIM/number ownership, shipping, reinstatement, forfeiture | | AGENTMAIL_BREAKDOWN.md | AgentMail patterns (sign-up, idempotency) | | AGENTPHONE_BREAKDOWN.md | Carrier limits and webhook patterns (10DLC background; AgentPhone registers in-product, AgentCell does not) |
Follow https://agentcell.store/docs/agent-onboarding.md
Phases: discover → sign-up → verify → pay (Stripe|x402|MPP) → create_cell → user 10DLC → webhook/WSS → first send → first receive → operate.
MCP: https://mcp.agentcell.store/mcp
Keep AGENTCELL_API_KEY server-side.