Build a structured extractor

Model a receipt, extract typed data, call the generated client, and strengthen the boundary.

What you will build

This tutorial builds a server-side function that turns receipt text into a typed list of items and an optional total. It assumes a configured model client and a generated TypeScript client.

1. Model the result

Start with the data the application needs. Keep monetary fields numeric so callers do not need to parse display strings.

receipts/extract-receipt.baml
class ReceiptItem {
  name string
  quantity int
  price float
}

class Receipt {
  items ReceiptItem[]
  total float?
}
class ReceiptItem {
  name string
  quantity int
  price float
}

class Receipt {
  items ReceiptItem[]
  total float?
}

2. Add the function

Declare the input and output, select the configured client, and include the generated output instructions in the prompt.

receipts/extract-receipt.baml
function ExtractReceipt(text: string) -> Receipt {
  client: "openai/gpt-4o-mini"
  prompt: `
    Extract the receipt from the following text.

    ${text}

    ${ctx.output_format()}
  `
}
function ExtractReceipt(text: string) -> Receipt {
  client: "openai/gpt-4o-mini"
  prompt: `
    Extract the receipt from the following text.

    ${text}

    ${ctx.output_format()}
  `
}

Keep the prompt focused on the task. The return type already carries the structural contract into the generated output instructions.

3. Call the client

Check the BAML project, regenerate the client, and call the function from server-side application code.

import { b } from "./baml_client"

export async function extractReceipt(text: string) {
  const receipt = await b.ExtractReceipt(text)

  return {
    lineItems: receipt.items.length,
    total: receipt.total ?? null,
  }
}

4. Harden the boundary

  • Add representative tests for clean, noisy, and incomplete receipts.
  • Decide whether a missing total is acceptable before making it optional.
  • Keep credentials and model calls in server-only modules.
  • Regenerate the client whenever the result model changes.

Review the TypeScript bridge for host-language details, or open the functions chapter for the underlying language model.