Skip to main content
Learn by building real applications on Telnyx Edge. Each tutorial walks you through a complete project from start to deployment.

Getting Started

Build a REST API

Create a JSON API with routing, validation, and error handling.
Time: 15 min · Level: Beginner

SMS Webhook Handler

Process incoming SMS messages and send automated replies.
Time: 20 min · Level: Beginner

Voice Call Router

Route calls based on time of day and caller location.
Time: 25 min · Level: Intermediate

Image Resizer

Resize and optimize images on-the-fly at the edge.
Time: 20 min · Level: Intermediate

Build a REST API

Build a complete REST API with CRUD operations, input validation, and error handling. What you’ll learn:
  • Routing requests to different handlers
  • Parsing JSON request bodies
  • Returning proper HTTP status codes
  • Error handling patterns

Prerequisites

  • Telnyx account with Edge Compute enabled
  • telnyx-edge CLI installed

Step 1: Create the Function

telnyx-edge new-func -l=python -n=my-api
cd my-api

Step 2: Implement the API

Replace src/main.py:
import json
from urllib.parse import urlparse, parse_qs

# In-memory storage (use KV for persistence)
items = {}
next_id = 1

class Function:
    async def handler(self, request):
        path = urlparse(request.url).path
        method = request.method
        
        # Route requests
        if path == "/items" and method == "GET":
            return self.list_items()
        elif path == "/items" and method == "POST":
            return await self.create_item(request)
        elif path.startswith("/items/") and method == "GET":
            return self.get_item(path.split("/")[-1])
        elif path.startswith("/items/") and method == "DELETE":
            return self.delete_item(path.split("/")[-1])
        else:
            return self.json_response({"error": "Not found"}, 404)
    
    def list_items(self):
        return self.json_response(list(items.values()))
    
    async def create_item(self, request):
        global next_id
        try:
            body = json.loads(await request.text())
        except json.JSONDecodeError:
            return self.json_response({"error": "Invalid JSON"}, 400)
        
        if "name" not in body:
            return self.json_response({"error": "name is required"}, 400)
        
        item = {"id": str(next_id), "name": body["name"]}
        items[str(next_id)] = item
        next_id += 1
        
        return self.json_response(item, 201)
    
    def get_item(self, item_id):
        if item_id in items:
            return self.json_response(items[item_id])
        return self.json_response({"error": "Not found"}, 404)
    
    def delete_item(self, item_id):
        if item_id in items:
            del items[item_id]
            return self.json_response({"deleted": True})
        return self.json_response({"error": "Not found"}, 404)
    
    def json_response(self, data, status=200):
        return {
            "status": status,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps(data)
        }

Step 3: Test Locally

telnyx-edge dev
In another terminal:
# Create an item
curl -X POST http://localhost:8787/items \
  -H "Content-Type: application/json" \
  -d '{"name": "Test Item"}'

# List items
curl http://localhost:8787/items

# Get single item
curl http://localhost:8787/items/1

# Delete item
curl -X DELETE http://localhost:8787/items/1

Step 4: Deploy

telnyx-edge ship
Your API is now live at https://my-api-{orgId}.telnyxcompute.com.

SMS Webhook Handler

Process incoming SMS messages and respond automatically based on keywords. What you’ll learn:
  • Handling Telnyx webhooks
  • Parsing SMS payloads
  • Sending SMS responses via Telnyx API

Prerequisites

  • Telnyx account with a phone number
  • Phone number configured for SMS

Step 1: Create the Function

telnyx-edge new-func -l=javascript -n=sms-handler
cd sms-handler

Step 2: Implement the Handler

Replace src/index.js:
export async function handler(request) {
    // Verify this is a POST from Telnyx
    if (request.method !== "POST") {
        return new Response("Method not allowed", { status: 405 });
    }
    
    const webhook = await request.json();
    const event = webhook.data;
    
    // Handle incoming SMS
    if (event.event_type === "message.received") {
        const from = event.payload.from.phone_number;
        const text = event.payload.text.toLowerCase().trim();
        
        // Keyword-based responses
        let reply;
        if (text === "help") {
            reply = "Commands: HOURS, LOCATION, STATUS";
        } else if (text === "hours") {
            reply = "We're open Mon-Fri 9am-5pm EST";
        } else if (text === "location") {
            reply = "123 Main St, New York, NY 10001";
        } else if (text === "status") {
            reply = "All systems operational ✓";
        } else {
            reply = `Thanks for your message! Reply HELP for options.`;
        }
        
        // Send reply via Telnyx API
        await sendSMS(from, reply);
    }
    
    return new Response(JSON.stringify({ received: true }), {
        headers: { "Content-Type": "application/json" }
    });
}

async function sendSMS(to, text) {
    const response = await fetch("https://api.telnyx.com/v2/messages", {
        method: "POST",
        headers: {
            "Authorization": `Bearer ${process.env.TELNYX_API_KEY}`,
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            from: process.env.TELNYX_PHONE_NUMBER,
            to: to,
            text: text
        })
    });
    
    if (!response.ok) {
        console.error("Failed to send SMS:", await response.text());
    }
}

Step 3: Configure Secrets

telnyx-edge secrets add TELNYX_API_KEY "your-api-key-from-portal"
telnyx-edge secrets add TELNYX_PHONE_NUMBER "+15551234567"
Get your API key from the Telnyx Portal under API Keys. Use your Telnyx phone number in E.164 format.

Step 4: Deploy and Configure Webhook

telnyx-edge ship
In the Telnyx Portal:
  1. Go to Messaging → Phone Numbers
  2. Select your number
  3. Set webhook URL to https://sms-handler-{orgId}.telnyxcompute.com

Step 5: Test

Send an SMS with “HELP” to your Telnyx number. You should receive the help menu.

Voice Call Router

Route incoming calls based on time of day, caller location, or custom logic. What you’ll learn:
  • TeXML for call control
  • Time-based routing logic
  • Geographic routing

Step 1: Create the Function

telnyx-edge new-func -l=python -n=call-router
cd call-router

Step 2: Add Dependencies

Create requirements.txt:
pytz>=2024.1

Step 3: Implement Call Routing

from datetime import datetime
import pytz

class Function:
    async def handler(self, request):
        webhook = await request.json()
        caller = webhook.get("from", "")
        
        # Determine routing
        destination = self.get_destination(caller)
        
        # Generate TeXML response
        texml = f"""<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say>Please hold while we connect your call.</Say>
    <Dial timeout="30">
        <Number>{destination}</Number>
    </Dial>
    <Say>We're sorry, no one is available. Please try again later.</Say>
    <Hangup/>
</Response>"""
        
        return {
            "status": 200,
            "headers": {"Content-Type": "application/xml"},
            "body": texml
        }
    
    def get_destination(self, caller):
        # Time-based routing
        est = pytz.timezone("America/New_York")
        now = datetime.now(est)
        hour = now.hour
        
        # Business hours: 9am-5pm EST
        if 9 <= hour < 17:
            # Route to main office
            return "+15551234567"
        else:
            # Route to after-hours support
            return "+15559876543"
        
        # Geographic routing example:
        # if caller.startswith("+44"):
        #     return "+441onal support"
        # return "+1 US support"

Step 4: Deploy

telnyx-edge ship
Configure your Telnyx number to use this function as the TeXML webhook.

Image Resizer

Resize and optimize images on-the-fly based on URL parameters. What you’ll learn:
  • Processing binary data
  • Query parameter handling
  • Caching strategies

Step 1: Create the Function

telnyx-edge new-func -l=python -n=image-resizer
cd image-resizer

Step 2: Add Dependencies

Create or update requirements.txt:
pillow>=10.0.0
httpx>=0.27.0

Step 3: Implement the Resizer

from PIL import Image
from io import BytesIO
from urllib.parse import urlparse, parse_qs
import base64
import httpx

class Function:
    def __init__(self):
        self.client = httpx.AsyncClient()
    
    async def handler(self, request):
        params = parse_qs(urlparse(request.url).query)
        
        # Get parameters
        image_url = params.get("url", [None])[0]
        width = int(params.get("w", [0])[0]) or None
        height = int(params.get("h", [0])[0]) or None
        quality = int(params.get("q", [85])[0])
        
        if not image_url:
            return {"status": 400, "body": "Missing url parameter"}
        
        # Fetch original image
        response = await self.client.get(image_url)
        if response.status_code != 200:
            return {"status": 404, "body": "Image not found"}
        
        # Process image
        img = Image.open(BytesIO(response.content))
        
        # Resize if dimensions provided
        if width or height:
            if width and height:
                img = img.resize((width, height), Image.LANCZOS)
            elif width:
                ratio = width / img.width
                img = img.resize((width, int(img.height * ratio)), Image.LANCZOS)
            else:
                ratio = height / img.height
                img = img.resize((int(img.width * ratio), height), Image.LANCZOS)
        
        # Convert to bytes
        output = BytesIO()
        img.save(output, format="JPEG", quality=quality, optimize=True)
        
        return {
            "status": 200,
            "headers": {
                "Content-Type": "image/jpeg",
                "Cache-Control": "public, max-age=86400"
            },
            "body": base64.b64encode(output.getvalue()).decode()
        }

Step 4: Deploy and Test

telnyx-edge ship
Test with:
https://image-resizer-{orgId}.telnyxcompute.com/?url=https://example.com/photo.jpg&w=400&q=80

More Tutorials

Authentication Middleware

Add JWT validation to protect your APIs.
Coming soon

Rate Limiter

Implement rate limiting with sliding windows.
Coming soon

A/B Testing

Route traffic between variants for experiments.
Coming soon

Webhook Validator

Verify signatures from third-party services.
Coming soon