Post labelled examples
A labelled example is an outcome with the judgment’s defaulthorizon of 0s. Write the documents, then post their labels. Each label is joined to the evaluation of the document revision that was current at observed_at, so a label posted before that evaluation finishes still joins it.
Post them to POST /namespaces/{ns}/outcomes:
value is what the answer should have been:
- A value that does not fit the judgment’s type is
invalid_request. An unknown judgment isnot_found. - An outcome for a document that has no evaluation of the judgment is kept, but counts nowhere.
- Outcomes are append-only. Retries are safe: the same document, judgment, value and
observed_atis stored as one outcome. - Outcomes belong to a namespace. Post them on the namespace’s own path, never on a template prefix.
Upload labels as a CSV
On the dashboard, open the judgment and go to its Labels tab. Upload a CSV with the headerdocument_id,value and an optional observed_at column in RFC 3339:
observed_at uses the time of the upload.
Read the calibration report
GET /namespaces/{ns}/judgments/{name}/calibration returns the report for the active version. It is ns.judgments.calibration(name) in both SDKs, and the Calibration tab on the judgment’s page in the dashboard.
-
outcomes, the outcomes joined to this epoch’s evaluations. -
method, how the epoch is calibrated, andfitted_at, when:Calibration is refitted every night. -
rawandcalibrated, the same metrics before and after calibration:accuracy. Aboolanswer counts astruewhenpis at least 0.5. Forchoiceandscore, the most probable option or level must match the outcome exactly.expected_calibration_error, how far the stated probabilities are from how often things came true. Lower is better.log_loss. Lower is better.reliability, 10 equal-width bins such as{"lower": 0.8, "upper": 0.9, "count": 137, "mean_predicted": 0.845, "observed": 0.883}. A well calibrated judgment hasobservedclose tomean_predictedin every bin. Forchoiceandscore, each answer is binned by the probability of its most probable option or level. Empty bins havenullmeans.mean_level_distance, forscorejudgments only: how many levels the most probable level is from the observed one, on average.
Epochs
An epoch is theengine_version recorded on an evaluation. An exact engine version is one epoch. Jev current starts a new epoch each time its behaviour changes, labelled like current+2026-09-24.1; see the engine page. Calibration is fitted per judgment version and per epoch, because a different model needs a different fit.
After a drift, the new epoch starts with no outcomes. Until it has 100, answers under it use the previous epoch’s calibration and say so with "from_previous_epoch": true.
Calibrated answers
Once a judgment’s fit rests on 100 outcomes, every answer carries acalibrated object beside the raw numbers:
- It has the answer type’s own fields:
pforbool;value,distandescape_pforchoice;scoreanddistforscore. It addsmethod,outcomes(what the fit rests on) andfrom_previous_epoch. - It never replaces
p,distorscore, which stay the engine’s raw output. - It is absent below 100 outcomes, not
null. - It is computed when the answer is read, from the current fit for the answer’s version and epoch. A refit changes it without recomputing anything.
- Thresholds, filters and ranking use the raw fields.
Change a question safely with a shadow report
To change a judgment’s question, criteria, context or engine, create version n+1 by posting the definition again under the same name. It stays inactive. Then activate it withPOST /namespaces/{ns}/judgments/needs_escalation/activate:
202 with a shadow job in awaiting_confirm. The job judges a random sample of 1,000 documents under version 4, or every document if there are fewer. While it samples, report is null and progress.documents_done counts the sampled documents. When it is done, the report compares the two versions on the same documents:
currentis the active version’s answers, andcandidatethe new version’s results. Forboolandscoreeach side has ameanand a 10-binhistogramofporscore. Forchoiceeach side hasdist, the mean probability of each option, so you can read the shift option by option.threshold_flipscounts, for each named threshold, the documents that would go from false to true and from true to false. The new side uses the thresholds that will apply once the version is active.recomputeestimates backfilling every document under the new version: documents, tokens, judgment units, cost and duration.
POST /jobs/{id}/confirm(db.jobs.confirm(id)) switches to the new version. You can confirm before the report is done. The job showsrunning, thendoneabout a second later, once the switch is committed.POST /jobs/{id}/cancelleaves the active version as it is.{"version": 4, "force": true}on activate skips the report and switches at once. So doesactivate: truewhen you create a version. A composite judgment is the exception: its shadow job fits it on your labels, so it always runs.
shadow: true. They never produce answers, never count toward calibration, and are not billed.
After the switch, existing answers keep their old judgment_version until their documents change. To recompute them all, run a backfill; recompute is its estimate. Thresholds you gave with the new version replace the current ones when it becomes active.
In the dashboard, the judgment page’s Overview shows the report, with Confirm, Cancel and Force.
Pick thresholds with the recommender
The recommender finds the threshold that meets a precision or recall target on your outcomes:target is precision:<x> or recall:<x>. For a choice judgment, add the option, such as &option=fraud; the threshold is then on the raw dist[fraud], and a choice without option is refused with invalid_request. score judgments get no recommendation: the call is refused with invalid_request.
200, and status says which of three it is:
- A precision target gets the lowest threshold that meets it, which keeps the most recall.
- A recall target gets the highest threshold that meets it, which keeps the most precision.
- The curve has 101 points, thresholds 0.00 to 1.00 in steps of 0.01, each with its precision and recall. The recommended threshold is one of them. Precision is
nullwhere no answer reaches the threshold. - The outcomes are the current epoch’s. When it has fewer than 100, the previous epoch’s are used and
from_previous_epochistrue.
PATCH /namespaces/{ns}/judgments/{name}. The thresholds you send replace the whole set, so include the ones you want to keep:
choice, a threshold names its option: {"fraud": {"value": "fraud", "gte": 0.62}}. {} removes every threshold.
The change applies at the next read to every answer, including answers already computed. Nothing is recomputed, no version is created, and the change is recorded in your audit log.
In the dashboard, the judgment’s Thresholds tab applies a recommendation in one click, after you confirm. It keeps your other thresholds.
Real-world outcomes with a horizon
Labelled examples say what the answer should have been at the time. Some questions are predictions, and the outcome arrives later. For “will this customer churn within 30 days?”, give the judgment ahorizon:
observed_at minus the horizon: here, the answer as it stood on 15 September. That is the prediction the outcome measures.
horizon is part of the definition, a whole number and a unit (s, m, h or d). It defaults to 0s, and changing it creates a new version.