# Migrate from Twilio Verify

Source: https://joinsimplesms.com/docs/migrate-from-twilio
Index: https://joinsimplesms.com/llms.txt

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' });

// SimpleSMS
await fetch('https://api.joinsimplesms.com/v1/verify', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.SIMPLESMS_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') { /* ... */ }

// SimpleSMS
const res = await fetch('https://api.joinsimplesms.com/v1/verify/check', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.SIMPLESMS_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"

# SimpleSMS: pip install joinsimplesms
from joinsimplesms import SimpleSMS
sms = SimpleSMS()  # reads SIMPLESMS_API_KEY
sms.verify.send(to=phone)
ok = sms.verify.check(to=phone, code=code)["verified"]
```

## What maps to what

| Twilio | SimpleSMS |
| --- | --- |
| `to` | `to` (or `phone`, both work) |
| Account SID + Auth Token | one API key |
| Verify Service SID | nothing; no service to create |
| A purchased phone number | nothing; we send from our pool |
| `check.status === 'approved'` | `verified === true` |
| `status: 'pending'` | `verified: false`, `status: 'pending'` |
| 404 on bad code | `200` with `verified: false` (a wrong code isn't an exception) |
| Fraud Guard | Shield, always on |
| ~$0.05 per attempt | $0.025, only when verified |

## 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.** Twilio charges per verification attempt; 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 joinsimplesms` adds typed errors, retries, and the CLI.
- Sandbox has no resend cooldown so you can iterate; live enforces 60 seconds
  per number.
