Voice AI

Extract Structured Data from Faxes with Telnyx AI Inference

Fax is not the first workflow most developers want to modernize.

It is also not going away in a lot of industries. Healthcare, insurance, logistics, legal, finance, and back-office operations still receive documents by fax. The problem is not only receiving the document. The problem is what happens next.

Someone usually has to read the fax, identify what kind of document it is, copy fields into another system, and route it to the right team.

The fax-to-structured-data-pipeline-python example shows how to turn that workflow into a small Python API:

  • Receive inbound fax webhook events
  • Verify the Telnyx webhook signature
  • Queue fax metadata
  • Extract structured JSON from document text with Telnyx AI Inference
  • List recent faxes and extracted data

The app is intentionally compact so builders can understand the workflow and adapt it to their own document pipeline.

What the Example Builds

The Flask app exposes four routes:

  • POST /webhooks/fax - receives Telnyx fax webhook events
  • POST /extract - extracts structured JSON from document text
  • GET /faxes - lists recently queued fax metadata
  • GET /extracted - lists recent extraction results
  • GET /health - checks app health

It uses Telnyx AI Inference through:

POST /v2/ai/chat/completions

The current default model in .env.example is:

AI_MODEL=moonshotai/Kimi-K2.6

The fax webhook route is responsible for receiving events. The extraction route is responsible for the document intelligence step.

Why Fax Extraction Is a Good AI App Pattern

Faxed documents are often semi-structured.

An invoice has predictable concepts: vendor, invoice number, due date, line items, subtotal, tax, and total. A purchase order has a PO number, vendor, ship-to address, items, and delivery date. A prescription has patient, prescriber, medication, dosage, frequency, quantity, refills, and date.

The exact layout can vary from sender to sender, but the fields your application needs are usually consistent.

That is where AI extraction is useful. Instead of building a pile of brittle parsers for every possible fax layout, you can ask the model to return a strict JSON shape for the document type you care about.

The app includes prompt templates for:

  • invoice
  • order
  • prescription
  • auto

The auto mode asks the model to identify the document type and return relevant fields.

Running the Example

Clone the examples repo and enter the project folder:

git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/fax-to-structured-data-pipeline-python

Create your environment file:

cp .env.example .env

Set your Telnyx values:

TELNYX_API_KEY=your_telnyx_api_key
TELNYX_PUBLIC_KEY=your_telnyx_public_key
AI_MODEL=moonshotai/Kimi-K2.6
HOST=127.0.0.1

Install dependencies and run the app:

pip install -r requirements.txt
python app.py

The app starts locally on http://localhost:5000.

Extract an Invoice

You can test the extraction workflow directly without sending a real fax:

curl -X POST http://localhost:5000/extract \
  -H "Content-Type: application/json" \
  -d '{
    "type": "invoice",
    "text": "Invoice #INV-1042 from Acme Medical Supplies dated 2026-07-01. Due 2026-07-31. Bill to North Clinic. Item: Nitrile gloves, quantity 10, unit price 12.50, total 125.00. Item: Face masks, quantity 5, unit price 20.00, total 100.00. Subtotal 225.00. Tax 18.00. Total 243.00. Payment terms Net 30."
  }'

The model is asked to return JSON like:

{
  "vendor": "Acme Medical Supplies",
  "invoice_number": "INV-1042",
  "date": "2026-07-01",
  "due_date": "2026-07-31",
  "line_items": [
    {
      "description": "Nitrile gloves",
      "quantity": 10,
      "unit_price": 12.5,
      "total": 125
    }
  ],
  "subtotal": 225,
  "tax": 18,
  "total": 243,
  "payment_terms": "Net 30"
}

The app stores successful extraction results in memory and adds an extracted_at timestamp.

Extract a Purchase Order

The same endpoint can use a different prompt:

curl -X POST http://localhost:5000/extract \
  -H "Content-Type: application/json" \
  -d '{
    "type": "order",
    "text": "Purchase Order PO-7781. Vendor: Harbor Office Supply. Ship to: 500 Market St, San Francisco, CA. SKU CHAIR-22, ergonomic chair, quantity 12, unit price 199.00. SKU DESK-10, standing desk, quantity 4, unit price 450.00. Total 4188.00. Delivery requested 2026-07-20."
  }'

That produces a purchase-order-shaped JSON response instead of an invoice-shaped one.

The Fax Webhook Route

The webhook route is:

POST /webhooks/fax

The app uses the Telnyx Python SDK to verify the inbound webhook signature before trusting the event:

client.webhooks.unwrap(request.get_data(as_text=True), headers=dict(request.headers))

When the event type is fax.received, the app queues metadata like fax ID, sender, recipient, page count, media URL, status, and timestamp.

That route is designed for a live Telnyx Fax Application. During local development, expose your server:

ngrok http 5000

Then configure your Fax Application webhook URL in the Telnyx Portal:

https://<id>.ngrok.io/webhooks/fax

Inspect Queued and Extracted Data

List queued fax metadata:

curl http://localhost:5000/faxes

List extracted data:

curl http://localhost:5000/extracted

Check service health:

curl http://localhost:5000/health

Making It Production-Ready

This sample keeps state in memory and focuses on the workflow. For production, you would likely add:

  • Persistent storage for fax metadata and extraction results
  • Media download and OCR before the /extract step
  • Authentication for internal API routes
  • Strong JSON schema validation per document type
  • Retry handling for inference calls
  • Human review for sensitive documents
  • Audit logs for document processing
  • Redaction and retention policies for regulated data
  • Queue processing for high-volume inbound fax traffic

The repo is intentionally agent-readable. The README, API reference, guide, environment template, and app code are structured so coding agents can inspect the project and help extend it. You can ask an agent to add OCR, storage, retries, stricter schema validation, or workflow integrations.

Frequently Asked Questions

What does this guide cover?
Fax is not the first workflow most developers want to modernize.
What is what the example builds?
The Flask app exposes four routes:
What is why fax extraction is a good ai app pattern?
Faxed documents are often semi-structured.
What is running the example?
Clone the examples repo and enter the project folder:
What is extract an invoice?
You can test the extraction workflow directly without sending a real fax:

Resources

  • Code: https://github.com/team-telnyx/telnyx-code-examples/tree/main/fax-to-structured-data-pipeline-python
  • Telnyx AI skills and toolkits: https://github.com/team-telnyx/ai
  • Telnyx Fax docs: https://developers.telnyx.com/docs/programmable-fax/send-a-fax-api
  • Telnyx AI Inference docs: https://developers.telnyx.com/docs/inference
  • Chat Completions API: https://developers.telnyx.com/api/inference/chat-completions
  • Telnyx Portal: https://portal.telnyx.com/

Ready to build with low-latency voice AI?

Join developers building the future of real-time conversations