> ## 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.

# GET /v3/otp/{otp_id} — Check OTP Transaction Status

> GET /v3/otp/{otp_id} retrieves the current status and delivery details of an OTP transaction. Read-only and safe to call repeatedly for polling.

Use `GET /v3/otp/{otp_id}` to inspect the current state of any OTP transaction you created. This endpoint is entirely read-only — it records no attempts, consumes no credits, and triggers no side effects. You can call it repeatedly to poll for status changes, for example to detect when a `whatsapp_inbound` transaction transitions from `pending` to `verified`.

## Endpoint

```text theme={null}
GET https://api.otp.id/v3/otp/{otp_id}
```

## Request Headers

| Header          | Value              | Required |
| --------------- | ------------------ | -------- |
| `Authorization` | `Bearer <api_key>` | ✅ Yes    |

## Path Parameters

<ParamField path="otp_id" type="string" required>
  The transaction ID to look up. This is the `otp_id` returned by `POST /v3/request` or `POST /v3/send`.

  Format: `OTP` + `YYYYMMDD` + 4 uppercase letters + 6 digits. Example: `OTP20260807ABCD000001`.
</ParamField>

## Response Fields

<ResponseField name="otp_id" type="string">
  The unique transaction ID, echoed from the path parameter.
</ResponseField>

<ResponseField name="status" type="string">
  The current lifecycle status of the transaction:

  * `pending` — created but not yet delivered or confirmed (typical for `whatsapp_inbound`)
  * `sent` — the OTP message was dispatched to the channel vendor
  * `success` — delivery confirmed by the vendor
  * `failed` — vendor delivery failed; no charge was applied
  * `verified` — the user has successfully verified the OTP code
</ResponseField>

<ResponseField name="channel" type="string">
  The delivery channel used for this transaction (e.g. `whatsapp`, `sms`, `email`).
</ResponseField>

<ResponseField name="number" type="string">
  The destination phone number or email address for this transaction.
</ResponseField>

<ResponseField name="attempts" type="integer">
  The number of **failed** verification attempts made against this transaction. The maximum is `5`; once reached, the transaction is locked and no further verify calls are accepted.
</ResponseField>

<ResponseField name="expires_at" type="string">
  The datetime at which this OTP expires, in `YYYY-MM-DD HH:MM:SS` format (WIB, UTC+7). After this time, calls to `POST /v3/verify` return `OTP_EXPIRED`.
</ResponseField>

<ResponseField name="verified_at" type="string">
  The datetime at which the OTP was successfully verified, in `YYYY-MM-DD HH:MM:SS` format (WIB, UTC+7). Returns an empty string `""` if the transaction has not been verified yet.
</ResponseField>

<ResponseField name="price" type="integer">
  The number of credits charged for this transaction.
</ResponseField>

## Example

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.otp.id/v3/otp/OTP20260807ABCD000001 \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```
</CodeGroup>

## Response Examples

### Sent — awaiting verification

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

### Verified — successfully confirmed

```json theme={null}
{
  "success": true,
  "data": {
    "otp_id": "OTP20260807ABCD000001",
    "status": "verified",
    "channel": "whatsapp",
    "number": "6281234567890",
    "attempts": 1,
    "expires_at": "2026-08-07 10:05:00",
    "verified_at": "2026-08-07 10:01:30",
    "price": 350
  },
  "error": null
}
```

## Error Codes

All errors follow the shape `{"success": false, "data": null, "error": {"code": "...", "message": "..."}}`.

| HTTP Status | Error Code       | Description                                                      |
| ----------- | ---------------- | ---------------------------------------------------------------- |
| 401         | `UNAUTHORIZED`   | The `Authorization` header is missing or the API key is invalid. |
| 404         | `OTP_NOT_FOUND`  | No transaction with the given `otp_id` was found.                |
| 429         | `RATE_LIMITED`   | You have exceeded the API-level request rate limit.              |
| 500         | `INTERNAL_ERROR` | An unexpected server-side error occurred.                        |

<Note>
  `OTP_NOT_FOUND` is returned both when the `otp_id` does not exist **and** when it belongs to a different merchant's API key. This prevents cross-merchant enumeration of transaction IDs.
</Note>
