Field notes / core-developer-intent

Batch Bank Statement Processing API: Keep Every File Recoverable

Coordinate statement jobs with a per-input ledger, bounded submissions, selective retries, and explicit partial-failure recovery.

2026-08-03 · 9 min read · bankstatement.ai Team

One bank statement stays tethered to its saved job and result while neighboring files follow separate recovery paths.

TL;DR

  • Create one durable ledger row for every statement before submitting any files.
  • Limit concurrent submissions with an application-controlled worker pool; do not treat the batch as one atomic request.
  • On HTTP 202, save the returned job ID before allowing that worker to take another input.
  • On HTTP 429, defer only the affected statement and honor retryAfterSeconds.
  • On HTTP 402, stop automatic retries for that statement and route it to a page-capacity decision.
  • Retry only rows that are eligible for another submission; preserve accepted jobs and completed results.
  • Retrieve required results and exports before their seven-day availability window closes.

A reliable batch bank statement processing API workflow treats every statement as an independent unit of work. In a mixed-result batch, one statement may be accepted while another is rate-limited and a third cannot proceed because the account needs more pages. Replaying the whole batch could duplicate accepted work and obscure which files still require attention. Preserve every input’s identity, record each outcome independently, and recover only the rows eligible for another action.

Key Takeaways

  • An input key identifies a statement in your system; a job ID identifies accepted processing work. Keep both.
  • Bound concurrency in your application, choosing the limit for your workload and operating environment rather than relying on an undocumented numeric recommendation.
  • Partial success is a valid batch outcome. Do not discard accepted jobs because neighboring submissions remain unresolved.
  • HTTP 202, HTTP 402, and HTTP 429 require different recovery decisions.
  • Processing completion is not operational completion. Each result needs a retrieval owner and a deadline within its seven-day availability window.

Give Every Statement Its Own Unit of Work

Define a batch as a client-coordinated collection of independent statement inputs. It is not an assumed atomic API operation in which every file must succeed before any result counts.

Give each input a stable client-side key before submission. That key can refer to an internal document record, an upload event, or a reproducible fingerprint. Its purpose is to answer a practical question later: has this exact unit of work already been submitted or resolved?

Keep the input key separate from server-issued identities. The input exists before submission. A job ID exists only after documented acceptance, and a result ID is recorded only after it is observed for a completed job. The API uses asynchronous jobs: successful job creation returns HTTP 202 with a job ID, clients follow accepted jobs until completion or failure, and completed jobs expose a result ID, as described in the API documentation.

These identities support different responsibilities:

  • The input key keeps the original statement traceable within the batch.
  • The job ID identifies accepted processing work.
  • The result ID identifies output available for retrieval.

Do not use a whole-batch success flag as the source of truth. A summary can help an operator scan a run, but it should be derived from the input rows. Otherwise, one blocked or deferred statement can conceal valid job IDs and completed results belonging to the rest of the batch.

Build the Per-Input Reconciliation Ledger

The ledger is the coordinator’s durable memory. It should let a developer or finance-operations owner account for every original statement without reconstructing events from process logs.

A practical ledger can include:

Field Purpose
Input key Stable client identity for one statement
Input fingerprint Optional client-generated duplicate guard
Submission attempt state Client record of whether submission has started and what happened
Job ID Server identity saved after HTTP 202
Observed job state Latest API job status observed by the client
Result ID Identity recorded when observed for a completed job
Recovery action Continue tracking, retry when eligible, retrieve, investigate, or request an operator decision
Retrieval status Whether required output has reached its approved destination
Retrieval deadline Deadline derived from the result’s seven-day availability window
Owner Service or person responsible for the next action

Except for values returned or observed through the API, these are client-defined operating fields. Categories such as deferred, blocked, and uncertain can make the coordinator easier to operate, but they should not be presented as server job states.

Enforce one central invariant: every admitted input has exactly one current ledger row, and every accepted job ID belongs to that row. Update the row as facts become available instead of creating disconnected records for each event.

This structure preserves useful work during partial failure. Rather than reporting only that a batch failed, the coordinator can show which inputs completed, which accepted jobs remain in progress, which rows are eligible for a later retry, and which rows require an operator decision.

Bound Submissions and Branch Before Retrying

Use an application-controlled queue with a finite number of submission workers. Bounded concurrency prevents the coordinator from opening work for every statement at once and creates a clear point at which each response must be persisted.

There is no evidenced universal worker count, batch limit, retry count, or throughput guarantee. Choose the concurrency bound for your environment and revise it using observed operational results.

Each worker should branch on the documented submission outcome before releasing its slot:

submit(input)

if status == 202:
    persist the returned job ID on this input row
    mark the client row as accepted

else if status == 429:
    record retryAfterSeconds
    defer this input only

else if status == 402:
    stop automatic resubmission
    request a page-capacity decision

else if acceptance is uncertain:
    mark the client row unresolved
    do not resubmit automatically

The documented branches are HTTP 202 for accepted job creation, HTTP 402 when more pages are needed, and HTTP 429 with retryAfterSeconds. Detailed job-status handling should follow the polling guidance in the API documentation without turning status uncertainty into a new submission.

For HTTP 202, persist the job ID before the worker moves on. Keeping that identity only in process memory creates a gap in which the server may have accepted the statement but the coordinator loses its durable reference.

For HTTP 429, defer only the affected input and honor retryAfterSeconds when determining when it becomes eligible again. Do not replay accepted neighbors or substitute an invented fixed delay.

For HTTP 402, stop automatic retries. Repeating the same request does not make the required page capacity available. Keep the row visible until an owner changes capacity or decides not to process it.

Reconcile Partial Success Instead of Replaying the Batch

Consider three synthetic statement inputs:

  • Statement A receives HTTP 202. The coordinator stores its job ID and continues following that accepted job.
  • Statement B receives HTTP 429. Its row records retryAfterSeconds and remains deferred until it is eligible for another submission.
  • Statement C receives HTTP 402. Its row is blocked and assigned to an owner for a page-capacity decision.

The batch is incomplete, but Statement A is not a failure. Its job remains attached to its original row, and any later result ID remains available for retrieval even if Statements B and C have not progressed.

Count every original input exactly once and assign it a current operational category. Client-side categories can distinguish:

  • Accepted work still being followed
  • Terminally completed work
  • Terminally failed work
  • Deferred inputs awaiting an eligible retry
  • Blocked inputs awaiting an operator decision
  • Uncertain inputs whose acceptance could not be established safely

These categories organize the client ledger; they do not add undocumented API states. Retry eligibility belongs to the row, not the batch. An accepted job is followed through its saved job ID rather than resubmitted merely because the batch remains incomplete. A terminal failure should follow the organization’s review policy instead of being assumed safe to replay.

Keep unresolved rows in an operator-visible queue. Close the batch only when every row has a defined disposition, such as delivered, intentionally abandoned, or assigned to an explicit recovery path.

Prevent Duplicate Work With Client-Controlled Guards

Duplicate prevention begins before the network call. Look up the stable input key or fingerprint in the ledger and refuse a new automatic submission when the row already contains a saved job ID, a result ID, or an unresolved acceptance outcome.

After HTTP 202, apply a persist-before-progress rule: save the job ID durably and then release the worker slot. That ordering reduces the chance that accepted work becomes detached from its input.

Transport uncertainty requires more caution. A connection can fail before the client learns whether the server accepted the request. Server-side idempotency keys, duplicate detection, and safe replay behavior are not established by the supplied API evidence, so the coordinator must not assume that repeating an uncertain submission is safe.

Mark the row as uncertain and route it through a manual or policy-controlled resolution path. The decision can use the organization’s own logs and records, but it should not silently convert an unknown response into permission to resubmit.

These safeguards are client-controlled. They do not claim that the API detects duplicate files; they prevent the coordinator from replaying work when it already has evidence of acceptance or unresolved uncertainty.

Close the Batch Before Results Expire

A completed job is only a processing milestone. The workflow is not operationally complete until the required result or export has been retrieved and stored in the team’s approved system.

Results and exports remain available for seven days from creation. After that period, structured results, exports, and associated job metadata are deleted automatically. Record a deadline when the result ID becomes known, assign a retrieval owner, and track whether the required output reaches its approved destination. The retention policy makes timely retrieval an operational responsibility.

Track retrieval separately from processing completion. For example, client-defined fields can distinguish output that is ready, output that has been retrieved, and output whose destination and input association have been verified. This prevents a batch from appearing complete while required artifacts remain exposed to expiry.

Keep detailed status-loop mechanics outside the batch coordinator. The batch layer needs the latest observed state, terminal outcome, and next action. For the broader path from submission through validation and retrieval, use the secure REST extraction workflow as the cluster-level implementation guide.

Rehearse the Coordinator With a Synthetic Batch

Before processing real financial documents, create an account, obtain an API token, and protect it on a trusted server. Use synthetic PDF, CSV, or XLSX statements within the documented 25 MB per-file limit.

Run a rehearsal that verifies coordination rather than extraction quality alone:

  1. Create one durable ledger row for every synthetic statement.
  2. Submit inputs through the bounded worker pool.
  3. Confirm that every HTTP 202 job ID is persisted before its worker advances.
  4. Exercise the planned HTTP 429 branch and verify that it honors retryAfterSeconds without replaying accepted rows.
  5. Exercise the HTTP 402 decision path without turning it into an automatic retry loop.
  6. Confirm that an uncertain submission remains unresolved instead of being resubmitted silently.
  7. Record observed result IDs, retrieval owners, and seven-day deadlines.
  8. Verify that required outputs reach the intended destination before the batch closes.

Use ledger-level acceptance questions: Can every input be located? Can every accepted job be traced to its source statement? Does every unresolved row have an owner and next action? Can the coordinator resume from durable state without replaying successful work?

Review the current pricing page when page capacity affects deployment decisions, and verify it again before publishing or relying on any price or allowance. No price figure is required to validate the coordinator itself.

Create an account, obtain a protected server-side API token, and process a synthetic batch. Once every statement remains independently traceable through acceptance, recovery, and retrieval, the coordinator is ready to support real finance workflows without sacrificing successful work when a neighboring input fails.

This article as llm.txt