A document job searches a merchant portal, sees no rows, and records invoice_missing. On the next run it repeats the same search. Nobody can tell whether the statement is unavailable, the wrong merchant was selected, or the results never finished loading.
The missing artifact is often the search evidence.
I work on BrowserAct. Our guide to Elavon Payments Insider statements describes filtering by merchant ID, statement type, and date range. That native workflow suggests a useful application contract: retain what was actually searched before interpreting an empty result.
The design below classifies metadata from a collection attempt. It does not operate Payments Insider, discover its selectors, call Elavon APIs, or inspect real statements. The code was checked locally with synthetic requests and receipts. These are proposed application fields, not a documented vendor response format.
Bind the observation to one request revision
A finance request can change. Someone corrects the MID or asks for another statement type, while a worker still holds the previous search result. If the application only stores a job ID, that old observation can be attached to the revised request.
Give the request a stable ID and a monotonically managed revision. The caller must issue a new revision when its intended scope changes. An attempt records the ID and revision it executed, independently of the filter values it later observed.
In this example, the requested filters contain an internal company reference, MID, statement type, and period label. The period is an application-defined scope token; a real adapter must establish how the actual date controls map to it. The function does not assume a supplier's default date range or convert a download date into a statement month.
Read back the filters instead of copying the inputs
There are two different objects: the intended filters and the filters observed when results were inspected. Copying the first dictionary into both fields would make the check meaningless.
An operator or separately validated adapter must establish the observed merchant and report scope. A successful click alone is insufficient. A filter might not have applied, a merchant switch might reset another control, or the result panel might still show a previous query.
The filter_readback_verified flag below is a boundary fact supplied by that adapter. This function cannot prove it. Preserve a reference to the supporting observation in the production receipt, keeping credentials and private document contents out of routine logs.
FIELDS = ("company", "mid", "statement_type", "period")
def filter_key(filters):
if not isinstance(filters, dict):
raise ValueError("Expected an observed filter object")
values = tuple(filters.get(field) for field in FIELDS)
if any(not isinstance(v, str) or not v.strip() for v in values):
raise ValueError("Filter fields must be nonempty strings")
return values
def classify_attempt(request, attempt):
request_id = request.get("id")
revision = request.get("revision")
if not isinstance(request_id, str) or not request_id.strip():
raise ValueError("Missing request ID")
if type(revision) is not int or revision < 1:
raise ValueError("Expected a positive integer revision")
expected = filter_key(request.get("filters"))
if (attempt.get("request_id") != request_id
or type(attempt.get("request_revision")) is not int
or attempt["request_revision"] != revision):
return "request_binding_mismatch"
if attempt.get("error"):
return "search_error"
if attempt.get("filter_readback_verified") is not True:
return "filter_evidence_required"
if filter_key(attempt.get("observed_filters")) != expected:
return "filter_scope_mismatch"
count = attempt.get("result_count")
if type(count) is not int or count < 0:
raise ValueError("Expected a nonnegative integer result count")
if attempt.get("listing_complete") is not True:
return "listing_incomplete"
if count == 0:
return "not_listed_in_verified_scope"
return "listed_file_delivery_pending"
Boolean checks are intentionally strict. The string "false" is truthy in Python, and True is an instance of int; neither should qualify as a verified readback or an integer result count here.
Give a narrow meaning to an empty result
not_listed_in_verified_scope says that this particular completed search showed no matching entries, assuming the supplied evidence is accurate. It does not prove that the supplier never issued a statement, that another account lacks it, or that the portal is unavailable.
Similarly, listed_file_delivery_pending means the listing returned entries. It does not claim the right file was saved. A sales report and a monthly processing statement remain different requested artifacts even if both concern the same merchant and month.
The upstream adapter needs an explicit definition of listing_complete. A blank panel during loading is not a complete empty result. If the interface paginates or truncates its results, capture that limitation rather than setting the flag merely because the first panel appeared.
Test the plausible false negatives
The local synthetic cases included the following boundaries. They exercise this classifier, not a live merchant account or a file-download process.
| Synthetic observation | Classification |
|---|---|
| Receipt belongs to an older request revision | request_binding_mismatch |
| Search reported an error and no rows | search_error |
| Filters were entered but not read back | filter_evidence_required |
| Another MID or statement type was observed | filter_scope_mismatch |
| Zero rows while listing completion is unconfirmed | listing_incomplete |
| Verified filters, completed listing, zero rows | not_listed_in_verified_scope |
| Verified filters, completed listing, positive count | listed_file_delivery_pending |
Missing fields and invalid counts raise validation errors. The caller should retain the failure reason at the input boundary, not catch it and convert it into an empty successful search.
Make the next action match the evidence
An incorrect filter asks for a corrected query. Incomplete loading asks for observation of the result state. Missing merchant access asks for the responsible account owner's review. A listed document with no saved file asks for delivery investigation.
Those actions should not collapse into a universal retry of every MID for the whole month. Keep attempt receipts immutable and attach the next attempt to the appropriate request revision. If the requested scope changes, preserve the earlier observation as history rather than rewriting it to resemble the new request.
Elavon's public guidance supports the statement-filtering context, but our article supplies no tested Payments Insider collector or universal publication schedule. This classifier can sit behind native manual retrieval or a separately permitted integration; it creates neither portal access nor evidence of a successful download.
An exception queue becomes more useful when “missing” is replaced by the smallest conclusion the observation actually supports.
Source and native retrieval context: Download Elavon statements in Payments Insider.
Top comments (0)