List Data Policy Rules
List the transformation rules of a data policy: lookups, Lua scripts, computed and conditional fields, all executed in dependency order during resolution.
List the transformation rules of a data policy. Rules are the executable logic of resolution: reference-table lookups (exact and fuzzy), sandboxed Lua scripts, deterministic computations, conditionals, coalescing, and validation checks. The policy compiler orders rules topologically by their field dependencies, so upstream fields always resolve before the rules that read them.
Each rule declares a rule_type, its configuration, and its position (ordinal) in the policy. The compiler validates every rule at compile time: unknown rule types and circular field dependencies are rejected before anything executes against your data.
Rule configuration is where most integration effort goes. A lookup_exact config names the reference table and its search and output columns; lookup_fuzzy adds match tiers and a confidence threshold; a script config carries the Lua source. Reference tables are first-class objects — manage their rows via the reference data endpoints, and a lookup reads the table's current rows at execution time, so updating reference data needs no policy edit.
As with fields, GET /v1/data-policies/{id} is the version-resolved view: it returns the current version's rules with input_field_keys, output_field_key, execution_order, and error_behavior — the compiled ordering a run actually follows — alongside the fields they populate. Use it when you need to reason about what the next resolution will execute rather than browse the rule inventory.
/v1/data-policies/{id}/rulesPath parameters
Response
Response fields
curl
curl -s https://api.talonic.com/v1/data-policies/p1a2b3c4-e5f6-7890-abcd-ef1234567890/rules \
-H "Authorization: Bearer tlnc_your_api_key"Response
{
"data": [
{
"id": "r1a2b3c4-0001",
"version_id": "v3a2b3c4-0003",
"field_key": "country_code",
"input_field_keys": ["country"],
"rule_type": "lookup_exact",
"config": {
"reference_table_id": "country_codes",
"search_column": "name",
"output_column": "iso2"
},
"ordinal": 1,
"error_behavior": "flag",
"provenance_label": null
},
{
"id": "r1a2b3c4-0002",
"version_id": "v3a2b3c4-0003",
"field_key": "currency",
"input_field_keys": ["country_code"],
"rule_type": "script",
"config": {
"script": "if record:get('country_code') == 'US' then return 'USD' end"
},
"ordinal": 2,
"error_behavior": "flag",
"provenance_label": "Derived currency"
}
],
"links": {
"self": "/v1/data-policies/p1a2b3c4-e5f6-7890-abcd-ef1234567890/rules",
"policy": "/v1/data-policies/p1a2b3c4-e5f6-7890-abcd-ef1234567890",
"version": "/v1/data-policies/p1a2b3c4-e5f6-7890-abcd-ef1234567890/versions"
}
}Errors
Error responses
Large reference tables in scripts
A script rule normally receives each reference table it uses as a whole. Tables above 20,000 rows or 64 MiB stored never reach the script whole. They are reachable by key only: lookup[name]:exact(col, val) and lookup[name]:where(col, val) each run one tenant-scoped query against the table and return at most 100 rows per call (rows past the cap are dropped and the script log notes the truncation; it is not an error). The result behaves like any other lookup result, so :get(), :row(), :any(), :count(), and further :where clauses work as usual. Keyed matching is exact: case-sensitive and type-sensitive, with a string that parses as a number retried as a number.
Operations that need the whole table, :all(), :fuzzy(), :matches(), and :llm_match(), raise a script error on a large table: reference table '<name>' is keyed-access only (...): :fuzzy() needs the whole table; use :exact(col, val) or :where(col, val). Like any script error, it marks the rule's outputs according to its error_behavior. Rewrite the script to look up by key, or split the reference data into smaller tables if you need fuzzy matching.
Keyed lookup on a large reference table
-- Works on any size of table: one keyed query, at most 100 rows back.
local supplier = lookup["suppliers"]:exact("supplier_id", record.supplier_id)
if supplier:any() then
record:set("supplier_country", supplier:get("country"))
end
-- Raises on a table over 20,000 rows / 64 MiB:
-- lookup["suppliers"]:fuzzy(...) -> "... is keyed-access only ...":fuzzy() or :all() keeps working while its reference table is small and starts failing once the table grows past 20,000 rows or 64 MiB. Prefer :exact() / :where() in scripts that read growing master data.Frequently asked questions
What rule types are available?+
select_field, transform, auto_transform, lookup_exact, lookup_fuzzy, compute, script (Lua), coalesce, conditional, temp_var, chain, validate, highlight, and bypass. Each rule type has its own configuration schema, validated at compile time.How does the Lua scripting work?+
script rules execute a sandboxed Lua 5.4 chunk per record. The script reads and writes record values, can match against reference tables via lookup helpers such as :matches(), and can escalate to LLM-powered fuzzy matching via :llm_match(). The returned value lands on the rule's target field.What happens when a lookup fails to find a match?+
error_behavior applies: flag the value for review, set it empty, substitute a default, skip, or fail.Why does my script fail with "keyed-access only"?+
How is rule execution order decided?+
country runs after the rule that produces country. Circular dependencies are rejected at compile time, before the policy executes.