UtteraUttera

Connectors

Uttera doesn't live alone: it lives inside what you already have. This section collects the ways of plugging it in — the ones that already work, the ones we bring ready-made, and the ones we're looking at — plus the open source it is built on.

Everything described here is code you can download: github.com/uttera/uttera-examples. Every example is run against the live API before it's published, and where something couldn't be tested end to end, its README says so.
FolderWhat's in it
openai-sdkUttera through the official OpenAI SDK, in Python, Node and curl
asterisk/recordingsTranscribing the calls your phone system already records, without touching the dialplan
asterisk/agiSpeaking inside the call and hearing the caller
n8nOur own node, plus workflows that import without installing anything
openclawA skill for agents: transcribe, summarize, translate and speak

We speak OpenAI's dialect

The two main services follow exactly the same shape as the OpenAI API: same routes, same parameter names, same voice names and same responses.

EndpointCompatible
POST /v1/audio/transcriptionsYes. file, model=whisper-1, language, prompt, response_format, temperature
POST /v1/audio/speechYes. model=tts-1 / tts-1-hd, input, voice, response_format, speed
GET /v1/modelsYes. Returns tts-1, tts-1-hd and whisper-1. It's what the SDK calls as soon as the client is constructed
Voices alloy · echo · fable · nova · onyx · shimmerYes, under those same names
/v1/translate · /v1/summarize · ?extras= · /v1/usage/lastOurs. They don't exist in OpenAI, and they don't get in the way of anyone who doesn't use them

In practice this means that anything written for the OpenAI API works by changing two lines: the base address and the key. Including their official SDK:

from openai import OpenAI

c = OpenAI(api_key="sk-echo-...",              # your Uttera key
           base_url="https://api.uttera.ai/v1") # and our address

audio = c.audio.speech.create(model="tts-1", voice="nova",
                              input="Your order ships tomorrow.")
open("voice.mp3", "wb").write(audio.content)

with open("recording.mp3", "rb") as f:
    print(c.audio.transcriptions.create(model="whisper-1", file=f,
                                        response_format="text"))
This is tested, not assumed. The example above runs with the official openai package without patching anything: it generates the audio, transcribes it back and returns the original text.

Which is where the interesting part comes from: everything that already knows how to talk to OpenAI knows how to talk to us — local agents, self-hosted chat interfaces, plugins, scripts somebody wrote a year ago — with the difference that the audio never leaves the European Union. For many companies that is the only reason the project moves from demo to production.

Asterisk and phone systems

It's the most requested integration, because it's where the voice actually is. We bring two ready-to-use AGI scripts: one says a text inside the call and the other listens to the caller and leaves what they said in a dialplan variable.

exten => 101,1,Answer()
 same => n,AGI(uttera-decir.agi,"How can I help you?")
 same => n,AGI(uttera-oir.agi,8,en)
 same => n,NoOp(The caller said: ${UTTERA_TEXTO})

From there it's your call: a menu, a database lookup, a language model. And for the most common case — transcribing calls that are already recorded — the example brings the skeleton with MixMonitor and a hangup hook.

What the example solves and what costs a day to discover on your own:

TrapWhat happens if you don't know
Uttera generates at 24 kHz, the telephone channel runs at 8 kHzAsterisk plays the file at its own rate: the voice comes out fast and high-pitched
Missing silence at the end of the fileAsterisk cuts off the last syllable
Whisper doesn't stay quiet at silenceIt returns an invented sentence — "Thanks for watching" — because that abounds in its training data. You pay for it, and worse, you act on it
The quoting in SET VARIABLEText with spaces arrives split and the variable keeps only the first word
API timeouts during a callTwo hours of waiting with a person on the phone. In telephony you cut at 30 s and say something

The complete code and dialplan are in the examples repository, with an explanation of each one.

The calls you're already recording

Before you consider speaking inside the call, look at this: your phone system is already writing audio files into some folder. There are two scripts that read them and leave the transcript and the analysis beside them, and you don't have to touch the dialplan. One cron line and you have last week's calls transcribed.

It's the code we run in production ourselves, with the parts specific to our installation taken out. It carries three things inside that cost us dearly: telling a recording in progress from an old one, asking for transcription and analysis in a single upload instead of four, and a date filter by default — a batch process with no window walks the entire history on every pass, and the day it meets a real archive it queues years of calls at once.

n8n and automation

If you use n8n, there are two ways and the second needs nothing installed.

Workflows ready to import. You download them, import them, and they work with the stock HTTP node — including on n8n cloud. The most useful is the one that watches a folder of recordings every fifteen minutes and sends each one to be summarized: it is exactly the case of someone who already has a phone system recording and only needs somebody to read those recordings. No dialplan to touch.

Our own node. n8n-nodes-uttera adds transcribe, summarize, translate and text-to-speech as operations of a single node, with the credential stored in n8n instead of loose in every request.

Two details the node has already solved: the timeout is two hours — n8n's default cuts off long recordings that were doing fine — and "continue on error" works per item, which matters when processing a whole folder, where there's always some corrupt recording that shouldn't bring the batch down.

Agents and assistants

Because of the compatibility above, any agent or interface that speaks OpenAI's dialect uses Uttera without an adapter: point it at our address. That covers local installs of assistants and self-hosted chat panels, which is exactly the scenario our engines were born for: keeping the audio inside the private domain.

If your agent supports tools, the endpoints that aren't OpenAI's — summarize, translate, analyze the voice — describe well as standalone tools: they take a file and return JSON.

For OpenClaw agents there's a ready-made skill: four scripts — transcribe, summarize, translate and speak — and a SKILL.md that tells the agent when to use each one and what the traps are. That part is what really matters: that it knows not to send silence to be transcribed, that line breaks cost money when synthesizing, and that what comes back from a transcript is untrusted text it must not execute.

The open source underneath

The engines that move Uttera are published under the Apache 2.0 license. It isn't marketing: they are the same ones running in production.

RepositoryWhat it is
uttera-tts-hotcoldText-to-speech server with cold and hot start. Several engines behind one API
uttera-tts-vllmThe same service on vLLM, for real concurrent load
uttera-stt-hotcoldSpeech-to-text server, same pattern
uttera-stt-vllmSpeech to text on vLLM
uttera-benchmarksThe corpora, the measurement harness and the raw results behind every number we publish

Why they're open

For three reasons, and none of them is generosity:

Because you can run it yourself. If your case can't have the audio leaving your network — or you simply prefer your own hardware — clone the repository and stand the service up. What we sell is not having to: the GPUs, the availability, the routing between nodes, and somebody getting out of bed if something breaks.

Because an engine nobody can audit doesn't deserve your audio. We say we keep nothing and that nothing goes to third parties. With the code in front of you, that can be checked instead of believed.

Because numbers without the setup are worthless. Everyone publishes "X requests per second on a Y" without saying with which corpus, at what concurrency, or at which percentile. The measurements repository carries the whole corpora, the protocol and the raw results, precisely so you can repeat the test and contradict us.

The edge that serves this API — routing, quotas, billing — and the portal are not published. They aren't a voice engine: they're the plumbing of a business, and there's nothing in there that would be any use to anyone.

Connectors in the works

What's next, in the order people ask for it:

ConnectorWhat for
CRMSo the transcript and summary of a call land in the customer's record on their own
Recordings from other phone systems (FreePBX, 3CX, Issabel)The same thing the Asterisk connector already does, adapted to where each one leaves its files
Voice note to text in messagingThe fastest-growing use case: nobody wants to listen to a four-minute voice note

If the one you need isn't there, tell us: the order is decided by whoever asks.