# Voiding and correcting

Both are irreversible, so both are done in two calls: a PREVIEW that says what would happen and returns a short-lived `preview_token`, and the action, which the server refuses without that token. Show the preview to a person before acting.

## Voiding an accepted invoice

1. `POST /v1/invoices/{id}/void/preview` (permission `invoices:void`): what would be sent to SUNAT, the last day it can still be voided, and the `preview_token`.

2. `POST /v1/invoices/{id}/void` (permission `invoices:void`; `Idempotency-Key` required) with `reason` and that `preview_token`. It answers `202`: the invoice stays `accepted` until SUNAT confirms, and only then becomes `voided`.

3. `GET /v1/invoices/{id}/void` (permission `invoices:read`): the state of the void, with whether it is final. The events `invoice.voided`, `invoice.void_rejected` and `invoice.void_failed` say how it ended.

It can refuse with: [`invoice_not_voidable`](https://api.invox.page/v1/errors/invoice_not_voidable), [`void_not_available`](https://api.invox.page/v1/errors/void_not_available), [`already_voided`](https://api.invox.page/v1/errors/already_voided), [`void_in_progress`](https://api.invox.page/v1/errors/void_in_progress), [`invoice_externally_reversed`](https://api.invox.page/v1/errors/invoice_externally_reversed), [`invoice_held`](https://api.invox.page/v1/errors/invoice_held), [`void_window_expired`](https://api.invox.page/v1/errors/void_window_expired), [`test_scenario_not_allowed`](https://api.invox.page/v1/errors/test_scenario_not_allowed), [`test_scenario_unknown`](https://api.invox.page/v1/errors/test_scenario_unknown), [`preview_token_required`](https://api.invox.page/v1/errors/preview_token_required), [`preview_token_invalid`](https://api.invox.page/v1/errors/preview_token_invalid).

Past its window an invoice cannot be voided through SUNAT. If it was reversed by hand at the provider, `POST /v1/invoices/{id}/mark_externally_reversed` (permission `invoices:void`; `Idempotency-Key` required) records that (a note, not a fiscal state).

| Why a void did not happen | Meaning | What to do |
| --- | --- | --- |
| `fiscal_rejected` | SUNAT rejected the void. The invoice is still accepted and in force. | Read `sunat` on the void for SUNAT's code and text. A new void can be requested once the cause is fixed. |
| `void_window_expired` | The window to void the invoice passed before the void could be sent (7 days from its issue date). | If it was reversed by hand at the provider, record it with POST /v1/invoices/{id}/mark_externally_reversed. |
| `test_scenario_expired` | A test void waited 24 hours for a manual resolution and was closed. The invoice is still accepted. | Nothing: it was a test. Request another void and resolve it by hand. |

## Correcting an invoice that ended `failed` or `rejected`

1. `GET /v1/invoices/{id}/correction` (permission `invoices:correct`): what would be issued now, in the SAME invoice (its number or the next one, and why), and the `preview_token`.

2. `POST /v1/invoices/{id}/correction` (permission `invoices:correct`; `Idempotency-Key` required) with the `preview_token` and what changes. The same invoice goes back to `processing`; its previous attempt stays in its history.

It can refuse with: [`invoice_not_correctable`](https://api.invox.page/v1/errors/invoice_not_correctable), [`void_in_progress`](https://api.invox.page/v1/errors/void_in_progress), [`invoice_held`](https://api.invox.page/v1/errors/invoice_held), [`test_scenario_not_allowed`](https://api.invox.page/v1/errors/test_scenario_not_allowed), [`test_scenario_unknown`](https://api.invox.page/v1/errors/test_scenario_unknown), [`preview_token_required`](https://api.invox.page/v1/errors/preview_token_required), [`preview_token_invalid`](https://api.invox.page/v1/errors/preview_token_invalid), [`correction_proposal_changed`](https://api.invox.page/v1/errors/correction_proposal_changed), [`issuer_not_configured`](https://api.invox.page/v1/errors/issuer_not_configured).

A token that expired, belongs to another invoice or no longer matches what would be done is refused ([`preview_token_invalid`](https://api.invox.page/v1/errors/preview_token_invalid)): ask for the preview again.
