Logic & Rule Compilation
Read and write an app's versioned logic manifest, compile plain-language rule cards against bound fields, and edit apps with natural-language proposals.
An app's behavior lives in its versioned content manifest — one JSON document per version carrying the mode, the logic, the input bindings, the output contract, thresholds, triggers, fallback policy, and presentation vocabulary. GET /v1/apps/:id/logic returns both sides at once: draft (the editable next version, or null) and active (the published version runs execute, or null). PUT /v1/apps/:id/logic — and its alias PATCH /v1/apps/:id — writes the whole manifest as the draft, creating version head + 1 if no draft exists yet. Drafts change nothing live until published.
Manifest content fields
Rules apps carry their policy as cards: each card holds the author's originalPolicy sentence plus a structured interpretation the engine executes. You rarely write interpretations by hand. Save cards with a pending_compile marker (the sentence only), then call POST /v1/apps/:id/logic/compile — the compiler grounds each pending sentence in the app's bound fields and writes executable scope/failure predicates back into the draft. A sentence that cannot be grounded stays pending, with the reason in warnings and on the card itself, so an ungrounded rule can never silently decide.
/v1/apps/:id/logic/compileBody
/v1/apps/:id/logic/fieldscurl — save a draft with a pending rule, then compile it
curl -s -X PUT https://api.talonic.com/v1/apps/$APP_ID/logic \
-H "Authorization: Bearer tlnc_your_api_key" \
-H "Content-Type: application/json" \
-d @manifest.json
# → { "version": 2, "status": "draft", "content_hash": "9df5a562cff6d2df..." }
curl -s -X POST https://api.talonic.com/v1/apps/$APP_ID/logic/compile \
-H "Authorization: Bearer tlnc_your_api_key" \
-H "Content-Type: application/json" \
-d '{"guidance": "\"billed amount\" is the total_amount field on the invoices binding"}'
# → { "cards": [ ...interpretations written... ], "warnings": [] }Natural-language editing
Existing apps are edited the same way they are drafted: in language. POST /v1/apps/:id/edit-proposals takes an instruction (8–2000 chars, e.g. "Raise the quantity tolerance from 100 to 150 units") and returns typed edit ops plus a full preview of the manifest after the change, a plain-language summary, open_questions, and warnings. Nothing persists at this step. The person reviews the ops and applies them explicitly with POST /v1/apps/:id/edit-proposals/apply — a deterministic step that re-validates the ops against the live draft, saves it, and compiles the touched cards.
Edit proposal — request and response excerpt
POST /v1/apps/:id/edit-proposals
{ "instruction": "Raise the quantity tolerance from 100 to 150 units." }
{
"ops": [
{ "op": "edit_rule", "card_id": "quantity-tolerance",
"sentence": "Hold any invoice whose quantity exceeds 150 units." }
],
"summary": "The quantity tolerance threshold on the \"quantity-tolerance\" rule has been raised from 100 to 150 units. All other rules and settings remain unchanged.",
"open_questions": [],
"warnings": [],
"preview": { "mode": "rules", "logic": { "kind": "rules", "cards": [ ... ] }, ... }
}The op vocabulary is closed and reviewable: add_rule, edit_rule, remove_rule, pause_rule, resume_rule, set_fallback, set_adjudication, and set_mode (rules ⇄ assisted). Because apply is a separate, deterministic call taking the exact ops you accepted, an agent can propose and a human can approve — or the same caller can chain both — without an LLM ever writing directly into the draft.
Two declared options cover subjects made of several records or matched against several candidates. A card's record_fold sets how it folds the outcomes of a subject's engaged records: worst (the default: fail > indeterminate > pass > not applicable) or any_passes (pass when any engaged record passes — for existential evidence such as one valid signed receipt; otherwise indeterminate outranks fail). The party_match_any and address_match_any predicates compare one reference party name, or one street and postal code, against every configured candidate with deterministic similarity, and the verdict cites the winning candidate's locator and comparison method. Both change the rule's fingerprint, so they count as semantic edits.
Frequently asked questions
What is a "pending" card?+
Can the compiler invent a field that is not in my data?+
What is the difference between PATCH /v1/apps/:id and PUT /v1/apps/:id/logic?+
How do rules apply to only some records?+
applies_to uses { field, equals } for one exact value or { field, one_of } for an explicit value set, and each input binding's select can narrow fields and rows at assembly time. Batch apps additionally scope card matching via decision_policy.subject.doc_type_field. Applicable records fold conservatively by default; a card may set record_fold: "any_passes" when one passing record is sufficient evidence.