Reference
<voice-orch-mic> widget
Standards-compliant Web Component. No framework dependency. Loads everything it needs (incl. the runtime client) from a single ~23 KB script.
Attributes
| Name | Required | Description |
|---|---|---|
token-endpoint | Yes | URL the widget POSTs to for a connect token. Your endpoint must respond with JSON { websocketUrl, connectToken }. See Quickstart for examples. |
end-user-id | No | Stable identifier forwarded to your token endpoint. Useful for per-user analytics and for RAG features that scope to a user. Your server should normally override this from the signed-in session rather than trusting the browser value. |
metadata | No | JSON string forwarded as metadataJson. Free-form; used by the voice_orch event log so you can correlate sessions to your own systems. |
label | No | Accessible label on the mic button. Defaults to "Toggle voice". |
auto-start | No | "true" attempts to start the session on mount. Browsers still require a user gesture for mic permission; this works only if the element is mounted in response to one. |
Events
All events bubble and have composed: true, so they cross shadow-DOM boundaries. Listen on the element itself, or on any ancestor.
| Event name | event.detail shape | When it fires |
|---|---|---|
voiceorch:status | { status, message? } | State changes: idle → connecting → live → idle/error. |
voiceorch:transcript | { role, text } | A user or assistant turn finalizes. For streaming text, listen for voiceorch:event with response.output_text.delta. |
voiceorch:tool-call | { name, arguments, raw } | The agent invoked a function tool. Useful for UI affordances (e.g. "Booking…"). |
voiceorch:error | { message, code } | Token fetch failed, WebSocket refused, runtime fault. Widget switches to error state; user can click to retry. |
voiceorch:event | raw runtime event | Every event the runtime emits, untouched. Use this if you want full protocol access. See WebSocket protocol for the schema. |
Programmatic API
You can drive the element from JavaScript without clicking the button. Useful for embedding voice as a feature inside a larger UI you already control.
const mic = document.querySelector('voice-orch-mic');
await mic.start(); // open the session (asks for mic permission)
mic.sendText('hello'); // send a text turn into the running session
mic.stop(); // close the session (mic, WS, playback)Theming
The widget uses shadow-DOM scoping so its internals don't collide with your styles. Customize via CSS custom properties on the element (or any ancestor that inherits down).
voice-orch-mic {
/* size of the round mic button (px or rem) */
--voiceorch-size: 64px;
/* idle / hover */
--voiceorch-bg: #0f172a;
--voiceorch-fg: #ffffff;
/* state-specific backgrounds */
--voiceorch-bg-connecting: #2563eb;
--voiceorch-bg-live: #16a34a;
--voiceorch-bg-error: #dc2626;
/* keyboard focus ring */
--voiceorch-focus: #60a5fa;
/* status text color (the small label next to the button) */
--voiceorch-status: currentColor;
/* font (defaults to system-ui stack) */
--voiceorch-font: 'Inter', system-ui, sans-serif;
}Framework wrappers
Web Components work in every modern framework, but the wiring style varies. Patterns below mostly differ in event-handler syntax.
'use client';
import { useEffect, useRef } from 'react';
type Props = {
tokenEndpoint: string;
endUserId?: string;
onTranscript?: (role: string, text: string) => void;
};
export function VoiceMic({ tokenEndpoint, endUserId, onTranscript }: Props) {
const ref = useRef<HTMLElement | null>(null);
useEffect(() => {
const el = ref.current;
if (!el || !onTranscript) return;
const handler = (e: Event) => {
const detail = (e as CustomEvent).detail as { role: string; text: string };
onTranscript(detail.role, detail.text);
};
el.addEventListener('voiceorch:transcript', handler);
return () => el.removeEventListener('voiceorch:transcript', handler);
}, [onTranscript]);
return (
// @ts-expect-error: custom element not in JSX intrinsics
<voice-orch-mic
ref={ref}
token-endpoint={tokenEndpoint}
end-user-id={endUserId}
/>
);
}<script setup lang="ts">
import { ref, onMounted } from 'vue';
const props = defineProps<{ tokenEndpoint: string; endUserId?: string }>();
const emit = defineEmits<{ (e: 'transcript', role: string, text: string): void }>();
const micRef = ref<HTMLElement | null>(null);
onMounted(() => {
micRef.value?.addEventListener('voiceorch:transcript', (event) => {
const d = (event as CustomEvent).detail;
emit('transcript', d.role, d.text);
});
});
</script>
<template>
<voice-orch-mic
ref="micRef"
:token-endpoint="props.tokenEndpoint"
:end-user-id="props.endUserId"
/>
</template>Vite / esbuild config. If your bundler complains about unknown custom elements, add 'voice-orch-mic' to the framework-specific custom-element allowlist (e.g. compilerOptions.isCustomElement in Vue, or just ignore the TypeScript JSX warning in React).