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

# Send an OTP

> How-to task page: how a partner calls POST /open-api/customer/v1/send-otp to deliver an OTP it generated itself to a user over the DCS SMS or email channel.

How-to task page: how a partner calls `POST /open-api/customer/v1/send-otp` to deliver an OTP it generated itself to a user over the DCS SMS or email channel.
This endpoint **neither generates nor verifies** the OTP. Generating and checking the OTP are the partner's job; DCS only delivers it.

## 📄 Guide

Whether you already run your own SMS gateway or would rather reuse the DCS delivery channels, this endpoint sends a verification code to the user's mobile number or email address: you generate the OTP and hand the delivery to us. The endpoint is for Partner-Managed partners and is typically used at the points in your own flows where you need to reach a user by SMS or email.

<Info>
  this endpoint **does not generate or verify OTPs**. You generate the OTP, keep it and check it yourself; DCS only sends the OTP content out over the `channel` you specify.
</Info>

### End-to-end flow (who does what)

| Step | Who | Action                                                                                                             |
| ---- | --- | ------------------------------------------------------------------------------------------------------------------ |
| 1    | You | Generate a 6-digit OTP and keep it in your own system so you can check it later                                    |
| 2    | You | Choose the delivery channel (`SMS` or `EMAIL`) and prepare the matching recipient (mobile number or email address) |
| 3    | You | Generate a unique `otpSendRef` (your idempotency key) and call this endpoint                                       |
| 4    | DCS | Verify your credentials and signature, then hand the OTP to the downstream SMS or email gateway for that channel   |
| 5    | DCS | Apply rate limiting per recipient and record the delivery result                                                   |
| 6    | You | Read the response `data.status` to see whether the submission succeeded                                            |

### Endpoint

**`POST /open-api/customer/v1/send-otp`**

The request headers (authentication and signature) are the same as for every other `/open-api/` endpoint: send `X-DAPI-API-KEY`, `X-DAPI-TIMESTAMP`, `X-DAPI-NONCE` and `X-DAPI-SIGN`, with `Content-Type: application/json`. See [Authentication and signing](../../integration-resources/authentication) for the details.

#### Request body fields

| Field              | Type   | Required    | Constraints | Description                                                                                                                                                         |
| ------------------ | ------ | ----------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `otpSendRef`       | string | Yes         | Max 50      | Idempotency key for this send, **globally unique**; a repeat is rejected                                                                                            |
| `channel`          | string | Yes         | Max 20      | Delivery channel, one of `SMS` / `EMAIL`                                                                                                                            |
| `language`         | string | Yes         | —           | Language of the message, one of `en` (English) / `zh` (Chinese)                                                                                                     |
| `encryptionIV`     | string | Conditional | Max 32      | IV for AES-GCM; **required whenever any encrypted field is populated**, and used to decrypt the ciphertext fields below                                             |
| `mobileEncryption` | string | Conditional | Max 128     | Mobile number as ciphertext (AES-GCM); when `channel=SMS`, supply either this or `mobile`                                                                           |
| `mobile`           | string | Conditional | 6-30        | Mobile number in plaintext; when `channel=SMS`, supply either this or `mobileEncryption`; with the country prefix it must start with `+`, for example `+6591234567` |
| `emailEncryption`  | string | Conditional | Max 256     | Email as ciphertext (AES-GCM); when `channel=EMAIL`, supply either this or `email`                                                                                  |
| `email`            | string | Conditional | Max 100     | Email in plaintext; when `channel=EMAIL`, supply either this or `emailEncryption`                                                                                   |
| `otpEncryption`    | string | Conditional | Max 64      | OTP as ciphertext (AES-GCM); supply either this or `otp`                                                                                                            |
| `otp`              | string | Conditional | Length 6    | Verification code in plaintext, 6 digits; supply either this or `otpEncryption`                                                                                     |

**Required combinations per channel:**

| `channel` | Required field combination                                    |
| --------- | ------------------------------------------------------------- |
| `SMS`     | (`mobile` or `mobileEncryption`) + (`otp` or `otpEncryption`) |
| `EMAIL`   | (`email` or `emailEncryption`) + (`otp` or `otpEncryption`)   |

#### Plaintext and ciphertext

The recipient fields (`mobile` / `email`) and the OTP itself (`otp`) can each be sent in plaintext or as ciphertext. For data this sensitive we **strongly recommend ciphertext**:

* **Ciphertext**: fill in `mobileEncryption` / `emailEncryption` / `otpEncryption` using **AES-GCM**, with your **SecretKey** as the key and the IV passed in `encryptionIV`. As soon as any ciphertext field is populated, `encryptionIV` becomes mandatory.
* **Plaintext**: fill in `mobile` / `email` / `otp` and send the values as they are.
* If you supply both the plaintext and the ciphertext of the same item, **the ciphertext wins**.

#### Minimum request example (SMS, plaintext)

```json theme={null}
{
  "otpSendRef": "otp-20260617-0001",
  "channel": "SMS",
  "language": "zh",
  "mobile": "+6591234567",
  "otp": "123456"
}
```

#### Minimum request example (EMAIL, ciphertext)

```json theme={null}
{
  "otpSendRef": "otp-20260617-0002",
  "channel": "EMAIL",
  "language": "en",
  "encryptionIV": "<base64-iv>",
  "emailEncryption": "<aes-gcm-ciphertext>",
  "otpEncryption": "<aes-gcm-ciphertext>"
}
```

#### Response example

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": "success",
  "messageDetail": null,
  "data": {
    "otpSendRef": "otp-20260617-0001",
    "status": "SUCCESS"
  }
}
```

> **Common response envelope**: every `/open-api/` endpoint returns `{ code, message, messageDetail, data }`. `code` is the business code (`SYS_SUCCESS` on success), and `messageDetail` carries human-readable copy (including `title`/`type`/`action`/`linkUrl` and so on) when there is something to show the end user, otherwise it may be empty. Decide success or failure from `code`, never from the HTTP status code or the wording of `message` alone.

#### Response fields (`data`)

| Field        | Type   | Description                                  |
| ------------ | ------ | -------------------------------------------- |
| `otpSendRef` | string | Echoes the idempotency key of this send      |
| `status`     | string | Delivery status, one of `SUCCESS` / `FAILED` |

> `status=SUCCESS` only means that **DCS handed the OTP to the downstream SMS or email gateway successfully**; it is **no guarantee that the user will receive it** (the carrier or the mailbox provider may still get in the way). When `status=FAILED`, the response `code` tells you why the downstream delivery failed.

### Rate limits

To keep the SMS and email channels from being abused, DCS rate-limits sends **to the same recipient under the same Enterprise**:

| Dimension                   | Limit                                   |
| --------------------------- | --------------------------------------- |
| Same (`channel`, recipient) | Only one successful send per 60 seconds |

Exceeding the limit returns `OTP_SEND_TOO_FREQUENT`. Ask the user to **try again shortly** rather than resending immediately.

### Idempotency

`otpSendRef` is your own business idempotency key, and **the same `otpSendRef` cannot be sent twice**:

* First request: the OTP goes out and that `otpSendRef` is recorded.
* Repeat request (same `otpSendRef`): returns `OTP_SEND_REF_NOT_UNIQUE`.

Make sure every send uses a unique `otpSendRef` (deriving it from your business reference plus a timestamp works well).

### Common error codes

| Error code                | Meaning                              | Retryable            | What to do                                                                 |
| ------------------------- | ------------------------------------ | -------------------- | -------------------------------------------------------------------------- |
| `OTP_SEND_REF_NOT_UNIQUE` | `otpSendRef` has already been used   | No (change the key)  | Send again with a fresh, unique `otpSendRef`                               |
| `OTP_SEND_TOO_FREQUENT`   | Too many sends to the same recipient | Yes (back off)       | Ask the user to try again shortly and resend once the cooldown has passed  |
| `ILLEGAL_ARGUMENT_ERROR`  | Invalid parameter                    | No (fix the request) | Check the required combination, the lengths and the enum values            |
| `SYSTEM_ERROR`            | System error                         | Yes                  | Back off and retry; if it keeps failing, contact the customer success team |

For the full set of business and error codes, see [Authorization declines and error codes](../transactions/decline-codes).

### Prerequisites

* You hold the Enterprise **ApiKey / SecretKey** and have completed the signing and whitelist setup (see [First steps](../../getting-started/first-steps) and [Authentication and signing](../../integration-resources/authentication)).
* If you send ciphertext, your system needs **AES-GCM** support, with the Enterprise SecretKey as the key.

## Next steps

Once OTP delivery works end to end, carry on with [creating a user](./create-customer) and the KYC and card issuance flows that follow.
