Skip to main content

Acceptance Sets

Give apps a machine-checkable definition of done: labeled expected verdicts with a tolerance, scored against any completed run as a pure ledger comparison.

An acceptance set is an app's machine-checkable definition of done: a list of labeled expectations plus a tolerance. Each case states what the app *should* decide — either per subject (subject_key + expected_outcome, optionally expected_auto) for batch apps, or per run (expected_decision) for single-decision apps — with an optional note explaining why. The check then scores a real run against the set as a pure ledger comparison: no model is involved, so the same run against the same set always scores identically.

GET /v1/apps/:id/acceptance returns the saved set or { "acceptance": null }. PUT /v1/apps/:id/acceptance replaces it wholesale — validation is total, never a partial save: a malformed case anywhere rejects the whole request with 422 naming the offending case (cases[0]: needs either subject_key + expected_outcome, or expected_decision). tolerance is the accepted mismatch share in [0,1]; a check passes when score >= 1 - tolerance, so tolerance 0 demands a perfect match and 0.1 allows one mismatch in ten evaluated cases.

GET /v1/apps/:id/acceptance/check scores the latest completed run by default, or a specific one via ?run_id=<uuid>. The response reports evaluated, matched, and skipped counts, the resulting score, the set's tolerance, the boolean pass, and a mismatches array pairing each expectation with what the ledger actually shows — the exact list a builder iterates on. Cases whose subject did not appear in the run are counted as skipped, not failed.

This is the loop a builder agent runs: draft logic, trigger a [dry run](app-runs), check acceptance, adjust, repeat — and the loop a maintainer re-runs after every edit, because the set survives version changes. Checking requires only the read tier; saving the set is operate. Checking before any set is saved answers 409 with "This app has no acceptance set yet — save one before checking."

PUT/v1/apps/:id/acceptance

Body

cases*arrayLabeled expectations. Subject case: { subject_key, expected_outcome: passed|failed|indeterminate|not_applicable, expected_auto?, note? }. Decision case: { expected_decision, note? }.
tolerancenumberAccepted mismatch share in [0,1]. The check passes when score >= 1 - tolerance. Default 0.

curl

curl -s -X PUT https://api.talonic.com/v1/apps/$APP_ID/acceptance \
  -H "Authorization: Bearer tlnc_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "tolerance": 0,
    "cases": [
      { "subject_key": "L-2026-0847", "expected_outcome": "passed", "expected_auto": true },
      { "subject_key": "L-2026-0851", "expected_outcome": "failed",
        "note": "Rate mismatch above 2% must never auto-bill." }
    ]
  }'

Response — GET /v1/apps/:id/acceptance/check

{
  "run_id": "a6d56a57-bc82-439f-a547-342a8ea81dd4",
  "evaluated": 2,
  "matched": 1,
  "skipped": 0,
  "score": 0.5,
  "tolerance": 0,
  "pass": false,
  "mismatches": [
    {
      "subject_key": "L-2026-0851",
      "expected": "failed",
      "actual": "passed",
      "note": "Rate mismatch above 2% must never auto-bill."
    }
  ],
  "warnings": []
}
Pair acceptance with the pre-publish verdict diff: acceptance tells you whether a candidate meets your labeled ground truth, while POST /v1/apps/:id/replay/verdict-diff tells you what else would change across recent history. Green acceptance plus an empty flip list is the strongest publish signal the surface offers.

Frequently asked questions

Which runs can I check against?+
Any completed run of the app: the check defaults to the latest completed run and accepts ?run_id= for a specific one (422 if the value is not a UUID). Dry runs work too — they produce a full ledger without side effects, which makes them the natural acceptance-loop vehicle.
How is the score computed?+
score = matched / evaluated over the cases the run could evaluate. Subject cases match when the subject's rolled outcome (and expected_auto, when stated) equals the expectation; decision cases match on the run's decision. Cases whose subject is absent from the run are skipped and excluded from the denominator.
Does saving a new acceptance set affect the app's behavior?+
No. The acceptance set is pure ground truth — it never feeds the decision engine, thresholds, or reviews. It only changes what /acceptance/check reports, so you can tighten or extend it at any time without a version bump.
Why did my save fail with 422?+
Validation is total: every case must be either a subject case (subject_key + expected_outcome, with expected_outcome one of passed/failed/indeterminate/not_applicable) or a decision case (expected_decision), and tolerance must sit in [0,1]. The error message names the first offending case index.