The agent reported success. That is a string it wrote about itself.
Send the outcome that was asked for, the world-state before and after, and the conditions you declared. Outcome Assurance evaluates every assertion against the actual states, diffs the two worlds to find changes no postcondition accounted for, and returns one of five verdicts: including unverifiable, which is what you get when there is nothing to check.
Runs the same engine the paid endpoint uses. Nothing is stored and no key is needed.
Press “Assure this run” to call the live API.
You can see what the agent said it did. You cannot see what it did.
An agent finishes, writes {"status":"success"}, and the trace shows every tool call returning 200. None of that is evidence about the world. Whether an agent was allowed to act is a separate question, decided before the fact; this one is answered afterwards, against the state.
Self-reported success is the agent grading its own work
The model that decided to act is the same model reporting that the action worked. A tool call returning 200 says the request was accepted, not that the order is refunded, the seat is provisioned or the ticket is closed.
Nobody diffs the world
Teams check the one field the outcome was about. Nothing walks the rest of the state to ask what else moved: so an agent that hit its goal and quietly rewrote a customer tier, a permission or a balance looks exactly like an agent that did the job cleanly.
“It passed” is not a diagnosis
An eval harness returns a boolean. When it is false, someone re-runs the agent by hand to find out which condition broke, what the value actually was, and whether the check could even be evaluated against the state that was captured.
The expensive failure is the one that also succeeded.
A run that fails loudly gets fixed the same day. A run that achieves its outcome and moves something else along the way passes every check you have, and is found weeks later by a customer.
-
Side effect
The refund worked. The account was also downgraded.
The postcondition was about the order status, so that is what got checked. The tier change was in the same write, in a field nobody asserted on, and there is no record that anything unusual happened: only a support ticket, later, from someone who lost a feature they paid for.
-
Money
A balance moved by an amount nobody bounded
Reconciliation catches it at the end of the month, in aggregate, without the run that caused it. Money in a state comparison is integer minor units and a currency mismatch is decided before any amount is compared: because 1000 JPY and 1000 USD are not the same number in different clothes.
-
False green
A typo in an assertion path that passed for six months
order.1188.statusinstead oforders.1188.statusaddresses nothing. A harness that treats a missing path as “not changed” reports a pass, forever, for a check that was never run. Here that isunevaluable, and an unevaluable assertion is never a pass.
Five verdicts, because “did it work” has more than two answers.
You supply the two states and the conditions. The engine evaluates them deterministically and refuses to certify anything it could not check. No model judges the outcome; the same input always produces the same output.
| Verdict | When you get it | What to do with it |
|---|---|---|
achieved | Every postcondition held, no policy was broken, and nothing changed that was not accounted for. | Ship it. This is the only verdict where certified is true. |
achieved_with_side_effects | Every postcondition held: and the run also moved something nobody declared, or broke a policy. | Review the findings. The goal was met; the blast radius was larger than the goal. |
partially_achieved | Some postconditions held and some definitely failed. | Retry or escalate. The failing assertions name themselves. |
not_achieved | No postcondition held. | Roll back. The run did not do the thing. |
unverifiable | No after-state, no postconditions, a precondition that did not hold, or checks the state cannot support. | Fix the instrumentation, not the agent. Nothing was certified and nothing is claimed. |
Agent run Assertions State diff Side effects Policies Verdict
-
01
Eleven assertion operators
equals,not_equals,changed,unchanged,increased,decreased,within_range,matches,exists,absent,count_equals. Every result carries its own reason code plusexpectedandactual, so a failure is a diagnosis rather than a boolean. -
02
Side-effect detection over the diff
Both states are flattened to leaf paths and compared. Every difference that no postcondition, no
expectedChangesglob and no policy accounted for is reported, classified and counted. This is the part nothing else in your stack does. -
03
Severity that ranks the irreversible first
Money moved, state destroyed and destructively named fields are
critical. Identity and permission material ishigh. An overwrite ismedium, a new valuelow. The heuristics are deliberately over-inclusive: a false alarm costs oneignorePathsentry, a miss costs a silent transfer. -
04
Policies that outrank declarations
forbid_change,forbid_effectandmax_deltasay what must not happen, whatever the outcome. A path named inexpectedChangescannot buy permission for something a policy forbids: prohibitions win, always. -
05
Effects that leave no trace in the state
An email is sent once; a capture at a PSP is not undone by rewinding your database. Report what the run did outside the state and every effect the outcome never declared comes back classified: an undeclared payment is
critical, an undeclared email ishigh. -
06
Regression against a baseline run
Attach a prior run of the same outcome and get back what changed: which assertions stopped holding, which side effects are new, which policy violations appeared. Regression is decided on the worst signal, not the net: a run that fixed one thing and broke another has regressed.
Both postconditions passed. The verdict is still not achieved.
The refund agent did exactly what it was asked: the order is refunded and a refund id exists. It also downgraded the customer's tier and emailed them, neither of which anybody declared. Abridged output of POST /v1/runs for the run in the demo panel above.
{
"runId": "run_7f3a91",
"outcomeId": "refund_order_1188",
"verdict": "achieved_with_side_effects",
"verdictReason": "policy_violated",
"certified": false,
"postconditions": { "total": 2, "satisfied": 2, "failed": 0, "unevaluable": 0 },
"sideEffects": {
"evaluated": true,
"changes": 4, "accounted": 3, "ignored": 0, "unaccounted": 1,
"maxSeverity": "critical",
"findings": [
{
"path": "customers.c_44.tier",
"change": "modified",
"before": "pro", "after": "free",
"severity": "critical",
"code": "forbidden_change",
"source": "policy"
}
]
},
"effects": {
"total": 2, "accounted": 1, "unaccounted": 1, "maxSeverity": "high",
"findings": [
{ "kind": "refund", "target": "psp.stripe", "accounted": true, "code": "declared" },
{ "kind": "email", "target": "ana@acme.test", "accounted": false, "code": "undeclared_effect" }
]
},
"policy": { "evaluated": 1, "compliant": false, "violations": [ … ] },
"evaluatedAt": "2026-08-31T09:00:00.000Z"
}
Four changes, three accounted for
The order status and the refund id are covered by postconditions. The ledger movement is covered by an expectedChanges glob. The tier change is covered by nothing, so it is a finding: and because a policy names that path, it is critical and the run is compliant: false.
The email is not in the state at all
No diff can find it, so the run reports it and the API decides. A declared refund against the PSP is accounted for; an undeclared email to a customer is high: irreversible from the recipient's side, whatever your database says afterwards.
Nothing is stored
Every endpoint is stateless. You send both states with each request and get the report back. The API keeps no copy of your world, and the same input at the same evaluation instant returns the same bytes: so a stored report can be diffed.
This service is newly launched. We publish no uptime percentage, customer count, logo wall or certification claim until we have the operating history to support one. The verdict is arithmetic and comparison over the states you supply; it is only as complete as the state you capture.
Three calls to put a verdict behind every agent run
Capture both states
Snapshot the slice of the world the outcome concerns, before the agent runs and after. The
beforestate is required: without it there is no diff, and side-effect detection is the point.Declare what success means
Postconditions define the outcome. Preconditions define the world the run was valid from.
expectedChangesdeclares what else you know will move; anything left over is a finding.POST the run and branch on the verdict
One run or up to 100 per call. Route
achievedonward, holdachieved_with_side_effectsfor review, retrypartially_achieved, and treatunverifiableas a bug in your instrumentation rather than in the agent.
Fail closed, or the verdict is worthless
- An unevaluable assertion is never a pass. A missing path, a currency it cannot compare, a pattern against a number: each returns
unevaluablewith its own code, and the run is not certified. - A path absent from both states is a typo, not “unchanged”. Blessing it would let a mistyped assertion report green forever.
- Money is integer minor units, and a currency mismatch is decided before any amount comparison. A fractional minor unit in the state is refused, not rounded.
- A prohibition outranks a declaration. Naming a path in
expectedChangesorignorePathscannot license a change aforbid_changepolicy covers. - Preconditions do not account for changes. Describing the world an agent could start from grants no permission to mutate it.
- Output is deterministic and nothing is stored. Same run, same evaluation instant, same bytes: and findings are sorted worst-first, then by path.
Small surface, versioned, OpenAPI-first
Authenticate with Authorization: Bearer <key>. Every error returns a stable code and a requestId you can quote, and validation failures name the exact field in details.path.
/v1/keysPublic. Emails a one-time claim token to the address you give it. No key is returned on this call, proving the mailbox is what stops anyone minting a key on your account./v1/keys/claimPublic. Spends the emailed token and returns the key, shown once: 500 assured runs a month, no card./v1/runsAssure one run or up to 100. Billed one assured run per entry; an attached baseline is not billed separately./v1/demo/assurePublic. Real engine, capped at 20 assertions and 200 state values. No key required./v1/assertion-typesPublic. Every operator, verdict, reason code, severity and policy kind, with meanings.curl -X POST https://outcomeassurance-api.com/v1/runs \ -H "Authorization: Bearer $OUTCOME_ASSURANCE_KEY" \ -H "content-type: application/json" \ -d '{"run":{"runId":"run_7f3a91","outcome":{"id":"refund_order_1188"}, "before":{"orders":{"1188":{"status":"paid","refundId":null}}}, "after":{"orders":{"1188":{"status":"refunded","refundId":"re_9021"}}}, "postconditions":[ {"op":"equals","path":"orders.1188.status","value":"refunded"}, {"op":"exists","path":"orders.1188.refundId"}]}}'
Python and TypeScript SDKs Service health: https://outcomeassurance-api.com/health
Priced per assured run, capped so a backfill can’t surprise you
One unit is one agent run assured. A baseline attached for regression comparison is assured with the same engine and is not billed separately: charging for it would price you out of the regression testing this API exists to make routine. Every plan includes an allowance, then a per-unit overage that is hard-capped per billing period: your worst-case bill is the base price plus the cap, never more.
- 10,000 assured runs included
- $0.05 per additional assured run
- Overage capped at $500 per period
- Full API access, Python and TypeScript SDKs
- 60,000 assured runs included
- $0.03 per additional assured run
- Overage capped at $2,000 per period
- Full API access, Python and TypeScript SDKs
- 250,000 assured runs included
- $0.02 per additional assured run
- Overage capped at $5,000 per period
- Full API access, Python and TypeScript SDKs
- 800,000 assured runs included
- $0.01 per additional assured run
- Overage capped at $10,000 per period
- Full API access, Python and TypeScript SDKs
Free tier: 500 assured runs per month, no card. Prices in USD, billed monthly. Payment is processed by Square through our shared billing platform; card details never touch this site.
What counts as an assured run?
baseline attached to a run for regression comparison is evaluated by the same engine and billed nothing: the unit is the run you are certifying, and pricing the comparison separately would only teach you to skip it.Is this the same thing as deciding whether the agent was allowed to act?
Does a model judge the outcome?
What if I only have the state after the run?
before state is required, because without it nothing can be diffed and side-effect detection cannot run at all. If you omit after instead, the verdict is unverifiable: the postconditions come back with the reason they could not be evaluated, and nothing is certified.Won't the severity heuristics flag ordinary noise?
ignorePaths entry and a miss files a silent transfer as “a value was modified”. Timestamps and audit fields are one glob: **.updatedAt.Can I evaluate it before paying?
Stop taking the agent's word for it.
The free tier is 500 assured runs a month, no card. Create a key, post one run with both states, and the response tells you what the agent achieved: and what else it touched.
One email address, no password, no card. Prefer the terminal? The same key, in two calls:
curl -X POST https://outcomeassurance-api.com/v1/keys \ -H "content-type: application/json" \ -d '{"email":"you@company.com","name":"Evaluation key","source":{"source":"web","medium":"landing"}}' # 202 Accepted. A one-time token is emailed to that address — spend it, # and the response carries the key, shown once. curl -X POST https://outcomeassurance-api.com/v1/keys/claim \ -H "content-type: application/json" \ -d '{"token":"<token from the email>"}'