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

# Send Verification Code

> Send a verification code via SMS, Email, or Voice

## Request

Send a one-time password to a recipient via your chosen channel.

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token with your API key: `Bearer YOUR_API_KEY`
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`
</ParamField>

### Body Parameters

<ParamField body="channel" type="string" required>
  Communication channel for the verification code. Must be one of: `sms`, `email`, or `voice`
</ParamField>

<ParamField body="recipient" type="string" required>
  Recipient address:

  * For SMS/Voice: E.164 formatted phone number (e.g., `+1234567890`)
  * For Email: Valid email address (e.g., `user@example.com`)
</ParamField>

<ParamField body="message" type="string">
  Custom message template. Use `{code}` as a placeholder for the verification code.

  Default: `"Your verification code is: {code}"`

  Example: `"Your Acme Corp verification code is: {code}. Valid for 5 minutes."`
</ParamField>

<ParamField body="subject" type="string">
  Email subject line (only used when `channel` is `email`)

  Default: `"Your Verification Code"`

  Example: `"Verify Your Acme Corp Account"`
</ParamField>

<ParamField body="expires_in" type="integer">
  Code expiration time in seconds. Must be between 60 and 3600.

  Default: `300` (5 minutes)
</ParamField>

<ParamField body="code" type="string">
  Custom verification code. Must be a numeric string between 4-10 digits.

  If not provided, a code will be automatically generated with a length defined in your account settings.

  Example: `"123456"`
</ParamField>

## Response

<ResponseField name="success" type="boolean">
  Always `true` for successful requests
</ResponseField>

<ResponseField name="verification_id" type="string">
  Unique identifier for this verification. Use this when calling the verify endpoint.

  Example: `"11f951d5-32d1-4b49-bdda-7da248e2615c"`
</ResponseField>

<ResponseField name="expires_at" type="string">
  ISO 8601 timestamp when the code expires

  Example: `"2024-01-01T12:05:00.000Z"`
</ResponseField>

<ResponseField name="request_id" type="string">
  Unique request identifier for debugging

  Example: `"7b4d6022-7260-4568-b6b7-29c366c47bbc"`
</ResponseField>

## Examples

<CodeGroup>
  ```bash SMS theme={null}
  curl -X POST https://app.easyotp.dev/api/v1/send \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "sms",
      "recipient": "+1234567890",
      "message": "Your verification code is: {code}",
      "expires_in": 300
    }'
  ```

  ```bash Email theme={null}
  curl -X POST https://app.easyotp.dev/api/v1/send \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "email",
      "recipient": "user@example.com",
      "subject": "Your Verification Code",
      "message": "Your verification code is: {code}",
      "expires_in": 600
    }'
  ```

  ```bash Voice theme={null}
  curl -X POST https://app.easyotp.dev/api/v1/send \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "voice",
      "recipient": "+1234567890",
      "message": "Your verification code is: {code}",
      "expires_in": 300
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://app.easyotp.dev/api/v1/send', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      channel: 'sms',
      recipient: '+1234567890',
      message: 'Your verification code is: {code}',
      expires_in: 300
    })
  });

  const data = await response.json();
  console.log(data);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://app.easyotp.dev/api/v1/send',
      headers={
          'Authorization': 'Bearer YOUR_API_KEY',
          'Content-Type': 'application/json',
      },
      json={
          'channel': 'sms',
          'recipient': '+1234567890',
          'message': 'Your verification code is: {code}',
          'expires_in': 300
      }
  )

  data = response.json()
  print(data)
  ```
</CodeGroup>

### Success Response

```json theme={null}
{
  "success": true,
  "verification_id": "11f951d5-32d1-4b49-bdda-7da248e2615c",
  "expires_at": "2024-01-01T12:05:00.000Z",
  "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
}
```

## Error Responses

<ResponseExample>
  ```json 400 - Invalid Channel theme={null}
  {
    "error": "channel is required and must be one of: sms, email, voice",
    "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
  }
  ```

  ```json 400 - Invalid Recipient theme={null}
  {
    "error": "recipient must be a valid E.164 phone number for SMS and voice channels",
    "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
  }
  ```

  ```json 401 - Authentication Failed theme={null}
  {
    "error": "API key is required. Provide it via Authorization: Bearer <token> or x-api-key header",
    "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
  }
  ```

  ```json 402 - Insufficient Credits theme={null}
  {
    "error": "Insufficient credits. Please purchase more credits.",
    "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
  }
  ```

  ```json 403 - API Key Disabled theme={null}
  {
    "error": "API key is disabled",
    "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
  }
  ```

  ```json 429 - Rate Limit Exceeded theme={null}
  {
    "error": "Rate limit exceeded: Maximum 2 verification codes per minute. Please try again in 42 seconds.",
    "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc",
    "retry_after": 42
  }
  ```

  ```json 500 - Internal Server Error theme={null}
  {
    "error": "Failed to send verification",
    "request_id": "7b4d6022-7260-4568-b6b7-29c366c47bbc"
  }
  ```
</ResponseExample>

## Rate Limiting

This endpoint enforces per-recipient rate limits to prevent spam and abuse:

* **2 sends per minute** per recipient
* **15 sends per hour** per recipient
* **50 sends per 24 hours** per recipient

When you exceed these limits, you'll receive a `429` response with a `retry_after` field indicating how many seconds until you can retry.

<Note>
  In addition to per-recipient limits, API keys are also rate limited to **120 requests per minute** across all endpoints. Test recipients (like `+15555550100`) are exempt from per-recipient limits but still count toward your API key limits. See the [Rate Limits](/api-reference/introduction#rate-limits) section for more details.
</Note>

## Best Practices

<Tip>
  **Keep messages clear and branded**: Include your app name in the message so users know where the code is coming from.

  Good: `"Your Acme Corp verification code is: {code}"`

  Bad: `"{code}"`
</Tip>

<Tip>
  **Set appropriate expiration times**:

  * SMS/Voice: 2-5 minutes (users typically verify immediately)
  * Email: 10-15 minutes (users may need to open their email client)
</Tip>

<Warning>
  **Phone number format**: Phone numbers must be in E.164 format (starting with `+` and country code). Invalid formats will be rejected.
</Warning>

<Note>
  **Message length limits**:

  * SMS: 160 characters recommended
  * Email: No hard limit, but keep it concise
  * Voice: Keep under 50 characters for best experience
</Note>

## Credits Consumed

Each send request consumes 1 credit regardless of the channel:

| Channel | Credits  |
| ------- | -------- |
| SMS     | 1 credit |
| Email   | 1 credit |
| Voice   | 1 credit |

Failed sends do not consume credits.
