Self-hosting
Self-hosting
Offhook is a backend you run on your own infrastructure: an API, a worker and Postgres, plus an optional dashboard. This page explains what each piece does. Deploying and Configuration cover the steps.
What you run
| Component | Command | What it does |
|---|---|---|
| APIrequired | python run.py | Serves the /v1 API, the carrier webhooks and the call audio stream. Keeps no state of its own, so you can run several behind a load balancer. |
| Workerrequired | python -m app.worker | Dials queued calls and delivers webhooks. Without it, calls stay queued. You can run several; each job is claimed by exactly one worker. |
| Postgresrequired | postgres:16 | The only datastore. Tenants, encrypted connections, agents, calls, the job queue, usage and the event log all live here. No Redis or message broker needed. |
| Dashboardoptional | static site in dashboard/ | A web console for your calls, agents and keys. Build it to static files and host it anywhere. Sign-in uses Clerk. |
How a call flows
Calls are asynchronous, which is why the worker is required:
client ──POST /v1/calls──▶ API ──enqueue──▶ Postgres (call_jobs)
│
worker ──claim job────▶─┘
│
├─ decrypt the tenant's connection secrets
├─ dial through the carrier (Plivo or Twilio)
└─ carrier answers ──▶ webhook to PUBLIC_BASE_URL
│
API audio stream (WebSocket) ◀┘
│
└─ STT ─▶ LLM ─▶ TTS (one pipeline per call)
This has two consequences for your setup. The worker needs outbound internet access
to reach the carrier and the AI providers. And PUBLIC_BASE_URL must be an
HTTPS address the carrier can reach from the internet, because the carrier calls back
into the API when the call is answered.
What you need
- Python 3.13 where the API and worker run, or one container image for both.
- Postgres that both can reach. The project is tested with Postgres 16.
- A master encryption key: 32 random bytes, base64-encoded, kept outside the database. If you lose it, every stored provider key is unrecoverable. Keep it in a secrets manager, or use AWS KMS.
- A public HTTPS address for the API, if you place real phone calls.
- Accounts with your providers: Deepgram, Cartesia, a language model and a carrier. Their keys go into each tenant's connections, not into server configuration.
Ready? Continue to Deploying.