---
title: "Verify a code sent to a phone number"
method: POST
path: "/phone_numbers/{phone_number_id}/attempt_verification"
tags: ["Phone Numbers"]
---

# Verify a code sent to a phone number

`POST /phone_numbers/{phone_number_id}/attempt_verification`

## Request

- Base URL: `https://api.clerk.com/v1`
- URL: `https://api.clerk.com/v1/phone_numbers/{phone_number_id}/attempt_verification`
- Auth: HTTP bearer

Checks a one-time code against the verification identified by
verification_id, and returns the verification with its updated status
(`verified`, `unverified`, `expired`, or `failed`) and attempt count, so a
backend driving its own frontend can react on every attempt — an incorrect
or expired code is reported through the status, not as an error. Resubmitting
a verification whose code was already accepted is rejected with a
`verification_already_verified` error. If the code for this verification
could not be sent (the SMS provider refused or failed the send after
prepare_verification had returned), the attempt is rejected with a
`verification_code_not_sent` error and no attempt is counted; call
prepare_verification again to send a new code. If too many codes have been
checked for this phone number recently, the attempt is rejected with a
`verification_code_too_many_attempts` error and no attempt is counted; the
limit is per phone number, so wait for the `Retry-After` period rather than
sending a new code. If the code
is correct and the phone number is not already verified, it is also marked
as verified as a side effect (just as it would be in a frontend verification
flow); an already verified phone number is left unchanged. It never creates
a session; to sign the user in afterwards, mint a sign-in token.

## Path parameters

- `phone_number_id` string, required

## Request body

- object
  - `verification_id` string, required: The ID of the verification to check, such as one returned by prepare_verification
  - `code` string, required: The verification code that was sent to the phone number

## Response `200`

The verification. After prepare_verification it is pending (unverified);
after attempt_verification its status reflects the attempt outcome.

- object
  - `object` string: The type of the verification object.
  - `id` string: The ID of the verification. Pass this to attempt_verification.
  - `status` string: The status of the verification (unverified, verified, expired, or failed).
  - `strategy` string: The verification strategy (email_code or phone_code).
  - `attempts` integer, nullable: The number of attempts made against this verification.
  - `expire_at` integer, nullable: Unix timestamp (milliseconds) at which the code expires.
  - `channel` string, nullable: The channel the code was sent over (phone numbers only).

## Other responses

- `400`: Request was not successful
- `401`: Authentication invalid
- `403`: Authorization invalid
- `404`: Resource not found
- `422`: Request was not successful
- `429`: Request was not successful
- `500`: Request was not successful

## Changes

- **2026-09-18** (2025-11-10) `1f66c253acac`: 2 info
  - added the non-success response with the status `422`
  - added the non-success response with the status `429`
- **2026-06-04** (2025-11-10) `4db0b9e2d5e6`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/clerk/apis/clerk-backend-api/changes/phone_numbers/:phone_number_id/attempt_verification/post.md)

---

[API](https://skmtc.dev/clerk/apis/clerk-backend-api.md) - [All operations](https://skmtc.dev/clerk/apis/clerk-backend-api/llms.txt) - [OpenAPI document](https://skmtc.dev/clerk/apis/clerk-backend-api/revisions/a490313abffa?raw)
