Skip to content
Talk to our solutions team

`to`, conversion

to converts between the types a rule handles. It is the namespace you reach for whenever a value came from a document rather than from a typed field, because what a document gives you is text, and what a rule wants to compare is a number, a date or a truth value.

Twenty-four functions, and three properties hold across every one of them.

Total. Nothing panics and nothing errors on any value: a conversion that cannot happen returns the zero value, or your explicit default from the *Or forms. (A default must still be the parameter’s type: to.NumOr(v, 0.0), not to.NumOr(v, 0).)

Predicated. Every conversion has an Is* twin, so a rule can ask “is this convertible?” instead of reading a silent zero as a real zero. This is the difference between a rule that says “the loan amount is 0” and one that says “the loan amount could not be read”, and on a document those are not the same finding.

Unwrapping. A value carried inside a result envelope ({"value": …}), a fact Value or a document read converts as the value it holds, so to.Num(out.Get("LoanAmount")) works without reaching through the envelope by hand. Every function takes any value, so a nil from an unwritten out.Get is simply unreadable, never an error.

rule LargeLoan "flag loans over the threshold" {
when
to.Num(in.Str("extracted.loanAmount", "")) > 500000.0
then
findings.Warn("LargeLoan", "underwriting", "Loan exceeds 500k");
out.Set("loanAmount", to.Num(in.Str("extracted.loanAmount", "")));
}
facts
{ "extracted": { "loanAmount": "$612,500.00" } }
out
{ "loanAmount": 612500.0 }

Reads a number out of document text. Currency symbols, thousands separators, percent signs and accounting parentheses are all handled: "$1,234.56" is 1234.56, "6.125%" is 6.125, and "(500.00)" is -500.

ParameterTypeMeaning
vanyThe value to read. Text, a number, or an envelope carrying either

Returns 0 when the value is not a number. That is the trap: use to.IsNum to tell a real zero from an unreadable value, or to.NumOr to supply a default.

rule InterestRateInRange "the rate must be within the permitted band" {
when
to.Num(bbox.Doc().Right("Interest Rate").Percent()) > 12.0
then
findings.Error("InterestRateInRange", "compliance", "Rate above 12%");
}

to.Num with an explicit fallback.

ParameterTypeMeaning
vanyThe value to read
deffloat64Returned when the value does not read as a number. Write it with a decimal point, 0.0: an integer literal here aborts the execution

Reach for this when the absence of a value has a defined meaning in your domain. A fee that is zero when unstated, a count that defaults to one.

rule ApplyDefaultFee "unstated origination fee is zero" {
when
in.Has("application")
then
out.Set("fee", to.NumOr(bbox.Doc().Right("Origination Fee").Currency(), 0.0));
}

Reads a whole number, truncating any fraction, "1,234.9" is 1234, not 1235.

ParameterTypeMeaning
vanyThe value to read

Truncation rather than rounding is deliberate: a term in months, a count of pages or a number of borrowers is a quantity you are extracting, not a measurement you are rounding.

rule TermTooLong "loan term over 360 months" {
when
to.Int(in.Str("extracted.term", "")) > 360
then
findings.Warn("TermTooLong", "underwriting", "Term exceeds 30 years");
out.Set("termMonths", to.Int(in.Str("extracted.term", "")));
}
facts
{ "extracted": { "term": "480.5" } }
out
{ "termMonths": 480 }

to.Int with an explicit fallback.

ParameterTypeMeaning
vanyThe value to read
defint64Returned when the value does not read as a whole number

Reports whether the value reads as a number. The guard that makes a zero trustworthy.

ParameterTypeMeaning
vanyThe value to test
rule LoanAmountUnreadable "distinguish a missing amount from a zero one" salience 20 {
when
!to.IsNum(in.Str("extracted.loanAmount", ""))
then
findings.Critical("LoanAmountUnreadable", "extraction", "Loan amount could not be read");
out.Set("status", "unreadable");
Complete();
}
rule LoanAmount "record the amount" salience 10 {
when
true
then
out.Set("loanAmount", to.Num(in.Str("extracted.loanAmount", "")));
}
facts
{ "extracted": { "loanAmount": "illegible" } }
out
{ "status": "unreadable" }

Writing this as to.Num(...) == 0.0 would fire on a genuine zero and report the wrong problem. Complete() ends the execution, so the lower-salience rule never records a zero amount.

Reports whether the value reads as a whole number. "12.5" is a number but not an integer, so this is false where to.IsNum is true.

ParameterTypeMeaning
vanyThe value to test

Use it where a fractional value is itself the error. A count, a term in whole months, a number of units.

rule UnitCountNotWhole "a fractional unit count is a data error" {
when
to.IsNum(in.Str("extracted.units", "")) && !to.IsInt(in.Str("extracted.units", ""))
then
findings.Error("UnitCountNotWhole", "extraction", "Unit count is not a whole number");
out.Set("unitsValid", false);
}
facts
{ "extracted": { "units": "2.5" } }
out
{ "unitsValid": false }

Reads a truth value. Text is trimmed and compared ignoring case: true, yes, y, 1, checked, x, selected, on and t are true; false, no, n, 0, unchecked, off, f and none are false; and anything unrecognised is false. A number is true when it is not zero.

ParameterTypeMeaning
vanyThe value to read

That last clause matters on a form: an unreadable checkbox and an unticked one both come back false. Where the difference is material, guard with to.IsBool first.

rule EscrowWaived "escrow waiver box is ticked" {
when
to.Bool(bbox.Doc().Checked("Waive Escrow"))
then
out.Set("escrowWaived", true);
audit.Log("EscrowWaived", "Borrower waived escrow");
}

to.Bool with an explicit fallback for unrecognised text.

ParameterTypeMeaning
vanyThe value to read
defboolReturned when the text is neither affirmative nor negative

Reports whether the value reads as a truth value at all.

ParameterTypeMeaning
vanyThe value to test
rule ConsentIllegible "the consent box could not be read either way" salience 15 {
when
!to.IsBool(bbox.Doc().Right("Consent"))
then
findings.Error("ConsentIllegible", "extraction", "Consent value is not readable as yes or no");
}

Reads a date, trying these layouts in order: 2006-01-02, 01/02/2006, 1/2/2006, 01-02-2006, 1-2-2006, 2006/01/02, 01/02/06, 1/2/06, 20060102, January 2, 2006, Jan 2, 2006, January 2 2006, Jan 2 2006, 2 January 2006, 2 Jan 2006, 02 Jan 2006 and RFC 3339. Slashed and dashed dates are month-first.

ParameterTypeMeaning
vanyThe value to read

An unreadable value returns the zero time. Test with to.IsDate rather than comparing against a sentinel date. The zero time is a real time, and comparisons against it succeed in ways you did not intend.

rule ClosingBeforeApplication "closing cannot precede application" {
when
to.IsDate(bbox.Doc().Right("Closing Date")) &&
to.IsDate(bbox.Doc().Right("Application Date")) &&
time.IsBefore(to.Date(bbox.Doc().Right("Closing Date")),
to.Date(bbox.Doc().Right("Application Date")))
then
findings.Critical("ClosingBeforeApplication", "consistency",
"Closing date is before the application date");
}

Both IsDate guards are load-bearing: without them, two unreadable dates are two zero times, and neither is before the other, so the rule silently passes.

Reads a date using one explicit layout, for input whose format is known and would be ambiguous under the default layouts, day-first versus month-first being the usual case.

ParameterTypeMeaning
vanyThe value to read
layoutstringThe layout to use, e.g. "02/01/2006" for day-first
rule ReadUKDate "this form is day-first, unlike the month-first default" {
when
bbox.Doc().Has("UK Mortgage Application")
then
out.Set("applicationDate",
to.DateIn(bbox.Doc().Right("Date"), "02/01/2006"));
}

Reports whether the value reads as a date.

ParameterTypeMeaning
vanyThe value to test

Reads a date from any recognised form and renders it in the given layout. One call in place of a parse-then-format pipeline, with no sentinel date to compare against on failure: an unreadable value renders as the empty string.

ParameterTypeMeaning
vanyThe value to read
layoutstringThe output layout, e.g. "2006-01-02"
rule NormaliseClosingDate "store the closing date in ISO form" {
when
to.IsDate(in.Str("extracted.closingDate", ""))
then
out.Set("closingDate", to.DateStr(in.Str("extracted.closingDate", ""), "2006-01-02"));
}
facts
{ "extracted": { "closingDate": "March 4, 2026" } }
out
{ "closingDate": "2026-03-04" }

Renders any value as text: numbers without float noise, dates in ISO form, nil as the empty string.

ParameterTypeMeaning
vanyThe value to render

“Without float noise” is the point. A float that prints as 1234.5600000000001 in most languages renders here as 1234.56.

Renders a number with a fixed number of decimals: "1234.5" with 2 becomes "1234.50".

ParameterTypeMeaning
vanyThe value to render
decimalsint64How many decimal places

Unreadable values render as the empty string, never as "0.00": a formatted zero would read as real data in the output. Halves round away from zero.

rule MonthlyPayment "the computed payment" salience 20 {
when
in.Has("loan.payment")
then
out.Set("monthlyPayment", in.Double("loan.payment", 0.0));
}
rule FormatMonthlyPayment "monthly payment for the summary, to the cent" salience 10 {
when
to.IsNum(out.Get("monthlyPayment"))
then
out.Set("monthlyPaymentDisplay", to.Fixed(out.Get("monthlyPayment"), 2));
}
facts
{ "loan": { "payment": 1842.5 } }
out
{ "monthlyPayment": 1842.5, "monthlyPaymentDisplay": "1842.50" }

Renders a number with thousands separators: "1234567.5" with 2 becomes "1,234,567.50".

ParameterTypeMeaning
vanyThe value to render
decimalsint64How many decimal places

Renders a number as a percentage string.

ParameterTypeMeaning
vanyThe value to render
decimalsint64How many decimal places

The number is not scaled. to.Num("6.125%") is 6.125 and to.Pct(6.125, 2) is "6.13%", so the pair round-trips. If your value is a fraction, 0.06125 meaning 6.125%, scale it explicitly before rendering.

rule DisplayRate "render the rate as it appeared on the note" {
when
to.IsNum(bbox.Doc().Right("Interest Rate").Percent())
then
out.Set("rateDisplay",
to.Pct(to.Num(bbox.Doc().Right("Interest Rate").Percent()), 3));
}

Converts a value to a list. A single scalar becomes a one-element list, so a rule can treat “one or many” uniformly; nil stays empty. Lists of text, numbers and rows convert element by element. Any other value, including a list of a kind it does not recognise such as a write history, becomes a one-element list holding it: count those with .Len() instead.

ParameterTypeMeaning
vanyThe value to convert

Converts every element to text.

ParameterTypeMeaning
vanyThe value to convert

Converts every element that reads as a number, skipping those that do not: so a stray header row or a blank cell does not silently become a zero in your sum.

ParameterTypeMeaning
vanyThe value to convert

That skipping behaviour is the reason to prefer it over converting element by element: a column extracted from a table usually carries at least one cell that is not a number.

Converts a value to a map, or an empty map.

ParameterTypeMeaning
vanyThe value to convert

Unlike the scalar conversions this does not unwrap envelopes, asking for the map means wanting the envelope itself, metadata included.

Converts a value to table rows, or an empty list.

ParameterTypeMeaning
vanyThe value to convert

The usual companion to a table read, where what you want to hand to out.Set is the records.

rule CaptureLineItems "store the line-item table" {
when
bbox.Doc().Table().Found()
then
out.Set("lineItems", to.Rows(bbox.Doc().Table().Records()));
}

Reports whether the value carries nothing: nil, blank text, a zero date, or an empty map or list.

ParameterTypeMeaning
vanyThe value to test

A zero number is not empty. That is the distinction this function exists to preserve, an amount of zero is a fact, and an amount that was never read is not.

rule BorrowerNameMissing "the name field is blank" salience 20 {
when
to.IsEmpty(bbox.Doc().Right("Borrower Name"))
then
findings.Critical("BorrowerNameMissing", "completeness", "Borrower name is blank");
Complete();
}

Names what a value effectively is after unwrapping, the type the conversions will see: null, bool, int, float, string, date, map, list or rows. An envelope reports the kind of the value it carries.

ParameterTypeMeaning
vanyThe value to inspect

Diagnostic. Log it when a conversion surprises you: it answers “what did the engine actually get?” without guessing.

rule DebugAmountType "why is this amount not converting" salience 100 {
when
!to.IsNum(bbox.Doc().Right("Loan Amount"))
then
log.Warn("amount did not convert",
"kind", to.Kind(bbox.Doc().Right("Loan Amount")),
"text", to.Str(bbox.Doc().Right("Loan Amount")));
}

Nearly every bug in a conversion-heavy rule is the same shape: a zero or a false or a zero time being read as data when it means “unreadable”. The namespace is built so the fix is always the same, guard with the Is* twin, or supply a default with the *Or form. If a rule compares a converted value against zero, it is usually a rule that should have asked to.IsNum first.