A browser job can reach the end of an invoice list and still leave finance with an incomplete archive. Retrying the whole month looks convenient, but it mixes already collected files with unresolved documents and makes the second run harder to explain.
The useful unit of recovery is an expected invoice, not a page visit or a ZIP file. Here is a small application-side design for turning a collection run into a specific retry list.
This is a design example based on our FedEx invoice collection guide. We have not tested a FedEx account or a BrowserAct FedEx collector. The code below only classifies supplied observations; it does not log in, download files, or confirm that a PDF contains the correct invoice.
Start with expected documents
Before running a browser, define the documents you expect for a particular account and invoice-date range. An identifier such as (supplier, account_ref, document_type, invoice_number) is a better archive key than invoice.pdf.
Keep invoice date separate from shipment date or due date. Otherwise a fallback filter can silently change which reporting period the job covers. Do not put passwords, cookies, or raw billing details into the retry log.
The register also needs a source for each expected invoice. An AP checklist and a fresh portal listing answer different questions. If finance expects an invoice but the portal does not list it, repeated download clicks will not establish where it went.
Preserve three different failures
Use separate outcomes for these observations:
- Not listed: the expected document was absent from the observed account and filter scope. Review access and scope before retrying.
- Retrieval failed: the document was listed, but no usable file was retrieved. A bounded retry may be appropriate.
- Mismatch: a file arrived, but the supplied verification result says it does not match the expected document. Keep the evidence and review it.
A fourth outcome, verified, means your independent checks passed. It should require both file checks and document/account identity checks. A successful HTTP response or a file with a PDF extension is not enough.
A deliberately small classifier
The following function receives observations made by other parts of the system. In particular, document_checks_passed must come from a real verification step. Setting that boolean to true is not verification by itself.
from dataclasses import dataclass
@dataclass(frozen=True)
class Observation:
listed: bool
retrieved: bool
document_checks_passed: bool
attempts: int
def route(item: Observation, max_attempts: int = 2) -> str:
if max_attempts < 1 or item.attempts < 0:
raise ValueError("invalid attempt limit")
if item.retrieved and not item.listed:
raise ValueError("inconsistent listing observation")
if item.document_checks_passed and not item.retrieved:
raise ValueError("verification without a retrieved file")
if not item.listed:
return "review_scope"
if not item.retrieved:
return "retry_retrieval" if item.attempts < max_attempts else "review_failure"
if not item.document_checks_passed:
return "review_mismatch"
return "verified"
attempts counts retrieval attempts already made for that invoice. With a limit of two, an invoice with one failed attempt can enter the retry queue once more. At two attempts it goes to review. The limit is an application choice, not a claim about any supplier portal's allowance.
Validate account permissions and document availability before retrying. A login challenge, rate limit, or permission failure should pause the relevant route; it should not become an instruction to keep clicking until the counter runs out.
Make recovery explainable
Save an observation timestamp, the expected key, the selected scope, the outcome, and a short reason. Record an output path only after a file exists. If a job generates an export in a portal, distinguish export creation from retrieval and local archiving.
On the next run, select only retry_retrieval items. Do not recreate verified files or automatically retry mismatches. If a supplier issues a changed version, preserve the old evidence and apply an explicit version policy instead of overwriting silently.
The same contract can sit above a manually retrieved export, a Cloud Bot, or an Agent using browser commands. The interface changes; the expected-document register and verification requirements remain useful. BrowserAct supplies workflow building blocks, but each portal-specific retrieval path still needs authorized testing.
For the final handoff, show expected, verified, retryable, and review counts. Include the unresolved keys and reasons. A folder full of files is a useful intermediate result; a reconciled register explains whether the collection job actually delivered what finance asked for.
Top comments (1)
Separating “not listed”, “retrieval failed”, and “mismatch” is exactly the control finance needs. I would also make the retry record idempotent by storing a source-document fingerprint plus the expected key; that prevents a portal’s regenerated export from being mistaken for a new invoice and keeps the audit trail understandable.