Send and verify a code
Surge's verification API sends a six-digit code to a phone number and then checks the code the user provides. It handles code generation, delivery, retry limits, and expiration. You don't manage any of that yourself.
How it works
Verification is a two-step flow:
- Create a verification: Surge sends a six-digit code to the number
- Check the code: Surge validates the code the user entered
POST /verifications → creates the verification, sends the code
POST /verifications/{verification_id}/checks → validates the user's input1. Send the code
from surge import Surge
surge = Surge()
verification = surge.verifications.create(phone_number="+18015551234")
print(verification.id)import Surge from "@surgeapi/node";
const surge = new Surge();
const verification = await surge.verifications.create({
phone_number: "+18015551234",
});
console.log(verification.id);require "surge_api"
surge = SurgeAPI::Client.new
verification = surge.verifications.create(phone_number: "+18015551234")
puts verification.idclient = Surge.Client.new(System.get_env("SURGE_API_KEY"))
{:ok, verification} =
Surge.Verifications.create(client, %{phone_number: "+18015551234"})
IO.puts(verification.id)curl -X POST https://api.surge.app/verifications \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+18015551234"
}'{
"id": "vfn_01jrzhe8d9enptypyx360pcmxj",
"phone_number": "+18015551234",
"status": "pending",
"attempt_count": 0
}The user receives a text like: "Your verification code is 847291. It expires in 10 minutes."
2. Check the code
When the user submits the code in your UI, send it to Surge for verification:
result = surge.verifications.check("{verification_id}", code="847291")
print(result.result) # "ok", "incorrect", "expired", etc.const result = await surge.verifications.check("{verification_id}", {
code: "847291",
});
console.log(result.result);result = surge.verifications.check("{verification_id}", code: "847291")
puts result.result{:ok, result} =
Surge.Verifications.check(client, "{verification_id}", %{code: "847291"})
IO.puts(result.result)curl -X POST https://api.surge.app/verifications/{verification_id}/checks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "847291"
}'The response includes a result field indicating whether the verification succeeded:
{
"result": "ok",
"verification": {
"id": "vfn_01jrzhe8d9enptypyx360pcmxj",
"phone_number": "+18015551234",
"status": "verified",
"attempt_count": 1
}
}Limits
- Expiration: 10 minutes from creation. After that, the verification moves to
expiredstatus and all check attempts returnexpired. - Max attempts: 3 incorrect guesses per verification. After 3 incorrect attempts, the verification moves to
exhaustedstatus. Create a new verification and prompt the user to resend the code.
Result values
| Result | Meaning | What to do |
|---|---|---|
ok | Code matched | Grant access |
incorrect | Wrong code | Show an error, let the user retry |
expired | Code is more than 10 minutes old | Prompt user to request a new code |
already_verified | This verification was already approved | Treat as ok (idempotent) |
exhausted | 3 incorrect attempts reached | Create a new verification; do not retry the same one |
Putting it together
A complete flow in Python:
from surge import Surge
surge = Surge()
# User enters their phone number
response = surge.verifications.create(phone_number="+18015551234")
verification_id = response.id
# Later: user submits the code
check = surge.verifications.check(
id=verification_id,
code=user_submitted_code
)
match check.result:
case "ok" | "already_verified":
# Grant access
await grant_access(user)
case "incorrect":
return {"error": "Incorrect code. Please try again."}
case "expired":
return {"error": "Code expired. Request a new one."}
case "exhausted":
return {"error": "Too many attempts. Please request a new code."}For deliverability quirks and edge cases, see Usage patterns.