Resume a batch
BuildingIn development for the v1 API. The contract is set; the route is not served yet.
Puts a paused batch back in processing, from where it stopped. Answers 202. Resuming a batch that is already processing answers 202 with no change. Any other state answers 409 BATCH_INVALID_STATE. A batch in processing whose documents have all finished also answers 409 BATCH_INVALID_STATE, because its report is being written and it is about to be done.
Needs the batches:write scope. It takes no body and no Idempotency-Key: repeating it is safe.
oauth2 batches:writeAuthorizationBearer <token>OAuth 2.0 client credentials. Exchange your client_id and client_secret at /oauth/token of the environment for a bearer token valid for one hour. The playground of every operation asks for your own client and keeps it in your browser tab only.
Scope: batches:write
id*stringThe batch id.
^bat_[0-7][0-9a-hjkmnp-tv-z]{25}$Accepted
application/json- response
id?string^bat_[0-7][0-9a-hjkmnp-tv-z]{25}$type?stringWhat the rows of the CSV are: invoice or retention_certificate. Each type has its own columns, listed in the batches guide.
"invoice""retention_certificate"status?stringawaiting_upload until the whole file arrives at upload_url; validating while heru checks every row; ready when at least one document is valid; done when no document is invalid and every document is a duplicate, so nothing is stamped and the batch closes with its report and fires batch.done, without going through POST /batches/{id}/process; invalid when no document is valid and at least one failed validation, the file itself has an error or the upload window ended; processing after POST /batches/{id}/process; paused after POST /batches/{id}/pause; cancelling after POST /batches/{id}/cancel, or when the batch reaches 20 days unfinished, while heru stops what is pending and writes the report, then cancelled; done when every valid document has its outcome. invalid, cancelled and done are final.
"awaiting_upload""validating""invalid""ready""processing""paused""cancelling""cancelled""done"created_at?stringWhen the batch was created with POST /batches. The 20 days of the batch's life count from here: see processing pace.
date-timedefaults?The defaults of the request, normalized: 16% comes back as 0.16.
upload_url?|nullupload_expires_at?|nullupload_max_bytes?integerThe largest file upload_url takes, in bytes, compressed or not: 34359738368 (32 GiB).
int64total_rows?integerData rows in the file. Final once heru has read the whole file.
validated_rows?integerRows checked so far. Compare it with total_rows while the batch is validating.
valid_rows?integerRows that passed their own checks. A valid row can belong to an invalid document, which is not processed: progress is counted in documents.
invalid_rows?integerRows with at least one problem.
total_documents?integerDocuments in the file: rows that share external_id are one document. Final once heru has read the whole file.
valid_documents?integerDocuments with every row valid and not stamped before. Only these are processed. It does not change after ready.
invalid_documents?integerDocuments with at least one invalid row. None of their rows is processed.
duplicates?integerDocuments whose external_id was already stamped. They are not stamped again and the report gives their id and uuid. A document already stamped wins over its own validation errors: it counts here, not in invalid_documents.
processed_documents?integerValid documents with an outcome so far: succeeded + failed + cancelled + pending_review. Compare it with valid_documents while the batch is processing.
in_flight_documents?integerDocuments already prepared and on their way to the PAC. They keep moving while the batch is paused, so this says how much is left before the counters stop.
succeeded?integerDocuments stamped.
failed?integerValid documents that failed when processed, e.g. refused by the stamping authority. The reason is in the report. A failed document can go again in a new batch.
cancelled?integerValid documents never processed because the batch was cancelled.
pending_review?integerDocuments with no outcome when the batch closed. They are not lost: if one gets stamped later, its webhook arrives.
estimated_completion_at?|nullfits_lifetime?|nullreceiver_overrides?integerDocuments whose receiver data in the file differs from the data registered in heru, which is what gets stamped. Estimated during validation and final when the batch closes; the report names the columns.
errors?array<>The first 100 problems, in row order. error_count has the total and errors_url all of them. Final when the batch leaves validating.
error_count?integerTotal problems found, which can be more than errors shows.
errors_url?|nullreport_url?|nullcurl -X POST 'https://api.dev.b2b.heru.app/v1/batches/bat_01m4ra8wpbev7sf851m6pn6gm7/resume'{ "id": "bat_01m4ra8wpbev7sf851m6pn6gm7", "type": "invoice", "status": "processing", "created_at": "2026-10-28T15:04:05Z", "defaults": { "payment_form": "03", "quantity": "1", "unit_code": "E48", "tax_object": "02", "vat_rate": "0.16" }, "upload_url": null, "upload_expires_at": null, "upload_max_bytes": 34359738368, "total_rows": 1250, "validated_rows": 1250, "valid_rows": 1247, "invalid_rows": 3, "total_documents": 1100, "valid_documents": 1092, "invalid_documents": 3, "duplicates": 5, "processed_documents": 318, "in_flight_documents": 120, "succeeded": 318, "failed": 0, "cancelled": 0, "pending_review": 0, "estimated_completion_at": "2026-11-09T18:00:00Z", "fits_lifetime": true, "receiver_overrides": 2, "errors": [ { "row": 18, "column": "receiver_rfc", "code": "INVALID_RFC", "reason": "Does not have the format of an RFC" }, { "row": 402, "column": "unit_price", "code": "NEGATIVE_AMOUNT", "reason": "Must not be negative" }, { "row": 977, "column": "product_code", "code": "INVALID_CODE", "reason": "Not in the SAT catalog c_ClaveProdServ" } ], "error_count": 3, "errors_url": "https://files.heru.app/batches/bat_01m4ra8wpbev7sf851m6pn6gm7-errors.csv.gz", "report_url": null}Pause a batchPOST
Stops heru from preparing more documents of a `processing` batch. Answers `202` and the batch shows `paused` right away. Documents already prepared still go to the PAC, so `succeeded` and `failed` can advance for a few minutes; `in_flight_documents` says how many are left. Pausing a batch that is already `paused` answers `202` with no change. A document with an `issue_date` can leave the 72-hour window during a pause, and the 20 days of a batch keep counting. Only a batch in `processing` can be paused: use `POST /batches/{id}/cancel` to stop one that is validating or `ready`. Any other state answers `409 BATCH_INVALID_STATE`. A batch in `processing` whose documents have all finished also answers `409 BATCH_INVALID_STATE`, because its report is being written and it is about to be `done`. Needs the `batches:write` scope. It takes no body and no `Idempotency-Key`: repeating it is safe.
Cancel a batchPOST
Stops what is pending. **It never cancels a CFDI:** documents already stamped stay stamped and appear in the report; to cancel one, use its own `DELETE` endpoint. What is on its way to the PAC finishes, and every document not yet sent becomes `cancelled` with `BATCH_CANCELLED`. The batch goes `cancelling`, which can last tens of minutes on a very large file, and then `cancelled`. The exception is `awaiting_upload`: it goes straight to `cancelled`, with no report. A later batch can take the same file: what was stamped comes back `duplicate` and only the rest is stamped. If everything was already stamped, the new batch ends `done` with its report and stamps nothing. A batch still unfinished 20 days after it was created is cancelled in the same way. Allowed in `awaiting_upload`, `validating`, `ready`, `processing` and `paused`. From `ready` on, the batch ends with `report_url`; before that it has no report. `batch.done` fires with `status` `cancelled` only if the batch had reached `POST /batches/{id}/process`. Cancelling a batch that is `cancelling` or `cancelled` answers `202` with no change. `done` and `invalid` answer `409 BATCH_INVALID_STATE`, and so does a batch in `processing` whose documents have all finished, because its report is being written and it is about to be `done`. Needs the `batches:write` scope. It takes no body and no `Idempotency-Key`: repeating it is safe.