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. A conversion that cannot happen returns the zero value, or your explicit default from the *Or forms.

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 converts as the value it holds, so to.Num(out.Get("LoanAmount")) works without reaching through the envelope by hand.

rule LargeLoan "flag loans over the threshold" salience 10 {
when
to.Num(bbox.Doc().Right("Loan Amount").Currency()) > 500000.0
then
findings.Warn("LargeLoan", "underwriting", "Loan exceeds 500k");
Retract("LargeLoan");
}

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

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(bbox.Doc().Right("Loan Term")) > 360
then
findings.Warn("TermTooLong", "underwriting", "Term exceeds 30 years");
}

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(bbox.Doc().Right("Loan Amount").Currency())
then
findings.Critical("LoanAmountUnreadable", "extraction", "Loan amount could not be read");
Complete();
}

Writing this as to.Num(...) == 0.0 would fire on a genuine zero and report the wrong problem.

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(bbox.Doc().Right("Units")) && !to.IsInt(bbox.Doc().Right("Units"))
then
findings.Error("UnitCountNotWhole", "extraction", "Unit count is not a whole number");
}

Reads a truth value. The affirmative words — yes, y, checked, x, 1 — are true, the negatives false, and anything unrecognised is false.

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 the format profile’s layouts in order.

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 profile — 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 regardless of the default profile" {
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(bbox.Doc().Right("Closing Date"))
then
out.Set("closingDate", to.DateStr(bbox.Doc().Right("Closing Date"), "2006-01-02"));
}

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 FormatMonthlyPayment "monthly payment for the summary, to the cent" {
when
to.IsNum(out.Get("monthlyPayment"))
then
out.Set("monthlyPaymentDisplay", to.Fixed(out.Get("monthlyPayment"), 2));
}

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.

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.

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.