Guide3 min read

How Agents Receive 2FA Codes With a Phone Number

Give each tenant agent a dedicated US phone number for inbound SMS — 2FA codes scoped per customer and readable through the governed Naïve /phone API.

Read the docs →

/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
/how /how /how /how /how /how /how /how /how /how
Guide
TL;DR
  • Agents that log into vendor portals hit SMS 2FA a shared team inbox leaks one customer's codes to another
  • Naïve provisions a real US number per tenant user via /phone; inbound SMS works immediately after provisioning
  • Codes arrive scoped to the number owner naive.forUser(clientId) ensures Client A's OTP never surfaces to Client B's agent
  • Agents with receive_sms permission read the inbox through the governed API, not a shared Google Voice tab
  • Pairs with /browser for no-API operators that need to complete login flows end to end
  • 10DLC registration runs at provisioning; inbound is live while outbound campaign approval is pending

A browser agent that can fill login forms still stops at the SMS gate. Banks, airlines, and legacy SaaS portals text a one-time code to a phone number — and if every customer shares one inbox, Customer A's OTP lands where Customer B's agent can read it. Naïve is the autonomous company infrastructure that gives agents real-world capabilities, and /phone is the primitive that gives each tenant agent its own US number for inbound SMS, including 2FA codes, scoped and readable through the governed API. Part of the autonomous company use-case cluster.

This guide shows how to provision per-tenant phone numbers for agents that operate vendor portals: inbound works immediately, messages stay isolated per user, and agents read codes with explicit receive_sms permissions. Pair with /browser for the full no-API login loop.

This tutorial connects to the Multi-Tenant Playbook, no-API operator, and Introducing /phone.

Why 2FA breaks multi-tenant agents

PhaseAgent-friendly?Needs a phone?
Navigate & submit loginYes — browser automationNo
Enter SMS OTPFails with shared inboxYes — inbound SMS
Complete sessionNeeds scoped credentialsYes — per-tenant identity

The failure mode is identity collapse: one number serves every customer, so verification codes are not scoped to the tenant user the agent represents. /phone fixes this by binding each number to one user and surfacing inbound SMS only to that user's workspace.

Architecture

Four pieces work together:

  • Users — one tenant user per customer.
  • Formation — LLC + EIN for carrier (10DLC) brand registration.
  • Phone — provision a US number; inbound SMS immediate.
  • Browser — optional, for portal login automation.

Setup

npm install @usenaive-sdk/server
import { Naive } from "@usenaive-sdk/server";
 
const naive = new Naive({ apiKey: process.env.NAIVE_API_KEY! });

Step 1 — Form the business (one-time prerequisite)

Phone provisioning requires a completed LLC and EIN for carrier brand registration:

naive verification start
naive formation submit

See formation docs for the full chain.

Step 2 — Create a tenant user and provision a number

const customer = await naive.users.create({
  external_id: "acme-portal-001",
  email: "ops@acme.com",
});
 
const client = naive.forUser(customer.id);
 
const { phone, campaign } = await client.phone.provision({
  ein: "12-3456789",
  area_code: "415",
  label: "Acme — vendor 2FA",
});
// phone.e164 is live for inbound SMS immediately

Each customer gets their own number. Inbound OTPs are stored against that number only.

Step 3 — Assign an agent with receive_sms permission

naive phone assign <phone-id> --agent-id <agent-uuid>

An agent without receive_sms is refused when it tries to read the inbox.

Step 4 — Poll for the OTP during login

When the browser agent submits credentials and the portal sends a code:

const messages = await client.phone.messages({ phoneId: phone.id, limit: 5 });
const latest = messages.items[0];
// Parse the OTP from latest.body and submit in the browser session

Subscribe to the sms.received webhook event for real-time flows instead of polling.

Step 5 — Release or revoke on offboarding

When the customer churns, release the number or revoke the agent session. A released number stops receiving new SMS permanently.

What this enables for portal operators

  • Vendor portals without APIs — complete SMS-gated login loops.
  • Per-customer isolation — OTPs never cross tenants.
  • Audit trail — inbound messages are stored and queryable per number.
  • Composes with /browser — the agent fills the form, reads the code, finishes login.

The agent still does the browser work. /phone is what makes the 2FA step real and scoped.

Related reading

Frequently Asked Questions
Can an AI agent receive SMS 2FA codes?+
Yes, when the agent controls a real inbound number. On Naïve, provision /phone for each tenant user. Vendor portals send OTPs to that number; the agent lists messages via the governed API and reads the code. Inbound SMS works immediately after provisioning. Each number is scoped to one user so codes never cross tenants.
Why not use a shared Google Voice number for agent 2FA?+
A shared inbox collapses identity. A code meant for Customer A's vendor login appears alongside Customer B's messages. Naïve gives each tenant user their own number and stores inbound SMS against that number only. Agents connect with explicit receive_sms permissions on the numbers they need.
How does multi-tenant 2FA work with agents?+
Create one tenant user per customer, provision a phone number bound to that user, and scope API calls with naive.forUser(customerId). The agent assigned to Customer A reads only Customer A's inbox. Revoke the agent or release the number when the engagement ends.
Does outbound SMS need to work for 2FA receive?+
No. Receiving OTPs is inbound-only. Inbound SMS is live the moment the number is provisioned on Naïve. Outbound SMS waits for 10DLC campaign approval, typically one to two business days, which does not block reading verification codes sent by vendor portals.
What is required before provisioning a phone number?+
US carrier registration requires a formed business and EIN per published docs. The chain is identity verification, LLC formation, then phone provision. Pass the EIN on first provision; subsequent numbers reuse the registered brand. See usenaive.ai/docs/getting-started/phone for the full quickstart.
NT
Naïve Team

Building the autonomous company infrastructure.

Keep reading