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

NameRequiredDescription
token-endpointYesURL the widget POSTs to for a connect token. Your endpoint must respond with JSON { websocketUrl, connectToken }. See Quickstart for examples.
end-user-idNoStable 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.
metadataNoJSON string forwarded as metadataJson. Free-form; used by the voice_orch event log so you can correlate sessions to your own systems.
labelNoAccessible label on the mic button. Defaults to "Toggle voice".
auto-startNo"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 nameevent.detail shapeWhen 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:eventraw runtime eventEvery 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.

JavaScript
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).

CSS
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.

components/VoiceMic.tsxReact / Next.js
'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}
    />
  );
}
VoiceMic.vueVue 3
<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).