AI Assistant Needs a Back End. Put It at the Edge Telnyx released a guide showing how to build a backend for voice AI assistants using a single Edge Compute function, handling dynamic variables and webhook tool calls without separate services. The approach simplifies deployment by consolidating all assistant callbacks into one Go function that connects to business logic like CRM and scheduling systems. Building a voice AI assistant has never been easier. You write a prompt, connect a phone number, pick a model, and within minutes your assistant is answering calls. The first successful conversation feels like magic. Then someone asks: "Can you tell me where my order is?" Or: "Can you schedule someone to come tomorrow?" At that point, your assistant needs information it doesn't have. It needs to talk to your CRM, your scheduling system, or your own APIs. The LLM isn't the application anymore, it's one component in a much larger system. That's the point where every production AI assistant grows a backend. In this article, we'll build that backend using a single Telnyx Edge Compute function. Instead of deploying another webhook service, we'll use one Go function to handle both dynamic variables and webhook tool calls, verify incoming requests, and connect the assistant to business logic running behind the scenes. The Architecture Let's look at what the sample application actually does. The example assistant is named Jordan and works for a fictional home services company. From the caller's perspective, the interaction feels simple. The assistant answers the phone. It greets the caller. It collects information. It schedules an on-site estimate. Behind the scenes, though, two very different backend interactions happen. Before Jordan says the first word, the assistant requests runtime information that shouldn't live inside the prompt. Later, after gathering enough information from the caller, Jordan needs to schedule the appointment. Both requests are handled by exactly the same Edge Compute function. Conceptually, the architecture looks like this: Instead of deploying separate webhook services for different responsibilities, one function owns all assistant callbacks. That may seem like a small implementation detail, but it has a few advantages. There's only one deployment, one endpoint, one place to manage secrets, one place to verify requests, and one place to connect your business logic to the assistant. As your application grows, that simplicity becomes surprisingly valuable. If you would like to follow along, the complete source code for the backend is available in the edge-ai-assistant-backend-go example repository, and the deployment guide walks through configuring the AI Assistant and Edge Compute function step by step. GitHub repository: https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-ai-assistant-backend-go https://github.com/team-telnyx/telnyx-code-examples/tree/main/edge-ai-assistant-backend-go Deployment guide: https://developers.telnyx.com/docs/edge-compute/guides/ai-assistant-backend https://developers.telnyx.com/docs/edge-compute/guides/ai-assistant-backend Understanding Dynamic Variables One of the first callbacks happens before the assistant even starts talking. This is where dynamic variables come in. Think about what happens when someone calls a business. The greeting usually isn't static. Maybe you want to say: "Thanks for calling Pinecrest Home Services." Maybe premium customers hear a different greeting or maybe the transfer number changes depending on the office that's currently open. Maybe the estimated wait time depends on today's schedule. None of that belongs inside a prompt. Prompts describe behavior. Runtime data belongs somewhere else. Dynamic variables solve that problem. Instead of hardcoding values into the assistant configuration, Telnyx asks your backend for them every time a call begins. The function responds with JSON like this: { "dynamic variables": { "company name": "Pinecrest Home Services", "timeframe": "two business days", "placeholder transfer destination": "+15551234567" } } One detail that's easy to overlook is the wrapper object. The response must be returned under the dynamic variables key. Returning a flat JSON object won't populate the assistant variables because the assistant expects a very specific response format. Once those values arrive, they're immediately available inside your prompt. From that point on, the assistant behaves as though those values had always been there. The difference is that they're resolved at runtime instead of being baked into your configuration. When the Assistant Needs Your Application Dynamic variables solve the first half of the problem. The second half happens during the conversation itself. Imagine the caller says: "I'd like someone to come out this Friday." At this point the assistant needs to create something that doesn't exist yet, an appointment. This isn't something an LLM should invent. It needs to ask your application. That's where webhook tools come in. The assistant recognizes that it has enough information to perform an action and invokes a tool named schedule estimate . The request is sent to the exact same Edge Compute function that handled the dynamic variables callback earlier. Your function performs whatever business logic you need like calling an internal scheduling API, checking technician availability, or creating a CRM record. The sample keeps things intentionally simple and returns: { "scheduled date": "2025-04-10", "scheduled time": "10:00", "confirmation number": "CONF-1715234567", "estimate id": "EST-1715234567" } The important part isn't the fake confirmation number. It's the pattern. The assistant doesn't generate business data. Your application does. The assistant simply knows when it needs information. Your backend decides what that information should be. That's a much healthier separation than asking an LLM to hallucinate identifiers, appointment slots, or customer records. One Function, Two Very Different Requests One thing I particularly like about this sample is how little infrastructure it requires. Many webhook-based systems end up looking something like this: /dynamic-variables /schedule /orders /customers /transfers /authentication ... Before long you're deploying an API solely to support your assistant. This example takes a different approach: - Both callback types point to exactly the same URL. - The function examines the incoming request. - If it contains data.event type , it knows it's handling dynamic variables. - If it receives a flat JSON payload, it treats the request as a webhook tool invocation. The implementation is small enough to understand in a few minutes while still demonstrating a production-friendly architecture. As your assistant becomes more capable, you can continue routing different callback types through the same function or split them into separate services later if your application grows beyond that model. Starting simple is usually the right decision. Why Put the Backend at the Edge? At this point you might be wondering: Why not just deploy a normal webhook server? That's a perfectly valid option. Many teams do exactly that. The question isn't whether a traditional backend works. It's whether it's the simplest architecture for this particular workload. Voice AI applications have a very different latency profile than most web applications. Dynamic variables are resolved before the assistant even says hello. Tool invocations happen while someone is actively waiting on the phone. Unlike a background API request, every additional network hop directly affects the conversational experience. That doesn't mean every millisecond determines success or failure, but it does mean callback latency is part of the user experience. Running your assistant's backend on Edge Compute keeps the callback logic close to the communications infrastructure that's already handling the call. It also removes an entire category of operational work. Instead of provisioning another web service, exposing it publicly, managing deployments, and maintaining separate infrastructure, you deploy a single Edge Compute function and point your assistant at its invoke URL. For many voice applications, that's enough. Your assistant still talks to your own databases, CRMs, scheduling systems, and internal APIs. The difference is that you don't need another standalone backend just to receive assistant callbacks. And perhaps more importantly, the architecture stays easy to reason about. There's a clear mental model: The AI Assistant owns the conversation. Edge Compute owns the business logic. That's a separation that continues to hold even as your application becomes significantly more sophisticated. Building a Production Backend The scheduling example in this repository is intentionally simple, but the architecture scales far beyond scheduling appointments. Once you have a backend sitting behind your AI Assistant, it becomes the bridge between the conversation and the rest of your application. Think about the kinds of actions your assistant might need to perform during a call. It might retrieve customer information from a CRM, check the status of an order, create a support ticket, schedule an appointment, trigger an internal workflow, or decide whether a call should be transferred to a human. From the assistant's perspective, these are all the same operation. It recognizes that it needs information it doesn't have, invokes a tool, waits for the response, and continues the conversation. The backend is where all of those differences live. One tool might call Salesforce. Another might query PostgreSQL. Another might invoke an internal REST API, while a fourth simply looks up data in Redis. The assistant doesn't need to know how any of that works. Its job is to recognize when business logic is required. Your backend decides how to execute that logic and what data should come back. Keeping conversational logic separate from application logic turns out to be a useful architectural boundary. Your prompts remain focused on how the assistant behaves, while your backend evolves independently as your business systems grow. Security Shouldn't Be a Production TODO One thing I appreciated about this sample is that it doesn't leave security as an exercise for the reader. If you've spent enough time browsing GitHub repositories, you've probably seen comments like: // TODO: Verify webhook signature That works for a demo, but not for an application that's participating in a live phone call. Every webhook request sent by Telnyx is signed using Ed25519. Before the function processes a request, it reads the telnyx-signature-ed25519 and telnyx-timestamp headers, reconstructs the signed payload by combining the timestamp with the raw request body, validates that the timestamp falls within an acceptable window, and finally verifies the signature using your Telnyx public key. Only after those checks succeed does the function continue with your business logic. That matters because this endpoint isn't simply returning JSON. It's creating appointments, resolving customer-specific information, and potentially interacting with internal systems. The function should have confidence that every request genuinely originated from Telnyx before it performs any of those operations. Managing Secrets Signature verification requires your Telnyx public key, and the example stores it as an Edge Compute secret rather than hardcoding it into the application. Fetching the key is straightforward: PUBLIC KEY=$ curl -s \ -H "Authorization: Bearer $TELNYX API KEY" \ https://api.telnyx.com/v2/public key \ | jq -r '.data.public' Once retrieved, it's stored securely using the CLI: telnyx-edge secrets add TELNYX PUBLIC KEY "$PUBLIC KEY" The same mechanism works for anything else your backend depends on, including API keys, OAuth credentials, database passwords, or authentication tokens for downstream services. Keeping configuration outside the application code makes deployments easier to manage and avoids the temptation to bake secrets into source control. Why Everything Goes Through One Function Perhaps the most interesting design decision in this example is that everything flows through a single Edge Compute function. At first glance, this might seem unusual. Most web applications expose multiple routes for different resources. You might expect separate endpoints for customers, appointments, transfers, or order lookups. This isn't really a REST application, though. It's an event-driven application. From the assistant's perspective, there are only two categories of incoming requests. Either it needs runtime values before the conversation begins, or it needs to invoke a tool while the conversation is already in progress. The function simply inspects the incoming payload and dispatches execution accordingly. That approach keeps the deployment model refreshingly simple. There's only one function to deploy, one invoke URL to configure, one place to verify signatures, one place to manage secrets, and one place to add logging or observability. As the backend grows, that simplicity becomes surprisingly valuable because there's very little infrastructure to reason about. Deploying the Function Getting the sample running only takes a few commands. First, scaffold a new Go function: telnyx-edge new-func -l go -n edge-ai-assistant-backend cd edge-ai-assistant-backend Replace the generated handler with the sample implementation, add your Telnyx public key as an Edge Compute secret, and deploy the function: telnyx-edge ship The sample repository already contains the complete handler.go implementation, so you don't need to build the webhook logic from scratch. Once you've deployed the function and copied the invoke URL, the deployment guide walks through configuring both the dynamic variables callback and the schedule estimate webhook tool to point at the same Edge Compute endpoint. Things I'd Think About Before Going to Production The sample intentionally focuses on the core architecture rather than every production concern. If I were extending it into a larger application, there are a few additional pieces I'd want to add. The first is structured logging. Voice AI systems involve multiple moving parts: the assistant, your backend, downstream APIs, and whatever business systems those APIs talk to. Being able to trace a request across that entire path makes debugging significantly easier when something goes wrong. I'd also pay close attention to latency. Unlike background jobs or asynchronous workflows, webhook tool calls happen while someone is actively waiting on the phone. If your backend spends several seconds waiting for another service to respond, the caller experiences that delay directly. Wherever possible, keep tool invocations fast and push long-running operations into asynchronous workflows. Idempotency is another consideration that's easy to overlook. Webhooks can occasionally be retried, so operations that create appointments, tickets, or other records should be safe to execute more than once. Otherwise, a single retry could produce duplicate records. Finally, I'd invest in good observability. When a voice application doesn't behave as expected, it's often difficult to tell whether the issue originated in the LLM, the backend, or one of the downstream services. Metrics, traces, and structured logs become incredibly valuable once your assistant starts handling real customer conversations. Where This Pattern Fits Although this sample schedules home-service estimates, the architecture itself is broadly applicable. The scheduling logic could just as easily be replaced with CRM lookups, order tracking, customer verification, support ticket creation, reservations, inventory queries, or lead qualification. That's really the takeaway from this example. The fake confirmation number isn't the interesting part. The reusable architecture is. The assistant remains focused on the conversation. The Edge Compute function becomes the bridge between that conversation and the rest of your application. As your backend grows, that architectural boundary continues to hold, regardless of how many systems the assistant eventually integrates with. Final Thoughts When developers first start experimenting with AI assistants, most of the attention naturally goes toward prompts. That's the part you interact with every day, and it's the fastest way to make an assistant sound smarter but once an assistant moves beyond demos, the backend becomes just as important as the prompt. An assistant that can answer questions is useful. An assistant that can securely interact with your business systems is significantly more valuable. That's why I like this example. It doesn't try to demonstrate every possible feature of Edge Compute or AI Assistants. Instead, it focuses on a simple architectural pattern that's easy to understand and easy to extend. Let the AI Assistant own the conversation, let Edge Compute own the application logic, and connect that function to the systems your business already depends on. Once you have that pattern in place, adding new capabilities usually becomes a matter of writing more business logic rather than redesigning the entire architecture.