Void a payment batch
Authorization: requires purchasing:write API-key scope AND the
per-user payments.refund_void permission (SoD parity sweep 2026-05-16).
Voiding a NACHA payment batch cascade-deletes the underlying
ap_payments rows + ap_payment_applications rows for the batch:
effectively a bulk void of every payment in the batch. Industry pattern
treats bank-batch reversal as the most-trusted AP action, hence the
elevated per-user gate. System-actor calls without an identifiable
principal are denied per AICPA SoC2 CC6.1.
State transitions enforced by the canonical voidPaymentBatch handler:
- If batch
status = 'confirmed_sent': HTTP 409cannot_void_confirmed_batch. Confirmed-sent batches have posted GL (DR Accounts Payable / CR Bank) and are bank-transmitted. To reverse, void each individualap_paymentviaDELETE /v1/ap-payments/{id}(each payment void reverses its own GL journal entry). - If batch
status = 'voided': HTTP 200 no-op (idempotent). - Otherwise (draft / nacha_generated): the cascade runs atomically inside one transaction. No GL reversal needed because Track I defers GL posting to confirm-sent; draft/generated batches have not posted. The voided batch row is retained (status -> voided, total_cents -> 0) for audit trail.
Body fields are optional; reason is logged to the activity feed and
broadcast on the payment_batch.voided event.
Authorizations
API key issued per entity via Settings > Developers > API Keys.
Each key carries scopes (e.g. orders:read, products:write).
Bearer token format: Authorization: Bearer ark_live_ent_Test keys use ark_test_ent_. Both are issued per entity
via Settings > Developers > API Keys.
Path Parameters
Body
Operator-supplied void reason (audit-logged).
Response
Batch voided (or already voided no-op). Response body:
{ data: { id, status: 'voided', removed_payments: <count> } }.

