# iraa.ai — Full Documentation > DemoAgent by iraa.ai: AI-powered interactive product demo platform. Deploy an AI agent that gives every website visitor a live, personalized product demo. --- ## Product Overview DemoAgent enables SaaS companies to embed a conversational AI demo agent on their websites. The agent: - Navigates the **actual product** inside a live iframe (not screenshots or recordings) - Answers visitor questions using product knowledge and documentation - Executes scripted demo flows (click, type, scroll, highlight) in real-time - Captures leads with configurable forms triggered by engagement milestones - Uses a multi-agent architecture powered by Claude (Anthropic) **Who it's for:** SaaS companies wanting to offer self-serve interactive product demos to website visitors without human involvement. **How it works:** 1. Define your product structure using a PDP (Product Discovery Protocol) YAML file 2. Upload the PDP via API or dashboard 3. Embed the widget on your website with a single script tag 4. Visitors interact with an AI agent that showcases your product live --- ## Quick Start ### Step 1: Create an Account ```bash curl -X POST https://api.iraa.ai/api/v1/auth/register \ -H "Content-Type: application/json" \ -d '{ "orgName": "My Company", "email": "admin@mycompany.com", "password": "securepassword123" }' ``` Response includes your API keys: ```json { "organization": { "id": "uuid", "name": "My Company", "slug": "my-company" }, "keys": { "publicKey": "pk_live_...", "secretKey": "sk_live_...", "testPublicKey": "pk_test_...", "testSecretKey": "sk_test_..." } } ``` ### Step 2: Upload a PDP Config ```bash curl -X POST https://api.iraa.ai/api/v1/pdp \ -H "Authorization: Bearer sk_live_YOUR_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{ "yaml": "pdp_version: \"1.0\"\nproduct:\n name: \"MyApp\"\n ..." }' ``` ### Step 3: Embed the Widget ```html ``` --- ## Widget Integration ### HTML Script Tag (Simplest) ```html ``` ### Programmatic Initialization ```html ``` ### React ```tsx import { useEffect } from 'react'; export function DemoAgentWidget({ apiKey }: { apiKey: string }) { useEffect(() => { const script = document.createElement('script'); script.src = 'https://cdn.iraa.ai/widget.js'; script.async = true; script.dataset.apiKey = apiKey; document.body.appendChild(script); return () => { document.body.removeChild(script); }; }, [apiKey]); return null; } // Usage: ``` ### Next.js ```tsx import Script from 'next/script'; export default function Layout({ children }) { return ( <> {children} ``` ### Configuration Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `apiKey` | `string` | *required* | Your public API key (`pk_live_...` or `pk_test_...`) | | `apiUrl` | `string` | `https://api.iraa.ai` | API base URL (override for self-hosted) | | `wsUrl` | `string` | derived from apiUrl | WebSocket URL (override for custom setup) | | `theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | Widget color theme | | `position` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Widget position on page | ### Events - `DemoAgentReady` — Dispatched on `window` when the widget script is loaded and ready for `init()`. --- ## Authentication DemoAgent uses two types of API keys: ### Public Keys (`pk_live_*`, `pk_test_*`) - Used in **client-side** widget code (visible to end users) - Domain-restricted: only work from whitelisted origins (configured in dashboard) - Can only access: session creation, chat, lead capture - Pass via `Authorization: Bearer pk_live_...` header ### Secret Keys (`sk_live_*`, `sk_test_*`) - Used in **server-side** code only (never expose to clients) - Full access: PDP upload, key management, analytics - Pass via `Authorization: Bearer sk_live_...` header ### Session Tokens - JWT issued when a session is created via `POST /api/v1/session` - Used to authenticate WebSocket connections and chat requests - Contains: sessionId, orgId, productId - Pass via `Authorization: Bearer ` header or `?token=` query param (WebSocket) --- ## REST API Reference **Base URL:** `https://api.iraa.ai` All request and response bodies are JSON unless otherwise noted. ### Auth Endpoints #### POST /api/v1/auth/register Create a new organization with initial user and API keys. **Auth:** None **Request body:** ```json { "orgName": "string (required)", "email": "string (required)", "password": "string (required, min 8 chars)" } ``` **Response (200):** ```json { "organization": { "id": "uuid", "name": "string", "slug": "string" }, "keys": { "publicKey": "pk_live_...", "secretKey": "sk_live_...", "testPublicKey": "pk_test_...", "testSecretKey": "sk_test_..." } } ``` #### POST /api/v1/auth/login Authenticate with email/password. Sets an httpOnly cookie (`da_session`). **Auth:** None **Request body:** ```json { "email": "string", "password": "string" } ``` **Response (200):** ```json { "user": { "id": "uuid", "email": "string", "name": "string", "role": "owner|admin|user" }, "organization": { "id": "uuid", "name": "string", "slug": "string", "plan": "string" } } ``` #### POST /api/v1/auth/logout Clear the auth cookie. **Auth:** None (clears existing cookie) **Response (200):** `{ "success": true }` #### GET /api/v1/auth/me Get the current authenticated user. **Auth:** Cookie (`da_session`) **Response (200):** ```json { "user": { "id": "uuid", "email": "string", "role": "string" }, "organization": { "id": "uuid", "name": "string", "slug": "string", "plan": "string" } } ``` #### GET /api/v1/auth/keys List API keys for the organization. **Auth:** Secret key (`sk_live_*` or `sk_test_*`) **Response (200):** ```json { "keys": [ { "id": "uuid", "type": "public|secret", "keyPrefix": "pk_live_abc...", "name": "string", "allowedDomains": ["string"], "lastUsedAt": "ISO8601|null", "isActive": true, "createdAt": "ISO8601" } ] } ``` #### POST /api/v1/auth/keys/rotate Rotate an API key (deactivates old key, creates new one). **Auth:** Secret key **Request body:** ```json { "keyId": "uuid" } ``` **Response (200):** ```json { "key": "pk_live_new_key_value...", "id": "uuid", "prefix": "pk_live_new..." } ``` --- ### Session Endpoints #### POST /api/v1/session Create a new demo session. Returns a session token, greeting message, and branding config. **Auth:** Public key (`pk_live_*` or `pk_test_*`) **Request body:** ```json { "productId": "string|'default' (required)", "visitorMeta": { "referrer": "string (optional)", "url": "string (optional)", "language": "string (optional)" } } ``` **Response (200):** ```json { "sessionId": "uuid", "token": "JWT session token", "greeting": { "text": "string", "speech": "string (TTS text)", "suggestions": ["string"] }, "agent": { "name": "string", "tone": "string" }, "branding": { "primaryColor": "#hex", "widgetPosition": "bottom-right|bottom-left", "agentName": "string", "logoUrl": "string|null" } } ``` #### POST /api/v1/session/:sessionId/chat Send a visitor message and get the agent's response. **Auth:** Session token (JWT from session creation) **Request body:** ```json { "message": "string (required)" } ``` **Response (200):** ```json { "text": "Agent's text response", "speech": "TTS-optimized text (optional)", "iframeCommands": [ { "action": "navigate|click|type|highlight|scroll|wait", "selector": "string", "value": "string" } ], "suggestions": ["Suggested reply 1", "Suggested reply 2"], "agentId": "demo|qa|discovery|docs", "usage": { "inputTokens": 0, "outputTokens": 0 } } ``` #### POST /api/v1/session/:sessionId/lead Capture lead information. **Auth:** Session token **Request body:** ```json { "email": "string (optional)", "company": "string (optional)", "name": "string (optional)", "phone": "string (optional)" } ``` **Response (200):** ```json { "leadId": "uuid", "captured": true } ``` #### POST /api/v1/session/:sessionId/end End a demo session. **Auth:** Session token **Response (200):** ```json { "ended": true } ``` --- ### PDP Endpoints #### POST /api/v1/pdp Upload and validate a PDP YAML file. Creates or updates the product and sets the config as active. **Auth:** Secret key (`sk_live_*`) **Request body:** ```json { "yaml": "string (PDP YAML content, required)", "productId": "uuid (optional, auto-resolved by product name)" } ``` **Response (200):** ```json { "id": "uuid (config ID)", "productId": "uuid", "version": 1, "product": "Product Name", "screens": 5, "flows": 2, "warnings": ["string"] } ``` **Response (422) — Validation failed:** ```json { "error": "PDP validation failed", "details": [{ "path": "string", "message": "string" }], "warnings": ["string"] } ``` #### POST /api/v1/pdp/validate Validate a PDP YAML without storing it. **Auth:** None **Request body:** ```json { "yaml": "string (PDP YAML content)" } ``` **Response (200):** ```json { "valid": true, "errors": [], "warnings": [], "summary": { "product": "Product Name", "screens": 5, "flows": 2, "demoAccounts": 1, "faqs": 3 } } ``` #### GET /api/v1/pdp/:id Retrieve a PDP config by ID. **Auth:** Secret key **Response (200):** Full PDP config record (id, productId, version, config, isActive, etc.) #### GET /api/v1/pdp/product/:productId/active Get the active PDP config for a product. **Auth:** None (public endpoint) **Response (200):** Full PDP config record --- ### Discovery Endpoints Discovery allows auto-generating a PDP by crawling your product's website. #### POST /api/v1/discover/start Start a discovery session. Returns a token URL to begin crawling. **Auth:** Cookie (dashboard user) **Request body:** ```json { "productId": "uuid" } ``` **Response (200):** ```json { "sessionId": "uuid", "token": "JWT", "discoveryUrl": "https://yourapp.com?da_discover=TOKEN", "expiresAt": "ISO8601", "product": { "id": "uuid", "name": "string", "baseUrl": "string" } } ``` #### POST /api/v1/discover/validate-token Validate a discovery token (called by the discovery script). **Auth:** None (token-based) **Request body:** `{ "token": "JWT" }` **Response (200):** `{ "valid": true, "sessionId": "uuid", "productId": "uuid" }` #### POST /api/v1/discover/snapshot Submit a page snapshot during discovery. **Auth:** Discovery token **Request body:** ```json { "sessionId": "uuid", "token": "JWT", "snapshot": { "urlPath": "/dashboard", "pageTitle": "Dashboard", "headings": ["string"], "elements": [{}], "navigation": [{}], "forms": [{}], "viewport": { "width": 1920, "height": 1080 } } } ``` **Response (200):** `{ "status": "saved", "id": "uuid", "uniquePages": 5 }` #### POST /api/v1/discover/agent-chat Chat with the discovery agent during exploration. **Auth:** Discovery token **Request body:** ```json { "sessionId": "uuid", "token": "JWT", "message": "This page is our main analytics dashboard", "currentSnapshot": {} } ``` **Response (200):** `{ "reply": "Agent's response text" }` #### POST /api/v1/discover/finalize End discovery and generate a PDP YAML. **Auth:** Discovery token **Request body:** `{ "sessionId": "uuid", "token": "JWT" }` **Response (200):** `{ "status": "complete", "pdpYaml": "YAML string" }` #### GET /api/v1/discover/status?productId=uuid Check discovery progress. **Auth:** Cookie (dashboard user) **Response (200):** ```json { "hasSession": true, "status": "active|finalizing|complete|expired", "uniquePages": 8, "totalElements": 142, "generatedPdp": "YAML string|null", "sessionId": "uuid" } ``` --- ### Dashboard Endpoints All require cookie authentication (dashboard login). - `GET /api/v1/dashboard/overview` — Product, session, and lead counts - `GET /api/v1/dashboard/products` — List products for the organization - `POST /api/v1/dashboard/products` — Create a new product - `GET /api/v1/dashboard/keys` — List active API keys - `POST /api/v1/dashboard/keys/:id/rotate` — Rotate an API key - `GET /api/v1/dashboard/org` — Get organization details - `PUT /api/v1/dashboard/org` — Update organization - `POST /api/v1/dashboard/pdp` — Upload PDP via dashboard - `GET /api/v1/dashboard/analytics` — Session analytics and recent sessions --- ## WebSocket API Real-time communication for live demo sessions. ### Connection ``` wss://api.iraa.ai/ws/session/:sessionId?token=SESSION_JWT ``` The session JWT is obtained from `POST /api/v1/session`. ### Client to Server Messages All messages are JSON with `type` and `payload` fields. #### visitor_message Send a chat message from the visitor. ```json { "type": "visitor_message", "payload": { "text": "Show me the dashboard" } } ``` #### heartbeat Keep the connection alive. ```json { "type": "heartbeat", "payload": {} } ``` #### iframe_event Report an event from the demo iframe (click, navigation, etc.). ```json { "type": "iframe_event", "payload": { "event": "click", "selector": "#btn", "url": "/page" } } ``` ### Server to Client Messages #### agent_response Agent's reply with text, TTS audio reference, iframe commands, and suggested replies. ```json { "type": "agent_response", "payload": { "text": "Here's the dashboard! Let me show you the pipeline.", "speech": "Here's the dashboard. Let me show you the pipeline.", "iframeCommands": [ { "action": "navigate", "url": "/dashboard" }, { "action": "highlight", "selector": "[data-testid='pipeline-chart']" } ], "suggestions": ["Create a deal", "Show contacts", "Tell me about pricing"], "agentId": "demo" } } ``` #### typing_indicator Agent is processing a response. ```json { "type": "typing_indicator", "payload": { "agentId": "demo", "isTyping": true } } ``` #### lead_form Trigger the lead capture form in the widget. ```json { "type": "lead_form", "payload": { "fields": ["email", "company"], "cta": { "text": "Start Free Trial", "url": "https://example.com/signup" } } } ``` #### error Error occurred during processing. ```json { "type": "error", "payload": { "code": "SESSION_EXPIRED", "message": "Session expired" } } ``` Error codes: `AUTH_REQUIRED`, `INVALID_TOKEN`, `SESSION_NOT_FOUND`, `SESSION_EXPIRED`, `INVALID_JSON`, `EMPTY_MESSAGE`, `UNKNOWN_TYPE`, `PDP_NOT_FOUND` #### pong Response to heartbeat. ```json { "type": "pong", "payload": { "ts": 1711000000000 } } ``` --- ## PDP Schema Reference PDP (Product Discovery Protocol) is a YAML specification that defines everything about your product's demo: screens, elements, flows, agent personality, and configuration. ### Minimal Example ```yaml pdp_version: "1.0" product: name: "MyApp" base_url: "https://demo.myapp.com" description: "Project management for small teams" industry: "SaaS / Productivity" target_personas: - "Project Manager" agent: name: "Alex" personality: "Friendly and knowledgeable product expert" tone: "friendly" llm: provider: "anthropic" model: "claude-sonnet-4-20250514" temperature: 0.5 max_tokens: 1200 demo_accounts: - id: "default_user" role: "Admin" credentials: login_url: "https://demo.myapp.com/login" login_method: "form" fields: "[name='email']": "enc:BASE64_ENCRYPTED_EMAIL" "[name='password']": "enc:BASE64_ENCRYPTED_PASSWORD" submit_selector: "button[type='submit']" is_default: true pool_size: 3 screens: - id: "dashboard" route: "/dashboard" title: "Dashboard" purpose: "Main overview with project stats" elements: - id: "create_project_btn" type: "button" selector: "[data-testid='create-project']" label: "Create Project" flows: - id: "create_project" title: "Create a New Project" description: "Walk through creating a project" trigger_phrases: - "create a project" - "new project" role: "default_user" steps: - screen_id: "dashboard" action: "click" element_id: "create_project_btn" narration: "Let me click the Create Project button." knowledge: product_faqs: - question: "Is there a free plan?" answer: "Yes, free for up to 5 users." config: lead_capture: enabled: true fields: ["email", "company"] trigger: "after_3_steps" branding: primary_color: "#6366f1" widget_position: "bottom-right" allowed_domains: - "myapp.com" - "localhost" ``` ### Complete Schema Reference #### `pdp_version` (required) - Type: `string` - Value: `"1.0"` #### `product` (required) | Field | Type | Required | Description | |-------|------|----------|-------------| | `name` | string | yes | Product name | | `version` | string | no | Product version | | `base_url` | string (URL) | yes | Demo environment URL | | `description` | string | yes | Product description | | `industry` | string | yes | Industry category | | `target_personas` | string[] | yes | Target user roles | #### `agent` (required) | Field | Type | Required | Description | |-------|------|----------|-------------| | `name` | string | yes | Agent display name | | `personality` | string | yes | System prompt personality description | | `tone` | `'friendly' \| 'professional' \| 'technical' \| 'casual'` | yes | Agent communication style | | `voice` | object | no | TTS config: `{ provider, voice_id }` | | `llm` | object | yes | LLM configuration | **`agent.llm`:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `provider` | string | yes | `"anthropic"` | | `model` | string | yes | Model ID (e.g., `"claude-sonnet-4-20250514"`) | | `temperature` | number | yes | 0.0 - 1.0 | | `max_tokens` | number | yes | Max response tokens | #### `demo_accounts` (required, array) | Field | Type | Required | Description | |-------|------|----------|-------------| | `id` | string | yes | Unique account identifier | | `role` | string | yes | User role name | | `credentials` | object | yes | Login configuration | | `credentials.login_url` | string (URL) | yes | Login page URL | | `credentials.login_method` | `'form' \| 'api' \| 'sso'` | yes | Auth method | | `credentials.fields` | object | yes | CSS selector → encrypted value pairs | | `credentials.submit_selector` | string | no | Submit button CSS selector | | `permissions_summary` | string | no | Description of account permissions | | `is_default` | boolean | no | Default account for new sessions | | `pool_size` | number | no | Number of concurrent sessions this account supports | | `reset_strategy` | `'api' \| 'db_snapshot' \| 'soft_delete' \| 'none'` | no | How to reset account state between sessions | #### `screens` (required, array) | Field | Type | Required | Description | |-------|------|----------|-------------| | `id` | string | yes | Unique screen identifier | | `route` | string | yes | URL path (e.g., `"/dashboard"`) | | `title` | string | yes | Human-readable screen name | | `purpose` | string | yes | What this screen is for | | `wait_for_selector` | string | no | CSS selector to wait for before screen is "ready" | | `elements` | array | yes | Interactive elements on this screen | | `tags` | string[] | no | Categorization tags | | `related_screens` | string[] | no | IDs of related screens | **`screens[].elements[]`:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `id` | string | yes | Unique element identifier | | `type` | string | yes | `button \| input \| select \| table \| chart \| tab \| link \| modal \| section \| textarea \| checkbox \| radio \| toggle` | | `selector` | string | yes | CSS selector or `data-testid` | | `label` | string | yes | Human-readable label | | `description` | string | no | What this element does | #### `example_values` (optional) Provides realistic data for demo form fills and searches: ```yaml example_values: entities: customers: - name: "Horizon Technologies" email: "sarah@horizontech.com" deals: - name: "Enterprise License" value: "$48,000" form_fills: new_deal: "[name='dealName']": "{{entities.deals[0].name}}" search_queries: - "Horizon Technologies" ``` #### `flows` (required, array) | Field | Type | Required | Description | |-------|------|----------|-------------| | `id` | string | yes | Unique flow identifier | | `title` | string | yes | Flow name | | `description` | string | yes | What this flow demonstrates | | `trigger_phrases` | string[] | yes | Phrases that activate this flow | | `role` | string | yes | demo_account ID to use | | `persona` | string | no | Target persona for this flow | | `estimated_duration_seconds` | number | no | Expected duration | | `steps` | array | yes | Ordered list of demo steps | **`flows[].steps[]`:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `screen_id` | string | yes | Target screen ID | | `action` | string | yes | `navigate \| click \| fill \| select \| highlight \| wait \| scroll` | | `element_id` | string | no | Target element ID (for click, fill, select, highlight) | | `value` | string | no | Value to fill or select | | `narration` | string | yes | What the agent says during this step | | `wait_ms` | number | no | Delay after this step (milliseconds) | #### `knowledge` (required) | Field | Type | Required | Description | |-------|------|----------|-------------| | `product_faqs` | array | yes | `{ question, answer, category? }` | | `competitive_positioning` | object | no | Competitor comparisons | | `pricing_info` | object | no | `{ can_disclose, plans: [{ name, price, features }] }` | | `docs_url` | string | no | External documentation URL | | `custom_instructions` | string | no | Additional agent instructions | #### `config` (required) | Field | Type | Required | Description | |-------|------|----------|-------------| | `lead_capture.enabled` | boolean | yes | Enable lead capture | | `lead_capture.fields` | string[] | yes | Fields to capture: `email`, `company`, `name`, `phone` | | `lead_capture.trigger` | string | yes | `'after_N_steps'` or `'never'` | | `cta` | object | no | `{ text, url }` — call-to-action button | | `branding.primary_color` | string | yes | Hex color (e.g., `"#6366f1"`) | | `branding.widget_position` | string | yes | `'bottom-right'` or `'bottom-left'` | | `branding.agent_name` | string | no | Override agent display name | | `branding.logo_url` | string | no | Custom logo URL | | `idle_nudge_seconds` | number | no | Seconds before idle nudge | | `session_timeout_seconds` | number | no | Session timeout | | `allowed_domains` | string[] | yes | Domains where the widget can run | | `gdpr_mode` | boolean | no | Enable GDPR-compliant mode | --- ## Multi-Agent Architecture DemoAgent uses a graph-based multi-agent system: ``` Visitor Message | v Router Agent (classifies intent) | +---> Demo Agent — navigates product, showcases features via iframe commands +---> Q&A Agent — answers questions using product knowledge + RAG +---> Discovery Agent — explores product structure with visitor +---> Docs Agent — references technical documentation ``` **Intent types:** `demo_request`, `qa`, `discovery`, `handoff`, `chitchat`, `off_topic` The Router Agent classifies each message and delegates to the appropriate specialized agent. Agents share conversation context and can hand off to each other mid-conversation. --- ## npm Packages | Package | Description | |---------|-------------| | `@iraaai/widget` | Embeddable chat widget (Web Component with Shadow DOM) | | `@iraaai/core` | Orchestrator, iframe controller, automation engine | | `@iraaai/pdp-schema` | PDP YAML validation and parsing (`parsePDP(yaml)`) | | `@iraaai/server` | Multi-agent graph, LLM adapters, RAG pipeline | | `@iraaai/overlay` | Visual highlights, tooltips, spotlights, pulse animations | | `@iraaai/cli` | CLI tools for PDP validation and credential encryption | --- ## Health & Meta Endpoints - `GET /` — API info: `{ name: "DemoAgent API", version: "1.0.0" }` - `GET /health` — Health check: `{ status: "ok" }` --- ## Full PDP Example See the complete example PDP for a CRM product: https://iraa.ai/docs/pdp#full-example --- ## Links - **Website:** https://iraa.ai - **Dashboard:** https://app.iraa.ai - **API Base URL:** https://api.iraa.ai - **Widget CDN:** https://cdn.iraa.ai/widget.js - **Contact:** hello@iraa.ai