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.
1. Run the first action
Install Bulla and run the fixed example. Its output directory contains a receipt, a receiving-system action log, and reports for inspection.
$ python -m pip install "bulla==0.49.2"
$ bulla demoFIRST ACTION DEMO · CONSTRUCTED LOCAL SCENARIO
ACTION
recorded action payments.charge
action id pay_demo_042
amount USD 125.00
declared limit USD 200.00
receipt receipts/pay_demo_042.json
LOCAL RECEIPT CHECKS
record integrity VERIFIED
declared bounds CONFORMS
evidence grounding SELF_ASSERTED
authority UNAUTHENTICATED
event occurrence NOT_ESTABLISHED
ALTERATION CONTROL
changed amount USD 125.01
record integrity FAILED
original receipt UNCHANGED
OMISSION CONTROL
coverage before 1/1
coverage after 1/2
unreceipted action pay_demo_043
original integrity VERIFIED
OFFLINE DRILL
checker agreement MATCH
network guard PYTHON_AUDIT_HOOK
The receipt caught alteration. The receiver's action record caught omission.
This is a constructed receiver record. Neither record establishes that funds moved.
artifacts /private/.../bulla-first-action-...About the published package
The command shown here comes from the package published on PyPI, not from unreleased repository code.
Look for the retained directory on the final line after artifacts. Below, that path is called BULLA_DEMO_DIR. Set it once in your terminal using the path from your own run:
BULLA_DEMO_DIR="/paste/the/artifacts/path/here"If you prefer to choose the directory before running, add --out PATH. That path must not already exist.
2. Inspect the retained artifacts
Your application does not need to hand-write JSON. Bulla creates receipts/pay_demo_042.json when application code records the action. A separate receiving-system log contains two actions; only the first has a receipt.
{
"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"
}
]
}Keep the JSON file as the portable transaction record. In a real integration, the customer or receiving system retains 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 "$BULLA_DEMO_DIR/receipts/pay_demo_042.json" --format json
$ bulla receipt drill "$BULLA_DEMO_DIR/controls/pay_demo_042.amount-changed.json" --format json
# original: exit 0 · record integrity VERIFIED
# changed copy: exit 1 · record integrity FAILEDChanged-copy content says USD 125.01 while its commitments still cover the original USD 125.00 record, so the integrity check fails. Original receipt bytes stay unchanged. Run the coverage walkthrough to combine changed-file and missing-receipt checks.
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.
4. Find the action with no receipt
Receipt verification cannot reveal an action that has no receipt. Compare the receipt files with a separate receiving-system log to run the coverage check. Its second action has no matching receipt.
{
"before": {
"coverage": "1/1",
"unreceipted": []
},
"after": {
"coverage": "1/2",
"unreceipted": [
"pay_demo_043"
]
},
"original_receipt_integrity": "VERIFIED"
}Changed-file verification detects the amount edit. Action-log comparison identifies the missing receipt. Coverage reaches only the actions in the supplied log.
5. Keep a checker with the receipt
Keep the verification kit with important records. It contains the receipt specification, a small checker, and known examples without a hosted-dashboard dependency.
$ bulla receipt drill "$BULLA_DEMO_DIR/receipts/pay_demo_042.json"
$ bulla receipt kit --out action-receipt-v0.2-verification-kit.zip
$ curl -fsSLO https://bullalabs.com/downloads/bulla/0.49.2/action-receipt-v0.2-verification-kit.zip.sha256
$ shasum -a 256 -c action-receipt-v0.2-verification-kit.zip.sha256Archive SHA-256: 8f2cdd16bcbd1a1121f49545b6a6512872b188221ca30ec054dfd6b2fb2142ab. Drill output compares Bulla with the included zero-dependency checker while network access is denied.
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. Models do not need to know about Bulla. Keep a separate receiving-system log when coverage reconciliation matters.
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.