Skip to main content
An integration is a folder. At its center is voiceos.integration.json — the manifest — plus a server implementing the tools it declares. VoiceOS reads the manifest, runs the server, and handles the rest: voice, the agent, confirmations, rendering, secrets.

A turn, step by step

1

Discovery

VoiceOS launches your server over MCP and lists its tools. Each tool’s description is what the agent reads when deciding whether to call it — write it as a routing rule.
2

Confirmation — acting tools only

If the tool declares a confirmation view, the notch renders it before any of your code runs. The user edits bound arguments inline; the edited values are what execute. Read-only tools skip this.

Rendered from the manifest. {{text}} and {{voice}} are bound to the tool call's arguments.

3

Execution

Your handler runs with the validated arguments. Secrets the user entered during setup arrive as environment variables.
4

Result

You return JSON. The model reads the data and narrates it. Attach a glance card with glanceResult([...]) and VoiceOS lifts it out of the payload — the model never sees UI JSON — validates every block, and renders it natively.

A read tool's glance card: header block + list block, returned as plain JSON.

Two UI languages

Integrations describe UI as data, never as components with host access. There’s one vocabulary per moment: Both are rendered by VoiceOS’s own themed components, so they inherit every restyle for free, and anything off-schema degrades safely instead of breaking the notch.
When the built-in blocks aren’t enough, either moment also accepts a single sandboxed custom widget — your own HTML in an isolated iframe with a strict message bridge.

Where your code runs

The manifest’s runtime picks one of three homes: Two isolation rules hold in every runtime:
  • Integration code never runs inside the VoiceOS process, and never sees VoiceOS secrets.
  • UI is data. Your cards can’t call host APIs, spoof system confirmations, or read anything from the app.

Permissions

Everything sensitive is opt-in, declared in the manifest, and shown to the user at install time. Least privilege is the rule: list only the domains you actually call.

Secrets

Declare what you need — API keys via auth: { kind: "apiKey" }, everything else via preferences. VoiceOS collects the values from the user right when they’re first needed, stores them encrypted, and injects them into your server as environment variables named after each field (process.env.MY_FIELD).

Declared an API key? The first time a tool needs it, the notch asks — you build no settings screen.

Secret values never appear in the agent transcript and are never persisted in plaintext.

Stability — the Minecraft rule

The contract is versioned by MANIFEST_SCHEMA_VERSION and UI_SCHEMA_VERSION, both currently 1. Additive changes never bump them, and the host renders unknown future blocks as safe placeholders — so an integration authored today keeps working across years of VoiceOS updates.

Quickstart

Build your first integration in the app — no code, three minutes.

Build with code

The same integration by hand: manifest, server, cards.