Send a verification code
POST /v1/verify
Generates a one-time code, sends it, and enforces expiry, attempt limits and anti-pumping controls. You do NOT need to own a phone number; SimpleSMS sends from its own verification pool. Nothing is billed here; a verification is charged only when the code is checked successfully. Supports the Idempotency-Key header, so a retried request does not text a second code.
Send your API key as a bearer token: Authorization: Bearer ssms_sk_.... Test keys run this endpoint against the sandbox; see Authentication for key modes and scopes.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | No | Makes retries safe for 24 hours: the same key and body replays the original successful response (with Idempotent-Replayed: true); a different body returns 409. Failed requests release the key. At most 255 characters. |
Request body
JSON (Content-Type: application/json).
| Field | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | Example: +14155550132. |
app_name | string | No | Your product name, shown in the message At most 24 characters. |
from | string | No | Optional: send from a number you own instead of the SimpleSMS pool |
customer_id | string | No | Optional: attribute the verification to one of your customers |
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Verification created | Verification |
| 401 | Missing, malformed, or revoked API key | Error |
| 402 | Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged | Error |
| 403 | Blocked by Shield (charged: false) | Error |
| 409 | Idempotency conflict | Error |
| 429 | Rate limit or quota exceeded | Error |
201: Verification fields
| Field | Type | Description |
|---|---|---|
id | string | Example: ver_a1B2c3D4e5F6g7H8. |
object | verification | |
phone | string | |
status | pending, approved, expired, max_attempts, blocked | |
attempts | integer | |
test | boolean | |
charged | boolean | |
created_at | string (date-time) | |
expires_at | string (date-time) | |
customer_id | string | The customer this is attributed to. Present only when set. |
Errors
| Status | When |
|---|---|
| 401 | Missing, malformed, or revoked API key |
| 402 | Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged |
| 403 | Blocked by Shield (charged: false) |
| 409 | Idempotency conflict |
| 429 | Rate limit or quota exceeded |
Every error has the same JSON shape, and request_id matches the X-Request-Id response header. Errors lists every code and what to do about it.
{
"error": {
"code": "invalid_request",
"message": "What went wrong, in plain words.",
"param": "the_field",
"request_id": "req_a1B2c3D4e5F6g7H8"
}
}Idempotency
Send an Idempotency-Key header to make retries safe. For 24 hours the same key with the same body replays the original successful response (with Idempotent-Replayed: true); the same key with a different body returns 409. A failed request releases its key.
Examples
curl
curl -X POST "https://api.joinsimplesms.com/v1/verify" \
-H "Authorization: Bearer $SIMPLESMS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2f0e-order-1042" \
-d '{
"phone": "+14155550132"
}'Node.js
import { SimpleSMS } from 'joinsimplesms'; // npm install joinsimplesms
const sms = new SimpleSMS(process.env.SIMPLESMS_API_KEY);
const verification = await sms.verify.send({ to: '+14155550132' });
console.log(verification.id, verification.status);Python
import os
from joinsimplesms import SimpleSMS # pip install joinsimplesms
client = SimpleSMS(os.environ["SIMPLESMS_API_KEY"])
verification = client.verify.send("+14155550132")
print(verification["id"], verification["status"])