InterviewRelay

Static HTML Example#

Embed an InterviewRelay AI interview directly into any static HTML page — no build tools, no framework, just plain HTML, CSS, and JavaScript.

Complete Working Demo

Copy the complete example below into an .html file, replace the invite token, and open it in your browser. That's all you need to get started.


Complete Example#

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>AI Interview</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
        Oxygen, Ubuntu, sans-serif;
      background: #0a0a0a;
      color: #ffffff;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }

    .header {
      text-align: center;
      margin-bottom: 2rem;
    }

    .header h1 {
      font-size: 1.5rem;
      font-weight: 600;
      margin-bottom: 0.5rem;
    }

    .header p {
      color: #888;
      font-size: 0.9rem;
    }

    #interview-container {
      width: 100%;
      max-width: 800px;
      min-height: 500px;
      border-radius: 12px;
      overflow: hidden;
      border: 1px solid #222;
      background: #111;
    }

    .loading {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 500px;
      color: #888;
    }

    .error {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 500px;
      color: #ef4444;
      padding: 2rem;
      text-align: center;
    }

    @media (max-width: 640px) {
      #interview-container {
        border-radius: 0;
        border-left: none;
        border-right: none;
        min-height: 100vh;
      }

      body {
        justify-content: flex-start;
      }

      .header {
        padding: 1rem;
      }
    }
  </style>
</head>
<body>

  <div class="header">
    <h1>Your Interview</h1>
    <p>Click the button below to start your AI-powered interview</p>
  </div>

  <div id="interview-container">
    <div class="loading">Loading interview...</div>
  </div>

  <!-- 1. Include the InterviewRelay SDK -->
  <script src="https://cdn.interviewrelay.com/sdk/v1/interviewrelay.min.js"></script>

  <script>
    // 2. Get the invite token (from URL parameter, server, or API)
    const params = new URLSearchParams(window.location.search);
    const inviteToken = params.get('token');

    if (!inviteToken) {
      document.getElementById('interview-container').innerHTML =
        '<div class="error">No invite token found. Please use the link from your invitation email.</div>';
    } else {
      // 3. Mount the interview SDK
      InterviewRelay.mount('#interview-container', {
        inviteToken: inviteToken,
        theme: {
          primaryColor: '#6366f1',
          backgroundColor: '#111111',
          textColor: '#ffffff',
          borderRadius: '12px',
        },
        onReady: function () {
          console.log('Interview ready');
        },
        onStart: function () {
          console.log('Interview started');
        },
        onComplete: function (result) {
          console.log('Interview completed:', result);

          document.getElementById('interview-container').innerHTML =
            '<div style="display:flex;align-items:center;justify-content:center;height:500px;color:#22c55e;font-size:1.2rem;">' +
            '✓ Thank you for completing the interview!' +
            '</div>';
        },
        onError: function (error) {
          console.error('Interview error:', error);

          document.getElementById('interview-container').innerHTML =
            '<div class="error">Something went wrong: ' + error.message + '</div>';
        },
      });
    }
  </script>

</body>
</html>

Breaking It Down#

1. Include the SDK#

Add the InterviewRelay SDK script tag to your page. This loads the InterviewRelay global object.

<script src="https://cdn.interviewrelay.com/sdk/v1/interviewrelay.min.js"></script>

SDK Versions

The /v1/ URL always points to the latest stable v1 release. For production, you can pin a specific version: https://cdn.interviewrelay.com/sdk/v1.2.3/interviewrelay.min.js

2. Create a Container#

Create an HTML element where the interview widget will be rendered.

<div id="interview-container"></div>

The container should have a defined width and minimum height. The SDK will fill the container and handle its own responsive layout.

3. Mount the SDK#

Call InterviewRelay.mount() with a CSS selector and configuration options:

InterviewRelay.mount('#interview-container', {
  inviteToken: 'your_invite_token_here',
  onComplete: function (result) {
    console.log('Done!', result);
  },
});
NameTypeDescription
inviteToken*stringThe invite token for this participant. Generated server-side via the API.
themeobjectCustom theme options (colors, border radius, etc.).
localestringLanguage locale code (e.g., 'en', 'es', 'fr'). Defaults to 'en'.
autoStartbooleanAutomatically start the interview without user interaction. Defaults to false.
onReadyfunctionCalled when the interview widget is fully loaded and ready.
onStartfunctionCalled when the participant starts the interview.
onCompletefunction(result)Called when the interview is completed. Receives the result object.
onErrorfunction(error)Called when an error occurs. Receives the error object.

4. Handle Events#

The SDK emits lifecycle events you can listen to:

InterviewRelay.mount('#interview-container', {
  inviteToken: inviteToken,

  onReady: function () {
    // Widget is loaded, hide your loading spinner
    document.querySelector('.loading').style.display = 'none';
  },

  onStart: function () {
    // Participant clicked "Start Interview"
    analytics.track('interview_started');
  },

  onComplete: function (result) {
    // Interview finished
    // result.sessionId - the session ID
    // result.duration - duration in seconds
    // result.questionsAnswered - number of questions answered
    analytics.track('interview_completed', result);
    window.location.href = '/thank-you';
  },

  onError: function (error) {
    // Something went wrong
    // error.code - error code
    // error.message - human-readable message
    console.error('Interview error:', error);
  },
});

Getting the Invite Token#

The invite token identifies the participant and the campaign. There are several ways to provide it to your page.

The simplest approach — include the token in the invite link:

https://yoursite.com/interview?token=inv_abc123xyz
const params = new URLSearchParams(window.location.search);
const inviteToken = params.get('token');

Server-Side Rendering#

If you're using a server-side language, inject the token into the page:

<!-- PHP -->
<script>
  var inviteToken = '<?php echo htmlspecialchars($invite_token); ?>';
</script>

<!-- EJS / Node.js -->
<script>
  var inviteToken = '<%= inviteToken %>';
</script>

<!-- Jinja2 / Python -->
<script>
  var inviteToken = '{{ invite_token }}';
</script>

API Fetch#

Generate a token on the fly by calling your own backend:

async function getInviteToken(participantEmail) {
  const response = await fetch('/api/create-invite', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email: participantEmail }),
  });

  const data = await response.json();
  return data.invite_token;
}

// Usage
const token = await getInviteToken('user@example.com');
InterviewRelay.mount('#interview-container', {
  inviteToken: token,
});

Customization#

Theme Colors#

Customize the interview widget to match your brand:

InterviewRelay.mount('#interview-container', {
  inviteToken: inviteToken,
  theme: {
    primaryColor: '#6366f1',       // Buttons, links, accent elements
    backgroundColor: '#111111',    // Widget background
    textColor: '#ffffff',          // Primary text color
    secondaryTextColor: '#888888', // Muted/secondary text
    borderRadius: '12px',         // Corner radius for cards and buttons
    fontFamily: 'Inter, sans-serif',
  },
});

Auto-Start#

Skip the start screen and begin the interview immediately:

InterviewRelay.mount('#interview-container', {
  inviteToken: inviteToken,
  autoStart: true,
});

Microphone Permission

When using autoStart, the browser will immediately prompt for microphone permission. Make sure the participant expects this — consider showing a notice before the widget loads.

Locale#

Set the interview language:

InterviewRelay.mount('#interview-container', {
  inviteToken: inviteToken,
  locale: 'es', // Spanish
});

Supported locales: en, es, fr, de, pt, ja, ko, zh.


Mobile Optimization#

The SDK is fully responsive, but here are some tips for the best mobile experience:

<!-- Always include the viewport meta tag -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
/* Make the container full-width on mobile */
@media (max-width: 640px) {
  #interview-container {
    border-radius: 0;
    border-left: none;
    border-right: none;
    min-height: 100vh;
    max-width: 100%;
  }
}

Mobile Browsers

On iOS Safari, the interview widget automatically handles the virtual keyboard and viewport resizing. No additional configuration is needed.


Error Handling#

Browser Compatibility#

The SDK supports all modern browsers. Add a fallback for older browsers:

if (!window.InterviewRelay) {
  document.getElementById('interview-container').innerHTML =
    '<div class="error">' +
    'Your browser is not supported. Please use the latest version of ' +
    'Chrome, Firefox, Safari, or Edge.' +
    '</div>';
}

Network Errors#

Handle connectivity issues gracefully:

InterviewRelay.mount('#interview-container', {
  inviteToken: inviteToken,
  onError: function (error) {
    if (error.code === 'NETWORK_ERROR') {
      document.getElementById('interview-container').innerHTML =
        '<div class="error">' +
        'Connection lost. Please check your internet and ' +
        '<a href="javascript:location.reload()" style="color:#6366f1;">reload the page</a>.' +
        '</div>';
    } else if (error.code === 'TOKEN_EXPIRED') {
      document.getElementById('interview-container').innerHTML =
        '<div class="error">' +
        'This interview link has expired. Please request a new invitation.' +
        '</div>';
    } else if (error.code === 'TOKEN_INVALID') {
      document.getElementById('interview-container').innerHTML =
        '<div class="error">' +
        'Invalid interview link. Please check your invitation email.' +
        '</div>';
    } else {
      document.getElementById('interview-container').innerHTML =
        '<div class="error">Something went wrong: ' + error.message + '</div>';
    }
  },
});

Next Steps#