Skip to main content
Inbound SMS is webhook-driven: Telnyx POSTs a message.received event to your messaging profile’s webhook, and your Edge Compute function is that webhook. Replying to another Telnyx number is on-net — no 10DLC campaign required.

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 received: any[] = []; // in-memory; use KV or SQL DB for durable storage

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) => {
  // GET: see what's been received
  if (req.method === "GET") {
    res.writeHead(200, { "content-type": "application/json" });
    res.end(JSON.stringify({ received }, null, 2));
    return;
  }

  // POST: Telnyx inbound webhook — parse it through the binding
  const evt = env.MY_TELNYX.webhooks.unsafeUnwrap<{ data: any }>(await body(req)).data;
  if (evt?.event_type === "message.received") {
    const p = evt.payload;
    const from = p.from.phone_number;
    const to = p.to[0].phone_number;
    received.unshift({ from, to, text: p.text, at: evt.occurred_at });

    // Auto-reply on-net (no 10DLC when the recipient is a Telnyx number)
    await env.MY_TELNYX.messages.send({ from: to, to: from, text: `You said: ${p.text}` });
  }
  res.writeHead(200);
  res.end();
}).listen(port);
Declare the binding in func.toml (see the Quick start):
[telnyx]
binding = "MY_TELNYX"

2. Ship

telnyx-edge ship

3. Point a messaging profile at it

Set a messaging profile’s inbound webhook to your function URL, then assign your number to that profile:
# webhook -> your function
curl -X POST https://api.telnyx.com/v2/messaging_profiles \
  -H "Authorization: Bearer $TELNYX_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"inbound-demo","webhook_url":"https://YOUR-FUNC.telnyxcompute.com","whitelisted_destinations":["US"]}'

# assign your number to the profile (use the profile id from the response)
curl -X PATCH https://api.telnyx.com/v2/phone_numbers/YOUR-NUMBER-ID/messaging \
  -H "Authorization: Bearer $TELNYX_API_KEY" -H "Content-Type: application/json" \
  -d '{"messaging_profile_id":"YOUR-PROFILE-ID"}'

4. Test on-net

Send from another Telnyx number on your account to your function’s number:
curl -X POST https://api.telnyx.com/v2/messages \
  -H "Authorization: Bearer $TELNYX_API_KEY" -H "Content-Type: application/json" \
  -d '{"from":"+1ANOTHER_TELNYX_NUMBER","to":"+1YOUR_FUNC_NUMBER","text":"hello"}'
You get back “You said: hello” on-net, and GET https://YOUR-FUNC.telnyxcompute.com shows what arrived. The inbound event the function parses looks like:
{
  "data": {
    "event_type": "message.received",
    "occurred_at": "2026-06-19T16:06:17.464+00:00",
    "payload": {
      "from": { "phone_number": "+1..." },
      "to": [ { "phone_number": "+1..." } ],
      "text": "hello"
    }
  }
}
Only on-net replies skip 10DLC. Receiving is always free. Replying to a Telnyx number is on-net (no campaign). Replying to an off-net number — e.g. a personal mobile — is application-to-person traffic and requires 10DLC registration.