Quick Start

Get up and running with Agent OTP in under 5 minutes. This guide will walk you through installation, configuration, and your first permission request.

Prerequisites

  • Node.js 18+ or Bun 1.0+
  • Docker and Docker Compose (for self-hosting)
  • An existing AI agent or application

Step 1: Start Agent OTP Server

Clone the repository and start the server locally using Docker:

# Clone the repository
git clone https://github.com/orristech/agent-otp.git
cd agent-otp

# Copy environment template
cp .env.example .env

# Start all services
docker compose up -d

The API will be available at http://localhost:3000.

Step 2: Create an agent and API key

Generate an API key using the CLI:

# Create a new agent and generate API key
docker compose exec api bun run cli agent:create --name "my-assistant"

# Output:
# Agent created successfully!
# API Key: ak_live_xxxxxxxxxxxxxxxxxxxx

Important: Save this API key securely. It will only be shown once and cannot be retrieved later.

Step 3: Install the SDK

Install the Agent OTP SDK in your project:

npm
npm install @orrisai/agent-otp-sdk
bun
bun add @orrisai/agent-otp-sdk
pnpm
pnpm add @orrisai/agent-otp-sdk

Step 4: Configure the client

Initialize the Agent OTP client with your API key. We recommend using environment variables:

// lib/otp.ts
import { AgentOTPClient } from '@orrisai/agent-otp-sdk';

export const otp = new AgentOTPClient({
  apiKey: process.env.AGENT_OTP_KEY!,
  // Point to your local or self-hosted instance
  baseUrl: process.env.AGENT_OTP_URL || 'http://localhost:3000',
});

Step 5: Request your first permission

Now you can request permissions for sensitive operations. Here's a complete example:

import { otp } from './lib/otp';

async function sendInvoiceEmail(clientEmail: string, invoiceId: string) {
  // Request permission to send an email
  const permission = await otp.requestPermission({
    action: 'email.send',
    resource: `email:${clientEmail}`,
    scope: {
      max_emails: 1,
      allowed_recipients: [clientEmail],
    },
    context: {
      reason: `Sending invoice #${invoiceId} to client`,
      invoiceId,
    },
    waitForApproval: true, // Blocks until approved/denied
    timeout: 60000, // 60 second timeout
  });

  // Check the result
  if (permission.status === 'approved') {
    console.log('Permission granted! Token:', permission.token);

    // Use the token to send the email
    // The token is scoped to exactly this operation
    await yourEmailService.send({
      to: clientEmail,
      subject: `Invoice #${invoiceId}`,
      // Pass the OTP token for verification
      otpToken: permission.token,
    });

    // Mark the token as used
    await otp.useToken(permission.token, {
      recipient: clientEmail,
      invoiceId,
    });

    return { success: true };
  } else if (permission.status === 'denied') {
    console.log('Permission denied:', permission.reason);
    return { success: false, error: 'Permission denied' };
  } else {
    console.log('Permission timed out');
    return { success: false, error: 'Approval timeout' };
  }
}

Step 6: Configure policies (optional)

By default, all permission requests require human approval. You can configure policies to auto-approve safe operations:

# Auto-approve file reads under 1MB
- name: "Auto-approve small file reads"
  conditions:
    action:
      equals: "file.read"
    scope.max_size:
      less_than: 1048576
  action: auto_approve

# Require approval for any financial operation
- name: "Financial operations"
  conditions:
    action:
      starts_with: "bank."
  action: require_approval
  priority: 100

See the Policies documentation for more details.

Next steps

Need help?

If you run into any issues, we're here to help: