Migrate from Twilio Verify

Two calls become two calls. The main differences: no Verify Service to create, no phone number to buy, and you're billed only when a code actually verifies.

Send a code

js
// Twilio
await twilio.verify.v2.services(SERVICE_SID)
  .verifications.create({ to: phone, channel: 'sms' });

// TextKit
await fetch('https://api.jointextkit.com/v1/verify', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.TEXTKIT_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ to: phone }),
});

Check a code

js
// Twilio
const check = await twilio.verify.v2.services(SERVICE_SID)
  .verificationChecks.create({ to: phone, code });
if (check.status === 'approved') { /* ... */ }

// TextKit
const res = await fetch('https://api.jointextkit.com/v1/verify/check', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.TEXTKIT_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ to: phone, code }),
});
const { verified } = await res.json();
if (verified) { /* ... */ }

In Python

python
# Twilio
client.verify.v2.services(SERVICE_SID).verifications.create(to=phone, channel="sms")
check = client.verify.v2.services(SERVICE_SID).verification_checks.create(to=phone, code=code)
ok = check.status == "approved"

# TextKit: pip install jointextkit
from jointextkit import TextKit
sms = TextKit()  # reads TEXTKIT_API_KEY
sms.verify.send(to=phone)
ok = sms.verify.check(to=phone, code=code)["verified"]

What maps to what

TwilioTextKit
toto (or phone, both work)
Account SID + Auth Tokenone API key
Verify Service SIDnothing; no service to create
A purchased phone numbernothing; we send from our own numbers
check.status === 'approved'verified === true
status: 'pending'verified: false, status: 'pending'
404 on bad code200 with verified: false (a wrong code isn't an exception)
Fraud GuardShield, always on

Bring your opt-outs

If you also send messages (not only verification codes) from Twilio, export the numbers that opted out there and import them before you send from TextKit. Twilio does not provide its opt-out list through an API, so it is a file you export and upload: one phone number per row, with or without a header. Console → Numbers → Bring your numbers → Bring your opt-outs, or POST /v1/consent/import. The console import of numbers, pools and webhooks does not run until this is done, or until you confirm you have no opt-outs to carry over. Details: Bring your opt-outs.

Things that get simpler

  • No Verify Service. Delete the VA... SID from your config.
  • No number. Delete the number provisioning step entirely.
  • Billing follows success. We charge when verified comes back true, so pumping attacks and abandoned signups cost you nothing.
  • Attempt/expiry state is in the response. attempts_remaining and expires_in let you render "2 tries left" and a countdown without tracking anything yourself.

Things to watch

  • US and Canada only right now. If you verify internationally, keep Twilio for those routes or talk to us.
  • The SDK is optional. The plain fetch calls above are the whole integration; npm install jointextkit adds typed errors, retries, and the CLI.
  • Sandbox has no resend cooldown so you can iterate; live enforces 60 seconds per number.