OTP API
The OTP API allows you to request, check status, and consume one-time verification codes securely.
Endpoints
POST /v1/otp/request- Request a new OTPGET /v1/otp/:id- Get OTP request statusPOST /v1/otp/:id/consume- Consume the OTPDELETE /v1/otp/:id- Cancel the request
Request OTP
Request a new OTP for verification purposes.
Request
POST /v1/otp/request
Content-Type: application/json
Authorization: Bearer ak_live_xxxxBody Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| reason | string | Yes | Why the agent needs this OTP |
| publicKey | string | Yes | Agent's RSA public key for E2E encryption |
| expectedSender | string | No | Hint for expected sender (e.g., "Google", "GitHub") |
| filter | object | No | Source and sender filters |
| ttl | number | No | Request TTL in seconds (default: 300, max: 3600) |
Filter Object
| Field | Type | Description |
|---|---|---|
| sources | string[] | OTP sources: "sms", "email", "whatsapp" |
| senderPattern | string | Glob pattern for sender (e.g., "*@acme.com") |
Example Request
{
"reason": "Sign up verification for Acme Inc",
"publicKey": "MIIBIjANBgkqhkiG9w...",
"expectedSender": "Acme",
"filter": {
"sources": ["email"],
"senderPattern": "*@acme.com"
},
"ttl": 300
}Response
Pending Approval (202 Accepted)
{
"id": "otp_xxxxxxxxxxxx",
"status": "pending_approval",
"reason": "Sign up verification for Acme Inc",
"expiresAt": "2026-01-28T12:05:00Z",
"createdAt": "2026-01-28T12:00:00Z"
}Approved (200 OK)
{
"id": "otp_xxxxxxxxxxxx",
"status": "approved",
"reason": "Sign up verification for Acme Inc",
"expiresAt": "2026-01-28T12:05:00Z",
"createdAt": "2026-01-28T12:00:00Z"
}Denied (403 Forbidden)
{
"id": "otp_xxxxxxxxxxxx",
"status": "denied",
"reason": "User denied the request"
}Get OTP Status
Check the current status of an OTP request.
Request
GET /v1/otp/:id
Authorization: Bearer ak_live_xxxxResponse
{
"id": "otp_xxxxxxxxxxxx",
"status": "otp_received",
"reason": "Sign up verification for Acme Inc",
"source": "email",
"sender": "noreply@acme.com",
"expiresAt": "2026-01-28T12:05:00Z",
"createdAt": "2026-01-28T12:00:00Z",
"receivedAt": "2026-01-28T12:01:30Z"
}Status Values
| Status | Description |
|---|---|
| pending_approval | Waiting for user to approve the request |
| approved | User approved, waiting for OTP to arrive |
| otp_received | OTP captured and ready to consume |
| consumed | OTP has been read and deleted |
| denied | User denied the request |
| expired | Request expired before completion |
| cancelled | Request was cancelled by the agent |
Consume OTP
Retrieve and delete the OTP. This is a one-time operation.
Request
POST /v1/otp/:id/consume
Authorization: Bearer ak_live_xxxxResponse
{
"id": "otp_xxxxxxxxxxxx",
"encryptedPayload": "base64-encoded-encrypted-otp...",
"source": "email",
"sender": "noreply@acme.com",
"consumedAt": "2026-01-28T12:02:00Z"
}Important: The encryptedPayload must be decrypted using your agent's private key. Use the SDK's decryptOTPPayload() function for this.
Cancel OTP Request
Cancel a pending OTP request.
Request
DELETE /v1/otp/:id
Authorization: Bearer ak_live_xxxxResponse
{
"success": true,
"id": "otp_xxxxxxxxxxxx",
"cancelledAt": "2026-01-28T12:02:00Z"
}WebSocket Updates
Connect to the WebSocket URL for real-time status updates:
const ws = new WebSocket('wss://api.agentotp.com/ws/otp_xxxx');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'status_change':
console.log('Status:', data.status);
if (data.status === 'otp_received') {
console.log('OTP is ready to consume!');
}
break;
case 'expired':
console.log('OTP request expired');
break;
}
};Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid request parameters |
| 401 | UNAUTHORIZED | Invalid or missing API key |
| 404 | NOT_FOUND | OTP request not found |
| 409 | ALREADY_CONSUMED | OTP has already been consumed |
| 410 | EXPIRED | OTP request has expired |
| 429 | RATE_LIMITED | Rate limit exceeded |