A billing job finishes its accounting-data export and turns green. Later, the person preparing an audit asks where the supplier invoice is. The job has an importable record, an order summary, and a delivery photo. None was accepted as the requested invoice file.
The problem is the completion contract. One boolean has hidden two deliverables.
I work on BrowserAct. Our QXO guide separates Order History, which supplies purchase context and invoice access, from the Billtrust route for account billing history and accounting-data exports. It also distinguishes invoices from order summaries and delivery photos. Those documented distinctions are useful when designing an application ledger, even before implementing a collector.
This article proposes that ledger. The Python example consumes synthetic metadata; it does not access QXO or Billtrust, parse a supplier PDF, or validate an accounting import. No portal-specific Bot or universal bulk-download capability is claimed.
Name the deliverables before choosing the route
An archive request and a data-entry request need different acceptance evidence. A finance team may want one, the other, or both. Asking for both should create two pending requirements rather than one vaguely named “download invoice” task.
| Requested deliverable | Evidence needed by the application | Does not satisfy it alone |
|---|---|---|
| Invoice file | Saved artifact reviewed against the requested invoice identity | Order summary, delivery photo, or billing-data row |
| Billing data | Saved data output reviewed against the requested billing identity and field contract | An invoice file without the required structured fields |
This is an application contract, not a declaration that either portal provides every format for every account. Confirm the available output in the actual authorized account. A native export may solve the data-entry task without solving document retention.
Keep supplier capabilities attached to their documented route. Our guide records an Order History history-window statement from its November 9 documentation review; that statement is not a retention guarantee for every Billtrust output. This ledger does not infer document coverage from a supplier name or a route label.
Use invoice identity, not the nearest order reference
An order reference helps locate a purchase. It should not silently become the identity of every attached invoice. One request below names a company, region, account reference, and invoice reference. These are internal normalized identifiers, not a guaranteed list of vendor fields.
An upstream reviewer or adapter must resolve that identity from the available evidence. If finance only has a job number, preserve the unresolved request and investigate it before claiming a matching invoice. Do not invent the invoice reference from a filename.
The scope also matters across branches and regional accounts. A document belonging to another account should remain visible as a mismatch, even when its invoice reference happens to look familiar.
Track two outputs without claiming to inspect their contents
The function below only aggregates reviewed receipts. It requires an explicit output kind, a saved-artifact reference, and a reference to the acceptance review. Both outputs may come from the same route or different routes; the route alone never qualifies a receipt.
KINDS = {"invoice_file", "billing_data"}
IDENTITY = ("company", "region", "account", "invoice")
def identity(record):
values = tuple(record.get(k) for k in IDENTITY)
if any(not isinstance(v, str) or not v.strip() for v in values):
raise ValueError("Resolve every identity field before aggregation")
return values
def delivery_state(request, receipts):
expected = identity(request)
required = request.get("required")
if (not isinstance(required, (list, tuple)) or not required
or any(k not in KINDS for k in required)):
raise ValueError("Declare supported deliverables explicitly")
accepted = {kind: set() for kind in required}
exceptions = []
for position, receipt in enumerate(receipts):
kind = receipt.get("kind")
if identity(receipt) != expected:
reason = "identity_mismatch"
elif kind not in accepted:
reason = "not_a_requested_deliverable"
elif receipt.get("review_passed") is not True:
reason = "acceptance_review_required"
elif not all(isinstance(receipt.get(k), str)
and receipt[k].strip()
for k in ("artifact_ref", "review_ref")):
reason = "receipt_evidence_incomplete"
else:
accepted[kind].add(receipt["artifact_ref"])
continue
exceptions.append({"position": position, "reason": reason})
state = {kind: "receipt_accepted" if refs else "pending"
for kind, refs in accepted.items()}
return {"deliverables": state, "exceptions": exceptions}
The strict boolean check rejects a string such as "true". Identical artifact references do not create additional accepted evidence. A receipt for an unrequested supporting document remains an exception rather than filling an invoice requirement.
review_passed is a trust boundary, not a PDF validator. The upstream review must establish document type, identifiers, readability, and whatever data-field checks the request requires. A copied URL or an existing path is not that review. Restrict who can create acceptance receipts and retain the supporting observation securely.
Make partial completion visible
With both kinds requested, a reviewed billing-data receipt leaves invoice_file pending. Adding an order summary or photo does not change it. A separately reviewed invoice receipt can satisfy that requirement; a wrong-account receipt cannot.
The output deliberately says receipt_accepted, not “paid,” “reconciled,” or “imported.” It reports what this metadata boundary accepted. Exceptions remain available even when both requirements have accepted receipts. A calling service still needs a policy for conflicting versions and additional review findings before presenting an overall completion badge.
Local synthetic checks covered data-only delivery, file-only delivery, both outputs, no outputs, duplicate references, wrong company/account/region/invoice, supporting-document types, missing evidence references, and strict review flags. They do not establish that a vendor export has the required columns or contains an original PDF.
Retry the remaining requirement
The exception queue should send an archive gap to document retrieval and a data-contract gap to the data export or transformation step. Keep accepted receipts and review history rather than downloading every related order again.
For QXO, the native route should be chosen after clarifying the missing artifact and verifying account scope. Document collection does not require scheduling a payment or changing payment settings. A proposed browser collector needs its own permission and output validation; this ledger creates neither access nor a working portal integration.
The useful green state is the one whose evidence matches the task finance actually requested.
Source and native retrieval context: Find QXO invoices: Order History or Billtrust?.
Top comments (0)