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

# How to Send an OTP via WhatsApp, SMS, Email, or Misscall

> Learn how to create and deliver OTPs using POST /v3/request for server-generated codes and POST /v3/send for client-supplied codes.

OTP.ID gives you two ways to deliver a one-time password to your users. Use **`POST /v3/request`** when you want OTP.ID to generate the code for you — the code is sent directly to the user and is never exposed in the API response. Use **`POST /v3/send`** when your application needs to supply the code itself, for example when you want to display it in your own UI before delivering it. Both endpoints share the same channel options, optional parameters, and response shape.

## Option A: Server-generated code — `POST /v3/request`

When you call this endpoint, OTP.ID generates a numeric code, delivers it to the user over the channel you choose, and returns a transaction record. The code itself is never included in the response — only the user receives it.

<Note>
  The OTP code is **never** returned in the API response. OTP.ID delivers it
  directly to the user over the requested channel. Your server only ever sees
  the `otp_id` transaction reference.
</Note>

### Supported channels

| `channel` value    | Delivery method                                                                              |
| ------------------ | -------------------------------------------------------------------------------------------- |
| `whatsapp`         | WhatsApp message                                                                             |
| `sms`              | SMS text message                                                                             |
| `email`            | Email message                                                                                |
| `misscall`         | Missed call (code derived from caller ID)                                                    |
| `whatsapp_inbound` | User-initiated WhatsApp message — see the [WhatsApp Inbound guide](/guides/whatsapp-inbound) |

### Request body

| Field         | Required                      | Description                                                                                         |
| ------------- | ----------------------------- | --------------------------------------------------------------------------------------------------- |
| `channel`     | ✅                             | Delivery channel (see table above)                                                                  |
| `number`      | ✅ (except `whatsapp_inbound`) | Phone number (digits only, min 10 chars) or email address                                           |
| `brand`       | ❌                             | Brand name shown in the OTP message. Defaults to your account's `brand_name`.                       |
| `otp_length`  | ❌                             | Code length from 4 to 8 digits. Default `6`. Values outside the range are clamped — never rejected. |
| `ttl`         | ❌                             | Seconds until the OTP expires. Range 60–900. Default `300`. Clamped, never rejected.                |
| `external_id` | ❌                             | Your own idempotency key. See the [Idempotency guide](/guides/idempotency).                         |

<Tip>
  Set `brand` to your product or company name so users immediately recognise
  the message. For example, `"brand": "TokoKita"` produces a message like
  *"Your TokoKita verification code is 482913"*. If you leave it out, OTP.ID
  falls back to the `brand_name` configured in your account settings.
</Tip>

### Examples

<CodeGroup>
  ```bash WhatsApp theme={null}
  curl -X POST https://api.otp.id/v3/request \
    -H "Authorization: Bearer <api_key>" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "whatsapp",
      "number": "6281234567890",
      "brand": "TokoKita",
      "otp_length": 6,
      "ttl": 300
    }'
  ```

  ```bash SMS theme={null}
  curl -X POST https://api.otp.id/v3/request \
    -H "Authorization: Bearer <api_key>" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "sms",
      "number": "6281234567890",
      "brand": "TokoKita"
    }'
  ```

  ```bash Email theme={null}
  curl -X POST https://api.otp.id/v3/request \
    -H "Authorization: Bearer <api_key>" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "email",
      "number": "user@example.com",
      "brand": "TokoKita"
    }'
  ```

  ```bash Misscall theme={null}
  curl -X POST https://api.otp.id/v3/request \
    -H "Authorization: Bearer <api_key>" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "misscall",
      "number": "6281234567890",
      "brand": "TokoKita"
    }'
  ```

  ```bash WhatsApp Inbound theme={null}
  curl -X POST https://api.otp.id/v3/request \
    -H "Authorization: Bearer <api_key>" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "whatsapp_inbound",
      "brand": "TokoKita",
      "ttl": 300
    }'
  ```
</CodeGroup>

### Success response

A `200 OK` response with `"status": "sent"` means the message was accepted for delivery.

```json theme={null}
{
  "success": true,
  "data": {
    "otp_id": "OTP20260807ABCD000001",
    "status": "sent",
    "channel": "whatsapp",
    "number": "6281234567890",
    "price": 350,
    "last_balance": 99650,
    "expires_at": "2026-08-07 10:05:00"
  },
  "error": null
}
```

### Failed delivery response

When a delivery attempt fails (for example, the number is unreachable), OTP.ID still returns HTTP `200` with `"status": "failed"`. Your balance is **not** deducted for failed deliveries.

```json theme={null}
{
  "success": true,
  "data": {
    "otp_id": "OTP20260807ABCD000002",
    "status": "failed",
    "channel": "sms",
    "number": "6281234567890",
    "price": 175,
    "last_balance": 99650,
    "expires_at": "2026-08-07 10:05:00"
  },
  "error": null
}
```

<Note>
  When `status` is `"failed"`, `last_balance` is not reduced. Check `status`
  in your application code and decide whether to retry on a different channel.
</Note>

***

## Option B: Client-supplied code — `POST /v3/send`

Use this endpoint when your application generates the OTP code. Include your code in the `otp` field and OTP.ID will deliver it for you. All other request parameters and response fields are identical to `POST /v3/request`.

<Note>
  The `whatsapp_inbound` channel is **not** supported by `POST /v3/send`.
  Use `POST /v3/request` for WhatsApp Inbound flows.
</Note>

### Additional request field

| Field | Required | Description                                                           |
| ----- | -------- | --------------------------------------------------------------------- |
| `otp` | ✅        | The code to deliver. Digits only, 4–8 characters. Example: `"482913"` |

All fields from `POST /v3/request` (`channel`, `number`, `brand`, `otp_length`, `ttl`, `external_id`) apply here as well, with the exception that `whatsapp_inbound` is not a valid `channel` value.

### Examples

<CodeGroup>
  ```bash WhatsApp theme={null}
  curl -X POST https://api.otp.id/v3/send \
    -H "Authorization: Bearer <api_key>" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "whatsapp",
      "number": "6281234567890",
      "otp": "482913",
      "brand": "TokoKita",
      "ttl": 300
    }'
  ```

  ```bash SMS theme={null}
  curl -X POST https://api.otp.id/v3/send \
    -H "Authorization: Bearer <api_key>" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "sms",
      "number": "6281234567890",
      "otp": "482913",
      "brand": "TokoKita"
    }'
  ```

  ```bash Email theme={null}
  curl -X POST https://api.otp.id/v3/send \
    -H "Authorization: Bearer <api_key>" \
    -H "Content-Type: application/json" \
    -d '{
      "channel": "email",
      "number": "user@example.com",
      "otp": "482913",
      "brand": "TokoKita"
    }'
  ```
</CodeGroup>

### Success response

```json theme={null}
{
  "success": true,
  "data": {
    "otp_id": "OTP20260807ABCD000001",
    "status": "sent",
    "channel": "whatsapp",
    "number": "6281234567890",
    "price": 350,
    "last_balance": 99650,
    "expires_at": "2026-08-07 10:05:00"
  },
  "error": null
}
```

***

## Response field reference

| Field          | Type    | Description                                                                                                        |
| -------------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
| `otp_id`       | string  | Unique transaction ID. Pass this to `POST /v3/verify`.                                                             |
| `status`       | string  | `"sent"` — delivery accepted. `"failed"` — delivery failed. `"pending"` — awaiting user action (WhatsApp Inbound). |
| `channel`      | string  | The channel used.                                                                                                  |
| `number`       | string  | The recipient phone number or email.                                                                               |
| `price`        | integer | Cost in credits for this transaction.                                                                              |
| `last_balance` | integer | Your remaining balance after the charge.                                                                           |
| `expires_at`   | string  | UTC datetime when the OTP expires.                                                                                 |

## Next steps

* **Verify the code** the user enters with [POST /v3/verify](/guides/verify-otp).
* **Prevent duplicate sends** on retries with the [Idempotency guide](/guides/idempotency).
* **Use WhatsApp Inbound** for a tap-to-verify flow with the [WhatsApp Inbound guide](/guides/whatsapp-inbound).
