Skip to main content
Inbound voice is webhook-driven through Call Control: Telnyx POSTs call events to your Call Control application’s webhook, and your Edge Compute function is that webhook. On call.initiated you answer the call; on call.answered you play audio.

1. Write the handler

// index.ts
import * as http from "node:http";
import { env } from "@telnyx/edge-runtime";

const port = Number(process.env.PORT ?? 8080);
const AUDIO_URL = "https://YOUR-HOST/song.mp3"; // a reachable HTTPS mp3/wav you have rights to

function body(req: http.IncomingMessage): Promise<string> {
  return new Promise((r) => {
    let b = "";
    req.on("data", (c) => (b += c));
    req.on("end", () => r(b));
  });
}

http.createServer(async (req, res) => {
  if (req.method === "GET") { res.writeHead(200).end(); return; } // health

  // parse the inbound webhook through the binding
  const evt = env.MY_TELNYX.webhooks.unsafeUnwrap<{ data: any }>(await body(req)).data;
  const id = evt?.payload?.call_control_id;

  if (evt?.event_type === "call.initiated" && id) {
    await env.MY_TELNYX.calls.actions.answer(id, {});
  } else if (evt?.event_type === "call.answered" && id) {
    await env.MY_TELNYX.calls.actions.startPlayback(id, { audio_url: AUDIO_URL });
  }

  res.writeHead(200);
  res.end();
}).listen(port);
Declare the binding in func.toml:
[telnyx]
binding = "MY_TELNYX"

2. Ship

telnyx-edge ship

3. Point a Call Control app at it

# create a Call Control app whose webhook is your function
curl -X POST https://api.telnyx.com/v2/call_control_applications \
  -H "Authorization: Bearer $TELNYX_API_KEY" -H "Content-Type: application/json" \
  -d '{"application_name":"voice-demo","webhook_event_url":"https://YOUR-FUNC.telnyxcompute.com"}'

# route your number to that app (connection_id = the app id from the response)
curl -X PATCH https://api.telnyx.com/v2/phone_numbers/YOUR-NUMBER-ID \
  -H "Authorization: Bearer $TELNYX_API_KEY" -H "Content-Type: application/json" \
  -d '{"connection_id":"YOUR-CALL-CONTROL-APP-ID"}'

4. Test

Call the number from any phone. The function answers and plays your audio.
audio_url must be a publicly reachable HTTPS .mp3 or .wav. The flow is two events — call.initiated (answer) then call.answered (play) — so handle both. To loop, hang up, or chain more actions, respond to later events (playback.ended, call.hangup) the same way.