Recovering from billing quarantine
Quarantine exists so a bad invoice does not become a bad invoice in the customer's inbox. It also means that at some point you will be staring at a child bill run full of rejected or suspect invoices, deciding what to do with them. These are the moves that make that decision fast instead of stressful.
Know your two entry points
Invoices land in quarantine one of two ways: automatically, when validation flags an invoice and the run's rejectAutoAction / suspectAutoAction is MOVE, or manually, when an operator reviews draft invoices and sends specific ones to quarantine before validating the rest. Both paths feed the same child bill run, so the recovery flow is identical either way.
POST /api/v1/billingRuns/{id}/quarantineInvoices
{ "invoiceIds": [12, 13], "description": "Carrefour amounts look wrong" }
Repeated manual quarantine calls consolidate, they do not multiply
If you call quarantineInvoices twice against the same origin bill run, both batches land in the same child quarantine BR, not two separate ones. Do not go hunting for a second quarantine run after a follow-up review pass. Check the origin BR's originBillingRunId back-reference on the existing child instead.
Manual quarantine is status-gated
quarantineInvoices only works while the origin BR sits at DRAFT_INVOICES or POSTINVOICED. Call it after the run has moved past those statuses and you get a 409, not a queued quarantine. If the UI "Send to quarantine" bulk action is missing from the Related Invoices table, that is the tell: the BR already moved on.
Read the counts before you open the review page
Every BillingRunV1Response for a quarantine BR carries a summary, so you can triage severity from a list view instead of opening every invoice:
{
"id": 542,
"isQuarantine": true,
"originBillingRunId": 540,
"quarantine": {
"rejectedInvoiceCount": 2,
"suspectInvoiceCount": 1
}
}
The Bill Runs list surfaces this as a Quarantine of #540 type column. Two rejected and one suspect is a five-minute review. Fifty rejected is a "call the offer owner" review.
Pick the release action that matches the actual problem
The quarantine review page gives you three actions per invoice. They are not interchangeable:
| Action | What it does | Use when |
|---|---|---|
| Revalidate | Flips status back to NEW and re-runs validation | The underlying data was fixed (BA corrected, tax mapping added) |
| Cancel | Drops the invoice; RTs stay BILLED | The invoice was a duplicate or should never have existed |
| Cancel and reopen RTs | Drops the invoice, reopens its RTs | The charge itself needs to be re-rated or re-billed next run |
Picking Cancel when you meant Cancel-and-reopen is the single most common quarantine mistake. It leaves revenue billed but invisible, since the RTs stay BILLED and never resurface on a future bill run.
POST /api/v1/billingRuns/{id}/releaseFromQuarantine
{ "invoiceIds": [12, 13, 14], "action": "REVALIDATE", "targetBillingRunId": null }
Know the release error codes before you script against this endpoint
| Code | Cause |
|---|---|
400 MISSING_INVOICE_IDS | invoiceIds is empty |
400 INVALID_ACTION | action is not REVALIDATE / CANCEL / CANCEL_RT |
404 BILLING_RUN_NOT_FOUND | BR id does not exist |
| 409 | BR is not a quarantine BR (isQuarantine=false) |
The 409 is the one people trip over: you passed the origin BR's id instead of the child quarantine BR's id. releaseFromQuarantine only accepts the child.
Suspect invoices are often a calendar alignment problem, not a data problem
Before assuming a suspect invoice means bad customer data, check whether the charge's invoicing gate and the bill run's cutoff actually line up. A work order only becomes eligible for a bill run once its invoicingDate clears the run's cutoff. If a charge's invoicing calendar is set to the same boundary as the bill run itself (both CAL_1ST_MONTH, say), work orders created right on the boundary can be excluded or flagged rather than picked up cleanly. The fix is upstream in the charge catalog, not in the quarantine review screen: align the charge's invoicing calendar so its next eligible date falls comfortably before the bill run's cutoff, not exactly on it.
Turn off auto-quarantine deliberately, not by accident
If a bill run keeps spawning quarantine children and you would rather fail loud or fail closed, change the auto-action instead of fighting the review queue:
| Action | Effect on a rejected/suspect invoice |
|---|---|
MOVE (default) | Move to a child quarantine BR |
CANCEL | Drop the invoice |
CANCEL_RT | Drop the invoice, reopen its RTs |
AUTOMATIC_VALIDATION | Treat as valid, continue (suspect only) |
MANUAL_ACTION | Stop the engine, wait for an operator |
The New Bill Run form flags this inline whenever either auto-action is set to MOVE, so it is worth a second look before you launch a run you expect to be clean.