DEV Community

BrowserAct
BrowserAct

Posted on Originally published at browseract.com Fully Autonomous

Deduplicate invoice bytes without deleting the need to check again

An invoice archive needs two histories: the documents it has stored and the observations made while checking them. Content deduplication can reduce repeated storage. It should not erase the observation that a record was checked again, or decide that the supplier has finalized it.

This matters when the first document is a preliminary snapshot. A collector that marks the invoice permanently complete after its first successful download can preserve an outdated file very reliably.

I work on BrowserAct's content and workflow documentation. This AI-assisted design example uses synthetic local bytes. It is not an ODFL API client, an authenticated invoice download, or a tested BrowserAct collector. The example creates no network connections.

A document API changes the starting point

Our Old Dominion guide describes a native Document API supporting Invoice PDFs, subject to bill-to permissions. It also explains the documentation's warning that an invoice snapshot can change. An integration design should preserve both facts: there is a native file route worth evaluating, and receiving a file does not automatically settle its business status.

The portal can still be useful for targeted exceptions, but it should not be chosen merely because the integration team assumed no PDF API existed. Conversely, access to a delivery document is not proof of permission to retrieve the invoice. Resolve entitlement for the requested document type before treating repeated requests as a recovery strategy.

This article focuses on what happens after an authorized worker has obtained and validated the expected artifact. It does not supply endpoint URLs, authentication handling, a decoding routine or a universal polling schedule.

Separate stored content from retrieval observations

Use the authorized billing-account scope, PRO and document type as a business key. Keep a content digest for deduplicating bytes. Then append an observation for every accepted retrieval, even when the bytes match a previously stored object.

Record the time and the available status evidence with that observation. In particular, absence of a finalization signal is not a finalization signal. The example intentionally has no is_final field or automatic completion decision.

from hashlib import sha256


def record_invoice(objects, observations, identity, payload, checked_at,
                   status_note="not established"):
    fields = ("account_ref", "pro_ref", "document_type")
    if any(not isinstance(identity.get(f), str) or not identity[f].strip()
           for f in fields):
        raise ValueError("Explicit account, PRO and document type required")
    if identity["document_type"] != "Invoice":
        raise ValueError("This worker records invoices only")
    if not isinstance(payload, bytes) or not payload:
        raise ValueError("Validated document bytes required")
    if not isinstance(checked_at, str) or not checked_at.strip():
        raise ValueError("Retrieval time required")

    business_key = tuple(identity[f] for f in fields)
    digest = sha256(payload).hexdigest()
    object_key = business_key + (digest,)
    objects.setdefault(object_key, payload)
    observation = {
        "identity": business_key,
        "sha256": digest,
        "checked_at": checked_at,
        "status_note": status_note,
    }
    observations.append(observation)
    return observation
Enter fullscreen mode Exit fullscreen mode

All field names are our own archive vocabulary, not claimed API response fields. status_note is an annotation, not a trusted supplier field and not an authorization to finalize an invoice. A production process needs to preserve the provenance and authority of any status judgment separately.

The function also does not parse PDFs or verify invoice identifiers inside a file. Those checks must happen before accepting the input. A digest identifies bytes; it does not establish that the file is readable, authentic, associated with this PRO or appropriate for the accounting request.

Keep revisions instead of silently replacing them

If a later retrieval contains different bytes for the same business key, the archive gains a new object. The old object remains available, and the observation log explains when each version was obtained.

If a later retrieval has identical bytes, the archive keeps one stored object but gains another observation. That second observation can support “checked at this time, same bytes observed.” It cannot support “the invoice is now final” unless some separate, authoritative business evidence establishes that conclusion.

Avoid making retrieval time part of the content object's identity simply to force every repeated file into storage. Equally, do not use a digest match to suppress the observation or cancel all future checks. Storage identity and monitoring state solve different problems.

Decide when to revisit outside the archive writer

The scheduling layer should use the finance requirement, supplier availability and documented limits. It needs different outcomes for an invoice not generated yet, missing entitlement, an absent document and a failed artifact validation. None of those is fixed by discarding already accepted versions.

Keep the missing-item register explicit. If the required final version has not been established, the item can remain open while its preliminary version is safely archived. When the business confirms the needed version, preserve that decision and its evidence rather than teaching the hash function to guess.

The local synthetic checks verify repeated-content storage, retained observations, changed-content preservation, account separation and input rejection. Placeholder strings stand in for document bytes; no PDF or carrier service was exercised.

Portal context and documented limits: Download Old Dominion invoices through the portal or PDF API. The archive model above is a proposed application design, not supplier-provided implementation code.

Top comments (0)