SDK Integration

The AI Interview SDK allows you to embed voice-to-voice interviews directly into your website or application. No backend required.

Installation

Using npm:

npm install @ai-interview/sdk

Using yarn:

yarn add @ai-interview/sdk

Using CDN:

<script src="https://unpkg.com/@ai-interview/sdk@latest"></script>

Quick Start

Basic Usage

import InterviewSDK from '@ai-interview/sdk';

// Mount the interview embed
const embed = InterviewSDK.mount('#interview-container', {
  invite: 'YOUR_INVITE_TOKEN',
});

// Listen to events
embed.on('completed', (payload) => {
  console.log('Interview completed:', payload);
});

HTML + Script Tag

<!DOCTYPE html>
<html>
<head>
  <title>AI Interview</title>
</head>
<body>
  <div id="interview-container"></div>

  <script src="https://unpkg.com/@ai-interview/sdk@latest"></script>
  <script>
    InterviewSDK.mount('#interview-container', {
      invite: 'inv_8f7d9e2c1a0b3h4k5j6m',
    });
  </script>
</body>
</html>

Configuration

Mount Options

NameTypeDescription
invite*stringUnique invite token for the interview session
baseUrlstringBase URL for the embed (default: production URL)
themeobjectCustom theme colors and styling
localestringLanguage locale (e.g., "en", "es", "fr")
autoStartbooleanAuto-start interview without consent screen (default: false)

Theme Options

Customize the look and feel:

InterviewSDK.mount('#interview-container', {
  invite: 'YOUR_TOKEN',
  theme: {
    primary: '#146EF5',      // Primary brand color
    background: '#0f172a',   // Background color
    text: '#f1f5f9',         // Text color
    borderRadius: '12px',    // Border radius for elements
    fontFamily: 'Inter, sans-serif', // Custom font
  },
});

White Label

The Pro and Enterprise plans allow full white-labeling, including custom domains and removal of AI Interview branding.

Events

Event Listeners

Listen to interview lifecycle events:

const embed = InterviewSDK.mount('#container', options);

// Interview lifecycle
embed.on('start', () => {
  console.log('Interview started');
});

embed.on('pause', () => {
  console.log('Interview paused');
});

embed.on('resume', () => {
  console.log('Interview resumed');
});

embed.on('completed', (payload) => {
  console.log('Interview completed:', payload);
  // payload.sessionId - Session ID for API calls
  // payload.transcript - Partial transcript (if enabled)
});

// Error handling
embed.on('error', (payload) => {
  console.error('Error:', payload.message);
  // Handle errors gracefully
});

// Progress tracking
embed.on('progress', (payload) => {
  console.log(`Progress: ${payload.percentage}%`);
  // Update UI with progress
});

Event Reference

NameTypeDescription
startvoidInterview has started
pausevoidInterview was paused by participant
resumevoidInterview was resumed
progress{ percentage: number }Progress update (0-100)
completed{ sessionId: string, transcript?: string }Interview successfully completed
error{ message: string, code: string }An error occurred

Methods

Instance Methods

const embed = InterviewSDK.mount('#container', options);

// Add event listener
embed.on('completed', handler);

// Remove event listener
embed.off('completed', handler);

// Remove embed from DOM
embed.destroy();

// Get current session ID (if started)
const sessionId = embed.getSessionId();

// Check if interview is in progress
const isActive = embed.isActive();

Advanced Usage

React Integration

import { useEffect, useRef } from 'react';
import InterviewSDK from '@ai-interview/sdk';

function InterviewEmbed({ inviteToken }) {
  const containerRef = useRef(null);
  const embedRef = useRef(null);

  useEffect(() => {
    if (containerRef.current && !embedRef.current) {
      embedRef.current = InterviewSDK.mount(containerRef.current, {
        invite: inviteToken,
        theme: {
          primary: '#146EF5',
        },
      });

      embedRef.current.on('completed', (payload) => {
        console.log('Interview completed:', payload.sessionId);
        // Handle completion
      });
    }

    return () => {
      if (embedRef.current) {
        embedRef.current.destroy();
        embedRef.current = null;
      }
    };
  }, [inviteToken]);

  return <div ref={containerRef} className="interview-container" />;
}

Vue Integration

<template>
  <div ref="interviewContainer" class="interview-container"></div>
</template>

<script>
import InterviewSDK from '@ai-interview/sdk';

export default {
  name: 'InterviewEmbed',
  props: {
    inviteToken: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      embed: null,
    };
  },
  mounted() {
    this.embed = InterviewSDK.mount(this.$refs.interviewContainer, {
      invite: this.inviteToken,
    });

    this.embed.on('completed', (payload) => {
      this.$emit('completed', payload);
    });
  },
  beforeUnmount() {
    if (this.embed) {
      this.embed.destroy();
    }
  },
};
</script>

Custom Invite Tokens

Generate invite tokens via API, then use in SDK:

// 1. Get invite token from your backend
const response = await fetch('/api/create-interview', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ userId: '123' }),
});

const { inviteToken } = await response.json();

// 2. Mount SDK with dynamic token
InterviewSDK.mount('#container', {
  invite: inviteToken,
});

Styling

Container Sizing

The embed automatically fills its container:

.interview-container {
  width: 100%;
  max-width: 800px;
  height: 600px;
  margin: 0 auto;
  border-radius: 12px;
  overflow: hidden;
}

Responsive Design

.interview-container {
  width: 100%;
  aspect-ratio: 16 / 9;
  max-width: 1000px;
}

@media (max-width: 768px) {
  .interview-container {
    aspect-ratio: 9 / 16;
    height: 100vh;
  }
}

Security

Token Validation

Invite tokens are:

  • One-time use only
  • Expire after configured period
  • Cryptographically signed
  • Cannot be reused after completion

Domain Restrictions

Restrict which domains can embed interviews:

  1. Go to SettingsSecurity
  2. Enable Domain Whitelist
  3. Add allowed domains
  4. SDK will refuse to load on unauthorized domains

Production Domains

Always configure domain restrictions before launching to production to prevent unauthorized embedding.

Error Handling

Common Errors

embed.on('error', (error) => {
  switch (error.code) {
    case 'INVALID_TOKEN':
      // Token is invalid or expired
      console.error('Invalid or expired invite token');
      break;
    
    case 'ALREADY_USED':
      // Token was already used
      console.error('This interview has already been completed');
      break;
    
    case 'MICROPHONE_DENIED':
      // User denied microphone access
      console.error('Microphone permission required');
      break;
    
    case 'NETWORK_ERROR':
      // Network connectivity issues
      console.error('Network connection lost');
      break;
    
    default:
      console.error('An error occurred:', error.message);
  }
});

Graceful Degradation

Handle unsupported browsers:

if (!InterviewSDK.isSupported()) {
  document.getElementById('interview-container').innerHTML = `
    <div class="unsupported-browser">
      <h3>Unsupported Browser</h3>
      <p>Please use Chrome, Firefox, Safari, or Edge to participate in this interview.</p>
    </div>
  `;
} else {
  InterviewSDK.mount('#interview-container', options);
}

Browser Support

| Browser | Minimum Version | |---------|----------------| | Chrome | 80+ | | Firefox | 75+ | | Safari | 14+ | | Edge | 80+ |

WebRTC and Web Audio API are required. Mobile browsers on iOS and Android are fully supported.

Examples

See our Examples section for complete working demos:

Troubleshooting

Embed Not Loading

Check:

  • Invite token is valid and not expired
  • Container element exists in DOM
  • No JavaScript errors in console
  • Domain is whitelisted (if restrictions enabled)

Audio Issues

Common causes:

  • Microphone permission not granted
  • Using HTTP instead of HTTPS (required for mic access)
  • Browser doesn't support WebRTC
  • Firewall blocking WebSocket connections

Event Not Firing

Ensure:

  • Event listener added before interview starts
  • Correct event name (case-sensitive)
  • Handler function is valid
  • No errors breaking JavaScript execution