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 yourself | Phonekit | |
|---|---|---|
| Numbers | Rent from your SMS API provider; line type depends on what it offers | Mobile: real SIM line on a mobile network. Standard: US local VoIP |
| Receiving texts | Write and host a webhook, verify its requests and store messages | Every text lands in the shared inbox |
| Reading a code | Write the parsing and the wait your tests poll | mark(), then waitForOtp() or waitForMessage() |
| Parallel tests | Design how runs avoid reading each other's codes | One number per worker, and only texts after the mark count |
| Coding agents | Build and secure a tool for them | Agent skill, MCP server, CLI, SDK and API |
| Who can read what | Build keys and permissions yourself | Each number shared with chosen people, test keys and agents |
| Storing message text | Choose encryption and retention | Encrypted, kept 30 days from receipt by default |
| Sending texts | Whatever your provider allows, under its rules | Closed 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.
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.
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`)
}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_messagestable. - Clocks.
aftercomes from the test machine andreceived_atfrom 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.
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.
| Item | Build it yourself (Twilio, US) | Phonekit |
|---|---|---|
| Plan | Pay as you go | Starter $29, Team $99 or Scale $199 a month |
| A US local number | $1.15 a month per number | 3 Standard numbers included on Starter; each extra Standard number $5 a month |
| A real SIM line | Depends on what your provider offers | 1 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 apply | Monthly pool included (150 Standard and 50 Mobile on Starter), then 2¢ Standard, 10¢ Mobile per message part |
| Hosting the webhook | Your server or function, reachable whenever tests run | Included |
| Database and retention | Your database, encryption and cleanup | Included: encrypted, 30 days from receipt by default |
| Building it | Your team's time | Install the SDK |
| Keeping it running | Your team's time: monitoring, key rotation, fixing it when it breaks CI | Phonekit's job |
| Agents and teammates | Build the tool and the permissions | Included 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:
- Twilio webhook security: the
X-Twilio-Signatureheader, andvalidateRequest(authToken, signature, url, params)in the Node library. - Twilio incoming message webhook: form-encoded fields including
MessageSid,From,ToandBody, with TwiML expected in reply. - Twilio SMS pricing for the US: $1.15 a month for a long code number, $0.0083 per inbound segment, carrier fees and A2P 10DLC registration fees.
- Phonekit pricing and the SDK reference.
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.
Keep reading
- One number per parallel testMake SMS tests repeatable: mark the inbox before you send, give each parallel worker its own number, budget the whole test and report the stage that failed.
- VoIP or real SIMA plain comparison of VoIP numbers and real SIM lines for SMS verification, with when to choose each, when to keep or release a number, and a checklist.
- SDK referenceInstall Phonekit's server-side SDK, capture message cursors, wait for OTPs, and handle deadlines and cancellation in JavaScript or TypeScript.
- Phonekit pricingPhonekit plans start at $29 a month per workspace, with Standard numbers and real SIM Mobile lines, the SDK, CLI, MCP server and API, and a shared inbox on every plan.