patter ships telemetry on by default and a Dockerfile that runs a missing path
Open-source voice-AI SDK. The Vapi/Retell alternative for builders who want to own the stack. Give your AI agent a phone number in 4 lines — Python and TypeScript, MIT licensed, Twilio, Telnyx, and Plivo.
At a glance
- What is it?
- An MIT-licensed voice agent SDK in Python and TypeScript that owns the whole stack between an application and a phone network, with a table of provider choices across six layers and three telephony carriers. The provider matrix is the product. The rough edges are in the container files, where the image installs the published package instead of the source it just copied, runs a script path the repository does not contain, and copies everything because there is no ignore file.
- Who is it for?
- The reason to look at this project is the layer table. Being able to say which speech-to-text provider, which text-to-speech provider, which realtime engine and which carrier you want, and swap any of them independently, is a real architectural choice that most hosted voice platforms do not let you make.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The Dockerfile runs a script path the repository does not contain
The container files are the least finished part of this project, and the default command is the clearest example.
The image installs the SDK from a package index with a local-mode extra, copies the entire repository into the working directory, exposes one port, and then runs a Python example at a path inside that copied tree:
FROM python:3.13-slim
WORKDIR /app
# Install Patter SDK with local mode dependencies
RUN pip install --no-cache-dir "getpatter[local]" python-dotenv
# Copy your agent script and .env
COPY . .
EXPOSE 8000
# Default: run the Python example at python/main.py
# Override with: docker run patter python your_script.py
CMD ["python", "python/main.py"]There is no python directory in the repository root. The top level holds the SDK sources, a scaffolding directory, a dashboard application, docs, examples, libraries, scripts and tests. So the image's default command points at a file this repository does not ship, and the comment above it describes a directory layout the tree does not have.
Two more details in the same file. The install step pulls the published package, not the source that the very next line copies in, so the image runs the released SDK even though it carries a full checkout. And there is no ignore file in the repository, so copying everything brings the environment template, the hook directory, the tests and the documentation into the image.
The compose file has no volume and no database
The compose file is four services' worth of nothing:
services:
patter:
build: .
ports:
- "8000:8000"
env_file: .env
restart: unless-stoppedOne service, built from the repository, with the environment file injected and the single port published. No volume, no database, no storage of any kind.
That is a sensible minimum for a development setup and a poor match for what the rest of the repository promises. The template list includes a dashboard described as real-time monitoring with cost and latency tracking, and a production setup described as having everything enabled including tools, guardrails and recording.
Neither of those can survive a container recreate in this configuration. Recordings are audio that has to go somewhere, and dashboard state that survives a restart needs somewhere to live. Both would need a volume, an object store, or a database, and the compose file declares none of them.
So the intended production path is not the one in the repository. The compose file is a local convenience, and anyone using it for a real deployment has to work out where the call audio and the cost records go before they need them.
Carrier parity is claimed in prose and qualified in the env example
The project makes a strong parity claim and then, a few paragraphs later, hands you the qualifications.
The claim is that built-in tools, call transfer and guardrails behave identically on every carrier.
The environment example then annotates the alternatives to the default carrier. The second option is described as having full general-availability support for DTMF, transfer and recording. The third is described as carrying audio streaming over a websocket, mu-law at 8 kHz, and a version three signature.
Read together, those two sentences are saying that on one carrier transfer and recording are fully supported features and on another the audio format and signature scheme differ. The parity claim is about the SDK's own layer, which is fair, since a tool call or a guardrail check does not care which carrier delivered the audio. It is not a claim that every carrier offers every telephony feature.
That distinction matters more than it sounds. Guardrails and tools are parity-safe. Transfer is not: press-to-transfer or warm transfer are carrier features with DTMF and signalling behind them, and the environment file tells you one carrier has them and leaves the other two unstated.
If a feature is carrier-dependent, this project tells you which carrier supports it. It does not tell you what the other carriers do instead.
Twenty-nine provider slots, and three environment variables to reach them
The layer table is the substance of this project. Six rows, and the names are countable.
Five language models. Seven speech-to-text providers. Seven text-to-speech providers. Four all-in-one realtime engines. Three carriers. Three audio components for voice activity detection and noise suppression. That is twenty-nine layer slots, against a headline claim of twenty-seven or more, with a handful of vendors appearing in more than one row because they sell more than one capability.
Now look at the documented setup path. The environment example asks for a telephony provider, then an OpenAI key for the all-in-one realtime engine, and optionally an Anthropic key for a custom model hook. Two speech-to-text and text-to-speech keys appear, commented, for the pipeline mode.
So the table advertises twenty-nine choices and the getting-started path uses three or four environment variables. The other providers are constructed in code rather than configured through the environment, which is normal for an SDK but means there is no catalog file to read and no list in the documentation of what each provider's constructor expects.
The one place the two language surfaces visibly diverge is also in a comment: a pipeline-mode custom model hook is referred to with a Python-style name, where the TypeScript side would use the camel-case equivalent. Everything else in the two quickstart snippets is the same program in each language.
Telemetry is opt-out, with an explicit list of what is never sent
The telemetry note is unusually specific, and its default is the part to notice.
The SDK collects anonymous usage data and describes it as opt-out. There is no flag in the quickstart that turns it off, and the only way to know it exists is to read this section.
What is collected: the SDK version, and bucketed provider, model and call facts. What is never collected: call content, prompts, phone numbers, keys, or free text. That exclusion list is the one that matters for a voice product, and it is a good one. Bucketing the provider and model rather than sending them verbatim is a further step in the right direction.
There are four ways to control it, which is generous. A constructor argument, a command to disable it, an environment variable, and the cross-vendor do-not-track header, which is honoured. Telemetry also switches itself off in continuous integration and in test runs, so a CI pipeline does not pollute the data.
And there is a debug flag that shows you exactly what would be sent without sending it. That is the feature to use first: run once with it on, read the payload, then decide.
The remaining question is whether you remember to, since nothing prompts you.
The development tunnel points your number at a quick tunnel
The quickstart ends with one line that contains the whole local development story, and the production story is a caveat attached to it.
Setting the tunnel option spawns a Cloudflare quick tunnel and points your phone number at it. For local work that is exactly right: no account setup, no DNS, no ingress.
For anything real, the instruction is to use a static webhook URL instead, or a commercial tunnel product. A quick tunnel gets a fresh random address each time it starts, which means your carrier's webhook configuration breaks every restart unless you reconfigure it.
That is the sort of thing the documentation is right to flag, and it is the sort of thing people skip. The compose file reinforces the gap, because it publishes the service's port and declares no ingress at all, so the only path from the public internet into a container started that way is something you add yourself.
Put together with the environment example, where the webhook URL is commented out with a note to omit it to auto-tunnel, the local and production topologies are genuinely different, and the default is the local one.
Eight template repositories, one clone command, Python only
The templates section lists eight separate repositories, and it is a well-chosen set because each one isolates a different problem.
An inbound agent answering calls as a restaurant booking assistant. Outbound calling with answering machine detection and voicemail drop. Tool calling for a CRM lookup and ticket creation over webhooks. A custom voice configuration showing the pipeline mode with one speech provider feeding another. Dynamic variables that personalise prompts per caller from CRM data. A bring-your-own model example. A dashboard for real-time monitoring with cost and latency tracking. And a production setup with everything switched on.
Each is described as self-contained: clone it, add your environment file, run it. Python and TypeScript are both included in each.
The one command block, though, is for the first template only, and it is Python only. It clones that repository, copies the environment example, and then descends into a python subdirectory to install a requirements file and run a main script.
So the claim that every template is dual-language is asserted rather than demonstrated in the visible text, and the single worked example shows half of it. Given that the whole parity argument in this repository rests on the two SDKs behaving identically, a template tree that differs by language is exactly the place where parity would first come apart.
The agent skills ship from a different repository
The coding-agent section is short and points sideways.
The install command names a repository that is not this one, and the text says the skills live in their own repository. So the instructions you follow inside the SDK repository fetch a bundle from elsewhere, which means the version you get is whatever that other repository has published rather than a tag matching your SDK version.
The bundle is described as working in roughly fifty-five agent harnesses that consume a particular skills standard, and the pitch is that installing once teaches every agent on the machine. That is a good idea and an unusual distribution channel for an SDK, but the count is approximate and there is no compatibility matrix in the README tying harness versions to skill versions.
The repository itself carries several pieces of that same infrastructure. There is an agents instructions file at the root. There is a custom hooks directory and, separately, a pre-commit configuration, so two hook mechanisms for one project. There is a scaffolding directory whose name matches the getting-started generator, and a dashboard application matching the monitoring claim. There are pinned interpreter versions for both languages, a changelog, a contributing guide and a security policy.
The skeleton is well maintained. It is the two files that disagree with the tree, the Dockerfile and the compose file, that would make a reviewer look twice.
Editorial conclusion
The reason to look at this project is the layer table. Being able to say which speech-to-text provider, which text-to-speech provider, which realtime engine and which carrier you want, and swap any of them independently, is a real architectural choice that most hosted voice platforms do not let you make. If that matters to you and you can live with holding carrier credentials and a model key yourself, the design fits. Three things to check first. Telemetry is opt-out rather than opt-in, so decide about it before your first run rather than after, and note that the only categories excluded are call content, prompts, numbers, keys and free text. Carrier feature parity is claimed in prose and then qualified in the environment example, so confirm that the specific capability you need, such as transfer or recording, behaves on the carrier you pick. And if you intend to containerise, the compose file in this repository does not build the same thing the SDK does, so plan your image separately.
Frequently asked questions
What is PatterAI/Patter?
It is an MIT-licensed SDK, published for Python and TypeScript, that gives an AI agent a phone number. It is not glue between a model and a carrier: it runs the agent loop and owns the language model, speech-to-text, text-to-speech, realtime voice, audio processing and telephony layers, and you choose a provider for each one. The repository description positions it as an alternative to hosted voice platforms for builders who want to own the stack.
Which voice providers does Patter support?
Six layers are covered with a countable list: five language models, seven speech-to-text providers, seven text-to-speech providers, four all-in-one realtime engines, three carriers, and three audio components for voice activity detection and suppression. That is twenty-nine layer slots, and a few vendors appear in more than one row because they offer more than one capability.
Does Patter send telemetry, and how do I turn it off?
It collects anonymous usage data by default, described as opt-out. The data is the SDK version plus bucketed provider, model and call facts, and never call content, prompts, phone numbers, keys or free text. You can disable it with a constructor argument, a command, an environment variable, or the cross-vendor do-not-track header, and it switches off automatically in CI and test runs. A debug flag shows the payload without sending it.
How do I run Patter locally with a phone number?
You pass a carrier and a phone number to the constructor, configure an engine and prompts, and serve with the tunnel option set. That spawns a Cloudflare quick tunnel and points the number at it, which is why the documentation says to use a static webhook URL or a commercial tunnel product for production, since a quick tunnel address changes on every start.
Can I build and run Patter from its docker-compose.yml?
The compose file builds a single service from the repository, injects an environment file, publishes one port and restarts unless stopped. It declares no volume and no database, so call recordings and dashboard state would not survive a container recreate, and the Dockerfile beside it installs the published SDK from a package index rather than the source it copies, then defaults to running a script path that is not in the repository tree.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/patterai-patter)