Integrations

Reflex Layer sits between your language model and your text-to-speech. Every integration does the same job: it holds the model's text one sentence at a time, checks it, and passes on either the sentence or the fallback line.

Available now: the HTTP API and the Vapi proxy. On request: Pipecat, LiveKit and the Node client, until those packages are published. There is no public download or package registry entry yet; email support if you need one of those clients.

How streaming checks work

base_url (or the REFLEX_BASE_URL environment variable) is required. If Reflex Layer can't be reached within the client timeout (600 ms by default), or answers with a server error, a quota error (402) or a rate limit (429), the client logs a warning and follows its on_unavailable policy: "pass" speaks the draft, and "fallback" speaks your fallback line.

Pipecat

pip install "reflex-layer[pipecat]"     # pre-release: pip install ./integrations/python[pipecat]
from reflex_layer import ReflexClient
from reflex_layer.pipecat import ReflexLayer

reflex = ReflexLayer(ReflexClient(api_key=os.environ["REFLEX_API_KEY"], agent_id="agt_...", base_url="https://YOUR-REFLEX-HOST"))

pipeline = Pipeline([
    transport.input(),
    stt,
    reflex.caller(),          # remembers what the caller said
    user_aggregator,
    llm,
    reflex.reply(),           # holds each sentence until it's approved
    tts,
    transport.output(),
    assistant_aggregator,     # records what was actually said
])

Create one ReflexLayer per call. Pass on_outcome= to receive each reply's result, for example to start a transfer when outcome.handoff_recommended is true. Interruptions cancel any pending check.

Tested with pipecat-ai 1.15.0.

LiveKit Agents

pip install "reflex-layer[livekit]"     # pre-release: pip install ./integrations/python[livekit]
from livekit.agents import Agent
from reflex_layer import ReflexClient
from reflex_layer.livekit import ReflexGuardMixin

class FrontDesk(ReflexGuardMixin, Agent):
    def __init__(self):
        super().__init__(instructions="You are the front desk for Bright Smile Dental.")
        self.reflex_setup(ReflexClient(agent_id="agt_...", base_url="https://YOUR-REFLEX-HOST"))

The mixin overrides llm_node, so the approved text is what reaches TTS, the transcript and the chat history. Tool calls pass through, unless the reply they belong to was blocked. If you already have a custom llm_node, move its logic into reflex_upstream(). After each reply, self.reflex_last holds the outcome.

Tested with livekit-agents 1.8.11.

Vapi

Reflex Layer can act as Vapi's Custom LLM. Vapi sends its OpenAI-style request to Reflex Layer. We forward it to your own model, check each sentence, and stream approved text back to Vapi.

  1. In the dashboard, open Connect and scroll to Vapi proxy: upstream model. Choose the agent and enter your model's OpenAI-compatible base URL (for example https://api.openai.com/v1), the model name, and its API key. The key is encrypted and never shown again.

  2. In Vapi, set the assistant's model provider to Custom LLM with this URL:

    https://YOUR-REFLEX-HOST/v1/vapi/agt_...
    

    Then add your Reflex Layer API key as the bearer token.

  3. Place a test call. Blocked turns appear in the call log within a few seconds.

Tool calls are passed through unchanged. Non-streaming requests are also supported, and the whole reply is checked at once.

Generic HTTP

Use POST /v1/check from any backend. Two rules matter:

  1. Set a client timeout (we suggest 600 ms) and decide what happens on timeout. Speaking the draft is the usual choice.
  2. Speak say, not your draft, whenever decision is block.
import { ReflexClient } from "reflex-layer";   // pre-release: integrations/node

const reflex = new ReflexClient({ agentId: "agt_...", baseUrl: "https://YOUR-REFLEX-HOST" });     // REFLEX_API_KEY from the environment
const r = await reflex.checkSafe({ callId, turnIndex, callerText, draftReply });
speak(r.say);
if (r.signals?.handoff?.recommended) offerTransfer();

To check streamed text in Node, use reflex.guardStream(chunks, { callId, callerText, turnIndex }). It yields the text to speak.

Webhooks

Set a webhook under Connect to receive turn.blocked, turn.review and call.handoff events, for example to text the office manager when a caller needs a callback. Always verify the signature:

from reflex_layer import verify_webhook

event = verify_webhook(request.body, request.headers["x-reflex-signature"], WEBHOOK_SECRET)
import { verifyWebhook } from "reflex-layer";
const event = verifyWebhook(rawBody, req.headers["x-reflex-signature"], process.env.REFLEX_WEBHOOK_SECRET);

Both reject a bad signature, or a timestamp more than five minutes old.