Primitive/phone5 min read

Introducing /phone: Give your AI agent a real phone number for SMS

Provision a real US phone number for any AI Employee — send and receive SMS — with carrier (10DLC) registration handled at provisioning. Inbound works immediately; outbound unlocks automatically on campaign approval.

Read the docs →

Primitive/phone
TL;DR
  • /phone provision a real US phone number for any Employee in one call
  • SMS in and out every user gets their own number, their own inbox, their own outbound
  • Carrier (10DLC) registration handled campaign registration is part of provisioning, not an afterthought
  • Inbound SMS works immediately; outbound unlocks automatically when the campaign is approved (~1-2 business days)
  • Per-user isolation + agent assignment numbers belong to a user; agents connect with send/receive permissions
  • Composes with /formation, /email, /cards the missing channel for agents that text customers or operate vendor portals

Today we're launching /phone — the primitive that gives an AI Employee a real US phone number for SMS. Send and receive text messages, with carrier (10DLC) registration handled at provisioning. Inbound works the moment the number is live; outbound unlocks automatically once the campaign is approved. The missing channel for agents that text customers, confirm orders, or operate portals that gate on a phone.

The problem: every real-world workflow eventually needs a phone

Confirming an order needs an SMS. Following up with a lead needs a text. Signing into a vendor portal sends a code to a number. The phone is one of the most under-recognized primitives in agent infrastructure — and the one most teams skip until they hit a wall.

The status quo:

  • A shared Google Voice the team rotates — every message lands in one inbox, nothing scopes per-agent or per-customer.
  • Raw Surge or Twilio — works, but you're now building the sub-account model, the 10DLC registration, the inbound storage, the billing, and an inbox UI from scratch.
  • A personal cell — a compliance and security disaster.

The reason it stays hard is that phone numbers are identity. A message sent to a number is meant for the entity that controls that number. If every user in your app shares one number, you've collapsed them all into one identity. That's how a one-time code meant for one customer ends up visible to another.

Until now. With /phone, every user has their own number, inbound is scoped to the number's owner, and the carrier-level compliance work (10DLC campaign registration) is done at provisioning.

How /phone works

The workflow is three steps:

  1. Provisionnaive phone provision --ein <ein> creates a Surge sub-account from the user's formed business, registers the 10DLC campaign, and buys a US number bound to the user.
  2. Send / Receive — the Employee calls send and reads its inbox through its API key. Inbound SMS surfaces as runtime objects scoped to the number.
  3. Compose — pair with /formation (the prerequisite that registers the brand), /email for cross-channel, or /cards for the full operate-a-business stack.

Prerequisite: provisioning requires a completed LLC formation and the company's EIN — numbers are carrier-registered against your business entity. The chain is naive verification startnaive formation submitnaive phone provision.

Two ways to provision a number: CLI or API

1. CLI

naive phone provision \
  --ein 12-3456789 \
  --area-code 415 \
  --label "Support"

The CLI provisions the number, registers the 10DLC campaign, and returns the assigned number — live for inbound SMS immediately.

2. API

const res = await fetch("https://api.usenaive.ai/v1/phone/provision", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.NAIVE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    ein: "12-3456789",
    area_code: "415",
    label: "Support",
  }),
});
 
const { phone, campaign } = await res.json();
// phone.e164 is live for inbound; campaign.status drives outbound

Carrier registration completes asynchronously (typically 1-2 business days). Until it's approved, outbound SMS returns compliance_pending — no message is sent and no credit is charged. Check progress any time with naive phone status.

Sending and receiving SMS

Once the campaign is approved, send from the number:

naive phone send \
  --from <phone-id> \
  --to "+14155551234" \
  --body "Your order shipped — track it at https://..."

Inbound texts are captured automatically and scoped to the number's owner. List and read them:

# Newest first, cursor-paginated
naive phone messages <phone-id>
 
# Full body + any media URLs
naive phone read <message-id>

You can also subscribe to the sms.received webhook to react to inbound messages in real time — the same runtime event model as inbound email.

10DLC, handled at provisioning

US carriers require A2P (application-to-person) campaigns to be registered before a number can send. Skipping this turns into delivered-but-not-actually-delivered messages — the silent killer of agent SMS.

Every /phone provision registers the campaign as part of the flow, using the user's formed-business details and EIN. Outbound stays gated behind the Naïve runtime until the campaign is approved, at which point the capability flips on automatically — no webhook for you to wire, no manual step.

# The carrier-registration pipeline: account → campaign → numbers
naive phone status

Per-user isolation and agent assignment

A number is a resource that belongs to a user; agents connect to it with explicit permissions:

# Connect an agent with send + receive permission
naive phone assign <phone-id> --agent-id <agent-id>
 
# See who's connected
naive phone assignments <phone-id>

Inbound SMS is scoped to the owning user's number — codes and replies are only ever visible within that user's workspace, never mixed with another user's numbers. An agent without send_sms on a number is refused when it tries to send. This is the same trust model as a real person's phone, enforced at the runtime layer rather than as a developer convention.

Release and billing, built in

Numbers are metered on the same credit balance as every other primitive: a one-time carrier registration on your first number, the monthly rental per number, and per-segment outbound SMS. Inbound is free. When you're done with a number, release it — that frees it back to the carrier and stops the rental:

naive phone release <phone-id>

What you can build with /phone

Send order and appointment updates — Provision a number per business, send confirmations and reminders. Inbound replies surface as runtime objects the agent can triage.

Run an autonomous SMS support line — Inbound texts route to the owning user's number; the support Employee reads, classifies, and replies — composed with /email for cross-channel cases.

Operate portals that gate on a phone — Many vendor and bank portals require a number on file. Provision one per user, pair with /browser, and the agent can complete signups that need a real number.

Give every customer their own number — With per-user isolation, each of your end-customers gets a dedicated number and inbox — scoped via forUser(id), billed on one balance.

Get started

Drop this starter prompt into any coding agent to wire up Naïve:

Read https://usenaive.ai/skill.md and use it to set up Naïve in my project.

Frequently Asked Questions
What is /phone?+
/phone is the Naïve primitive that provisions a real US phone number for an AI Employee and lets it send and receive SMS. It's backed by Surge and includes carrier (10DLC) campaign registration as part of provisioning. Each number belongs to one user; inbound messages are captured and scoped to that user's number.
How does /phone work?+
Run `naive phone provision --ein <ein>` (or POST /v1/phone/provision). Naïve creates a Surge sub-account from the user's formed business, registers the 10DLC campaign, and buys a US number. Inbound SMS surfaces as runtime objects on the number. Outbound SMS is sent via `naive phone send`. Outbound is gated until the carrier campaign is approved — Naïve flips it on automatically when that happens.
Why does outbound SMS not work immediately?+
US carriers require an approved 10DLC campaign before a number can send application-to-person SMS. Inbound works the moment the number is provisioned, but outbound is gated until the campaign is approved (typically 1-2 business days). The approval is automatic — `naive phone send` returns `compliance_pending` (no message sent, no charge) until then, and unlocks on its own.
Why does provisioning need an EIN?+
The EIN registers the carrier brand for 10DLC. Numbers are registered against your formed business, so provisioning requires a completed LLC formation and the company's EIN. You pass it on the first provision; subsequent numbers reuse the registered brand.
How are numbers isolated across users?+
Each user (tenant) gets their own number. Inbound SMS is captured and scoped to the number's owning user, so messages — including one-time codes — are only ever visible within that user's workspace, never mixed with another user's numbers. Agents are connected to a number with explicit send_sms / receive_sms permissions.
How much does /phone cost?+
Numbers are billed per-month (a one-time carrier registration applies to your first number), and outbound SMS is billed per segment. Inbound SMS is free. Release a number with `naive phone release` to stop rental billing. See the docs for current credit amounts.
What's the difference between /phone and using Surge or Twilio directly?+
Raw CPaaS providers leave you to build the sub-account model, 10DLC registration, per-user routing, inbound storage, billing, and an inbox UI yourself. /phone wraps the provider in the Naïve runtime: numbers bind to users, agents connect with permissions, 10DLC happens at provisioning, inbound SMS is stored and queryable, and everything is metered on one credit balance. Naïve uses Surge as the underlying carrier.
How do I get started with /phone?+
Run `naive phone provision --ein 12-3456789 --area-code 415`. Naïve provisions a number, registers the 10DLC campaign, and returns the number — live for inbound SMS immediately. The full quickstart is at usenaive.ai/docs/getting-started/phone.
M
MyxEngineering

Engineering at Naïve. Owns the comms primitives.

@myx_naive
Keep reading