Vercel
Serve the pipedog endpoints from a Bun server on Vercel Functions.
Vercel runs a server.ts with the Bun runtime, so the Bun snippet's shape
works, with a routes map for static responses.
Install
bun add @pipedog/core @pipedog/asyncio @pipedog/transport-smallwebrtc @pipedog/transport-websocket @pipedog/serializer-protobuf @pipedog/openai @pipedog/audio-silero @pipedog/audio-smart-turn@pipedog/audio-smart-turn isn't imported anywhere: the default turn strategies load it. To run
the snippet from a checkout instead, see Installation.
Files
Set the runtime in vercel.json:
{ "bunVersion": "1.x" }The endpoints come from the shared module on the overview.
voice.ts re-exports it:
export { offer, runSocketAgent, socket } from "../shared/voice.ts";
import { bunWebSocket, type UpgradingServer } from "@pipedog/transport-websocket/fetch";
import { offer, socket } from "./voice.ts";
// Vercel Functions run this server with the Bun runtime (`"bunVersion": "1.x"` in
// vercel.json). Each WebSocket is pinned to one function instance for its lifetime,
// and messages don't cross instances: keep shared state in a database.
const config = {
routes: {
"/": new Response("voice agent", { headers: { "content-type": "text/plain" } }),
},
fetch(request: Request, server: UpgradingServer) {
const { pathname } = new URL(request.url);
if (pathname === "/api/offer") return offer(request);
// `socket` upgrades with the server Bun passes to `fetch`.
if (pathname === "/ws") return socket(request, server);
return new Response("not found", { status: 404 });
},
websocket: bunWebSocket,
};
export default config;
Run it
Locally, Bun serves the default export:
OPENAI_API_KEY=sk-... bun server.tsThen deploy with vercel deploy, and set OPENAI_API_KEY in the project's environment variables.
Caveats
- WebRTC media. The offer route is mounted, but its media runs over UDP, which a function isn't expected to carry. It's untested here. Use the WebSocket for audio on Vercel.
- One instance per socket. Each WebSocket is pinned to one function instance for its lifetime, and messages don't cross instances. Keep shared state in a database.
- Duration. A function's duration limit also ends a long session.
- Daemons. Vercel Daemons are always-on workers with no inbound HTTP or TCP traffic, so they can't serve these endpoints. They suit background workers that pull their own work.
Authentication
Neither route has auth. Check the session in fetch before offer and socket run, as in Authentication.
pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.
Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).