---
title: Verify receipts
description: Confirm a Felspar decision with one REST call before your code acts on it.
sidebar:
  order: 10
---

Verify a decision's receipt whenever your code is about to act on it: send an email, update a record, approve a request. Verification asks Felspar to replay the decision from what it recorded and confirm it matches. It calls no model.

## The call

```sh
curl -s "https://api.felspar.ai/v0/executions/$EXECUTION_OBJECT_ID/verify" \
  -H "Authorization: Bearer $FELSPAR_AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d @verify.json
```

Where `verify.json` holds the receipt exactly as the decision returned it:

```json
{
  "receipt": { "...": "result.receipt" },
  "expectedReceiptDigest": "the value of result.receipt.receiptDigest",
  "deliveredCapsule": { "...": "result.context, if your code used it" }
}
```

`EXECUTION_OBJECT_ID` is `result.executionObjectId` from the decision.

## The answer

```json
{
  "authority": "unapproved",
  "status": "reproduced",
  "mismatches": [],
  "decided": {
    "jobId": "review-nda",
    "entityId": "CTR-2026-0117",
    "releaseId": "release-...",
    "outcome": "review",
    "subject": { "type": "contract", "related": [{ "relationship": "counterparty", "subject": "company", "id": "northwind.com", "questions": ["law"] }] },
    "escalation": { "id": "legal-review", "trigger": "outcome" }
  }
}
```

Act only when **all** of these hold:

1. `status` is `reproduced`;
2. `decided.jobId` is the job you expect;
3. `decided.entityId` is the entity you're about to act on;
4. `decided.outcome` is an outcome that allows the action;
5. if you hold a release, `decided.releaseId` is it;
6. if `decided.escalation` is present, the action is the hand-off your code maps to that `id`, not the action itself;
7. the decision's evidence is still fresh (`result.explanation.evidenceFreshUntil` is in the future).

`decided.subject` and `decided.escalation` appear when the release declares a subject or escalations; Felspar re-derives both from the release and the replayed outcome. Use the values in `decided`, which come from Felspar's replay, never values the agent passed along. If `status` is `mismatch`, `mismatches` names what differs. Don't act.

## In TypeScript

The SDK's verifier does the same call and returns `decided` only when the receipt was reproduced:

```ts
import { felsparVerifier } from "@felspar/sdk/draft/action-gate";

const verify = felsparVerifier({ origin: "https://api.felspar.ai", agentKey: process.env.FELSPAR_AGENT_KEY });
const decided = await verify(decision); // null unless reproduced
```

For a whole tool-call gate, see [The pre-action gate](/guides/pre-action-gate).

## Who can verify what

- An agent key verifies receipts of decisions **that agent** made, and no others.
- A signed-in member, approver or owner can verify any decision in the workspace.

Verification isn't counted toward decision [rate limits](/reference/limits).

## When verification fails

| Response | Meaning |
| --- | --- |
| `reproduced` | Act if the checks above pass. |
| `mismatch` | The receipt doesn't match what Felspar recorded. Don't act. |
| `403 forbidden` | This caller can't verify this decision, or it no longer exists. Decisions are kept for 30 days. |
| Network error or timeout | You don't know. Don't act; retry the verification. |
