Skip to main content
Each recipe is a complete script in JavaScript and Python. They use fake clients and the sandbox. Run them as they are after you put your own files next to them. The JavaScript recipes share one small helper, shown first. All recipes were run against a local stand-in server that returns canned /extract answers and runs Sahl’s verification and scoring code on the bodies they send (the eID recipe polls a stand-in that answers pending, then complete). They were not run against the live API.

Helper for the JavaScript recipes

Save as common.mjs.
The helper throws an error that carries status, body and requestId (the X-Request-ID header). Quote the request id when you write to Sahl.

1. Loan file onboarding

An applicant sends an ID, a proof of address and a payslip. You want one verdict for the file and a route: continue, human review, or refuse.
Output for the fake file (no street, phone or email in values, so completeness is below 100 percent):
How it decides: a critical failure (passed: false) means refuse or fix. Flags without a critical failure mean a person reviews. Nothing at all means continue. These routes are your policy, not Sahl’s.

2. Account opening file

The client fills your application form and uploads a photo ID. You verify the whole file, fold in your own duplicate check, and keep the case_id.
Output for the fake data, which fills every required data point except sin/ssn. The engine’s list of required data points is North American and a Moroccan client has no SIN, so sin/ssn stays in missing and completeness is 96 percent. That is above the 80 percent warning line, so nothing is flagged:
Points to copy:
  • The recipe overwrites the form’s identity values with the ones read from the ID. If you would rather catch a typo in the form, leave the form’s names in values and let consistency:profile_name_id compare them with the ID (critical when they differ).
  • A failing extra_checks entry with severity critical makes passed false, so your own rule can block the file.
  • policy.overrides_refused is empty unless you sent a switch your workspace policy locks.
  • For an entity, use kind: "corporation" (or another entity kind), send legal_name, business_number, director_names and beneficial_owners, and read the constituting document with step_key=articles_of_incorporation. See the required data points.

3. Income check from a payslip

The client declares an income and sends a payslip. The API reads the payslip. It sets no income rule, and it returns a figure only when the payslip prints one. So the rules here are yours: holder, employer, date, and income if stated.
Output for the fake payslip, which does not print an annual figure:
What this recipe relies on:
  • annual_income comes back only if the payslip states it. The reader is told never to estimate.
  • The API checks the age of address documents, not payslips. The 90 days here are your rule.
  • capacity uses the declared income only. With net_liquid_assets and total_net_worth missing the score is pulled down; send them if you have them. See Capacity.

4. eID check

A Canadian client opens an account remotely. eID covers Canadian clients only in this version, so this recipe is the one that keeps country CA. You start the check, wait for the client, keep the PDF report, then verify under the same reference.
This sends a real email through the eID provider, in sandbox too. Replace the address with one you control. Your workspace needs its own eID provider account.
Output, with a client who finishes after the third poll:
Keep the PDF: the provider deletes the personal details after about seven days. See eID check.

5. Handle a field the reader did not read

The API returns no per-field confidence. A field is either in fields or it is not, and the checks say when the key fields of a document are missing. This recipe turns those signals into what to ask the client, retries only when the file was never read, and lets what the client typed override what was read.
When a CIN comes back without id_number, the output is:
Rules to keep: