Skip to content

StartQuickstart

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 demo
FIRST 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:

bash
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.

json
{
  "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 FAILED

Changed-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.

json
{
  "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.sha256

Archive 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.

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.