Outcomes
What happened after AbuseZero allowed a claim, and how that history protects every claim that follows.
An outcome records what happened after AbuseZero allowed a claim: did the user actually receive the benefit? Outcomes are the claim history that every later evaluation is checked against.
Why it matters
An allowed claim isn't always a delivered benefit. A card can fail after the trial starts, or you may refund a purchase and take the credits back. Outcomes keep AbuseZero's history in step with what really happened. A user whose claim fell through can try again, and a benefit you actually delivered keeps counting.
Key capabilities
- Recorded outcomes. Every granted outcome becomes history that later evaluations check, across every identifier the claim carried.
- Two tracking models. AbuseZero can treat every allowed claim as delivered, or wait for your app to confirm delivery.
- Missing outcome behavior. When your app is expected to confirm delivery but doesn't, AbuseZero settles the outcome by the rule you chose for that benefit.
- Outcome corrections. Only valid changes are accepted, such as granted to reversed. Conflicting reports are refused with
OUTCOME_CONFLICT, and every change is recorded in your audit log. - Outcome conflicts. Under
release_eligibility, when a delivery is confirmed after its deadline and other claims have since used the same identifiers, the response lists those overlapping outcomes inconflictsso you can review them.
Tracking models
Each benefit chooses one in config.outcome_tracking.
| Model | How it works | Choose it when |
|---|---|---|
assume_granted (default) | Every allowed claim counts as delivered straight away, with the state assumed_granted. You report only corrections. | Your app grants the benefit as soon as AbuseZero allows it. |
report_outcome | An allowed claim starts as awaiting_outcome, with a due_at time returned in the evaluation. Your app reports granted or not_granted before then. | Delivery can still fail after the evaluation, for example at a payment step. |
When a report is due but doesn't arrive, the outcome becomes outcome_unknown. The benefit's config.missing_outcome_behavior then decides whether it counts:
hold_eligibility(default): the claim still counts as an earlier claim.release_eligibility: the claim doesn't count, and the user can claim again.
Outcome states
| State | Meaning | Counts as an earlier claim |
|---|---|---|
assumed_granted | Allowed under assume_granted. | Yes |
awaiting_outcome | Allowed under report_outcome, waiting for your report. | Yes |
granted | Your app confirmed the user received the benefit. | Yes |
not_granted | Your app reported that the user didn't receive it. | No |
reversed | The benefit was delivered, then taken back. | Yes |
outcome_unknown | The report window closed without a report. | Depends on missing_outcome_behavior |
Reporting an outcome
Report against the evaluation that allowed the claim, with POST /v1/evaluations/{id}/outcome:
{ "outcome": "not_granted", "reason": "Card declined at checkout", "external_outcome_id": "order_20481" }{
"object": "outcome_report",
"evaluation": "eval_9a41c7e2-5b3d-4e8f-a216-0c7d3b9e5f81",
"state": "not_granted",
"conflicts": []
}| Field | Meaning |
|---|---|
outcome | granted, not_granted or reversed. |
reason | 3 to 500 characters. Required for not_granted and reversed. |
external_outcome_id | Optional. Your own ID for this outcome, unique within your workspace. |
| You report | Accepted from |
|---|---|
granted | awaiting_outcome, outcome_unknown, assumed_granted |
not_granted | awaiting_outcome, outcome_unknown, assumed_granted |
reversed | granted, assumed_granted |
Reporting the state an outcome already has is safe and changes nothing. Every report needs an Idempotency-Key, so a retry never applies twice.
Outcomes and eligibility
claim_id ties evaluations and outcomes to one claim in your system:
- Already allowed: evaluating the same claim again returns
allowwith the reasonpreviously_authorized. It doesn't count a second claim. - Reported
not_granted: the claim is evaluated afresh. - Report overdue: under
report_outcome, a claim whose report window closed without a report returnsOUTCOME_CONFLICTwhen evaluated again. Report its outcome first.
Response data
GET /v1/outcomes lists outcomes, and each one looks like this:
{
"object": "outcome",
"id": "out_6e3b9a12-4c7d-4f58-b0a9-1d2e8c5f7a36",
"evaluation": "eval_9a41c7e2-5b3d-4e8f-a216-0c7d3b9e5f81",
"state": "granted",
"source": "evaluation",
"occurred_at": "2026-10-28T16:02:11.000Z",
"due_at": "2026-10-28T16:12:11.000Z"
}source is evaluation for outcomes created by an evaluation, and import for outcomes you imported.
Programmatic access
| Interface | Use |
|---|---|
| AbuseZero REST API | POST /v1/evaluations/{id}/outcome, GET /v1/outcomes, GET /v1/outcomes/{id}, and POST /v1/imports for outcome history |
| AbuseZero Node.js SDK | outcome(), importOutcomes(), outcomes(), getOutcome() |
| AbuseZero CLI | abusezero outcomes report, list, get |
| AbuseZero MCP server | abusezero_report_outcome, abusezero_list_outcomes, abusezero_get_outcome |
| Dashboard | Evaluations, and Developers → Data import for outcome history |
Next steps
How is this guide?
Last updated on