Step 2: Set the fields
A contract covers exactly one field. Give it a name, the values it may take, and for each value the words that would actually appear in one of your records.
{
"outputField": "allergens",
"sourceColumns": ["name", "description"],
"cardinality": "many",
"policy": "ratchet",
"minConfidence": 0.7,
"allowedValues": [
{ "value": "peanut", "synonyms": ["peanut", "groundnut", "satay"] },
{ "value": "dairy", "synonyms": ["cream", "milk", "butter", "cheese"],
"definition": "Milk from any animal, and anything made from it.",
"includes": ["paneer", "whey"],
"excludes": ["cocoa butter", "coconut milk"] },
{ "value": "gluten", "synonyms": ["wheat", "flour", "pastry", "bread"] },
{ "value": "shellfish", "synonyms": ["prawn", "shrimp", "crab", "lobster"] }
]
}- 1
cardinality: one or many
A dish can contain peanut and dairy at once, so allergens is
many. A difficulty rating cannot be both beginner and advanced, so that would beone. Pick wrong and you will fight the contract forever. - 2
policy: normal or ratchet
ratchetmakes it a safety field. Later runs may add a value, but if a run would ever take one away, that goes to a person instead of happening quietly. Allergens should never silently disappear, so: ratchet. - 3
minConfidence
Below this, the model's answer is not stored; it becomes your decision. The default is 0.7. Raise it and you review more and serve less. Lower it and you serve more and trust the model further.
- 4
whenUnsure: review or flag
Only on a safety field that takes several values.
review, the default, stores nothing below minConfidence.flagstores the values the model leaned towards, markedflaggedon the receipt, and still asks you. It can only add, and until you decide the field counts as unknown, so the record stays out of any exclusion on it: an agent sees "may contain tree nuts, unconfirmed" instead of nothing. - 5
sourceColumns
Which of your columns the rules and the model are allowed to read. Up to ten. Keep it tight: pointing a contract at a column full of marketing adjectives is how you get confident nonsense.
One more rule with teeth. No word may be a synonym of two values of the same field. If “cream” meant both dairy and a sauce style, it settles nothing and quietly mis-tags everything. The editor will not let you save it.
Say what each value means. A value can carry a definition, examples that count as it (includes) and phrases that look like it but don't (excludes). The model reads all three, so it follows your boundary rather than its own idea of the word. Excludes also bind the rules: with cocoa butter excluded from dairy, a record saying cocoa butter no longer matches butter, while one that says butter on its own still does. The receipt records any word an exclude held back.
Let the model draft it, then decide. *Draft from my data* reads 50 of your records and writes a definition and examples for each value, filling only what you have left empty, and lists the cases you need to decide (is ghee dairy?). Anything still exactly as drafted is marked until you change it. On a safety field nothing is drafted as not counting: those come back as decisions for you. Where the definitions come from is a field only you fill in.
Your examples are tests. *Check the examples* runs each one through the contract: what you say counts must come out as that value, and what you say doesn't must not. Rules first; the model at most 20 times.
Read your own PDFs as a source of truth. Upload a supplier allergen sheet or spec sheets and add them to the contract under *Sources*. One PDF naming many products is matched line by line to the record it names, by a column you choose (name or code); one PDF per product is matched by its file name. *Preview the match* shows how many records it covers before anything runs. A line naming two products goes to neither, because evidence about the wrong product is worse than none.
What a source of truth may do. It can add a safety value on its own. Where a record's own words give a safety value the source of truth doesn't, the value is kept and the record goes to review, because only a person can say which is wrong. Every value it touched says so on its receipt: the file, the page and the line. Text PDFs only for now; a scanned PDF is kept but marked, not read.
A contract is yours to keep. Through the API it exports as a JSON file (GET /contracts/:id/export) to keep in your own repository or review like code, and imports into another dataset (POST /datasets/:id/contracts/import), checked like any new contract. Its PDF sources stay behind: they are files in this dataset.
Every version is kept. Editing a contract bumps its version, every receipt records the version it was decided under, and each version is stored exactly as it stood, definitions included. A value tagged under version 3 still points at what version 3 said after you edit to version 4. A change to the values, their meaning or the instructions re-checks every record on the next run; a change to the name, the description or the source doesn't.