> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surge.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Send and verify a code

> Send a six-digit OTP to a phone number and verify the user's code with two API calls, expiry and retry limits included.

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. But the biggest benefit to using Surge Verifications is that codes are sent through our registered numbers, so you don't need to register a campaign, toll-free number, or sender ID to be able to send verification codes.

## How it works

Verification is a two-step flow:

1. **Create a verification**: Surge sends a six-digit code to the number
2. **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 input
```

## 1. Send the code

<CodeGroup>
  ```python Python theme={null}
  from surge import Surge

  surge = Surge()

  verification = surge.verifications.create(phone_number="+18015551234")
  print(verification.id)
  ```

  ```typescript TypeScript theme={null}
  import Surge from "@surgeapi/node";

  const surge = new Surge();

  const verification = await surge.verifications.create({
    phone_number: "+18015551234",
  });
  console.log(verification.id);
  ```

  ```ruby Ruby theme={null}
  require "surge_api"

  surge = SurgeAPI::Client.new

  verification = surge.verifications.create(phone_number: "+18015551234")
  puts verification.id
  ```

  ```elixir Elixir theme={null}
  client = Surge.Client.new(System.get_env("SURGE_API_KEY"))

  {:ok, verification} =
    Surge.Verifications.create(client, %{phone_number: "+18015551234"})

  IO.puts(verification.id)
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.surge.app/verifications \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "phone_number": "+18015551234"
    }'
  ```
</CodeGroup>

```json theme={null}
{
  "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:

<CodeGroup>
  ```python Python theme={null}
  result = surge.verifications.check("{verification_id}", code="847291")
  print(result.result)  # "ok", "incorrect", "expired", etc.
  ```

  ```typescript TypeScript theme={null}
  const result = await surge.verifications.check("{verification_id}", {
    code: "847291",
  });
  console.log(result.result);
  ```

  ```ruby Ruby theme={null}
  result = surge.verifications.check("{verification_id}", code: "847291")
  puts result.result
  ```

  ```elixir Elixir theme={null}
  {:ok, result} =
    Surge.Verifications.check(client, "{verification_id}", %{code: "847291"})

  IO.puts(result.result)
  ```

  ```bash cURL theme={null}
  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"
    }'
  ```
</CodeGroup>

The response includes a `result` field indicating whether the verification succeeded:

```json theme={null}
{
  "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 `expired` status and all check attempts return `expired`.
* **Max attempts:** 3 incorrect guesses per verification. After 3 incorrect attempts, the verification moves to `exhausted` status. 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:

```python theme={null}
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](./usage-patterns).
