---
title: "Embedding"
description: "Add the Speak AI recorder to any page. Includes the fix for Wix and Webflow, which strip microphone and camera permissions."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.speakai.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Embedding

If the Embed recorder is not working on your site, such as Wix or Webflow, please follow the following steps.

Usually, websites like Wix or Webflow remove the Microphone and Camera permission which creates an issue for a recorder on any browser.

Please include the below script to include the permission in your iFrame dynamically.

```html
<script>
function addPermissions(iframe) {
    const ALLOW_PERMISSIONS = 'camera; microphone; fullscreen; display-capture';
    if (iframe.allow === ALLOW_PERMISSIONS) { return }
    iframe.setAttribute('allow', ALLOW_PERMISSIONS)
    iframe.allow = ALLOW_PERMISSIONS
    iframe.src = iframe.src
}

document.querySelectorAll('iframe').forEach(addPermissions);

const observer = new MutationObserver((mutationsList, observer) => {
    for (let mutation of mutationsList) {
        if (mutation.addedNodes && mutation.addedNodes.length) {
            for (let node of mutation.addedNodes) {
                if (node.querySelectorAll) {
           node.querySelectorAll('iframe').forEach(addPermissions)
                }
            }
        }
    }
})
observer.observe(document.body, { attributes: false, childList: true, subtree: true })
</script>
```

Please ensure to include the above script to **End of the Body** to include the permission to all the iFrames on the page.

## For Wix:

Here're a few steps to follow for the Wix website.<br /><br />Go to **Settings** and scroll to **Advanced**(the last section), and you can see Custom Code.<br /><br />Include the **`script`** code under the **`body - END`** option.<br /><br />That will ask you to apply on all the pages or specific pages.

## Iframe Controls - Quick Start Guide

## 5-Minute Quick Start, Speak AI Recorder Embed

***

[Test Embed Iframe](https://recorder.speakai.co/assets/embed-tester.html)

### Step 1: Basic Iframe Embedding

```html
<!-- Minimal Embedding -->
<iframe
  src="https://recorder.speakai.co/iframe/YOUR_TOKEN_HERE"
  allow="microphone; camera"
  width="100%"
  height="700px">
</iframe>
```

***

### Step 2: Add Query Parameters

```html
<!-- With Query Parameters -->
<iframe
  src="https://recorder.speakai.co/iframe/YOUR_TOKEN_HERE?hideWaveform=true&hideTitle=true&submitLabel=Send"
  allow="microphone; camera"
  width="100%"
  height="700px">
</iframe>
```

***

### Step 3: Add PostMessage Control

```js
// Get iframe reference
const iframe = document.querySelector('iframe');

// Start recording
iframe.contentWindow.postMessage({ action: 'start' }, 'https://recorder.speakai.co');

// Stop recording
iframe.contentWindow.postMessage({ action: 'stop' }, 'https://recorder.speakai.co');

// Listen for responses
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://recorder.speakai.co') return;
  const response = JSON.parse(event.data);
  console.log('Response:', response);
});
```

***

### ⚙️ Query Parameters Reference

| **Parameter** | **Type** | **Default** | **Description** |
| --- | --- | --- | --- |
| hideWaveform | boolean | false | Hide audio waveform visualization |
| hideTitle | boolean | false | Hide title/header text |
| submitLabel | string | "Upload" | Custom submit button text |
| hideSubmit | boolean | false | Hide submit button |
| preselect | string | "audio" \| "video" \| "upload" \| "screenshare" | Pre-select recording type |
| name | string | "" | Pre-fill name field |
| email | string | "" | Pre-fill email field |
| folderId | string | "" | Pre-fill folder ID |
| field1 - field10 | string | "" | Pre-fill custom question answers (up to 10) |

### Examples

```text
Hide waveform:
?hideWaveform=true

Hide title:
?hideTitle=true

Custom button:
?submitLabel=Send%20Recording

All combined:
?hideWaveform=true&hideTitle=true&submitLabel=Complete

Pre-select recording type:
?preselect=video

Pre-fill name:
?name=John%20Doe

Pre-fill email:
?email=john.doe@example.com

Pre-fill folder ID:
?folderId=123456

Pre-fill custom question answers:
?field1=Answer%201&field2=Answer%202&field3=Answer%203
```

***

### 🔄 PostMessage API Reference

### Commands (Parent → Iframe)

```js
// Start recording
{
  action: 'start',
  timestamp: Date.now()  // optional
}

// Stop recording
{
  action: 'stop',
  timestamp: Date.now()  // optional
}
```

### Responses (Iframe → Parent)

```js
// Success
{
  source: 'speak-embed-recorder',
  status: 'success',
  message: 'Recording started',
  timestamp: '2025-10-09T10:30:00.000Z',
  data: { action: 'start' }
}

// Error
{
  source: 'speak-embed-recorder',
  status: 'error',
  message: 'Recording already in progress',
  timestamp: '2025-10-09T10:30:00.000Z'
}
```

***

### 💡 Common Patterns

### Pattern 1: Minimal UI

```html
<iframe
  src="https://recorder.speakai.co/iframe/TOKEN?hideWaveform=true&hideTitle=true"
  allow="microphone; camera"
  style="width: 100%; height: 500px; border: none;">
</iframe>
```

### Pattern 2: Custom Branding

```html
<iframe
  src="https://recorder.speakai.co/iframe/TOKEN?submitLabel=Submit%20to%20Support"
  allow="microphone; camera">
</iframe>
```

### Pattern 3: External Controls

```html
<div class="recording-controls">
  <button onclick="startRecording()">🔴 Start</button>
  <button onclick="stopRecording()">⏹ Stop</button>
  <div id="status">Ready</div>
</div>

<iframe id="recorder" src="https://recorder.speakai.co/iframe/TOKEN"></iframe>

<script>
const iframe = document.getElementById('recorder');
const status = document.getElementById('status');

function startRecording() {
  iframe.contentWindow.postMessage({action: 'start'}, '*');
  status.textContent = 'Recording...';
}

function stopRecording() {
  iframe.contentWindow.postMessage({action: 'stop'}, '*');
  status.textContent = 'Stopped';
}

window.addEventListener('message', (e) => {
  const response = JSON.parse(e.data);
  if (response.status === 'error') {
status.textContent = 'Error: ' + response.message;
  }
});
</script>
```

***

### ⚙️ Troubleshooting

### Iframe Not Loading

```js
const iframe = document.querySelector('iframe');
console.log('Iframe loaded:', iframe.contentWindow !== null);

iframe.addEventListener('load', () => {
  console.log('Iframe loaded successfully');
});
```

### PostMessage Not Working

```js
const iframe = document.querySelector('iframe');

console.log('Iframe found:', iframe !== null);
console.log('ContentWindow:', iframe.contentWindow);

function sendDebugMessage(action) {
  console.log('Sending:', action);
  iframe.contentWindow.postMessage({ action: action }, '*');
  console.log('Message sent');
}

sendDebugMessage('start');
```

### Parameters Not Applied

```js
const iframe = document.querySelector('iframe');
console.log('Iframe src:', iframe.src);

const url = new URL(iframe.src);
console.log('hideWaveform:', url.searchParams.get('hideWaveform'));
console.log('hideTitle:', url.searchParams.get('hideTitle'));
console.log('submitLabel:', url.searchParams.get('submitLabel'));
```

***

### ⚛️ Framework Examples

### React

```js
import { useEffect, useRef } from 'react';

function RecorderEmbed({ token }) {
  const iframeRef = useRef(null);

  useEffect(() => {
const handleMessage = (event) => {
  if (event.origin !== 'https://recorder.speakai.co') return;
  const response = JSON.parse(event.data);
  console.log('Recorder:', response);
};

window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
  }, []);

  const startRecording = () => {
iframeRef.current?.contentWindow.postMessage({ action: 'start' }, 'https://recorder.speakai.co');
  };

  const stopRecording = () => {
iframeRef.current?.contentWindow.postMessage({ action: 'stop' }, 'https://recorder.speakai.co');
  };

  return (
<div>
  <div>
    <button onClick={startRecording}>Start</button>
    <button onClick={stopRecording}>Stop</button>
  </div>
  <iframe
    ref={iframeRef}
    src={`https://recorder.speakai.co/iframe/${token}?hideWaveform=true`}
    allow="microphone; camera"
    style=#{{ width: '100%', height: '700px', border: 'none' }}
  />
</div>
  );
}
```

***

### Vue

```html
<template>
  <div>
<div>
  <button @click="startRecording">Start</button>
  <button @click="stopRecording">Stop</button>
</div>
<iframe
  ref="recorder"
  :src="iframeUrl"
  allow="microphone; camera"
  style="width: 100%; height: 700px; border: none"
/>
  </div>
</template>

<script>
export default {
  props: ['token'],
  computed: {
iframeUrl() {
  return `https://recorder.speakai.co/iframe/${this.token}?hideWaveform=true`;
}
  },
  mounted() {
window.addEventListener('message', this.handleMessage);
  },
  beforeUnmount() {
window.removeEventListener('message', this.handleMessage);
  },
  methods: {
handleMessage(event) {
  if (event.origin !== 'https://recorder.speakai.co') return;
  const response = JSON.parse(event.data);
  console.log('Recorder:', response);
},
startRecording() {
  this.$refs.recorder.contentWindow.postMessage(
    { action: 'start' },
    'https://recorder.speakai.co'
  );
},
stopRecording() {
  this.$refs.recorder.contentWindow.postMessage(
    { action: 'stop' },
    'https://recorder.speakai.co'
  );
}
  }
}
</script>
```

***

### Angular

```js
import { Component, ElementRef, ViewChild, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-recorder',
  template: `
<div>
  <button (click)="startRecording()">Start</button>
  <button (click)="stopRecording()">Stop</button>
</div>
<iframe
  #recorder
  [src]="iframeUrl"
  allow="microphone; camera"
  style="width: 100%; height: 700px; border: none">
</iframe>
  `
})
export class RecorderComponent implements OnInit, OnDestroy {
  @ViewChild('recorder') iframeElement: ElementRef;
  iframeUrl = 'https://recorder.speakai.co/iframe/TOKEN?hideWaveform=true';

  ngOnInit() {
window.addEventListener('message', this.handleMessage);
  }

  ngOnDestroy() {
window.removeEventListener('message', this.handleMessage);
  }

  handleMessage = (event: MessageEvent) => {
if (event.origin !== 'https://recorder.speakai.co') return;
const response = JSON.parse(event.data);
console.log('Recorder:', response);
  };

  startRecording() {
const iframe = this.iframeElement.nativeElement as HTMLIFrameElement;
iframe.contentWindow.postMessage({ action: 'start' }, 'https://recorder.speakai.co');
  }

  stopRecording() {
const iframe = this.iframeElement.nativeElement as HTMLIFrameElement;
iframe.contentWindow.postMessage({ action: 'stop' }, 'https://recorder.speakai.co');
  }
}
```

***

### Support

For issues or questions:

1. Check the troubleshooting section above
1. Review the full test plan document
1. Use the test embedder page to debug
1. Check the browser console for error messages
1. Contact us at **[success@speakai.co](mailto:success@speakai.co)**

New to Speak AI? [Create a Speak AI account](https://speakai.co/?utm_source=docs&utm_medium=referral&utm_campaign=help&utm_content=help-article-embed-a-recorder-on-your-site) and work through Getting Started.

## Iframe controls

## 5-Minute Quick Start, Speak AI Recorder Embed

***

[Test Embed Iframe](https://recorder.speakai.co/assets/embed-tester.html)

### Step 1: Basic Iframe Embedding

```html
<!-- Minimal Embedding -->
<iframe
  src="https://recorder.speakai.co/iframe/YOUR_TOKEN_HERE"
  allow="microphone; camera"
  width="100%"
  height="700px">
</iframe>
```

***

### Step 2: Add Query Parameters

```html
<!-- With Query Parameters -->
<iframe
  src="https://recorder.speakai.co/iframe/YOUR_TOKEN_HERE?hideWaveform=true&hideTitle=true&submitLabel=Send"
  allow="microphone; camera"
  width="100%"
  height="700px">
</iframe>
```

***

### Step 3: Add PostMessage Control

```js
// Get iframe reference
const iframe = document.querySelector('iframe');

// Start recording
iframe.contentWindow.postMessage({ action: 'start' }, 'https://recorder.speakai.co');

// Stop recording
iframe.contentWindow.postMessage({ action: 'stop' }, 'https://recorder.speakai.co');

// Listen for responses
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://recorder.speakai.co') return;
  const response = JSON.parse(event.data);
  console.log('Response:', response);
});
```

***

### ⚙️ Query Parameters Reference

| **Parameter** | **Type** | **Default** | **Description** |
| --- | --- | --- | --- |
| hideWaveform | boolean | false | Hide audio waveform visualization |
| hideTitle | boolean | false | Hide title/header text |
| submitLabel | string | "Upload" | Custom submit button text |
| hideSubmit | boolean | false | Hide submit button |
| preselect | string | "audio" \| "video" \| "upload" \| "screenshare" | Pre-select recording type |
| name | string | "" | Pre-fill name field |
| email | string | "" | Pre-fill email field |
| folderId | string | "" | Pre-fill folder ID |
| field1 - field10 | string | "" | Pre-fill custom question answers (up to 10) |

### Examples

```text
Hide waveform:
?hideWaveform=true

Hide title:
?hideTitle=true

Custom button:
?submitLabel=Send%20Recording

All combined:
?hideWaveform=true&hideTitle=true&submitLabel=Complete

Pre-select recording type:
?preselect=video

Pre-fill name:
?name=John%20Doe

Pre-fill email:
?email=john.doe@example.com

Pre-fill folder ID:
?folderId=123456

Pre-fill custom question answers:
?field1=Answer%201&field2=Answer%202&field3=Answer%203
```

***

### 🔄 PostMessage API Reference

### Commands (Parent → Iframe)

```js
// Start recording
{
  action: 'start',
  timestamp: Date.now()  // optional
}

// Stop recording
{
  action: 'stop',
  timestamp: Date.now()  // optional
}
```

### Responses (Iframe → Parent)

```js
// Success
{
  source: 'speak-embed-recorder',
  status: 'success',
  message: 'Recording started',
  timestamp: '2025-10-09T10:30:00.000Z',
  data: { action: 'start' }
}

// Error
{
  source: 'speak-embed-recorder',
  status: 'error',
  message: 'Recording already in progress',
  timestamp: '2025-10-09T10:30:00.000Z'
}
```

***

### 💡 Common Patterns

### Pattern 1: Minimal UI

```html
<iframe
  src="https://recorder.speakai.co/iframe/TOKEN?hideWaveform=true&hideTitle=true"
  allow="microphone; camera"
  style="width: 100%; height: 500px; border: none;">
</iframe>
```

### Pattern 2: Custom Branding

```html
<iframe
  src="https://recorder.speakai.co/iframe/TOKEN?submitLabel=Submit%20to%20Support"
  allow="microphone; camera">
</iframe>
```

### Pattern 3: External Controls

```html
<div class="recording-controls">
  <button onclick="startRecording()">🔴 Start</button>
  <button onclick="stopRecording()">⏹ Stop</button>
  <div id="status">Ready</div>
</div>

<iframe id="recorder" src="https://recorder.speakai.co/iframe/TOKEN"></iframe>

<script>
const iframe = document.getElementById('recorder');
const status = document.getElementById('status');

function startRecording() {
  iframe.contentWindow.postMessage({action: 'start'}, '*');
  status.textContent = 'Recording...';
}

function stopRecording() {
  iframe.contentWindow.postMessage({action: 'stop'}, '*');
  status.textContent = 'Stopped';
}

window.addEventListener('message', (e) => {
  const response = JSON.parse(e.data);
  if (response.status === 'error') {
status.textContent = 'Error: ' + response.message;
  }
});
</script>
```

***

### ⚙️ Troubleshooting

### Iframe Not Loading

```js
const iframe = document.querySelector('iframe');
console.log('Iframe loaded:', iframe.contentWindow !== null);

iframe.addEventListener('load', () => {
  console.log('Iframe loaded successfully');
});
```

### PostMessage Not Working

```js
const iframe = document.querySelector('iframe');

console.log('Iframe found:', iframe !== null);
console.log('ContentWindow:', iframe.contentWindow);

function sendDebugMessage(action) {
  console.log('Sending:', action);
  iframe.contentWindow.postMessage({ action: action }, '*');
  console.log('Message sent');
}

sendDebugMessage('start');
```

### Parameters Not Applied

```js
const iframe = document.querySelector('iframe');
console.log('Iframe src:', iframe.src);

const url = new URL(iframe.src);
console.log('hideWaveform:', url.searchParams.get('hideWaveform'));
console.log('hideTitle:', url.searchParams.get('hideTitle'));
console.log('submitLabel:', url.searchParams.get('submitLabel'));
```

***

### ⚛️ Framework Examples

### React

```js
import { useEffect, useRef } from 'react';

function RecorderEmbed({ token }) {
  const iframeRef = useRef(null);

  useEffect(() => {
const handleMessage = (event) => {
  if (event.origin !== 'https://recorder.speakai.co') return;
  const response = JSON.parse(event.data);
  console.log('Recorder:', response);
};

window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
  }, []);

  const startRecording = () => {
iframeRef.current?.contentWindow.postMessage({ action: 'start' }, 'https://recorder.speakai.co');
  };

  const stopRecording = () => {
iframeRef.current?.contentWindow.postMessage({ action: 'stop' }, 'https://recorder.speakai.co');
  };

  return (
<div>
  <div>
    <button onClick={startRecording}>Start</button>
    <button onClick={stopRecording}>Stop</button>
  </div>
  <iframe
    ref={iframeRef}
    src={`https://recorder.speakai.co/iframe/${token}?hideWaveform=true`}
    allow="microphone; camera"
    style=#{{ width: '100%', height: '700px', border: 'none' }}
  />
</div>
  );
}
```

***

### Vue

```html
<template>
  <div>
<div>
  <button @click="startRecording">Start</button>
  <button @click="stopRecording">Stop</button>
</div>
<iframe
  ref="recorder"
  :src="iframeUrl"
  allow="microphone; camera"
  style="width: 100%; height: 700px; border: none"
/>
  </div>
</template>

<script>
export default {
  props: ['token'],
  computed: {
iframeUrl() {
  return `https://recorder.speakai.co/iframe/${this.token}?hideWaveform=true`;
}
  },
  mounted() {
window.addEventListener('message', this.handleMessage);
  },
  beforeUnmount() {
window.removeEventListener('message', this.handleMessage);
  },
  methods: {
handleMessage(event) {
  if (event.origin !== 'https://recorder.speakai.co') return;
  const response = JSON.parse(event.data);
  console.log('Recorder:', response);
},
startRecording() {
  this.$refs.recorder.contentWindow.postMessage(
    { action: 'start' },
    'https://recorder.speakai.co'
  );
},
stopRecording() {
  this.$refs.recorder.contentWindow.postMessage(
    { action: 'stop' },
    'https://recorder.speakai.co'
  );
}
  }
}
</script>
```

***

### Angular

```js
import { Component, ElementRef, ViewChild, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-recorder',
  template: `
<div>
  <button (click)="startRecording()">Start</button>
  <button (click)="stopRecording()">Stop</button>
</div>
<iframe
  #recorder
  [src]="iframeUrl"
  allow="microphone; camera"
  style="width: 100%; height: 700px; border: none">
</iframe>
  `
})
export class RecorderComponent implements OnInit, OnDestroy {
  @ViewChild('recorder') iframeElement: ElementRef;
  iframeUrl = 'https://recorder.speakai.co/iframe/TOKEN?hideWaveform=true';

  ngOnInit() {
window.addEventListener('message', this.handleMessage);
  }

  ngOnDestroy() {
window.removeEventListener('message', this.handleMessage);
  }

  handleMessage = (event: MessageEvent) => {
if (event.origin !== 'https://recorder.speakai.co') return;
const response = JSON.parse(event.data);
console.log('Recorder:', response);
  };

  startRecording() {
const iframe = this.iframeElement.nativeElement as HTMLIFrameElement;
iframe.contentWindow.postMessage({ action: 'start' }, 'https://recorder.speakai.co');
  }

  stopRecording() {
const iframe = this.iframeElement.nativeElement as HTMLIFrameElement;
iframe.contentWindow.postMessage({ action: 'stop' }, 'https://recorder.speakai.co');
  }
}
```

***

### Support

For issues or questions:

1. Check the troubleshooting section above
1. Review the full test plan document
1. Use the test embedder page to debug
1. Check the browser console for error messages
1. Contact us at **[success@speakai.co](mailto:success@speakai.co)**

New to Speak AI? [Create a Speak AI account](https://speakai.co/?utm_source=docs&utm_medium=referral&utm_campaign=help&utm_content=help-article-iframe-controls-quick-start-guide) and work through Getting Started.

Evaluating Speak AI for a team? [Book a demo and see your own recordings analyzed](https://calendly.com/speak-ai/demo?utm_source=docs&utm_campaign=book-demo).

---
Related: [Embeddable recorder](/help/recorder/) · [Browser support](/help/recorder/browser-support/)

Source: https://docs.speakai.co/help/recorder/embedding/index.mdx
