# Generate and validate 2FA / MFA codes via SMS

## SMS Verification Overview

SMS verification is a form of two-factor authentication (2FA) where a user receives a one-time code via SMS to verify access to an application. SMS verification ensures that even if a password is stolen, an attacker would also need access to the user's phone to complete a login request.

The [Mobile Text Alerts SMS Verification API](/api-reference/verify.md) provides an easy way to manage SMS multi-factor authentication workflows within your application. These endpoints provide verification code generation, SMS message delivery, and validation of user-supplied codes.

<figure><img src="/files/VGQjsciobZKChV9CRpLb" alt=""><figcaption><p>Why use SMS verification?</p></figcaption></figure>

## Steps for SMS validation with the Mobile Text Alerts API

1. [Generate an SMS verification code](#generate-sms-verification-code) to send to your user’s phone number by calling the [/send-code endpoint](#post-verify-sms-send-code).
2. If the phone number is valid, the user receives a code and enters it into your application.
3. You then [send this code to Mobile Text Alerts to validate it](#validate-sms-code) via the [/check-code endpoint](#post-verify-sms-check-code).
4. Mobile Text Alerts returns the [Verification Status](#verification-status) for the request. You can use this status to allow or deny access to your application.

## Generate and send SMS verification code

One-time verification codes can be generated and sent to a user by calling the [send verification code endpoint](#post-verify-sms-send-code) detailed below.

The user will receive a message in this format:

<pre><code>Your ${<a data-footnote-ref href="#user-content-fn-1">serviceName</a>} verification code is: ${code}
</code></pre>

Optional request parameters allow you to customize the verification code, such as how long it will be valid and the code's length.

Successful responses include an identifying `verificationId` that can be used to check the validity of a user-supplied code after it has been sent.

### Send Verification Code endpoint [`verify/sms/send-code`](/api-reference/verify.md#post-verify-sms-send-code)

#### Duplicate Requests (Idempotency)

This endpoint is idempotent, which means it deduplicates requests when network retries or other issues leave the request state unclear.

When sending your request to the endpoint, include an `X-Request-Id` header with an alphanumeric value. If you're unsure whether the request succeeded, you can resend the request with the same `X-Request-Id`, and the send endpoint will detect whether it's a duplicate.

For duplicate requests, a `409 Conflict` error is returned.

#### Request Fields

<table><thead><tr><th width="175">Name</th><th width="114.5">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>to</code> - <em>required</em></td><td><code>string</code></td><td>The destination phone number to send the SMS verification code to. A <a href="/pages/blyzOtVQ7uz9KxWfaCuB#recommended-format-e.164">E.164 formatted</a> string is recommended.</td></tr><tr><td><code>serviceName</code> - <em>required</em></td><td><code>string</code></td><td>A customer-friendly name for your service that will be included in the verification message.</td></tr><tr><td><code>timeoutSeconds</code></td><td><code>number</code></td><td><p>The number of seconds the verification code will be considered valid. Mobile Text Alerts stores a timestamp for this timeout and returns an <code>EXPIRED</code> status if a code is checked after the timeout period has elapsed.</p><p>Default: <code>300</code></p></td></tr><tr><td><code>codeLength</code></td><td><code>number</code></td><td><p>Determines the length of the generated code. <code>codeLength</code> must be between 4 and 8 digits.</p><p>Default: <code>6</code></p></td></tr><tr><td><code>externalId</code></td><td><code>string</code></td><td>The <a href="/pages/MLJ7XFyQW5wVegwth8qV#include-the-externalid-parameter"><code>externalId</code> parameter</a> is an optional field for creating your own identifier for each message request. The <code>externalId</code> will be included in webhook notifications.</td></tr><tr><td><code>realtime</code></td><td><code>boolean</code></td><td>Indicates the number should be validated using the number verification service.</td></tr><tr><td><code>bypass</code></td><td><code>boolean</code></td><td>Indicates if you want to bypass number validation. If <code>true</code>, a <a href="#risk-types">risk type</a> will be returned in the response.</td></tr><tr><td><code>longcodeId</code></td><td><code>number</code></td><td>The ID for a longcode to use when sending the verification code.</td></tr><tr><td><code>tags</code></td><td><code>object</code></td><td>A set of properties to attach to the outgoing message. These are for internal use only.</td></tr><tr><td><code>poolId</code></td><td><code>number</code></td><td>The ID for a pool of longcodes to use when sending the verification code.</td></tr></tbody></table>

#### Response Fields

<table><thead><tr><th width="174.5">Name</th><th width="114.5">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>to</code></td><td><code>string</code></td><td>The destination number supplied in the request.</td></tr><tr><td><code>timeoutSeconds</code></td><td><code>number</code></td><td>The number of seconds the verification code will be considered valid. This value is provided in the request.</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td><p>The type of verification code.</p><p>For this response, this value will always be <code>sms</code>.</p></td></tr><tr><td><code>status</code></td><td><code>enum</code></td><td>An <code>enum</code> value representing the current status of this verification request. See <a href="#verification-status">Verification Status</a> for possible status codes.</td></tr><tr><td><code>verificationId</code></td><td><code>string</code></td><td>A unique identifier for this verification request. Use it to check code validity for the associated request.</td></tr><tr><td><code>messageId</code></td><td><code>string</code></td><td><p>An additional identifier you can use to correlate webhook events triggered by this verification message.</p><p>Webhook event payloads for <a href="/pages/haN1dBJk3ZMcTJDCyH96#message-send"><code>message-send</code></a> and <a href="/pages/haN1dBJk3ZMcTJDCyH96#delivery-status"><code>delivery-status</code></a> include this <code>messageId</code> in the <code>tags</code> array.</p></td></tr><tr><td><code>deliverable</code></td><td><code>boolean</code></td><td>Indicates whether the number is valid.</td></tr><tr><td><code>reason</code></td><td><code>string</code></td><td>If the number is invalid, this contains additional information from the verification service.</td></tr><tr><td><code>carrier</code></td><td><code>string</code></td><td>The carrier identified for the <code>to</code> number.</td></tr><tr><td><code>lineType</code></td><td><code>string</code></td><td><p>The phone number type identified for the <code>to</code> number.</p><p>Possible values: <code>landline</code>, <code>mobile</code>, <code>tollfree</code>, <code>satellite</code>, <code>voip</code>, <code>premium</code>, <code>pager</code>, <code>unknown</code></p></td></tr><tr><td><code>risk</code></td><td><code>string</code></td><td>Any validation issues with the number. Only present when <code>bypass</code> is <code>true</code>. See <a href="#risk-types">Risk Types</a> below.</td></tr></tbody></table>

#### Risk Types

This endpoint will return a risk type when the `bypass` field in the request is set to `true`.

**Possible values:**

* `invalid_format`
  * The phone number format itself is invalid.
* `deny_list`
  * On our internal deny list pulled from delivery reports.
* `unsubscribed`
  * Marked as unsubscribed for your account.
* `suspended`
  * The number has been temporarily suspended by the carrier.
* `deactivated`
  * The number has been deactivated by the carrier.
* `landline`
  * The number is a landline number.
* `voip`
  * The number is a VoIP (Voice-over-IP) number.
* `unknown`
  * The number is invalid, but the reason is unknown.

## POST /verify/sms/send-code

> Trigger SMS Verification Code

```json
{"openapi":"3.0.0","info":{"title":"Mobile Text Alerts API","version":"8.0.0"},"servers":[{"url":"https://api.mobile-text-alerts.com/v3"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"apiKey"}},"schemas":{"SendCode.Response":{"type":"object","properties":{"to":{"type":"string"},"timeoutSeconds":{"type":"number"},"type":{"type":"string","enum":["sms"]},"status":{"$ref":"#/components/schemas/VerifyCodeStatus"},"messageId":{"type":"string"},"verificationId":{"type":"string"},"deliverable":{"type":"boolean"},"reason":{"type":"string"},"carrier":{"type":"string"},"lineType":{"type":"string"},"risk":{"allOf":[{"$ref":"#/components/schemas/VerifyRisk"},{"$ref":"#/components/schemas/NullReference"}]}},"required":["to","timeoutSeconds","type","status","messageId","verificationId","deliverable","reason","carrier","lineType"]},"VerifyCodeStatus":{"type":"string","enum":["PENDING","APPROVED","EXPIRED","INVALID"]},"VerifyRisk":{"type":"string","enum":["invalid_format","deny_list","unsubscribed","suspended","deactivated","landline","voip","unknown"]},"NullReference":{"title":"Null Reference","description":"Used when a reference can be null","enum":[null]},"SendCode.Request":{"type":"object","properties":{"to":{"type":"string"},"serviceName":{"type":"string"},"timeoutSeconds":{"type":"number"},"codeLength":{"type":"number"},"realtime":{"type":"boolean"},"bypass":{"type":"boolean"},"externalId":{"type":"string"},"longcodeId":{"type":"number"},"tags":{"type":"object","additionalProperties":{"type":"string"}},"poolId":{"type":"string"},"senderName":{"type":"string"},"gated":{"type":"boolean"}},"required":["to"]}},"responses":{"BadRequestError":{"description":"BadRequestError","content":{"application/json":{"schema":{"type":"object","properties":{"httpCode":{"type":"number","enum":[400]},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["bad_request_error"]},"name":{"type":"string","enum":["MTABadRequestError"]},"requestId":{"type":"string","format":"uuid"}}}}}},"UnauthorizedError":{"description":"UnauthorizedError","content":{"application/json":{"schema":{"type":"object","properties":{"httpCode":{"type":"number","enum":[401]},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["unauthorized_error"]},"name":{"type":"string","enum":["MTAUnauthorizedError"]},"requestId":{"type":"string","format":"uuid"}}}}}},"ForbiddenError":{"description":"ForbiddenError","content":{"application/json":{"schema":{"type":"object","properties":{"httpCode":{"type":"number","enum":[403]},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["forbidden_error"]},"name":{"type":"string","enum":["MTAForbiddenError"]},"requestId":{"type":"string","format":"uuid"},"reason":{}}}}}},"InternalServerError":{"description":"InternalServerError","content":{"application/json":{"schema":{"type":"object","properties":{"httpCode":{"type":"number","enum":[500]},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["internal_server_error"]},"name":{"type":"string","enum":["MTAInternalServerError"]},"requestId":{"type":"string","format":"uuid"}}}}}}}},"paths":{"/verify/sms/send-code":{"post":{"tags":["Verify"],"summary":"Trigger SMS Verification Code","operationId":"verify_send_sms_verification_code","responses":{"200":{"description":"Success","headers":{"RateLimit-Limit":{"description":"The maximum number of requests that the consumer is permitted to make per window","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"The number of requests remaining in the current rate limit window","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"The remaining window before the rate limit resets in milliseconds","schema":{"type":"integer"}},"Retry-After":{"description":"The number of seconds to wait before retrying the request","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"The maximum number of requests that the consumer is permitted to make per window","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"The number of requests remaining in the current rate limit window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"The remaining window before the rate limit resets in milliseconds","schema":{"type":"integer"}},"X-Request-ID":{"description":"A unique identifier for the request","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendCode.Response"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"500":{"$ref":"#/components/responses/InternalServerError"}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendCode.Request"}}},"required":true}}}}}
```

## Validate SMS code

After initiating an SMS verification request, the user receives a code via SMS to enter into your application. Once you receive the code, you can validate it using the check SMS code endpoint. You must retain one identifier for the request so you can validate the user-supplied code.

### Check SMS Code endpoint [`verify/sms/check-code`](/api-reference/verify.md#post-verify-sms-check-code)

#### Request Fields

<table><thead><tr><th width="174.5">Name</th><th width="114.5">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>to</code>*</td><td><code>string</code></td><td>The destination phone number this SMS verification code was sent to.</td></tr><tr><td><code>verificationId</code>*</td><td><code>string</code></td><td>The unique identifier returned by the send-code request for this verification attempt.</td></tr><tr><td><code>code</code> <em>- required</em></td><td><code>string</code></td><td>The code supplied by the user to validate for this verification request. Length must match the configured <code>codeLength</code> for this request.</td></tr></tbody></table>

{% hint style="warning" %}
\*Either the `to` or `verificationId` field is required to identify the request.
{% endhint %}

#### Response Fields

<table><thead><tr><th width="175">Name</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>to</code></td><td><code>string</code></td><td>The destination number supplied in the request.</td></tr><tr><td><code>verificationId</code></td><td><code>string</code></td><td><p>The verification request ID supplied in the request.</p><p>If only <code>to</code> is supplied in the request, the response will still include this field for reporting purposes.</p></td></tr><tr><td><code>status</code></td><td><code>enum</code></td><td>An <code>enum</code> value representing the current status of this verification request. See <a href="#verification-status">Verification Status</a> to see possible status codes.</td></tr></tbody></table>

### Verification Status

This Verification Status is an `enum` value representing the current status of the SMS verification request. This is used to confirm the validity of the user-supplied code.

**Status codes:**

* `PENDING`
  * The verification request has been processed by Mobile Text Alerts and was sent to the user.
* `APPROVED`
  * The supplied verification code is valid and the request has not yet expired.
* `EXPIRED`
  * The verification request has expired. This does not indicate whether the supplied code was valid.
* `INVALID`
  * The verification request has not yet expired but the supplied code did not match the code sent to the user via SMS.

## POST /verify/sms/check-code

> Check SMS Verification Code

```json
{"openapi":"3.0.0","info":{"title":"Mobile Text Alerts API","version":"8.0.0"},"servers":[{"url":"https://api.mobile-text-alerts.com/v3"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"apiKey"}},"schemas":{"CheckCode.Response":{"type":"object","properties":{"to":{"type":"string"},"verificationId":{"type":"string"},"status":{"$ref":"#/components/schemas/VerifyCodeStatus"}},"required":["to","verificationId","status"]},"VerifyCodeStatus":{"type":"string","enum":["PENDING","APPROVED","EXPIRED","INVALID"]},"CheckCode.Request":{"type":"object","properties":{"to":{"type":"string"},"verificationId":{"type":"string"},"code":{"type":"string"}},"required":["code"]}},"responses":{"BadRequestError":{"description":"BadRequestError","content":{"application/json":{"schema":{"type":"object","properties":{"httpCode":{"type":"number","enum":[400]},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["bad_request_error"]},"name":{"type":"string","enum":["MTABadRequestError"]},"requestId":{"type":"string","format":"uuid"}}}}}},"UnauthorizedError":{"description":"UnauthorizedError","content":{"application/json":{"schema":{"type":"object","properties":{"httpCode":{"type":"number","enum":[401]},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["unauthorized_error"]},"name":{"type":"string","enum":["MTAUnauthorizedError"]},"requestId":{"type":"string","format":"uuid"}}}}}},"ForbiddenError":{"description":"ForbiddenError","content":{"application/json":{"schema":{"type":"object","properties":{"httpCode":{"type":"number","enum":[403]},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["forbidden_error"]},"name":{"type":"string","enum":["MTAForbiddenError"]},"requestId":{"type":"string","format":"uuid"},"reason":{}}}}}},"InternalServerError":{"description":"InternalServerError","content":{"application/json":{"schema":{"type":"object","properties":{"httpCode":{"type":"number","enum":[500]},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["internal_server_error"]},"name":{"type":"string","enum":["MTAInternalServerError"]},"requestId":{"type":"string","format":"uuid"}}}}}}}},"paths":{"/verify/sms/check-code":{"post":{"tags":["Verify"],"summary":"Check SMS Verification Code","operationId":"verify_check_sms_verification_code","responses":{"200":{"description":"Success","headers":{"RateLimit-Limit":{"description":"The maximum number of requests that the consumer is permitted to make per window","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"The number of requests remaining in the current rate limit window","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"The remaining window before the rate limit resets in milliseconds","schema":{"type":"integer"}},"Retry-After":{"description":"The number of seconds to wait before retrying the request","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"The maximum number of requests that the consumer is permitted to make per window","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"The number of requests remaining in the current rate limit window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"The remaining window before the rate limit resets in milliseconds","schema":{"type":"integer"}},"X-Request-ID":{"description":"A unique identifier for the request","schema":{"type":"string","format":"uuid"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckCode.Response"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"500":{"$ref":"#/components/responses/InternalServerError"}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckCode.Request"}}},"required":true}}}}}
```

[^1]: See serviceName request field.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers.mobile-text-alerts.com/use-cases/generate-and-validate-2fa-mfa-codes-via-sms.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
