{"site":{"name":"Koji","description":"AI-native customer research platform that helps teams conduct, analyze, and synthesize customer interviews at scale.","url":"https://www.koji.so","contentTypes":["blog","documentation"],"lastUpdated":"2026-05-18T13:46:16.284Z"},"content":[{"type":"documentation","id":"658b5225-6556-47c2-bded-7c5659bea401","slug":"embed-widget-reference","title":"Embed Widget Reference","url":"https://www.koji.so/docs/embed-widget-reference","summary":"The Koji embed widget places interactive interviews inside websites via iframe. Served from koji.so/api/v1/embed/:projectId with dark theme by default and 12px border-radius. Supports external_id for respondent tracking, structured question widgets, PostMessage API for two-way communication, and voice mode with the microphone permission.","content":"# Embed Widget Reference\n\nThe Koji embed widget lets you place an interactive interview directly inside your website or application using an iframe. This reference covers every parameter, the PostMessage API for two-way communication, and styling options.\n\n---\n\n## Basic Embed Code\n\nThe simplest embed uses a single iframe tag:\n\n```html\n<iframe\n  src=\"https://koji.so/api/v1/embed/:project_id\"\n  width=\"400\"\n  height=\"600\"\n  frameborder=\"0\"\n  allow=\"microphone\"\n></iframe>\n```\n\nReplace `:project_id` with your actual project ID. The `allow=\"microphone\"` attribute is required if your interview uses voice mode.\n\n---\n\n## iframe URL Parameters\n\nAppend these as query parameters to the embed URL:\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `theme` | string | `dark` | Visual theme: `light` or `dark` |\n| `accent_color` | string | Project default | Hex color code without the hash (e.g., `4F46E5`) |\n| `hide_header` | boolean | `false` | Hides the widget header bar |\n| `hide_branding` | boolean | `false` | Removes Koji branding from the widget (requires Growth+ plan) |\n| `respondent_name` | string | — | Pre-fills the respondent name |\n| `respondent_email` | string | — | Pre-fills the respondent email |\n| `external_id` | string | — | Your own identifier for the respondent, useful for linking back to your system |\n| `language` | string | Project default | Language code (e.g., `en`, `es`, `fr`) |\n| `mode` | string | Project default | Interview mode: `text` or `voice` |\n| `metadata` | string | — | URL-encoded JSON object of custom metadata |\n| `auto_start` | boolean | `false` | Automatically starts the interview when the widget loads |\n\n### Using external_id\n\nThe `external_id` parameter lets you pre-fill a respondent identifier from your own system. This is useful for tracking which of your users completed the interview without requiring them to enter information manually. It is passed through to webhooks, exports, and the API responses.\n\n```html\n<iframe\n  src=\"https://koji.so/api/v1/embed/a1b2c3d4?external_id=user_12345\"\n  width=\"400\"\n  height=\"600\"\n  frameborder=\"0\"\n></iframe>\n```\n\n### Example with Parameters\n\n```html\n<iframe\n  src=\"https://koji.so/api/v1/embed/a1b2c3d4?theme=dark&accent_color=4F46E5&respondent_name=Jane&auto_start=true\"\n  width=\"400\"\n  height=\"600\"\n  frameborder=\"0\"\n  allow=\"microphone\"\n></iframe>\n```\n\n---\n\n## Structured Question Widgets\n\nWhen your research brief includes [structured questions](/docs/structured-questions-guide), the embed widget automatically renders interactive input widgets during the interview. These include:\n\n- **Scale widgets** — sliders or number selectors for rating questions\n- **Choice widgets** — radio buttons for single choice, checkboxes for multiple choice\n- **Ranking widgets** — drag-and-drop lists for ranking questions\n- **Yes/No widgets** — toggle buttons for binary questions\n\nThe widgets are styled to match your theme and accent color settings. Responses from these widgets are captured as structured data, enabling quantitative analysis alongside the qualitative conversation.\n\n---\n\n## PostMessage API\n\nThe embed widget communicates with your host page via the browser's `postMessage` API. This enables two-way interaction: you can send commands to the widget, and the widget sends events back to you.\n\n### Sending Commands to the Widget\n\nUse `postMessage` on the iframe's content window:\n\n```javascript\nconst iframe = document.getElementById('koji-embed');\n\n// Start the interview programmatically\niframe.contentWindow.postMessage({\n  type: 'koji:start',\n  payload: {\n    respondent_name: 'Jane Doe',\n    metadata: { source: 'homepage' }\n  }\n}, 'https://koji.so');\n```\n\n### Available Commands\n\n| Command Type | Payload | Description |\n|---|---|---|\n| `koji:start` | `{ respondent_name?, respondent_email?, metadata? }` | Starts the interview with optional respondent info |\n| `koji:complete` | `{}` | Ends the interview and triggers analysis |\n| `koji:set_theme` | `{ theme: 'light' \\| 'dark' }` | Changes the theme dynamically |\n| `koji:set_accent` | `{ color: '#4F46E5' }` | Changes the accent color dynamically |\n| `koji:reset` | `{}` | Resets the widget to its initial state |\n\n### Receiving Events from the Widget\n\nListen for messages from the iframe:\n\n```javascript\nwindow.addEventListener('message', (event) => {\n  if (event.origin !== 'https://koji.so') return;\n\n  const { type, payload } = event.data;\n\n  switch (type) {\n    case 'koji:ready':\n      console.log('Widget is loaded and ready');\n      break;\n    case 'koji:interview_started':\n      console.log('Interview started:', payload.interview_id);\n      break;\n    case 'koji:interview_completed':\n      console.log('Interview completed:', payload.interview_id);\n      break;\n    case 'koji:error':\n      console.error('Widget error:', payload.message);\n      break;\n  }\n});\n```\n\n### Available Events\n\n| Event Type | Payload | When It Fires |\n|---|---|---|\n| `koji:ready` | `{}` | Widget has loaded and is ready to accept commands |\n| `koji:interview_started` | `{ interview_id }` | An interview session has begun |\n| `koji:message_sent` | `{ message_count }` | The respondent sent a message |\n| `koji:interview_completed` | `{ interview_id, stats }` | The interview has ended |\n| `koji:error` | `{ code, message }` | An error occurred in the widget |\n| `koji:resize` | `{ width, height }` | The widget's ideal size changed (useful for responsive containers) |\n\nAlways check `event.origin` before processing messages to ensure they come from `https://koji.so`.\n\n---\n\n## Responsive Sizing\n\nThe embed widget adapts to the dimensions you give it, but works best within certain ranges:\n\n| Dimension | Minimum | Recommended | Maximum |\n|---|---|---|---|\n| Width | 320px | 400px | 100% of container |\n| Height | 500px | 600px | 100% of viewport |\n\nFor a responsive layout, use CSS to make the iframe fluid:\n\n```html\n<div style=\"width: 100%; max-width: 480px; height: 70vh; min-height: 500px;\">\n  <iframe\n    src=\"https://koji.so/api/v1/embed/:project_id\"\n    style=\"width: 100%; height: 100%; border: none; border-radius: 12px;\"\n    allow=\"microphone\"\n  ></iframe>\n</div>\n```\n\nListen for `koji:resize` events to dynamically adjust the container size based on the widget's content.\n\n---\n\n## Styling and Branding\n\nThe widget defaults to a dark theme. Use `?theme=light` in the URL to switch to light mode. The default border-radius is 12px.\n\nThe widget respects your [branding settings](/docs/customizing-branding) configured in the project dashboard. Beyond that, the URL parameters let you override theme and accent color per embed instance.\n\nThe iframe itself can be styled with CSS on the host page:\n\n```css\n#koji-embed {\n  border: none;\n  border-radius: 12px;\n  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);\n}\n```\n\nHiding Koji branding with `hide_branding=true` requires a Growth plan or higher.\n\n---\n\n## Security Considerations\n\n- **Always validate `event.origin`** when listening for PostMessage events. Only accept messages from `https://koji.so`.\n- **Do not pass sensitive data** through URL parameters. Use the PostMessage API for any information you do not want visible in the URL.\n- **The `allow` attribute** should only include `microphone` if your interview uses voice mode. Do not grant unnecessary permissions.\n\n---\n\n## Browser Support\n\nThe embed widget works in all modern browsers. Voice features perform best in Chrome and Edge. See [Browser Compatibility](/docs/browser-compatibility) for the full support matrix.\n\n---\n\n## Next Steps\n\n- [Get started with the embed widget](/docs/using-the-embed-widget)\n- [Customize your project branding](/docs/customizing-branding)\n- [Learn about structured questions](/docs/structured-questions-guide)\n- [Explore the headless API overview](/docs/headless-api-overview)","category":"API Reference","lastModified":"2026-05-14T03:18:53.717222+00:00","metaTitle":"Embed Widget Reference — Koji Docs","metaDescription":"Technical reference for the Koji embed widget covering iframe parameters, PostMessage API, styling, and responsive design.","keywords":["embed widget","iframe","postmessage api","embed parameters","widget integration"],"aiSummary":"The Koji embed widget places interactive interviews inside websites via iframe. Served from koji.so/api/v1/embed/:projectId with dark theme by default and 12px border-radius. Supports external_id for respondent tracking, structured question widgets, PostMessage API for two-way communication, and voice mode with the microphone permission.","aiPrerequisites":["using-the-embed-widget"],"aiLearningOutcomes":["Configure iframe parameters","Use the PostMessage API for commands and events","Implement responsive widget sizing","Apply branding and styling"],"aiDifficulty":"intermediate","aiEstimatedTime":"8 min read"}],"pagination":{"total":1,"returned":1,"offset":0}}