Quickstart for design partners

Start in 10 minutes.

A license, one install and the snippet for your stack. When the call ends, its summary shows up in your dashboard.

  1. 01License
  2. 02Install
  3. 03Wire it in
  4. 04Focus profile
  5. 05Dashboard
  6. 06Troubleshooting

01

Get a license.

A license is one signed token, tenms1.…, scoped to your modules, your session limits and, for the bridge, the ElevenLabs agents it may reach. It is verified offline, so audio never waits on it.

Keep it on your servers. Browsers and devices get a 5-minute session token instead (step 03, ElevenLabs).

# Your license: keep it on your servers, never in a browser
export TENMS_LICENSE="tenms1.…"

# Where the SDK posts call summaries (your dashboard)
export TENMS_CONTROL_URL="https://app.tenms.cloud"

Licenses are Ed25519-signed and verified offline by the SDK and Tenms Edge. How credentials work.

02

Install the SDK.

One Python package, CPython 3.10 or newer, CPU only. The Pipecat and LiveKit extras add the integrations. The ElevenLabs bridge and the raw Edge WebSocket need nothing in the audio path; tenms gives you the Edge client and the session-token helper.

pip install "tenms[pipecat]"    # Pipecat
pip install "tenms[livekit]"    # LiveKit Agents
pip install tenms               # engine + Edge client + session tokens

Tested with pipecat-ai 1.15 and livekit-agents 1.8. The package is on PyPI from release v0.1.0; if pip cannot find it yet, ask us for a build.

03

Wire it into your stack.

Pick the one you run. Each fills slots the framework already has, so nothing else in your pipeline changes.

Focus in the transport filters, Turn in the VAD and turn strategies, Pulse through an observer. Your STT, LLM and TTS stay as they are.

# pip install "tenms[pipecat]" "pipecat-ai[daily,deepgram,openai,elevenlabs]"
import os
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineTask
from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair, LLMUserAggregatorParams
from pipecat.services.deepgram.stt import DeepgramSTTService
from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
from pipecat.services.openai.llm import OpenAILLMService
from pipecat.transports.daily.transport import DailyParams, DailyTransport
from tenms.pipecat import TenmsCall

async def run_bot(room_url: str, token: str | None):
    tenms = TenmsCall()   # one per call; license from TENMS_LICENSE, focus_profile="stt"

    transport = DailyTransport(room_url, token, "Tenms Bot", DailyParams(
        audio_in_enabled=True, audio_out_enabled=True,
        audio_in_filter=tenms.focus_filter(),        # Focus: cleaned caller audio
        audio_out_filter=tenms.reference_filter(),   # agent playback -> echo reference
    ))
    stt = DeepgramSTTService(api_key=os.environ["DEEPGRAM_API_KEY"])
    llm = OpenAILLMService(api_key=os.environ["OPENAI_API_KEY"],
                           settings=OpenAILLMService.Settings(model="gpt-4o-mini"))
    tts = ElevenLabsTTSService(api_key=os.environ["ELEVENLABS_API_KEY"],
                               settings=ElevenLabsTTSService.Settings(voice=os.environ["ELEVENLABS_VOICE_ID"],
                                                                     model="eleven_flash_v2_5"))

    context = LLMContext([{"role": "system", "content": "You are a helpful phone agent."}])
    user_agg, assistant_agg = LLMContextAggregatorPair(context, user_params=LLMUserAggregatorParams(
        vad_analyzer=tenms.vad_analyzer(),                   # gated VAD
        user_turn_strategies=tenms.user_turn_strategies(),   # Turn
    ))
    proc = tenms.processor()                                 # after STT, before the user aggregator
    pipeline = Pipeline([transport.input(), stt, proc, user_agg, llm, tts,
                         transport.output(), assistant_agg])
    task = PipelineTask(pipeline, observers=[tenms.observer()])   # agent text -> echo rejection

    @proc.event_handler("on_call_summary")
    async def on_summary(processor, summary):
        print(summary["failure"], summary["quality"])        # also posted to the dashboard

    await PipelineRunner().run(task)

ElevenLabs bridge, browser side: keep the ElevenLabs SDK and point it at the URL your backend returns.

// Browser: keep @elevenlabs/client; only the URL changes. WebSocket transport only
// (WebRTC goes straight to ElevenLabs and bypasses Tenms).
import { Conversation } from "@elevenlabs/client";

const res = await fetch("/api/voice-session?agent_id=" + agentId, { method: "POST" });
const { signed_url } = await res.json();
const conversation = await Conversation.startSession({
  signedUrl: signed_url,
  connectionType: "websocket",
});

Full guides: Pipecat, LiveKit Agents, ElevenLabs bridge, Edge protocol, Python SDK.

04

Pick a Focus profile.

Leave it on stt for anything your agent transcribes. Switch only for audio people listen to. The listening profiles are never meant for STT input: full suppression raised WER in the Arena.

sttDefault
Anything your agent transcribes. Echo removal, speaker gate, gentle noise floor: the lowest measured WER.5 ms at 16 kHz
listening
Audio people hear, full noise suppression, no model.5 ms at 16 kHz
listening_miniRecordings
Recordings, QA review, listen-in. Our trained model removes about 2x the background of DSP. Edge and the Python SDK.5 ms at 16 kHz
listening_proBest listening
The best listening quality Edge offers, for recordings where 30 ms is fine. Edge only.27–30 ms
// First message on /v1/stream. Default is "stt" (audio for your STT).
// "listening", "listening_mini", "listening_pro": audio people listen to.
{"type": "start", "sample_rate": 16000, "focus_profile": "listening_mini"}

// The server echoes it back:
{"type": "ready", "focus_profile": "listening_mini", ...}

The ElevenLabs bridge takes stt or listening only. Measurements: Focus profiles.

05

See your calls.

  1. Open app.tenms.cloud and sign in with your license token.
  2. Each call appears when it ends: quality, turns, interruptions, the failure cause and processing time.
  3. The overview shows call volume, failure causes, interruptions and regions for your customer only.

Edge and the ElevenLabs bridge post the summary for you. The SDK posts it when TENMS_CONTROL_URL is set; reporter=False turns that off.

curl -s "https://app.tenms.cloud/v1/calls?limit=5" \
     -H "Authorization: Bearer $TENMS_LICENSE"
# -> {"calls": [...], "next_before": ..., "next_before_id": ..., "since": ...}

The same data over HTTP: control-plane API, CallSummary.

06

Troubleshooting.

HTTP codes come back on the WebSocket upgrade as {"error": "<code>"}; the rest arrive as error events. EdgeClient raises them as EdgeError with .fatal and .retryable.

StatusCodeCauseDo
401
missing_licenseinvalid_license
No token, or a malformed one. Servers send Authorization: Bearer; browsers add ?token= with a session token. Fix, don't retry
401
license_expired
The license passed its expiry, or a session token was not used within 5 minutes. Mint a new session token
403
license_revoked
The license, or a session token's parent, was revoked. Never retry; contact us
403
agent_not_allowed
The ElevenLabs agent id is not on your license. Ask us to add the agent id
403
no_modules
None of the requested modules are on the license. Request licensed modules
429
rate_limitedlicense_session_limit
More than 10 connections a second (bursts of 20), or the license's concurrent-session cap. Back off and retry
503
at_capacitydrainingnot_ready
The replica is full, shutting down or still loading the revocation list. Back off and retry
error
start_timeoutexpected_start
The first message was not a JSON start within 10 s. Send start first
error
unsupported_sample_rate
Only 8, 16, 24 and 48 kHz are processed. Resample or change the rate
error
profile_unavailableprofile_capacity
A listening tier this server has not loaded, or listening_pro is full on this replica. Use listening_mini, or retry
error
idle_timeout
No audio for 30 s. Send audio, or end

The agent's own voice still reaches STT

Feed what the agent plays, as it plays: reference_filter() in Pipecat, attach() in LiveKit, 0x02 frames on Edge. The reference need not be sample-aligned, but cancelled, unplayed audio must not be fed.

Every "mm-hmm" interrupts the agent

Act only on barge_in. Tell the engine when the agent speaks (agent_speaking / set_agent_speaking); the framework integrations do this for you.

Disconnected after about 10 s

Read the socket continuously. A client that stops reading for 10 s is dropped.

Audio stalls when streaming a file

Send in real time. Edge allows 1.5x real time after a 5 s burst, then stops reading until you are back in budget.

No calls in the dashboard

SDK integrations post a summary only when TENMS_CONTROL_URL is set. Edge and the bridge post it when the call ends.

Every code: Edge error codes, upgrade refusals, bridge errors.

Stuck? Wire it in with us.

Bring your pipeline and a recording your agent got wrong. We integrate Tenms with your engineers on a call and compare the events side by side.