An overnight AI job reads a folder of supplier documents and returns a results file. The dashboard says the batch ended. A morning operator still needs to know which supplier records are ready, which documents failed and whether someone changed a record after the job started. Batch completion is a runtime status; the operating team needs an account of every input.
Anthropic's Message Batches API processes requests asynchronously at half the standard API price. Its documentation describes succeeded, errored, canceled and expired outcomes for individual requests. Results can arrive in a different order from the inputs and must be joined using custom_id. Anthropic: batch processing and result states
That makes a batch useful for work that can wait, such as preparing a document review queue or classifying an archive. It is a poor fit for a customer who needs an answer during a live call. Before building a batch worker, check whether the source system already imports structured fields reliably; an AI extraction step adds little when the data already has a dependable machine-readable format.
Freeze the input list before submitting work
For a hypothetical supplier-document job, I would create an input manifest with an internal item ID, source document version, destination record and prompt version. The manifest determines what the run owes the operator. Adding another file later creates another manifest entry or another run, so the completion count does not drift while results are arriving.
Keep provider request IDs separate from business identity. A custom_id can identify the request for a particular source version without including a customer name or personal information. The application retains the mapping locally. Store the provider batch ID after submission and record an uncertain submission outcome if the response is lost; guessing that no batch was created can duplicate paid processing.
run_id: supplier-review-20261012
item_id: item_044
provider_custom_id: item_044_v7
source_document: doc_981
source_version: 7
destination_record: supplier_44
prompt_version: extract_3
provider_result: pending
application_result: unresolvedJoin results by identity, then validate their meaning
A JSONL file's first line might correspond to the last input. Zipping result lines with the input array can silently put one supplier's fields on another supplier's record. Build an identity-based join that rejects unexpected IDs and detects duplicate results. An unmatched entry remains unresolved, even if the worker has reached the end of the file.
A provider success says the model request completed. The application still needs to check whether the response finished normally, can be parsed and contains supported values. Structured output constraints can help with schema shape; refusal or a token cutoff can still return an unusable output. Neither a matching schema nor a successful response proves that an extracted account number belongs to the document. Anthropic: structured outputs and invalid-output cases
For this job, I would require evidence locations for fields that the team intends to use. An absent field stays absent. A conflicting company name goes to review. Compare totals with the source using ordinary code where possible, and keep OCR failures distinct from interpretation mistakes. Those distinctions tell an operator whether to obtain a better scan, correct a value or ask the supplier.
Work through a 120-record example
The following numbers are a worked illustration, not measured results. The manifest contains 120 items. The provider returns 112 succeeded results, five errors, two expired requests and one canceled request. Content checks find 105 usable outputs among the successes; seven need operator review. A fresh read then finds that three destination records changed during the job. Their proposed updates are stale, leaving 102 ready records.
| Application status | Count | Next owner or step |
|---|---|---|
| Ready for the permitted release process | 102 | Release worker or reviewer checks exact fields |
| Content needs review | 7 | Operator inspects missing or conflicting evidence |
| Source or destination changed | 3 | Refresh evidence and prepare a new proposal |
| Provider error, expiry or cancellation | 8 | Worker or operator resolves each outcome |
| Total manifest items | 120 | Every input appears once in the account |
The queue must preserve those categories after a worker restart. A headline of 112 successes would overstate ready work. A headline of 102 completed records would also be wrong if those records have only been prepared. After release, record the actual write result separately. If the destination rejects an update, the item remains visible with its reason and owner.
Retain failures without repeating successful work
Anthropic documents a 24-hour expiry for requests that have not been processed and makes results available for 29 days from batch creation. Canceling a batch can leave partial results. Collect and preserve the per-item outcomes within the provider's availability period, using the retention and access rules appropriate to your data. Anthropic: expiry, partial results and retention window
Our proposed worker would select a retry candidate only after classifying its failure. An invalid request needs repair. An expired request needs a new attempt if the work still applies. A canceled item might reflect an operator's deliberate decision to stop. Preserve attempt history and associate a new request with the same business item; do not erase the earlier result to make the run appear clean.
Before retrying, recheck the source version and intended destination. A document corrected overnight should not be processed again as though its old version were current. Keep generation retries separate from destination-write retries. The latter need the destination API's own duplicate-protection and recovery contract. Read: recovering an uncertain external action
Prove the operator can close the run
| Injected condition | Expected visible result |
|---|---|
| Results returned in reverse order | Correct fields remain attached to their original item IDs |
| One result is missing | Its manifest entry stays unresolved |
| The same result is imported twice | No second destination update occurs |
| Model output has a missing source value | Operator sees missing evidence, without an invented field |
| Destination record changes during the run | Old proposal cannot overwrite the correction |
| Worker restarts during results import | Previously handled items and unresolved work survive |
Ask the operating team to close a small test run using its normal screen. They should be able to identify each unresolved item, see the source and decide its next step. Track accepted outputs, correction time, stale proposals and actual writes separately. Then calculate cost per usable record, including retries and review time. The API discount applies to inference charges, so it cannot by itself establish a saving for the whole operation.
Start with one document queue that already has an owner and a defined next step. Keep destination writes disabled until the identity join and recovery tests pass. The first release should make the morning queue easier to resolve, with every outstanding record accounted for.



