Voice AI

Build Semantic Search for Support Tickets with Telnyx AI Inference

Search usually starts simple.

You add a text box, run a keyword query, and call it done.

Then real users show up.

One customer says "international SMS is failing." Another says "texts to Germany are not going through." Someone else writes "messages to +49 numbers are stuck." Those tickets might describe the same underlying issue, but a plain keyword search can easily split them apart.

That is where semantic search becomes useful.

Instead of matching only exact words, semantic search compares meaning. The query and the documents are converted into embeddings, and the app returns the items whose vectors are closest to the query vector.

This example builds that pattern for support tickets using Telnyx AI Inference.

Code:

What the example builds

The project is a small Flask API that indexes support tickets and searches them by meaning.

It includes:

  • a bundled sample_tickets.json dataset
  • Telnyx embeddings through POST /v2/ai/openai/embeddings
  • an in-memory numpy vector store
  • cosine similarity ranking
  • endpoints for indexing, searching, adding tickets, viewing stats, and health checks

The flow looks like this:

Support tickets
  -> embed subject + body
  -> store vectors in memory
  -> embed search query
  -> compare with cosine similarity
  -> return top matching tickets

The point of the sample is not to replace a production search stack in 100 lines of Python. The point is to make the core pattern visible and runnable.

The API shape

The app exposes:

  • POST /index to build the embedding index
  • POST /search to search tickets by meaning
  • POST /tickets to add a new ticket to the index
  • GET /tickets/<id> to fetch one ticket
  • GET /stats to inspect the current index
  • GET /health to check service status

After the app starts, you build the index:

curl -X POST http://localhost:5000/index

That embeds the bundled support tickets and stores their vectors in memory.

Then you can ask a natural-language question:

curl -X POST http://localhost:5000/search \
  -H "Content-Type: application/json" \
  -d '{
    "query": "I cannot send text messages to international phone numbers",
    "top_k": 3
  }' | python3 -m json.tool

The response returns the closest ticket matches:

{
  "query": "I cannot send text messages to international phone numbers",
  "model": "thenlper/gte-large",
  "total_indexed": 15,
  "results": [
    {
      "ticket_id": "TKT-1002",
      "subject": "International SMS routing issue",
      "category": "messaging",
      "priority": "high",
      "score": 0.9077
    }
  ]
}

That score is a similarity score, not a keyword count. A ticket can rank well even when the wording is different from the query.

How it uses Telnyx embeddings

The app sends text to:

POST /v2/ai/openai/embeddings

The default model is:

EMBEDDING_MODEL=thenlper/gte-large

In the sample, each ticket is represented by its subject and body:

"{subject}. {body}"

Those strings are sent to the embeddings endpoint in batches. The returned vectors are stored in a numpy matrix.

When a search request arrives, the app embeds the query, compares it to every ticket vector, sorts by similarity, and returns the top matches.

That is the core loop behind many retrieval systems:

  1. embed the documents
  2. embed the query
  3. compare vectors
  4. return the nearest results

Why support tickets are a good example

Support data is messy in a way that makes semantic search feel practical.

Customers do not use your internal taxonomy. They describe symptoms. They mention regions, carriers, products, numbers, error codes, partial context, or business impact.

For example, these might all be related:

  • "SMS to France is delayed"
  • "messages to EU numbers are not delivering"
  • "international text routing problem"
  • "DLRs are missing for Germany"

Keyword search can still help, especially for identifiers and exact error codes. But semantic search gives you another layer: it can surface related tickets even when the language changes.

That makes it useful for:

  • duplicate ticket detection
  • support-agent assist tools
  • internal help desk search
  • incident triage
  • routing similar issues to the same team
  • finding historical tickets before opening a new case

Running the example

Clone the repo:

git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/semantic-search-python

Create your environment file:

cp .env.example .env

Add your Telnyx API key:

TELNYX_API_KEY=your_telnyx_api_key
EMBEDDING_MODEL=thenlper/gte-large
HOST=127.0.0.1
PORT=5000

Install dependencies and start the Flask app:

pip install -r requirements.txt
python app.py

Check health:

curl http://localhost:5000/health

Index the sample tickets:

curl -X POST http://localhost:5000/index

Try a few searches:

curl -X POST http://localhost:5000/search \
  -H "Content-Type: application/json" \
  -d '{"query":"audio quality problems on calls","top_k":3}' | python3 -m json.tool

curl -X POST http://localhost:5000/search \
  -H "Content-Type: application/json" \
  -d '{"query":"my API key stopped working after I changed it","top_k":3}' | python3 -m json.tool

curl -X POST http://localhost:5000/search \
  -H "Content-Type: application/json" \
  -d '{"query":"phone number transfer is taking too long","top_k":3}' | python3 -m json.tool

What to change for production

The example uses in-memory storage so the mechanics are easy to inspect. A production version would probably move the vector layer into a dedicated database or search system.

Possible next steps:

  • store embeddings in pgvector, Qdrant, Weaviate, Pinecone, Redis, or another vector store
  • persist ticket metadata in Postgres
  • update vectors incrementally when tickets change
  • combine semantic search with filters for category, priority, date range, or customer account
  • add authentication to the Flask endpoints
  • log search queries and clicked results for relevance tuning
  • use similarity thresholds to detect likely duplicates
  • add keyword search for exact IDs, phone numbers, or error codes

The important part is the separation of concerns.

Telnyx AI Inference turns text into vectors. Your application decides how to store, rank, filter, and present the results.

That makes semantic search feel less like a black box and more like normal application architecture.

Frequently Asked Questions

No FAQs yet.

Resources

  • Code:
  • Telnyx AI skills and toolkits:
  • Telnyx AI Inference docs:
  • Embeddings API:
  • Available models:
  • Telnyx Portal:

Ready to build with low-latency voice AI?

Join developers building the future of real-time conversations