Skip to content
Talk to our solutions team

Built-in functions

This is the complete call surface of a rule. Everything a when or then can invoke is either an unqualified built-in, a method on a value, or a method on one of the bindings below. Nothing else is reachable — there is no import, no user-defined function, and no way to add one from a rule file.

BindingHoldsBound
infact reader plus per-execution scratch memoryevery execution
outthe result collector returned to the callerevery execution
findingsseverity-graded validation issuesevery execution
audittimestamped decision trailevery execution
num strings time math map arrayhelper librariesevery execution
utilnamespace holder; carries no helper functionsevery execution
bboxthe analysed document and the typed-value factoriesevery execution that supplies documents
logstructured loggernever — no shipped binary binds it

bbox and log fail hard rather than degrading: naming an unbound one aborts the whole execution with got non existent key <name>. See the bbox binding and the log binding.

These four constraints decide whether a signature in the tables below is usable, and they are the reason some functions are marked not callable.

ConstraintConsequence
Integer literals are int64, float literals are float64, with no coercionA parameter typed plain int, float32 or a named integer type cannot be satisfied. math.Min(10, 20) panics; write math.Min(10.0, 20.0)
Methods returning (value, error) are rejected by the dispatcherout.JSON(), audit.JSON() and six document loaders parse but fail at evaluation
There is no array, map or struct literal in the grammarA collection argument must be the direct result of another call, never a local variable
Any evaluation error aborts the whole executionOne bad call discards every value already collected

Chain a collection straight into the call that consumes it, or stage it through in.MemSet:

// WRONG - `rows` is a local variable holding a slice
rows = bbox.ExtractColumnAlignedSection("loan", "Origination Charges", "", "At Closing");
out.Set("row", array.FindRowBy(rows, "description", "homeowner"));
// CORRECT - stage through scratch memory
in.MemSet("rows", bbox.ExtractColumnAlignedSection("loan", "Origination Charges", "", "At Closing"));
out.Set("row", array.FindRowBy(in.MemGetRows("rows"), "description", "homeowner"));

Called with no binding prefix. These come from the rule engine itself.

SignatureBehaviour
Retract(ruleName string)Marks the named rule retracted for the rest of this execution. Reset on the next execution
Complete()Hard stop. The engine exits the cycle loop immediately; lower-salience rules never run
Forget(snippet string)Clears the working-memory cache for an expression so it is re-evaluated next cycle
Changed(variableName string)Alias of Forget, kept for compatibility
Now() time.TimeCurrent time
MakeTime(year, month, day, hour, minute, second int64) time.TimeBuilds a time value in the local zone
GetTimeYear(t time.Time) intYear component. Also GetTimeMonth, GetTimeDay, GetTimeHour, GetTimeMinute, GetTimeSecond
IsTimeBefore(t, before time.Time) boolOrdering test. Also IsTimeAfter(t, after time.Time) bool
TimeFormat(t time.Time, layout string) stringFormats with a Go layout string
StringContains(str, substr string) boolSubstring test
ContainsStr(s []string, v string) boolMembership test over a []string produced by another call
IsZero(i interface{}) boolZero-value test for time, pointer, string and numeric kinds. A bool always reports false
IsNil(i interface{}) boolNil test. Panics on a non-nilable value such as a string
Log(text string)Writes to the engine’s own logger. Unrelated to the log binding
LogFormat(format string, i interface{})Formatted engine log line
Max(vals ...float64) float64Variadic maximum. Float literals only. Also Min
Round(x float64) float64Rounds to a whole number. One argument — not the two-argument math.Round

Go’s math package is also wrapped unqualified: Abs, Acos, Acosh, Asin, Asinh, Atan, Atan2, Atanh, Cbrt, Ceil, Copysign, Cos, Cosh, Dim, Erf, Erfc, Erfcinv, Erfinv, Exp, Exp2, Expm1, Float64bits, Float64frombits, Floor, Gamma, Hypot, Ilogb, IsInf, IsNaN, J0, J1, Jn, Ldexp, MathLog, Log10, Log1p, Log2, Logb, Mod, NaN, Pow, Pow10, Remainder, RoundToEven, Signbit, Sin, Sinh, Sqrt, Tan, Tanh, Trunc. They take and return float64, except that Ilogb returns int, IsInf/IsNaN/Signbit return bool, Float64bits returns uint64, and IsInf, Jn, Ldexp and Pow10 take an int64 where the Go original takes int. Float64frombits is not callable — its parameter is uint64 and a rule literal is int64. MathLog is the natural logarithm — the name Log is taken by the engine logger.

Any string-typed expression carries these directly, without a binding prefix.

ReceiverMethods
stringIn(...), Compare(s) int, Contains(s) bool, Count(s) int, HasPrefix(s) bool, HasSuffix(s) bool, Index(s) int, LastIndex(s) int, Repeat(n) string, Replace(old, new) string, Split(sep) []string, ToLower() string, ToUpper() string, Trim() string, Len() int, MatchString(pattern) bool
array or sliceLen() int, Append(v) — appends in place, returns nothing
mapLen() int
number or boolnone — calling any method on a numeric or boolean value is an error

"gold".In("gold", "platinum") is the idiomatic membership test and needs no helper.

Reads facts and holds per-execution scratch memory.

SignatureBehaviour
in.Has(key string) boolTrue when the fact exists and is non-nil. The safe guard for an optional fact
in.Get(key string) anyDoes not return the fact. Hands back the engine’s internal reflection wrapper — same cause as the broken typed accessors below
in.MemSet(key string, v any)Writes into the default scratch slot
in.MemGet(key string) anyReads from the default slot
in.MemGetStr(key string) stringReads a concrete string; "" when absent or another type
in.MemGetRows(key string) []map[string]anyReads a concrete row slice; nil when absent or another type
in.MemDelete(key string)Removes one key from the default slot
in.MemClear()Empties the default slot
in.GetMemAsMap() map[string]anyIndependent deep copy of the default slot, preserving int64 and float64 exactly. Only the default slot
in.Mem(name string) *MemoryReturns a named slot object
in.NewMapWithValues(kv ...any) map[string]anyBuilds a fresh map from alternating key/value pairs. A new map each call, so emitted values never alias
in.Transform(key string, fn string)Applies a transform operation to the string at key and writes the result back into memory under the same key

The object returned by in.Mem(name) carries Get(key) any, Set(key, v), Delete(key), Clear(), ToMap() map[string]any and GetRows(key) []map[string]any.

Named slots are private to the execution and are not copied out by in.GetMemAsMap(). A rule that stages into in.Mem("other") must copy the value into out itself.

The collector whose contents are returned to the caller. Every out.Set also appends a history entry stamped with the rule that wrote it.

SignatureBehaviour
out.Set(key string, val any)Records a value and appends a history entry
out.Append(key string, val string)Concatenates onto an existing string using the separator; sets the key when absent
out.SetSeparator(sep string)Separator used by Append. Default ", ". Read at append time
out.Has(key string) boolKey presence. The guard to use before any out.Get*
out.Get(key string) anyRaw value
out.GetStr(key string) string"" when absent or not a string
out.GetInt(key string) int640 when absent or not a number
out.GetFloat(key string) float640 when absent or not a number
out.GetBool(key string) boolfalse when absent or not a bool
out.GetMap(key string) map[string]anyNil when absent or not a map. Returns the live internal map
out.All() map[string]anyEvery collected value. Returns the live internal map
out.History() []OutputChangeEvery change, each carrying key, value, rule name and timestamp
out.HistoryFor(key string) []OutputChangeChanges for one key
out.SetCurrentRule(rule string)Overrides the rule name stamped on subsequent history entries. The engine sets this per firing
out.UnwrapColumn(input map[string]any, section, columnName, renameName string, maxN int64)Reads input[section]["rows"], pulls columnName from the first maxN rows and sets one output key per row named "<renameName> <n>", 1-based. Each value is a map of value, label, confidence
out.UnwrapColumnWithTemplate(input map[string]any, section, columnName, renameName string, maxN int64)Same, but renameName is a format template taking the 1-based index, for example "Borrower %d Name"

Unlike in, the typed out accessors read the collector’s own map and work correctly.

out.Append only concatenates when the key currently holds a string. If it holds a number, bool or map, that value is replaced by the new string rather than appended to.

Mutating the map returned by out.All() or out.GetMap() writes into the collector without recording history, so a history dump under-reports those changes.

Not callableWhy
out.JSON() ([]byte, error)Two return values; the dispatcher rejects it

The working way for a rule to record why it did something.

SignatureBehaviour
audit.Log(ruleID, message string)Appends a timestamped entry
audit.LogWithData(ruleID, message string, data map[string]any)Appends an entry with a structured payload. data must be a direct call result, typically in.NewMapWithValues(...)
audit.All() []AuditEntryEvery entry
audit.Count() intNumber of entries
audit.ForRule(ruleID string) []AuditEntryEntries for one rule ID

The engine also appends one executed entry per rule firing, so audit.Count() is never zero after a non-trivial execution.

Not callableWhy
audit.JSON() ([]byte, error)Two return values; the dispatcher rejects it
rule ApproveHighValue salience 50 {
when
order["Total"] > 10000
then
audit.Log("ApproveHighValue", "High-value order requires manager approval");
out.Set("requires_approval", true);
Retract("ApproveHighValue");
}

Severity is a plain integer: 0 INFO, 1 WARNING, 2 ERROR, 3 CRITICAL.

SignatureBehaviour
findings.Passed() boolTrue when no ERROR and no CRITICAL finding exists. INFO and WARNING never fail it
findings.Count() intNumber of findings
findings.All() []FindingThe findings slice. A rule can only take .Len() of it
Not callableWhy
findings.Add(finding Finding)Takes a struct and the grammar has no struct literal, so the argument cannot be constructed
findings.HasSeverity(s Severity) boolTakes the named type Severity; an integer literal is int64, so findings.HasSeverity(2) panics

A rule cannot log.

SignatureBehaviour
num.Between(val, min, max int64) boolInclusive range test. Integer literals only
num.BetweenF(val, min, max float64) boolInclusive range test. Float literals only — num.BetweenF(5.0, 1, 10) fails
Not callableWhy
num.ToInt64(v int) int64Plain int parameter; a rule literal is int64
num.ToFloat64(v float32) float64float32 parameter; a rule literal is float64

The I prefix always means case-insensitive.

SignatureBehaviour
strings.Sprintf(format string, args ...any) stringGo-style formatting
strings.ToUpper(s string) stringUpper case
strings.ToLower(s string) stringLower case
strings.ToTitle(s string) stringTitle case
strings.TrimSpace(s string) stringTrims leading and trailing whitespace
strings.Contains(s, substr string) boolSubstring test
strings.IContains(s, substr string) boolCase-insensitive substring test
strings.HasPrefix(s, prefix string) boolPrefix test
strings.HasSuffix(s, suffix string) boolSuffix test
strings.Replace(s, old, new string) stringReplaces every occurrence
strings.ReplaceChars(s, replacement, charset string) stringReplaces each character of s that appears in charset with replacement
strings.Split(s, sep string) []stringSplit. Index the call directly: strings.Split(s, ",")[0]
strings.Join(elems []string, sep string) stringJoin. elems must be a direct call result
strings.Len(s string) intByte length
strings.MatchRegex(pattern, s string) boolRegex test. A malformed pattern yields false, not an error
strings.FindRegex(pattern, s string) stringFirst match; "" on no match or a malformed pattern
strings.FindRegexGroups(pattern, s string) map[string]stringNamed capture groups of the first match. Unnamed groups are ignored; nil on no match. Index the call directly — this is a map[string]string, which every map helper rejects
strings.ExtractRegex(pattern, s, sep string) stringJoins every non-empty capture group across every match with sep
strings.ReplaceRegex(pattern, s, replacement string) stringReplaces all matches. Supports $1 and ${name}. Returns s unchanged on a malformed pattern
strings.CountPhrases(text string, phrases ...string) int64Total occurrences of all phrases. Also strings.ICountPhrases
strings.HasAllPhrases(text string, phrases ...string) boolTrue when every phrase occurs. Also strings.IHasAllPhrases
strings.PhraseCount(text, phrase string) int64Occurrences of one phrase. Also strings.IPhraseCount
strings.PhraseLineNum(text, phrase string) int641-based line number of the first line containing the phrase; 0 when absent. Also strings.IPhraseLineNum
strings.PhraseLineNums(text, phrase string) []int64Every matching line number. Also strings.IPhraseLineNums

Every regex helper degrades silently on a malformed pattern rather than erroring, so a broken pattern looks like a document that simply did not match.

SignatureBehaviour
time.Now() time.TimeCurrent time
time.ParseTime(layout, value string) time.TimeParses with a Go layout. A parse failure yields the zero time and no error
time.FormatTime(t time.Time, layout string) stringFormats with a Go layout
time.DaysBetween(t1, t2 time.Time) intWhole days between two times. Also time.MonthsBetween, time.YearsBetween
time.IsAfter(t1, t2 time.Time) boolOrdering test
time.IsBefore(t1, t2 time.Time) boolOrdering test
Not callableWhy
time.AddDays(t time.Time, days int) time.TimePlain int parameter
time.AddMonths(t time.Time, months int) time.TimePlain int parameter

Every parameter is float64, so every numeric argument needs a decimal point.

SignatureBehaviour
math.Abs(x float64) float64Absolute value
math.Min(a, b float64) float64Smaller of two values. Exactly two arguments
math.Max(a, b float64) float64Larger of two values
math.Floor(x float64) float64Round down
math.Ceil(x float64) float64Round up
math.Percent(part, whole float64) float64part / whole * 100; returns 0 when whole is 0
Not callableWhy
math.Round(x float64, decimals int) float64decimals is a plain int. Use the unqualified one-argument Round(x) for whole numbers

Operates on map[string]any values. The first argument must be the direct result of another call — out.GetMap(...), in.GetMemAsMap(), in.NewMapWithValues(...), map.New(), or a document extraction result.

SignatureBehaviour
map.New() map[string]anyEmpty map
map.Has(m map[string]any, key string) boolKey presence
map.Set(m map[string]any, key string, value any)Writes a key
map.Delete(m map[string]any, key string)Removes a key
map.GetStr(m map[string]any, key, defaultVal string) stringValue or default when absent or another type
map.GetInt(m map[string]any, key string, defaultVal int64) int64Accepts int, int64 and float64 values
map.GetFloat(m map[string]any, key string, defaultVal float64) float64Accepts int, int64 and float64 values
map.GetBool(m map[string]any, key string, defaultVal bool) boolValue or default
map.GetMap(m map[string]any, key string) map[string]anyNested map, or nil
map.GetList(m map[string]any, key string) []anyNested list, or nil
map.Keys(m map[string]any) []stringKeys, in unspecified order
map.Len(m map[string]any) intEntry count
map.IsEmpty(m map[string]any) boolTrue when empty
map.Transform(m map[string]any, key string, fn string)Applies a transform operation to the string at key, in place. Non-string values are left alone

map.Set panics on a nil map, and map.GetMap returns nil for a missing key, so map.Set(map.GetMap(m, "absent"), "k", "v") aborts the execution. Every other map helper, map.Transform included, is nil-safe.

SignatureBehaviour
array.New() []anyEmpty slice
array.Len(arr []any) intLength
array.IsEmpty(arr []any) boolTrue when empty
array.Contains(arr []any, value any) boolEquality membership test
array.ContainsStr(arr []string, value string) boolMembership over a []string, such as a strings.Split result
array.IndexOf(arr []any, value any) intIndex of the first equal element; -1 when absent
array.First(arr []any) anyFirst element, or nil
array.Last(arr []any) anyLast element, or nil
array.Append(arr []any, value any) []anyReturns a slice with the value appended
array.Prepend(arr []any, value any) []anyReturns a slice with the value prepended
array.Reverse(arr []any) []anyReturns a reversed copy
array.FindRowBy(rows []map[string]any, key, value string) map[string]anyFirst row whose string at key contains value, case-insensitively; nil when none match
array.FindRowByRegex(rows []map[string]any, key, pattern string) map[string]anyFirst row whose string at key matches the pattern; nil on no match or a malformed pattern
Not callableWhy
array.Get(arr []any, index int) anyPlain int index
array.GetStr(arr []any, index int, defaultVal string) stringPlain int index
array.GetInt(arr []any, index int, defaultVal int64) int64Plain int index
array.GetFloat(arr []any, index int, defaultVal float64) float64Plain int index
array.Slice(arr []any, start, end int) []anyPlain int bounds

Positional access is therefore bracket indexing on the call itself — strings.Split(s, ",")[0] — not array.Get.

Bound on every execution that supplies documents, and identical on both surfaces. From the CLI, kis rules --bbox and --with-bbox supply them, and kis ocr page and kis ocr doc always do. From rules.svc, the execute-rule request carries them alongside the facts under a bbox object keyed by document name:

{
"name": "closing-disclosure-checks",
"loan_id": "L-4471",
"bbox": {
"page": {"pages": [{"number": 1, "width": 612, "height": 792, "words": []}]}
}
}

bbox is reserved on the request body the way name is — it names the documents for the execution and does not become a fact. The inner keys are the names a rule passes to bbox.Has and bbox.Get, so a rule set written against kis ocr page --bbox-name page runs unchanged over HTTP. An execution that supplies no documents binds no bbox at all, and a rule naming it aborts with got non existent key bbox. See Rules over documents for the payload shapes and The rules service for the route.

SignatureBehaviour
bbox.Has(name string) boolTrue when a document is loaded under that name. Guard with this before Get
bbox.Get(name string) *DocumentThe analysed document, or nil for an unknown name
bbox.Names() []stringNames of the loaded documents
bbox.MustLoadFromFile(name, path string) *DocumentLoads and analyses an OCR JSON file. Panics on failure
bbox.Remove(name string)Drops one document from this execution’s set
bbox.Clear()Drops all documents from this execution’s set
bbox.Box(x0, y0, x1, y1 float64) BoundingBoxBuilds a bounding box
bbox.Pt(x, y float64) PointBuilds a point
bbox.Dir(name string) DirectionBuilds a direction from left, right, above/up, below/down
bbox.RelDir(name string) RelativeDirRelative direction, same vocabulary
bbox.GeoBox(x0, y0, x1, y1 float64) BoxGeometry-typed box
bbox.GeoDir(name string) DirectionGeometry-typed direction
bbox.ExtractColumnAlignedSection(docName, sectionStart, sectionEnd, columnHeaders string) []map[string]anyReads a column-aligned section as rows of maps; nil for an unknown document
bbox.PostprocessRows(rows []map[string]any, key string, fn string) []map[string]anyApplies a transform operation to the string at key in every row. An unknown operation returns the rows unchanged

The six factories are the whole bridge to the spatial half of the document API: a rule can only produce strings, integers, floats and booleans, so any document method taking a typed direction or box is unreachable without them. doc.FuzzyTextNear("Name", "right", 1, 2, 0.3) fails with reflect: Call using string as type Direction — pass bbox.Dir("right").

Not callableWhy
bbox.LoadFromFile(name, path string)Two return values
bbox.LoadFromReader(name string, r io.Reader)Two return values; no rule-side reader type
bbox.LoadFromBytes(name string, data []byte)Two return values; no rule-side byte-slice type
bbox.LoadFromMap(name string, data map[string]any)Two return values
bbox.GetOrLoad(name, path string)Two return values
bbox.LoadFromEnv(name, envVar string)Two return values

The object returned by bbox.Get carries the document query surface — lines, fields, checkboxes, tables, patterns and fuzzy spatial lookup. Those methods are documented under The document model.

The vocabulary shared by in.Transform, map.Transform and bbox.PostprocessRows.

OperationEffect
trimTrims leading and trailing whitespace
upperUpper case
lowerLower case
normalize_spacesCollapses every run of whitespace to a single space, then trims
trim_currencyTrims, removes every $ and ,, trims again
ExtractRegex("pattern", "defaultVal")Applies the pattern and joins its non-empty capture groups, or returns the whole match when there are no groups; returns defaultVal on no match

ExtractRegex is written as a call-shaped string argument, with the inner quotes escaped:

in.Transform("amount", "ExtractRegex(\"[0-9.]+\", \"\")");

Chain by calling Transform again — each call reads the value the previous one wrote.