Skip to content
Talk to our solutions team

`out`, writing results

out is the result. What it holds when the execution finishes is the answer: there is no separate return step, and no rule needs to assemble a final envelope.

It is also readable, and that makes out the way rules communicate. A later rule’s when that reads out sees what an earlier rule wrote, with nothing else to call:

rule Classify "a statement names its period" salience 20 {
when
in.Has("statement.period")
then
out.Set("type", "statement");
}
rule ExtractPeriod "statements carry their period through" salience 10 {
when
out.Str("type", "") == "statement"
then
out.Set("period", in.Str("statement.period", ""));
}
facts
{ "statement": { "period": "2026-09" } }
out
{ "type": "statement", "period": "2026-09" }

Writes a value at path.

ParameterTypeMeaning
pathstringWhere to write. A dotted path nests: "decision.status" writes status inside decision
vanyThe value: text, a number, a boolean, a date, a map, rows (a list of maps), or a document read

How each kind of v lands:

vWritten as
Text, number, booleanItself
A date (in.Date, to.Date)YYYY-MM-DD text
A map (map.Of, in.Map)A JSON object
Rows (in.Rows, Records())A JSON array of objects
A document read that missed, or a nullNothing. The key is not written
Any other list, such as a list of stringsIts text form, "[a b c]". Put such a list inside a map.Of to keep it a list

A document read that carries metadata, a confidence, the text that actually matched, keeps those attributes, so you do not have to unpack the value to keep its provenance.

rule Decide "nested output from dotted paths" {
when
in.Double("application.score", 0.0) >= 700.0
then
out.Set("decision.status", "approved");
out.Set("decision.tier", "prime");
}
facts
{ "application": { "score": 742.0 } }
out
{ "decision": { "status": "approved", "tier": "prime" } }

A write under a key that already holds a plain value (out.Set("decision", "x") then out.Set("decision.status", …)) is refused and nothing changes.

rule CaptureLoanAmount "read the loan amount and keep its confidence" {
when
bbox.Doc().Right("Loan Amount").Found()
then
out.Set("loanAmount", bbox.Doc().Right("Loan Amount").Currency());
}

out.Set for a value that is already typed, such as one from in.Value.

ParameterTypeMeaning
pathstringWhere to write
vValueAn already-typed value

Removes the value at path.

ParameterTypeMeaning
pathstringWhat to remove

Useful where a later rule supersedes an earlier classification and the earlier key would otherwise remain alongside the correction.

rule ProvisionalType "assume an invoice until proven otherwise" salience 20 {
when
in.Has("document")
then
out.Set("type", "invoice");
out.Set("provisional", true);
}
rule ConfirmedCreditNote "a credit note replaces the provisional type" salience 10 {
when
in.Eq("document.kind", "credit")
then
out.Set("type", "credit_note");
out.Delete("provisional");
}
facts
{ "document": { "kind": "credit" } }
out
{ "type": "credit_note" }

Attaches one attribute to an already-written path. Nothing happens when path holds nothing.

ParameterTypeMeaning
pathstringThe path that already holds a value
namestringThe attribute name
vanyThe attribute value

This is the escape hatch for metadata the producing value could not supply. It should stay rare, because a value that carries its own provenance is better than one annotated afterwards by a rule that happened to know something. Attributes travel beside the value: they do not appear in the output map.

rule ReadAmount "the amount and the confidence the extractor reported" salience 20 {
when
in.Has("extraction.amount")
then
out.Set("loanAmount", in.Double("extraction.amount", 0.0));
out.Attr("loanAmount", "confidence", in.Double("extraction.confidence", 1.0));
}
rule MarkManualReview "this amount came from a low-confidence read" salience 10 {
when
out.Has("loanAmount") && out.AttrNum("loanAmount", "confidence", 1.0) < 0.7
then
out.Attr("loanAmount", "needsReview", true);
out.Set("queue", "manual-review");
}
facts
{ "extraction": { "amount": 412000.0, "confidence": 0.55 } }
out
{ "loanAmount": 412000.0, "queue": "manual-review" }

Every read takes the same shape as the in equivalents, takes a dotted path, and returns a default rather than raising.

Reports whether anything is present at path.

ParameterTypeMeaning
pathstringThe path to test
rule ClassifyDisclosure "the form code names a closing disclosure" salience 20 {
when
in.Eq("form.code", "CD")
then
out.Set("docType", "closing_disclosure");
}
rule ClassifyFallback "only classify if nothing else did" salience 1 {
when
!out.Has("docType")
then
out.Set("docType", "unknown");
}
facts
{ "form": { "code": "CD" } }
out
{ "docType": "closing_disclosure" }

The low salience plus the Has guard is the standard “nobody claimed this” idiom: it runs last, and only when it is still needed.

Reports whether path is absent or holds an empty value: an empty string, an empty map or no rows.

ParameterTypeMeaning
pathstringThe path to test

Prefer it to Has when a blank counts as unclassified: Has is true for a key written as an empty string, and usually that is not what the rule meant.

Returns the raw value at path, or nil.

ParameterTypeMeaning
pathstringThe path to read

Returns the typed value at path, or Null, with its attributes. The methods are listed under in.Value.

ParameterTypeMeaning
pathstringThe path to read
ParameterTypeMeaning
pathstringThe path to read
defstringReturned when absent
ParameterTypeMeaning
pathstringThe path to read
defint64Returned when absent
ParameterTypeMeaning
pathstringThe path to read
deffloat64Returned when absent

Write the default with a decimal point, 0.0, not 0, for the same type-dispatch reason as in.EqNum. out.Double("k", 0) loads and then aborts the execution when it runs.

ParameterTypeMeaning
pathstringThe path to read
defboolReturned when absent

Returns the date at path, or the zero date. The Date methods are listed under in.Date.

ParameterTypeMeaning
pathstringThe path to read

Returns the map at path, or nil.

ParameterTypeMeaning
pathstringThe path to read

Attributes are opaque producer metadata recorded alongside a value: a confidence score, the text that matched, the page it came from. They ride along with out.Set and are readable afterwards, but they are not part of the output map.

Returns everything recorded for path, or nil.

ParameterTypeMeaning
pathstringThe path to inspect

Reports whether path carries the named attribute.

ParameterTypeMeaning
pathstringThe path to inspect
namestringThe attribute name

Returns the named numeric attribute, or def.

ParameterTypeMeaning
pathstringThe path to inspect
namestringThe attribute name
deffloat64Returned when the attribute is absent

The confidence gate is the usual use, and the default is load-bearing: pass 1.0 so a value carrying no confidence is treated as certain rather than as zero-confidence.

rule LowConfidenceExtraction "flag anything read below the threshold" {
when
out.Has("borrowerName") && out.AttrNum("borrowerName", "confidence", 1.0) < 0.85
then
findings.Warn("LowConfidenceExtraction", "extraction",
strings.Sprintf("Borrower name read at %s confidence",
to.Fixed(out.AttrNum("borrowerName", "confidence", 1.0), 2)));
}

Returns the named string attribute, or def.

ParameterTypeMeaning
pathstringThe path to inspect
namestringThe attribute name
defstringReturned when the attribute is absent

Returns everything collected so far.

Returns every path’s attributes, keyed by path.

Returns every write in order, with the rule that made it.

Returns the writes to one path.

Both return a list a rule can only count, with .Len(). Do not count them with array.Len(to.List(...)): a write history is not a list to.List recognises, so it comes back as a one-element list and the count is always 1.

History is the debugging tool for the question that otherwise takes a bisect: which rule set this, and did anything overwrite it? On a ruleset where several rules can claim the same key, that is the first thing to look at. rules.svc returns the history when the request asks for it (options.history), and kis rules --history prints it.

rule DraftType "a first guess" salience 30 {
when
in.Has("document")
then
out.Set("docType", "letter");
}
rule FinalType "a notice overrides the guess" salience 20 {
when
in.Eq("document.heading", "NOTICE")
then
out.Set("docType", "notice");
}
rule TraceDocType "record how many times the classification changed" salience 1 {
when
out.Has("docType")
then
out.Set("docTypeWrites", out.HistoryFor("docType").Len());
}
facts
{ "document": { "heading": "NOTICE" } }
out
{ "docType": "notice", "docTypeWrites": 2 }

It returns a value and an error, and a rule cannot take two return values: the call loads, then aborts the execution with calling function JSON which returns multiple values. The caller already receives the output as JSON.

These still load and run, with a warning from the load-time check naming the replacement.

DeprecatedWrite insteadNotes
out.GetStr(key)out.Str(path, def)Takes a default
out.GetInt(key)out.Int(path, def)Takes a default
out.GetFloat(key)out.Double(path, def)Takes a default
out.GetBool(key)out.Bool(path, def)Takes a default
out.GetMap(key)out.Map(path)Same shape, current name
out.Append(key, val)out.SetAppends text to what the key holds, always joined with ", ": 5 then Append("k", "x") is "5, x". There is no separator option. Lineage it was used for is in the write history
out.UnwrapColumn(...)delivery.ExplodeColumn(...)Moved to the delivery plugin
out.UnwrapColumnWithTemplate(...)delivery.ExplodeColumn(...)Moved to the delivery plugin

The collector also carries the methods the engine uses to run an execution: BindInvalidator, SetCurrentRule, SetCurrentCycle and EnablePathKeys. They are not part of the rule surface. The load-time check does not stop a rule calling them, and one that does changes how every later write in the execution is attributed or nested. Do not call them.