---
title: "Verify (OTP)"
description: "Confirm a user owns a phone number by sending a one-time code and checking it."
---

> Documentation Index
> Fetch the complete documentation index at: https://dev.touchsms.com.au/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify (OTP)

The Verify API sends a one-time code to a phone number by SMS and checks the code your user enters. Use it to confirm a number at sign-up, or as a second factor at login. We generate, send, store and expire the codes, so you don't have to.

The Verify endpoints are under `/v3`, for example `https://app.touchsms.com.au/api/v3/verify`.

## The flow

1. **Create a verification**

   Call [Create verification](/api/verify/create/) with the user's number. We generate a code and send it by SMS.

   ```sh
   curl -u "ACCESS_TOKEN:TOKEN_ID" \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -X POST https://app.touchsms.com.au/api/v3/verify \
     -d '{ "destination": "+61412345678" }'
   ```

   Store the returned `id` (a UUID) against the user's session. You need it to check the code.
2. **The user enters the code**

   Ask the user for the code they received.
3. **Check the code**

   Call [Check verification](/api/verify/check/) with the stored `id` and the code the user entered:

   ```sh
   curl -u "ACCESS_TOKEN:TOKEN_ID" \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -X POST https://app.touchsms.com.au/api/v3/verify/VERIFY_ID/verify \
     -d '{ "code": "123456" }'
   ```

## Reading the result

The check always returns the verification. Use `verify_success` to decide whether the user passed:

```json
{
  "data": {
    "id": "cb4d9527-1d3a-4def-85ba-f1fec9489c4f",
    "status": "verified"
  },
  "verify_success": true,
  "verify_error": null
}
```

When the check fails, `verify_success` is `false` and `verify_error` says why:

```json
{
  "data": {
    "id": "cb4d9527-1d3a-4def-85ba-f1fec9489c4f",
    "status": "sent"
  },
  "verify_success": false,
  "verify_error": "Provided code does not match verify"
}
```

| `verify_error` | Meaning |
|---|---|
| `Provided code does not match verify` | Wrong code. The user can try again until the verification expires. |
| `Verify expired before check request` | The code expired. Create a new verification. |
| `Verify cancelled before check request` | The verification was cancelled. |
| `Verify already used` | The code was already checked successfully. Each verification can be used once. |

A verification's `status` is one of `created`, `sent`, `verified`, `expired`, `failed` or `cancelled`.

## Options

[Create verification](/api/verify/create/) accepts optional fields to tailor the message and the code:

| Field | Default | Notes |
|---|---|---|
| `source` | Your account's default sender | Sender ID for the SMS. It must be a verified sender ID on your account. |
| `template` | `Your verification code is %code%` | Message text. Must contain `%code%`. |
| `expiry_seconds` | `300` | How long the code stays valid, from 10 to 3600 seconds. |
| `token_length` | `6` | Code length, from 6 to 10 characters. |
| `metadata` | | Key/value pairs to store with the verification, such as the action the user was attempting. Same limits as [message metadata](/guides/sending-messages/#metadata). |

## Check or cancel by destination

You can also [check a code by destination number](/api/verify/check-by-destination/) instead of by `id`. This checks the most recent verification for that number.

> **Caution**
>
> Prefer checking by `id`. If you check by destination, make sure two users can't verify the same number at the same time, and that a user can't change their number partway through the flow.

To stop a code from being used, [cancel the verification](/api/verify/cancel/), or [cancel every verification for a number](/api/verify/cancel-by-destination/). For example, cancel outstanding codes when a user asks for a new one.

## Protect the flow

The API doesn't limit how many times a code can be checked before it expires, so add your own limits:

- Limit how many codes one user, number or IP address can request in a period.
- Limit wrong guesses per verification, then cancel it and make the user request a new code.
- Put a CAPTCHA or similar in front of the "send code" step on public forms.

See [Security](/concepts/security/).

Source: https://dev.touchsms.com.au/guides/verify/index.mdx
