Skip to content
Compare

Phonekit vs building it yourself on a phone number API

You can rent a number from an SMS API provider, point its inbound webhook at your own service and have tests poll for the code. It works, and you then own that service. Here is the code a team usually writes, what it costs as of November 2026, and the Phonekit equivalent.

Phonekit team Updated

Compare the two

Build it yourselfPhonekit
NumbersRent from your SMS API provider; line type depends on what it offersMobile: real SIM line on a mobile network. Standard: US local VoIP
Receiving textsWrite and host a webhook, verify its requests and store messagesEvery text lands in the shared inbox
Reading a codeWrite the parsing and the wait your tests pollmark(), then waitForOtp() or waitForMessage()
Parallel testsDesign how runs avoid reading each other's codesOne number per worker, and only texts after the mark count
Coding agentsBuild and secure a tool for themAgent skill, MCP server, CLI, SDK and API
Who can read whatBuild keys and permissions yourselfEach number shared with chosen people, test keys and agents
Storing message textChoose encryption and retentionEncrypted, kept 30 days from receipt by default
Sending textsWhatever your provider allows, under its rulesClosed loop: your own numbers, teammates and recent senders, with daily limits

The code you write to build it

This is the usual minimum with an SMS API provider such as Twilio, using its twilio Node library. First, a webhook that checks each request really came from the provider and stores the text. Twilio signs every webhook with an X-Twilio-Signature header, sends the fields as a form post, and expects TwiML back.

server/sms-webhook.jsJavaScript
const express = require("express")
const twilio = require("twilio")
const { Pool } = require("pg")
 
const db = new Pool({ connectionString: process.env.DATABASE_URL })
const app = express()
app.use(express.urlencoded({ extended: false }))
 
app.post("/sms", async (req, res) => {
  const valid = twilio.validateRequest(
    process.env.TWILIO_AUTH_TOKEN,
    req.header("X-Twilio-Signature") ?? "",
    "https://sms-tests.example.com/sms", // the exact URL set on the number
    req.body
  )
  if (!valid) return res.status(403).end()
 
  await db.query(
    "insert into sms_messages (sid, to_number, body, received_at) values ($1, $2, $3, now())",
    [req.body.MessageSid, req.body.To, req.body.Body]
  )
  res.type("text/xml").send("<Response></Response>")
})
 
app.listen(3000)

Then a test helper that pulls the code out of the text and polls until a message newer than the request arrives, or gives up.

tests/helpers/sms-code.tsTypeScript
import { Pool } from "pg"
 
const db = new Pool({ connectionString: process.env["DATABASE_URL"] })
 
export function extractCode(body: string): string | null {
  return body.match(/\b(\d{4,8})\b/)?.[1] ?? null
}
 
export async function waitForSmsCode(to: string, after: Date, timeoutMs = 120_000) {
  const deadline = Date.now() + timeoutMs
  while (Date.now() < deadline) {
    const { rows } = await db.query(
      "select body from sms_messages where to_number = $1 and received_at > $2 order by received_at limit 1",
      [to, after]
    )
    const code = rows[0] ? extractCode(rows[0].body) : null
    if (code) return code
    await new Promise((resolve) => setTimeout(resolve, 1_000))
  }
  throw new Error(`No SMS code for ${to} within ${timeoutMs} ms`)
}
tests/sign-in.spec.tsTypeScript
const after = new Date()
await page.getByRole("button", { name: "Send code" }).click()
const code = await waitForSmsCode(process.env["TEST_PHONE_NUMBER"]!, after)

That is about 50 lines, and it still leaves work out:

  • A table and a host. The webhook needs a public HTTPS address that is up whenever a test runs, and the database needs the sms_messages table.
  • Clocks. after comes from the test machine and received_at from the database, so a few seconds of clock skew can make a test miss its code or read an old one.
  • Code formats. One regular expression won't fit every sender. A text with a date or an order number in it can match first.
  • Parallel runs. Two tests on one number can read the same code. You still need one number per worker, and a way to hand them out.
  • Access and retention. Who can read sms_messages, how long rows stay, and whether the bodies are encrypted are now yours to decide.

The same test with Phonekit

The webhook, the table, the parser and the poll are Phonekit's job. Your test keeps three lines around the action that sends the text.

tests/sign-in.spec.tsTypeScript
const after = await number.mark()
await page.getByRole("button", { name: "Send code" }).click()
const code = await number.waitForOtp({ after, timeoutMs: 120_000 })

mark() records a position on Phonekit's side, so the wait never compares two clocks. The Playwright guide has the complete test, and the SDK reference covers errors, timeouts and resends.

What each costs

Prices for building it yourself are from Twilio's US SMS pricing page as of November 2026. The rows without a price are real costs too, but they depend on your team.

ItemBuild it yourself (Twilio, US)Phonekit
PlanPay as you goStarter $29, Team $99 or Scale $199 a month
A US local number$1.15 a month per number3 Standard numbers included on Starter; each extra Standard number $5 a month
A real SIM lineDepends on what your provider offers1 Mobile line included on Starter; each extra Mobile line $15 a month
Receiving a text$0.0083 per segment inbound on a local number; Twilio notes extra carrier fees may applyMonthly pool included (150 Standard and 50 Mobile on Starter), then 2¢ Standard, 10¢ Mobile per message part
Hosting the webhookYour server or function, reachable whenever tests runIncluded
Database and retentionYour database, encryption and cleanupIncluded: encrypted, 30 days from receipt by default
Building itYour team's timeInstall the SDK
Keeping it runningYour team's time: monitoring, key rotation, fixing it when it breaks CIPhonekit's job
Agents and teammatesBuild the tool and the permissionsIncluded on every plan

Twilio also notes that US A2P 10DLC traffic is subject to registration onboarding fees.

On the number alone, building it yourself costs less. The service around the number is where the time goes.

Choose building it yourself when

  • You already run inbound SMS for your product and the webhook exists.
  • You need countries or number types Phonekit doesn't offer.
  • You want the whole pipeline inside your own infrastructure.
  • One test reads one number, with no agents or teammates involved.

Choose Phonekit when

  • You'd rather ask for the code than run a service to fetch it.
  • A service your product depends on only texts mobile numbers. Phonekit Mobile numbers are real SIM lines on a mobile network.
  • Coding agents need codes while they work on your own app, limited to the numbers you choose.
  • Tests run in parallel. Give each worker its own number and mark it before the text is sent.
  • Teammates need the same numbers. Everyone sees messages in one shared inbox, and numbers stay with the company when people leave.
  • You test a product that texts people. Phonekit can text your own numbers, your teammates' verified phones, anyone who texted you in the last 30 days and up to five numbers you add, at 10 texts a minute and 100 a day per number.

No number is accepted by every service. Phonekit is for testing and running your own product, not for verifying accounts on other services.

Sources

As of November 2026:

Questions

Can I keep my current SMS provider for sending?

Yes. Your app keeps sending its codes through the provider it already uses. Phonekit is the test number that receives the text and hands the code to your test or agent.

Is building it myself cheaper?

The number is. As of November 2026, Twilio lists a US local number at $1.15 a month and inbound SMS at $0.0083 per segment. The difference is the service around it: the webhook, storage, the wait, parallel runs, access and cleanup. Count the time to build and keep that running.

Do I get a real SIM line if I build it myself?

That depends on what your provider offers, so check the line type before you rely on it. Phonekit Mobile numbers are real SIM lines on a mobile network, and Standard numbers are US local VoIP. No number is accepted by every service.

Can agents use a number I built myself?

Yes, if you build the tool for them and decide what each agent can read. Phonekit agents read codes through the agent skill, MCP server, CLI, SDK or API, limited to the numbers you choose.

What happens to messages in Phonekit?

They are stored encrypted and kept 30 days from receipt by default. Releasing a number doesn't reset that clock.

Get a number.

Tell us what you're testing. We'll set up your workspace.

Get a number