Skip to content

Create your first ActionReceipt.

Run one fixed action. Bulla writes a portable transaction file you can keep, check locally, and compare with your application’s action log.

This is a constructed local example. It records a payment request but does not contact a payment network or establish whether money moved.

Limits of this constructed example

Bulla can find actions in the supplied action log that have no matching receipt. It cannot find actions missing from that log.

Technical evidence

Coverage is computed relative to the supplied denominator; a compromised or incomplete independent log narrows what reconciliation can find. The browser recomputes digests and the set difference over unsigned fixture receipts and does not verify signer identity or the denominator's independence.

1. Run the first action

Install Bulla and run the fixed example. Bulla creates an output directory containing the receipt, the receiving system’s action log, and reports you can inspect.

$ python -m pip install "bulla==0.47.1"
$ bulla demo --out first-action

ACTION
recorded action       payments.charge
amount                USD 125.00
declared limit        USD 200.00

OMISSION CONTROL
coverage before       1/1
coverage after        1/2
unreceipted action    pay_demo_043
About the published package

The command shown here comes from the package published on PyPI, not from unreleased repository code.

Technical evidence

Commands labelled published are captured from the exact artifact accepted by PyPI; repository source is not substituted for it.

The command refuses an existing output path. Run it without --out to use a new preserved private temporary directory.

2. Inspect the retained artifacts

Your application does not need to hand-write JSON. Bulla creates receipts/pay_demo_042.json when the application records the action. The receiving system’s final action log contains two actions; only the first has a receipt.

json
{
  "generated_by": "bulla demo 0.47.1",
  "receipt": {
    "action": {
      "outcome": {
        "result_hash": "sha256:2547d779732c4e111eb7b58289cbce03623b8e6e1ee61874d6c5982d2e7806ce",
        "status": "ok"
      },
      "subject": {
        "amount_minor": 12500,
        "currency": "USD",
        "event_id": "pay_demo_042"
      },
      "type": "payments.charge"
    },
    "mandate": {
      "authority": {
        "delegation": [],
        "policy": "policy://constructed-payment-v1",
        "principal": "did:web:example.test#agent"
      },
      "bounds": {
        "rollback_window": "P7D",
        "scope": "payments.charge amount_minor<=20000 currency=USD"
      }
    },
    "evidence_refs": [
      {
        "grounding": "self_asserted",
        "hash": "sha256:2547d779732c4e111eb7b58289cbce03623b8e6e1ee61874d6c5982d2e7806ce",
        "name": "constructed_receiver_record"
      }
    ]
  },
  "receiver_actions": [
    {
      "amount_minor": 12500,
      "currency": "USD",
      "id": "pay_demo_042",
      "kind": "payments.charge",
      "receiver": "constructed-local-receiver",
      "record_sha256": "sha256:2547d779732c4e111eb7b58289cbce03623b8e6e1ee61874d6c5982d2e7806ce"
    },
    {
      "amount_minor": 5000,
      "currency": "USD",
      "id": "pay_demo_043",
      "kind": "payments.charge",
      "receiver": "constructed-local-receiver",
      "record_sha256": "sha256:a5c37224918e0a97298938a9218a739572d98a7ba9fb807d6c88f9de691f1e02"
    }
  ]
}

The JSON file is the portable transaction record. In a real integration, the customer or next system keeps its own copy.

3. Verify the receipt and the altered control

Check the original receipt, then check a copy whose amount was changed. Bulla reports the file checks separately from claims that need evidence from another system.

$ bulla receipt drill first-action/receipts/pay_demo_042.json --format json
$ bulla receipt drill first-action/controls/pay_demo_042.amount-changed.json --format json
# original: exit 0 · record integrity VERIFIED
# changed copy: exit 1 · record integrity FAILED

The changed copy says USD 125.01 but still carries commitments for the original USD 125.00 record, so the integrity check fails. The original file is unchanged. You can run the same sequence in the homepage example.

What the receipt check does not prove

This checks the receipt file. Evidence from the systems that performed or observed the action is still needed to establish what happened.

Technical evidence

Verification establishes only the reported digest, identity, or inclusion depth; it does not establish the truth of the underlying process.

4. Find the action with no receipt

Checking a receipt cannot reveal an action that has no receipt. Bulla therefore compares the receipt files with a separate action log from the receiving system. That log contains a second action with no matching receipt.

json
{
  "before": {
    "coverage": "1/1",
    "unreceipted": []
  },
  "after": {
    "coverage": "1/2",
    "unreceipted": [
      "pay_demo_043"
    ]
  },
  "original_receipt_integrity": "VERIFIED"
}

The receipt check found a changed file. The action-log comparison found a missing receipt. The comparison is only as complete as the action log you supply; Bulla cannot find actions missing from that log.

5. Keep a checker with the receipt

The verification kit contains the receipt specification, a small checker, and known examples. Keep it with important records when you want a verifier that does not depend on a hosted dashboard.

$ bulla receipt drill first-action/receipts/pay_demo_042.json
$ bulla receipt kit --out action-receipt-v0.2-verification-kit.zip
$ curl -fsSLO https://glyphstandard.com/downloads/bulla/0.47.1/action-receipt-v0.2-verification-kit.zip.sha256
$ shasum -a 256 -c action-receipt-v0.2-verification-kit.zip.sha256

Archive SHA-256: 8f2cdd16bcbd1a1121f49545b6a6512872b188221ca30ec054dfd6b2fb2142ab. The drill checks that Bulla and the included zero-dependency checker return the same result without network access.

Download the verification kit or download its detached digest.

6. Add Bulla where your application acts

Put the wrapper in the API gateway, tool router, payment handler, or agent runtime that authorizes or sends the action. The model itself does not need to know about Bulla. Keep the receiving system’s action log separately when you want to find actions with no receipt.

python
from bulla import event_coverage, observed_record_sha256, wrap_action

observed = []
receipts = []

record = {"id": "pay-1", "kind": "payments.charge", "amount_minor": 12500}
record["record_sha256"] = observed_record_sha256(record)
with wrap_action("payments.charge", {"event_id": "pay-1", "amount_minor": 12500}) as act:
    observed.append(record)
    # dispatch the consequential action here
    act.add_evidence("receiver_action_record", record["record_sha256"], "self_asserted")
    act.set_result(record["record_sha256"])

receipts.append(act.receipt)
report = event_coverage(observed, receipts, anchor="receiver-action-record")

Continue with the Python SDK guide, the deeper coverage walkthrough, or the buyer requirement for vendors.