heso
Learn more

Receipts

A signed record of what your agent did.

Every action heso handles becomes a receipt: a signed record of what the action was and what heso decided about it. You do not build one by hand. When an action is allowed, the SDK mints the receipt for you and returns it from the same call that ran the action.

What you get back

On an allowed action, the gate hands back an outcome with the signed receipt on it. Read outcome.allowed to confirm it ran, take the receipt off outcome.receipt, and keep it as your proof.

import heso

config = heso.init()  # reads heso.toml + your operator key

outcome = heso.process(
    heso.Action(
        verb=heso.Verb.PAYMENT,
        tool_name="stripe.charge",
        workflow=config.workflow,
        account=config.account,
        fields={"method": "POST", "path": "/v1/charges", "body": {"amount": 5000}},
    )
)

if outcome.allowed:
    # the action ran, and this receipt is your signed proof it did
    receipt = outcome.receipt
    print(outcome.action_hash, receipt["content"]["trust_level"])  # "...", "L0"

Why it is signed

Signed means the receipt cannot be changed later without that being obvious. The SDK signs it with your key the moment the action is allowed, so any edit afterward breaks the signature and the receipt no longer checks out. That is the whole point: a receipt is something you can hand to an auditor, a customer, or a court and have them confirm it for themselves.

Your team holds the signing key, not heso

The receipt is signed with your operator key, which lives on your side. So a valid receipt is proof that your side stands behind the action. heso stores the record in its cloud, but heso cannot mint a receipt in your name, and cannot quietly rewrite one.

What is in a receipt, and what is not

A receipt names what the action was: the tool it called, the kind of action, who it ran for, and what your policy decided. It does not carry your raw sensitive inputs. Anything you redact is stripped before the receipt is signed, so the signed record never contains the plaintext. See Receipt anatomy for the full list of fields.

Redaction happens before signing

When a policy rule redacts a field, the receipt carries a marker that a field was redacted, not the value. So you can prove an action happened without putting the secret in the record.

The trust level on a receipt

Every receipt carries a trust level that says who stood behind it. Read it from receipt.content.trust_level. There are only two.

L0
Signed by your operator key alone. The default for an allowed action.
L1
Signed by your operator key plus a human approver. Minted when a held action is cleared.

An ordinary allowed action is L0: signed by your operator key alone. When a held action is cleared by a person, the receipt picks up the approver as a second signer and becomes L1. There is no L2 or L3.

Checking one

Because a receipt is signed, anyone can re-check it later without calling heso. They need only the receipt and the signer key. The check passes with a single accept word, Valid; anything else is a reject that names why. Verify a receipt walks through it. The only crypto detail you need: change one byte of the receipt and the check stops returning Valid.

Next

Receipt anatomy

Every field a receipt carries, and what each one means.

Verify a receipt

Re-check a receipt offline and read the verdict.

Approvals

How a held action picks up a second signer and becomes L1.

Limits

A receipt proves your side signed this exact action under your policy. It does not prove the action was a good idea, and it does not prove the outside system actually carried it out. A receipt is the signed claim, not the outcome. For exactly what a receipt does and does not prove, see What a receipt proves.