> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gigstack.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Cancel a CFDI and verify the result

> Submit a Mexican cancellation once, then distinguish a pending request from a canceled invoice.

**Case:** an issued Mexican income invoice contains an error and will not be replaced.
The chosen motive is `02`. Use the motive that actually describes your case; motive `01`
requires the replacement invoice's `substitution_uuid`.

<Note>
  This walkthrough was reviewed against the Mexican cancellation handler, status mapper,
  and scheduler. It has not been executed as part of the recipe test run. It applies to
  Mexican CFDIs. Colombian annulment creates a credit note and may leave the original
  invoice valid; do not apply these completion checks to Colombia.
</Note>

## What a test cancellation proves

For built-in Mexican test RFCs, the Prodigia integration simulates an accepted
cancellation locally. A response with `cancellation_type: "test"` and
`cancellation_status: "accepted"` can therefore arrive without a cancellation request
reaching the provider. Other Prodigia test cancellations request its accepted test
scenario. Neither verifies SAT acceptance or a live recipient’s approval.

Use test mode to verify your request, response handling and persisted invoice status.
Confirm live fiscal completion using the business’s required cancellation evidence.

## 1. Read the invoice before requesting cancellation

Use Bash with `curl` and `jq`; set the environment variables with the
[quickstart commands](/quickstart#1-set-your-environment) and check the
[environment/key distinction](/authentication#environments). `INVOICE_UUID` must identify the intended invoice
issued by your selected team. For practice, use an authorized synthetic test invoice
and a matching test key.

```bash theme={null}
set -euo pipefail
: "${GIGSTACK_BASE_URL:?Set the correct API base URL}"
: "${GIGSTACK_API_KEY:?Set the matching key locally}"
: "${INVOICE_UUID:?Set the intended issued invoice UUID}"

curl --fail-with-body "$GIGSTACK_BASE_URL/invoices/income/$INVOICE_UUID" \
  -H "Authorization: Bearer $GIGSTACK_API_KEY" > before-cancellation.json

jq '{uuid: .data.uuid, status: .data.status, livemode: .data.livemode,
     client: .data.client.id, total: .data.total, cancellation: .data.cancellation}' \
  before-cancellation.json
```

Verify the business, recipient, amount, mode and reason with the person authorizing
cancellation. An unissued draft is [deleted separately](/reference/deleteInvoicesDraftById).
If the invoice is already `canceled`, reconcile that result instead of submitting again.

## 2. Submit the authorized cancellation once

```bash theme={null}
curl --fail-with-body -X DELETE \
  "$GIGSTACK_BASE_URL/invoices/$INVOICE_UUID" \
  -H "Authorization: Bearer $GIGSTACK_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"motive":"02"}' > cancellation-request.json

jq . cancellation-request.json
```

The source-defined success response is HTTP `200` with a **top-level**
`cancellation_status`, plus provider fields. It is not wrapped in `data`.
Provider acceptance of the request does not by itself prove the stored invoice is canceled:
the handler can keep a live invoice `valid` and mark the request pending when the
subsequent status check disagrees. Always read the invoice again.

The Mexican status mapper uses these cancellation labels:

| Label | How to handle it |
| - | - |
| `accepted` | Read the invoice and confirm persisted `data.status: "canceled"` |
| `pending` | Preserve the request; wait for later status reconciliation |
| `rejected` | Read the provider reason and resolve it before deciding on another request |
| `expired` | Read the persisted invoice; the scheduler can mark it canceled for this provider result |
| `none` or missing | No usable completion evidence; inspect the invoice and escalate if ambiguous |

A background reconciliation failure may also be stored as `failed`. The public invoice
response does not reliably expose every internal cancellation field: the nested
`data.cancellation.cancellation_status` can be absent, and receipt fields can be empty.
Do not use an absent field as proof of cancellation or rejection.

## 3. Verify the persisted state with bounded reads

This loop performs at most three reads, 30 seconds apart. It never resubmits cancellation.
A short polling window is a convenience for your integration, not a completion guarantee.

```bash theme={null}
CANCELLATION_COMPLETE=false
for attempt in 1 2 3; do
  curl --fail-with-body "$GIGSTACK_BASE_URL/invoices/income/$INVOICE_UUID" \
    -H "Authorization: Bearer $GIGSTACK_API_KEY" > after-cancellation.json || break
  jq '{uuid: .data.uuid, status: .data.status, cancellation: .data.cancellation}' \
    after-cancellation.json
  if jq -e '.data.status == "canceled"' after-cancellation.json >/dev/null; then
    CANCELLATION_COMPLETE=true
    break
  fi
  if [ "$attempt" -lt 3 ]; then sleep 30; fi
 done

if [ "$CANCELLATION_COMPLETE" = true ]; then
  echo 'The Mexican invoice is persisted as canceled.'
else
  echo 'Cancellation is not confirmed. Preserve the request and reconcile later.'
fi
```

Pending live cancellations are checked by the current backend scheduler at 00:00, 08:00
and 16:00 in `America/Mexico_City`; provider acceptance may take longer. Test documents
are not included in that live-only scheduled sweep. Do not poll rapidly expecting a
new SAT query on every GET: reading the invoice returns stored state.

For a pending or ambiguous outcome, save the UUID, team, request time and redacted provider
response. Arrange a later read, or ask [support](mailto:support@gigstack.io) to check the
provider state and cancellation receipt. Do not report completed until persisted state
and the business's required fiscal evidence agree.

## When the request fails or times out

| Result | Next step |
| - | - |
| `403` mode mismatch | Use the key matching the original invoice's mode; do not recreate the invoice |
| `422` imported external invoice | Cancel through the original stamping provider |
| `400` fiscal rejection | Inspect the provider code/reason and the selected motive or dependencies |
| `412` issuer connection error | Have the owner repair the issuing connection before another attempt |
| Timeout or `500` / `503` | Read and reconcile the original invoice first; do not blindly repeat DELETE |

This endpoint does not document an idempotency key. Cancellation does not refund a
payment. Use the [refund walkthrough](/recipes/refund) for the separate payment action.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.