TypeScript SDK Reference
The official TypeScript SDK for Agent OTP. Full type safety, end-to-end encryption support, and comprehensive error handling.
Installation
npm install @orrisai/agent-otp-sdkAgentOTPClient
The main client class for interacting with the Agent OTP API.
Constructor
import { AgentOTPClient } from '@orrisai/agent-otp-sdk';
const client = new AgentOTPClient(config: AgentOTPClientConfig);Config Options
| Property | Type | Required | Description |
|---|---|---|---|
| apiKey | string | Yes | Your Agent OTP API key |
| baseUrl | string | No | API base URL (default: https://api.agentotp.com) |
| timeout | number | No | Request timeout in ms (default: 30000) |
| retryAttempts | number | No | Number of retry attempts (default: 3) |
requestOTP()
Request an OTP from the relay service.
const request = await client.requestOTP(
options: RequestOTPOptions
): Promise<OTPRequestResult>;RequestOTPOptions
interface RequestOTPOptions {
// Why the agent needs this OTP (shown to user)
reason: string;
// Expected sender name (e.g., "Google", "GitHub")
expectedSender?: string;
// Filter criteria for matching OTPs
filter?: OTPSourceFilter;
// Agent's public key for E2E encryption (base64)
publicKey: string;
// Request TTL in seconds (default: 300)
ttl?: number;
// Block until OTP arrives (default: false)
waitForOTP?: boolean;
// Wait timeout in ms (default: 120000)
timeout?: number;
// Callback when pending user approval
onPendingApproval?: (info: PendingApprovalInfo) => void;
}
interface OTPSourceFilter {
// OTP sources to accept: 'sms', 'email', 'whatsapp'
sources?: OTPSource[];
// Sender pattern with wildcards (e.g., '*@google.com')
senderPattern?: string;
// Content pattern to match in message
contentPattern?: string;
}OTPRequestResult
interface OTPRequestResult {
// Unique request ID
id: string;
// Current status
status: OTPRequestStatus;
// URL for user to approve (if pending)
approvalUrl?: string;
// WebSocket URL for real-time updates
webhookUrl?: string;
// When the request expires
expiresAt: string;
// Denial reason (if denied)
reason?: string;
}
type OTPRequestStatus =
| 'pending_approval' // Waiting for user approval
| 'approved' // Approved, waiting for OTP
| 'otp_received' // OTP ready to consume
| 'consumed' // OTP has been read
| 'denied' // User denied access
| 'expired' // Request expired
| 'cancelled'; // Request was cancelledExample
const request = await client.requestOTP({
reason: 'Sign up verification for Acme Inc',
expectedSender: 'Acme',
filter: {
sources: ['email'],
senderPattern: '*@acme.com',
},
publicKey: await exportPublicKey(publicKey),
ttl: 300,
waitForOTP: true,
timeout: 120000,
onPendingApproval: (info) => {
console.log(`Please approve at: ${info.approvalUrl}`);
},
});
if (request.status === 'otp_received') {
const { code } = await client.consumeOTP(request.id, privateKey);
console.log('OTP code:', code);
}getOTPStatus()
Check the current status of an OTP request.
const status = await client.getOTPStatus(
requestId: string
): Promise<OTPRequestResult>;
// Example
const status = await client.getOTPStatus('otp_abc123');
console.log(status.status); // 'pending_approval' | 'approved' | 'otp_received' | ...consumeOTP()
Consume and decrypt the OTP. This is a one-time operation - the OTP is deleted after reading.
const result = await client.consumeOTP(
requestId: string,
privateKey: CryptoKey
): Promise<OTPConsumeResult>;
interface OTPConsumeResult {
// The decrypted OTP code
code: string;
// Full message content (optional)
fullMessage?: string;
// Metadata about the OTP
metadata?: OTPMetadata;
}
interface OTPMetadata {
source: 'sms' | 'email' | 'whatsapp';
sender?: string;
subject?: string;
receivedAt: string;
}Example
// Load your private key
const privateKey = await importPrivateKey(process.env.AGENT_PRIVATE_KEY!);
// Consume the OTP
const { code, metadata } = await client.consumeOTP('otp_abc123', privateKey);
console.log('Code:', code); // e.g., "123456"
console.log('From:', metadata?.sender); // e.g., "noreply@acme.com"
console.log('Source:', metadata?.source); // e.g., "email"cancelOTPRequest()
Cancel a pending OTP request.
await client.cancelOTPRequest(requestId: string): Promise<void>;
// Example
await client.cancelOTPRequest('otp_abc123');Encryption Utilities
The SDK provides utilities for generating and managing encryption keys used for end-to-end encryption.
generateKeyPair()
Generate a new RSA key pair for E2E encryption.
import { generateKeyPair } from '@orrisai/agent-otp-sdk';
const { publicKey, privateKey } = await generateKeyPair();
// publicKey: CryptoKey (for encrypting)
// privateKey: CryptoKey (for decrypting)exportPublicKey() / exportPrivateKey()
Export keys to base64 strings for storage or transmission.
import {
exportPublicKey,
exportPrivateKey,
} from '@orrisai/agent-otp-sdk';
// Export to base64 strings
const publicKeyBase64 = await exportPublicKey(publicKey);
const privateKeyBase64 = await exportPrivateKey(privateKey);
// Store these securely
// publicKeyBase64 can be shared
// privateKeyBase64 must be kept secretimportPrivateKey()
Import a previously exported private key.
import { importPrivateKey } from '@orrisai/agent-otp-sdk';
// Load from environment variable or secrets manager
const privateKey = await importPrivateKey(process.env.AGENT_PRIVATE_KEY!);
// Use for consuming OTPs
const { code } = await client.consumeOTP(requestId, privateKey);Error Handling
The SDK throws typed errors for different failure scenarios:
import {
AgentOTPError,
AuthenticationError,
ValidationError,
RateLimitError,
NetworkError,
TimeoutError,
OTPNotFoundError,
OTPExpiredError,
OTPAlreadyConsumedError,
OTPApprovalDeniedError,
DecryptionError,
} from '@orrisai/agent-otp-sdk';
try {
const { code } = await client.consumeOTP(requestId, privateKey);
} catch (error) {
if (error instanceof OTPApprovalDeniedError) {
console.log('User denied access:', error.reason);
} else if (error instanceof OTPExpiredError) {
console.log('Request expired at:', error.expiredAt);
} else if (error instanceof OTPAlreadyConsumedError) {
console.log('OTP already used at:', error.consumedAt);
} else if (error instanceof DecryptionError) {
console.log('Failed to decrypt - wrong private key?');
} else if (error instanceof AuthenticationError) {
console.log('Invalid API key');
} else if (error instanceof RateLimitError) {
console.log('Rate limited, retry after:', error.retryAfter);
} else if (error instanceof AgentOTPError) {
console.log('API error:', error.code, error.message);
}
}Error Types
| Error | Description |
|---|---|
| AuthenticationError | Invalid or missing API key |
| ValidationError | Invalid request parameters |
| RateLimitError | Rate limit exceeded (has retryAfter) |
| TimeoutError | Request timed out |
| NetworkError | Network connectivity issue |
| OTPNotFoundError | No matching OTP request found |
| OTPExpiredError | OTP request has expired (has expiredAt) |
| OTPAlreadyConsumedError | OTP already read (has consumedAt) |
| OTPApprovalDeniedError | User denied OTP access (has reason) |
| DecryptionError | Failed to decrypt OTP payload |
Types
All types are exported from the package:
import type {
// Client config
AgentOTPClientConfig,
// OTP types
OTPSource,
OTPRequestStatus,
OTPSourceFilter,
OTPMetadata,
OTPConsumeResult,
// Request/Response
RequestOTPOptions,
OTPRequestResult,
PendingApprovalInfo,
} from '@orrisai/agent-otp-sdk';Best Practices
- Store private keys securely - Use environment variables or a secrets manager, never commit to source control
- Generate keys once - Reuse the same key pair for your agent; only regenerate if compromised
- Provide clear reasons - Help users make informed approval decisions with descriptive reasons
- Use filters - Narrow down expected senders to avoid capturing unrelated OTPs
- Handle all statuses - Always handle denied, expired, and error cases gracefully
- Consume promptly - Once an OTP is received, consume it quickly as requests have TTLs