---
title: "How do you get AI to read a document and fill in CRM fields reliably?"
blueprint: 002
slug: ai-fields-read-documents
category: AI fields
revised: 2026-09-02
platform: Coevera CRM (formerly Pipeliner CRM)
canonical: https://blueprints.coevera.com/blueprints/ai-fields-read-documents/
publisher: Coevera
customer_data: none
---

# How do you get AI to read a document and fill in CRM fields reliably?

**Short answer.** **Recognise once, read many.** Reading a document is by far the most expensive
thing an AI field does, so exactly one field reads the attachments and writes a structured
worksheet — JSON, into a long-text field. Every other field is a cheap text-only reader pointed at
a path inside that worksheet. Twenty extracted values cost one document read plus twenty text
reads, and every value is derived from the same reading rather than twenty independent ones that
disagree.

**The constraint that shapes everything else:** a process reads a record snapshot taken when it
starts, so an AI field cannot see what another AI field wrote in the same process. Every AI-to-AI
dependency is therefore a process boundary — one AI pass per process, chained on completion.

## 01 · The business problem

A reseller buys from suppliers and sells on to end customers. Each deal carries two documents: the
**supplier's quote** — what the goods cost — and the **company's own quote** to the customer —
what they will be sold for. The margin on the deal is the difference, and it is sitting in those
two PDFs.

In practice nobody worked it out at deal time. The CRM's margin field was pre-filled with a
default percentage, and the real number surfaced weeks later at invoicing. Every pipeline report,
every forecast and every commission projection in between was built on a guess that nobody had
reason to trust.

What the business wanted was simple to state:

- the actual cost of sale and actual margin, computed from the documents, at the moment the quotes
  are attached;
- a visible audit trail — which document each figure came from, and how the calculation was
  reached;
- an explicit signal when the documents cannot support a reliable answer, rather than a confident
  wrong number;
- no new data entry for the sales team.

The complications are the interesting part. The two documents rarely line up one-to-one. A
supplier *framework* quote may cover far more capacity than the deal actually sells, so comparing
document totals produces a wildly wrong margin — the correct comparison scales by the quantity
actually being sold. Documents carry more than one grand total, and the right one is not always
the largest. Company names appear on both documents, so the buy side and the sell side must be
told apart by role rather than by name. And a sister company in another country makes an
intercompany purchase look like a third-party one.

## 02 · Why the obvious approach fails

The instinct is direct: decide which twenty values you want, create twenty AI fields, point every
one of them at the record's documents, write a prompt for each, and press *Update Fields*. It
fails in four distinct ways, and only the first is obvious.

### It is twenty document reads, not one

Reading a PDF is the expensive operation — measured at roughly **23 seconds** for a two-document
set, against about **11 seconds** for a text-only read. Twenty fields each opening the same
documents multiplies the slowest part of the job by twenty, for information that was already
extracted nineteen times.

### The numbers disagree with each other

Worse than slow. Each field independently re-derives the underlying figures, and on a non-trivial
document set they do not all reach the same answer. You end up with a cost of sale from one field
and a margin from another that are not consistent with each other, and no way to tell which is
wrong.

### There is no guaranteed order

Fields fired from the *Update Fields* button run in no defined sequence. Any field meant to build
on another's output is a race, and it will appear to work on the first test and fail
intermittently forever after.

### A field cannot see what a sibling just wrote

> **Platform behaviour.** An automation process reads a **record snapshot taken when it starts**.
> An AI field cannot see what an earlier AI field wrote inside the *same* process — it silently
> reads the previous value. Across processes it works correctly.
>
> This is the single biggest architectural constraint in the whole design, and it fails silently:
> the value it reads is a real value, just the old one.

## 03 · Data model — recognise once, read many

One field does the reading. Everything else reads that field's output.

- **A worksheet field** — long text — has the documents enabled as a data source. Its prompt tells
  it to return a single JSON object and nothing else. This is the only field that ever opens a
  PDF.
- **Reader fields** — one per value you want in a real typed field — have documents *disabled*.
  Each reads the worksheet and extracts one path from it.
- **Formula fields** handle arithmetic between recognised values. They are always live and cannot
  drift out of step, which a third AI field doing sums would.

> **The layering rule.** A field either reads the *documents* or reads a *worksheet field* — never
> both. A reader left with document access enabled will re-derive values from the source and
> contradict the worksheet it was meant to parse. Turn documents off on every reader explicitly.

### Why JSON rather than labelled lines

There is no JSON field type, but a long-text field whose prompt says *"return only this JSON
object, no prose before or after"* reliably emits valid JSON. It nests, it carries arrays, and a
reader can be pointed at a path such as `totals.cost_of_sale` rather than at a line prefix. Flat
`KEY: value` output also works and is fine where already proven, but JSON is the better default
for new work.

### Dependency layers

Each field belongs to a layer determined by what it needs, not by preference. In the reference
build:

| Layer | Fields | Reads |
|---|---|---|
| L0 | Document classification | the PDFs |
| L1 | Document-set status | L0 |
| L2 | 10 identification readers + the calculation + an independent cross-check | L0 / the documents |
| L3 | The descriptive worksheet | L2 |
| L4 | 3 money readers + 4 descriptive readers | L2, L3 |

The status field at L1 is deliberately alone in its own layer. It is the gate that decides whether
the document set is usable at all — and keeping it separate is what stops twelve AI calls being
spent on a set that was never going to produce an answer.

## 04 · Field-level configuration

### What an AI field can and cannot write

The server enumerates its own supported types:

| Supported | Not supported |
|---|---|
| `currency`, `email`, `float`, `input` (single-line text), `integer`, `phone`, `text_area`, `url` | `date`, `dropdown`, `checkbox`, `lookup` |

Two consequences worth designing around from the start. **A recognised date must be captured as
text** — there is no way to have an AI field populate a real date field. And **an enum-style value
must be a text field** whose prompt constrains the output to a fixed code list, with reporting
grouping on the resulting string rather than on dropdown options.

### Writing the JSON schema into a prompt

> **The prompt editor parses prompts as HTML and silently eats anything in angle brackets.**
> Express the schema with literal `""`, `0`, `[]` and `null` — never with `<placeholders>`.
>
> In the reference build a single UI edit destroyed **23 of 26** placeholders in a prompt's output
> contract, leaving bare labels and mangled fragments. Every prose rule survived, so the damage
> was invisible without a diff. Keep prompts in version control outside the platform and diff
> after any edit made through the UI.

### Formula fields for the arithmetic

- **Simple formulas support no function calls at all** — arithmetic over field references and
  literals, nothing more. Function names are rejected at parse time.
- **Advanced formulas do support functions, but the field must be created as a calculated-formula
  field.** Retrofitting an advanced formula onto an existing numeric field is accepted, publishes,
  reads back intact — and never computes, because the field keeps its stored-column nature. Create
  it; do not convert it.
- **Never divide by a recognised value.** An empty numeric field reads as zero with no guard, so a
  percentage formula throws a divide-by-zero on every record that has not been processed yet.

### Form membership is functional

> **An AI field can only read a field that is on the entity's edit form.** A field that exists, is
> published, has interface preview enabled and reads perfectly over the API is *invisible* to an
> AI reader if it is not placed on the form. The reader writes nothing at all — not even the
> fallback value its own prompt specifies.
>
> The evidence: two worksheet fields that were on the form had all fifteen of their readers
> working; a newly created worksheet that was not on the form had all three of its readers write
> zero on every run. Field-by-field configuration diffs showed the working and failing readers to
> be **identical in every attribute**. The entire fix was adding the field to the form.
>
> **The same applies to calculated fields**, which do not compute at all off-form. And a newly
> added formula field stays empty on every pre-existing record until that record is written again
> — update one field to its own value to force recalculation.

## 05 · Automation & logic

The reference build runs twelve processes. That is not over-engineering; it is the direct
consequence of the snapshot constraint in §2.

### One AI node per dependency layer

Node completion is the only signal that says "this layer is finished". Splitting a layer across
two chained nodes produces two independent completion signals, and whichever finishes first
triggers downstream too early. So every field in a layer goes into a single node — in the
reference build one node carries twelve fields and another carries seven. This is a correctness
rule, not tidiness.

### Chaining on completion, not on child nodes

> **Since AI fields became asynchronous, the AI action node itself carries a trigger-process
> property that fires on completion of the write.** That is now the only safe way to reach
> anything that reads what the node wrote.
>
> An ordinary *child* node no longer waits for the AI. A child trigger fires while the fields are
> still being written, and the downstream process reads the previous value — silently. The run log
> makes the distinction visible: the node is first reported as *scheduled*, then as successful
> with a field count, and only then does the trigger line appear.

### A process is a single linear path

Conditions may branch, but once you are inside an action chain you cannot narrow it again — and an
action node may have exactly one child, so there is no fan-out from an action either.

> **The trap:** a condition's second branch is stored, validated, reads back byte-identical and
> reports healthy — and is *never executed*. Not skipped: never evaluated, with no line in the run
> log at all. Anything conditional past the first action has to become another process reached by
> a trigger node.

Where one completion must reach several downstream processes, it triggers a **router** — a process
whose only nodes are a chain of trigger nodes. Because each sub-process gates itself and a false
gate stops that process, chaining self-gating processes in series works, and the order is
meaningful when a later one reads what an earlier one wrote.

One more structural rule: **the first node of any process must be a filter node.** An action at
the root stores successfully, reports healthy, and renders an empty canvas with no error.

### Prompt techniques that measurably mattered

- **Check the arithmetic in your worked examples.** A margin percentage was wrong for weeks
  because the example inside the prompt stated a subtly incorrect result. The model was faithfully
  copying a bad example, not rounding carelessly.
- **A self-check must force an independently derived number.** "Multiply the percentage back and
  compare" worked. "Sum the list and compare to the total" was satisfied by writing the target
  twice, and concealed two real errors.
- **Replace judgement with mechanical triggers.** A confidence rating described in prose was
  awarded inconsistently; deriving it from an explicit table over the flag list made it reliable.
- **Embed the observed failure as a counter-example.** Every fix that stuck quotes the actual
  wrong output it was written to prevent.
- **Make the model print its reasoning where you need to audit it.** An intermediate breakdown
  block turned a number that varied irreproducibly between runs into one that could be diagnosed
  within a single run.
- **Tolerances on ill-conditioned checks must be proportional.** A fixed absolute tolerance on a
  check that multiplies the difference of two nearly-equal percentages produced false disagreement
  verdicts on provably exact deals.

## 06 · Limits & trade-offs

### The failure modes all report success

This is the theme. Almost every way this design goes wrong looks healthy from the outside.

| Symptom | Actual cause | How to tell |
|---|---|---|
| Node logs success, *updated 0 fields* | AI credit exhaustion | Bites at the **tail** of a chain, because earlier nodes spent the last credits. Reads exactly like "the last step is broken". |
| Node logs success, *updated 0 fields* | A field the prompt names is not on the form | Other AI nodes in the same run wrote successfully. |
| Reader returns a plausible but stale value | Same-process snapshot, or a child node that did not wait for an async write | The value is a real previous value, not an error. |
| Branch never runs, no error | A condition's second branch — stored, validated, never evaluated | No evaluation line for it in the run log at all. |
| Formula field permanently empty | Not on the form, or an advanced formula retrofitted rather than created | Configuration diff against a working formula field shows them identical. |
| Field reported missing right after a run | Read taken before the last write settled | The same read moments later returns the value. |

### The blocker that shaped the whole project

> **AI Smart Fields originally ran synchronously and held the database while they ran.** A full
> recognition pass locked the space for two to five minutes. On a shared space with a dozen users
> that is not deployable, and the build was held back from production for exactly that reason — it
> was correct and unusable at the same time.
>
> This was **resolved by the asynchronous release**, and the chain was reworked onto completion
> triggers the day it shipped. Worth recording rather than quietly deleting: it is why the
> architecture is shaped the way it is, and re-running both test cases after the rework produced
> figures identical to the synchronous originals — which is how you know plumbing changed and
> nothing else did.

### Other constraints hit in this build

- **Batch operations cap at 100 records.**
- **Advanced formulas cannot be validated through the API** — invented function names are accepted
  without complaint, and calculated values are not returned by a normal record read. Trust only
  the formula editor in the UI.
- **Execution is admin-gated.** A personal access token can configure everything and run nothing;
  manual process execution requires a real user.
- **Space-scoped processes are read-only over the API.** Updates are refused with a permissions
  error, so process work needs a window where they are personal-scoped.
- **Renaming a field does not rename it on the form** — the form stores its own copy of each
  label.
- **Document file-type, size and volume limits are not published.** Verify empirically for your
  own document set.
- **Asynchronous is marginally slower end to end** — around 3½ minutes against 2–3 synchronous for
  the same chain, because each handoff waits for a completion event. It no longer holds the space,
  which was the entire point.

### The cost/detail trade-off

Of the 22 AI fields in the reference build, only six are load-bearing — the status, two money
totals, the margin percentage, the confidence rating and the flag list. The other 16 are
descriptive readers that populate the record for humans. Trimming them roughly halves the run
time, at the cost of the recognised detail on the record. Worth knowing before assuming the whole
chain is necessary.

## 07 · Verification

> **The run log is the only honest account of what executed.** Configuration read-backs, success
> statuses and health indicators all lie in the specific ways catalogued in §6. Read the process
> activity log after every run.

What was actually checked:

- **Field count per node, every run.** A node that should write twelve fields must log twelve.
  This is the check that matters most, because a field that silently fails to write keeps its
  *previous, correct* value — so a run against an uncleared record can look perfect while being
  stale.
- **Clear and re-run from empty.** Every AI field set to null before a full pass, so no value can
  be inherited from an earlier run.
- **Two contrasting document sets end to end** — a straightforward one-to-one pair and a framework
  pair where the supplier quote covers far more than the deal sells. The second is the one that
  proves the design, because comparing document totals gives a badly wrong answer there.
- **Every reader compared against its own source worksheet**, not just checked for plausibility.
- **An independent cross-check field** that reads the documents directly and derives the same
  figure by a different route, with the delta surfaced on the record. Two independently derived
  numbers agreeing is evidence; one number looking sensible is not.
- **Trigger ordering confirmed from the log** — that the router fired its targets in the order a
  downstream gate depends on.
- **Re-run after the async rework and compared to the synchronous fixtures.** Identical figures
  proved the plumbing changed and the logic did not.

**What would signal a regression:** a node logging fewer fields than its layer contains; a reader
returning a value that contradicts the worksheet it reads; the cross-check delta widening;
recognition producing confident output on a document set that should have been rejected as
unusable.

---

Published by Coevera. Abstracted to the reusable pattern — no customer names, no client
data, no personal data.
