Post-Processing Is Where Bugs Hide
You extract a subtotal and a tax amount from an invoice. Then you add them together in your application code to get the total. Simple math, hard to get wrong.
Until the decimal separator is a comma instead of a period. Or the subtotal extraction rounds differently than the total extraction. Or a new developer changes the rounding logic six months later and nobody notices because the tests still pass.
Post-processing extracted values is a common source of bugs. You’re rebuilding the document’s own calculations in your code, and your code might not match the document’s math.
The Document Extraction API has a better approach: CALCULATED fields. You define the operation and the source fields in your schema, and the parser computes the result for you.
How Calculated Fields Work
A CALCULATED field takes two or more source fields and applies an operation: sum, subtract, multiply, or divide.
const schema = {
fields: [
{
name: "subtotal",
type: "CURRENCY_AMOUNT",
description: "Invoice subtotal before tax",
is_required: true,
},
{
name: "taxAmount",
type: "CURRENCY_AMOUNT",
description: "Tax amount on the invoice",
is_required: true,
},
{
name: "computedTotal",
type: "CALCULATED",
description: "Computed total: subtotal + tax",
operation: "sum",
source_field_names: ["subtotal", "taxAmount"],
},
],
};
The parser extracts the subtotal and tax amount from the document, then computes computedTotal = subtotal + taxAmount. No post-processing in your code.
Cross-Checking Extracted Totals
The real power of CALCULATED fields is validation. Extract the total that’s printed on the document and compute it from the components:
const schema = {
fields: [
{
name: "subtotal",
type: "CURRENCY_AMOUNT",
description: "Invoice subtotal before tax",
is_required: true,
},
{
name: "taxAmount",
type: "CURRENCY_AMOUNT",
description: "Tax amount",
is_required: true,
},
{
name: "extractedTotal",
type: "CURRENCY_AMOUNT",
description: "Total amount as printed on the invoice",
is_required: true,
},
{
name: "computedTotal",
type: "CALCULATED",
description: "Computed total: subtotal + tax",
operation: "sum",
source_field_names: ["subtotal", "taxAmount"],
},
],
};
Now you have two values for the total: one extracted directly from the document, one computed from components. If they match, you have high confidence in all three values. If they don’t match, something went wrong — flag the document for review.
const { data } = await response.json();
const extractedTotal = data.extractedTotal.value;
const computedTotal = data.computedTotal.value;
const isConsistent = Math.abs(extractedTotal - computedTotal) < 0.01;
if (!isConsistent) {
// Flag for review — the extracted values don't add up
flagForReview(document, { extractedTotal, computedTotal });
}
This cross-check catches extraction errors that confidence scores alone might miss. A subtotal of 1,234.56 might have 0.95 confidence, but if it doesn’t add up with the tax to match the total, you know something is off.
Available Operations
CALCULATED fields support four operations:
- sum — adds all source field values
- subtract — subtracts subsequent values from the first
- multiply — multiplies all source field values
- divide — divides the first value by subsequent values
Each operation takes a source_field_names array with at least two field names. The source fields must be numeric types (INTEGER, DECIMAL, CURRENCY_AMOUNT, or other CALCULATED fields).
Operations in Detail
Each operation handles its source fields in a specific order. This matters for non-commutative operations like subtract and divide.
sum adds all source field values together. Order doesn’t matter. If you have three source fields with values 100, 20, and 5, the result is 125. This is the most common operation — invoice subtotals, combined line item totals, aggregated expenses.
{
name: "totalDeductions",
type: "CALCULATED",
description: "Sum of all deduction amounts",
operation: "sum",
source_field_names: ["federalTax", "stateTax", "insurancePremium", "retirementContribution"],
}
subtract takes the first source field and subtracts all subsequent values from it. With source fields valued at 1000, 200, and 50, the result is 750. Useful for net-of-deductions calculations — gross pay minus taxes, revenue minus expenses.
{
name: "netPay",
type: "CALCULATED",
description: "Gross pay minus all deductions",
operation: "subtract",
source_field_names: ["grossPay", "totalDeductions"],
}
multiply multiplies all source field values together. With values 25.00, 4, and 1.19, the result is 119.00. Common for line item totals (price times quantity) and tax-inclusive calculations (amount times tax multiplier).
{
name: "lineTotal",
type: "CALCULATED",
description: "Unit price times quantity",
operation: "multiply",
source_field_names: ["unitPrice", "quantity"],
}
divide divides the first source field by each subsequent value. With values 1000 and 4, the result is 250. Useful for per-unit costs, ratios, and averages where you have a total and a count.
{
name: "costPerUnit",
type: "CALCULATED",
description: "Total cost divided by quantity received",
operation: "divide",
source_field_names: ["totalCost", "quantityReceived"],
}
Chaining Calculations
CALCULATED fields can reference other CALCULATED fields. The parser resolves dependencies automatically:
const schema = {
fields: [
{
name: "unitPrice",
type: "CURRENCY_AMOUNT",
description: "Price per unit",
},
{
name: "quantity",
type: "INTEGER",
description: "Number of units",
},
{
name: "lineTotal",
type: "CALCULATED",
description: "Line total: unit price times quantity",
operation: "multiply",
source_field_names: ["unitPrice", "quantity"],
},
{
name: "taxRate",
type: "DECIMAL",
description: "Tax rate as a decimal (e.g., 0.19 for 19%)",
},
{
name: "taxAmount",
type: "CALCULATED",
description: "Tax: line total times tax rate",
operation: "multiply",
source_field_names: ["lineTotal", "taxRate"],
},
],
};
The parser extracts unitPrice, quantity, and taxRate from the document, computes lineTotal from the first two, then computes taxAmount from lineTotal and taxRate. The dependency graph is resolved automatically — you don’t need to worry about field ordering.
Use Cases
Invoice verification. Compute totals from line items and compare against the printed total. Flag discrepancies before they enter your accounting system.
Expense ratio calculation. Extract revenue and expense figures, compute the ratio using divide. No spreadsheet step needed.
Margin analysis. Extract cost and selling price, compute margin using subtract and divide. The parser does the math on the document’s own numbers.
Unit cost derivation. Extract total cost and quantity, compute unit cost using divide. Useful for procurement documents where unit cost isn’t always listed explicitly.
Real-World Example: Multi-Line Invoice with Tax
Here’s a complete schema for an invoice where you need to verify that the line items, tax, and total are internally consistent. The schema extracts the printed values and computes them independently, giving you two sources of truth to compare.
const schema = {
fields: [
{
name: "lineItems",
type: "ARRAY",
description: "Invoice line items",
item_schema: {
fields: [
{ name: "description", type: "TEXT", description: "Item description" },
{ name: "unitPrice", type: "CURRENCY_AMOUNT", description: "Price per unit" },
{ name: "quantity", type: "INTEGER", description: "Quantity ordered" },
{ name: "lineTotal", type: "CURRENCY_AMOUNT", description: "Line total as printed on invoice" },
],
},
},
{
name: "subtotal",
type: "CURRENCY_AMOUNT",
description: "Invoice subtotal as printed",
is_required: true,
},
{
name: "taxRate",
type: "DECIMAL",
description: "Tax rate as a decimal (e.g., 0.19 for 19%)",
},
{
name: "taxAmount",
type: "CURRENCY_AMOUNT",
description: "Tax amount as printed on the invoice",
is_required: true,
},
{
name: "printedTotal",
type: "CURRENCY_AMOUNT",
description: "Total amount as printed on the invoice",
is_required: true,
},
{
name: "computedTax",
type: "CALCULATED",
description: "Computed tax: subtotal times tax rate",
operation: "multiply",
source_field_names: ["subtotal", "taxRate"],
},
{
name: "computedTotal",
type: "CALCULATED",
description: "Computed total: subtotal plus tax amount",
operation: "sum",
source_field_names: ["subtotal", "taxAmount"],
},
],
};
After extraction, you have three cross-checks available. Compare computedTotal against printedTotal to verify the invoice math. Compare computedTax against taxAmount to verify the tax calculation. If any pair diverges, the document needs human review — either the extraction made an error, or the invoice itself has a calculation mistake.
When Calculations Return Null
A CALCULATED field depends on its source fields. If a source field fails to extract — the document doesn’t contain a tax rate, or the OCR misreads a number — the CALCULATED field returns null instead of an incorrect value. This is intentional. A missing result is better than a wrong result.
Check for null values in your validation logic before comparing computed and printed values. A null computed total doesn’t mean the printed total is wrong — it means the parser couldn’t verify it.
What’s Next
Calculated totals feed directly into Document Generation for invoice summary PDFs — same auth, same credit pool.
Get Started
Check the docs for the full CALCULATED field reference, including how dependency resolution works with chained calculations. The TypeScript and Python SDKs return computed values alongside extracted values — same response format, same confidence scores.
Sign up for a free account — no credit card required. Add a CALCULATED field to your existing schema and see the cross-checking in action.