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-sdk

AgentOTPClient

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

PropertyTypeRequiredDescription
apiKeystringYesYour Agent OTP API key
baseUrlstringNoAPI base URL (default: https://api.agentotp.com)
timeoutnumberNoRequest timeout in ms (default: 30000)
retryAttemptsnumberNoNumber 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 cancelled

Example

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 secret

importPrivateKey()

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

ErrorDescription
AuthenticationErrorInvalid or missing API key
ValidationErrorInvalid request parameters
RateLimitErrorRate limit exceeded (has retryAfter)
TimeoutErrorRequest timed out
NetworkErrorNetwork connectivity issue
OTPNotFoundErrorNo matching OTP request found
OTPExpiredErrorOTP request has expired (has expiredAt)
OTPAlreadyConsumedErrorOTP already read (has consumedAt)
OTPApprovalDeniedErrorUser denied OTP access (has reason)
DecryptionErrorFailed 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

See Also