Skip to content
Back to blog
launch document types

Introducing custom document types

You can now define your own field schemas and have dOCR return exactly those typed fields — nothing more, nothing less. What custom document types are, how to define one, and why they beat generic extraction.

Priya Raghavan

Co-founder & CTO

4 min read

Custom document types are generally available today on every dOCR account. Instead of accepting whatever fields our generic extractor decides are interesting, you declare the schema you want — field names and types — and dOCR returns exactly that, as structured JSON, not raw OCR text.

The short version: a document type is a named contract. You tell dOCR “an invoice has invoiceNumber (string), total (number), dueDate (date), and lineItems (array),” and every extraction against that type comes back shaped the same way, every time. During the private preview, teams that moved from generic extraction to a defined type cut their post-processing code by a median of 70% and saw field-level accuracy climb on the fields they actually cared about.

What a document type is

A document type is a schema you own. Each field has a name, a type, and an optional description that tells the model what it’s looking for. Supported types:

TypeExample valueNotes
string"ACME-2024-0148"Free text, IDs, names
number1284.50Parsed to a real number, not a string
date"2026-05-31"Normalized to ISO 8601
booleantruePresence checks, paid/unpaid flags
array[ ... ]Repeating rows — line items, transactions

The description field matters more than it looks. “Total amount due including tax” extracts more reliably than a bare total, because the model has the same context a human reader would.

Defining and extracting

Create a document type once in the Dashboard, or define it inline through the API. Either way, you reference it by documentType on the extract call. Here is the full round trip with curl:

curl -X POST https://docr.dev/api/v1/extract \
  -H "Authorization: Bearer $DOCR_API_KEY" \
  -F "file=@invoice.pdf" \
  -F "documentType=invoice"

The same call from JavaScript:

const form = new FormData();
form.append('file', file); // a File or Blob
form.append('documentType', 'invoice');

const res = await fetch('https://docr.dev/api/v1/extract', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.DOCR_API_KEY}` },
  body: form,
});

const { data } = await res.json();

Because the type pins the shape, the response is predictable — you can write it straight into a typed struct or a database row:

{
  "data": {
    "invoiceNumber": "ACME-2024-0148",
    "total": 1284.50,
    "dueDate": "2026-05-31",
    "lineItems": [
      { "description": "Annual license", "quantity": 1, "amount": 1200.00 },
      { "description": "Onboarding", "quantity": 1, "amount": 84.50 }
    ]
  }
}

No total that’s sometimes "$1,284.50" and sometimes 1284.5. No dates in three regional formats. The type does the normalizing.

Why this beats generic extraction

Generic extraction is great for “what’s in this document?” It’s a poor fit for “give me these eight fields, the same way, ten thousand times.” Three things change when you define a type:

  • Consistency. Field names and types are fixed by the schema, so downstream code never has to guess at keys or coerce strings. The contract is the same on document one and document ten thousand.
  • Accuracy. Telling the model exactly what to find — with your descriptions for context — narrows the search. Ambiguous fields like “the customer’s PO number, not our invoice number” resolve correctly far more often than with an open-ended pass.
  • Less glue code. Typed values arrive parsed. Numbers are numbers, dates are ISO strings, and absent fields are explicit nulls instead of silently missing keys.

It works the same on a one-page receipt or a 15-page multi-page statement — the type describes the fields, and dOCR finds them wherever they live in the document.

Available in the Dashboard and the API

Custom document types are first-class in both places. In the Dashboard, build a type in the schema editor, upload a sample, and see the typed result in Extraction History before you write a line of code. When you’re ready to automate, the same type is addressable by name from the API — the Dashboard-to-API path we designed for exactly this. Types you create in one surface are immediately available in the other; there is no separate publish step.

All the usual limits apply: PDF, JPG, PNG, BMP, WebP, and DOCX up to 10MB and 15 pages.

What’s next

This is a foundation, not a finish line. Next on the roadmap: nested object fields for documents with sub-records, field-level validation rules, and shared type libraries so a team can standardize on one invoice schema across every project. If you’ve been bolting a normalization layer onto generic extraction to get clean, typed data, custom document types are the release where you can take it out. Read the full reference in the docs and define your first type today.