Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions website/docs/guides/invoices/async-invoices.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ Cuando el tamaño de tus facturas es muy grande, o necesitas facturar muchas fac
y recibir una respuesta inmediata con el objeto Invoice y su ID, pero sin timbrar todavía. El timbrado de la factura se realiza en segundo plano, y puedes consultar el estado de la factura en cualquier momento
por medio del método de obtener factura.

## Recuperación automática tras un error temporal

Al crear una factura de forma síncrona, el SAT o el proveedor de certificación puede devolver un error temporal después de haber timbrado el CFDI. Cuando Facturapi detecta uno de estos casos, conserva la factura con `status: "pending"` y responde con el código HTTP `202 Accepted` en lugar de devolver un error definitivo. Esta respuesta significa que la solicitud fue aceptada, pero su procesamiento todavía no ha terminado.

Facturapi intentará recuperar el CFDI automáticamente hasta 5 veces, con un intervalo de 10 minutos entre intentos. Los intentos ocurren aproximadamente 10, 20, 30, 40 y 50 minutos después de la respuesta inicial.

- Si Facturapi recupera el CFDI, la factura cambia a `status: "valid"`, se guardan su UUID y timbre, y se envía el webhook `invoice.status_updated`.
- Si no es posible recuperarlo después del quinto intento, la factura cambia a `status: "failed"` y no se realizan más intentos.

Mientras la factura permanezca en `pending`, no vuelvas a crearla ni asignes su folio a otro comprobante. Puedes consultar su estado mediante el método de obtener factura o suscribirte al webhook `invoice.status_updated`.

## Crear una factura asíncrona

Una factura se creará de manera asíncrona en cualquiera de estos 2 casos:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ When the size of your invoices is very large, or you need to invoice many invoic
and receive an immediate response with the Invoice object and its ID, but without stamping yet. The stamping of the invoice is done in the background, and you can check the status of the invoice at any time
using the get invoice method.

## Automatic recovery after a temporary error

When creating an invoice synchronously, the SAT or the certification provider may return a temporary error after stamping the CFDI. When Facturapi detects one of these cases, it preserves the invoice with `status: "pending"` and responds with HTTP `202 Accepted` instead of returning a definitive error. This response means that the request was accepted, but processing has not finished yet.

Facturapi will automatically try to recover the CFDI up to 5 times, with a 10-minute interval between attempts. The attempts occur approximately 10, 20, 30, 40, and 50 minutes after the initial response.

- If Facturapi recovers the CFDI, the invoice changes to `status: "valid"`, its UUID and stamp are saved, and the `invoice.status_updated` webhook is sent.
- If it cannot be recovered after the fifth attempt, the invoice changes to `status: "failed"` and no further attempts are made.

While the invoice remains `pending`, do not create it again or assign its folio to another document. You can check its state using the get invoice method or subscribe to the `invoice.status_updated` webhook.

## Create an asynchronous invoice

An invoice will be created asynchronously in either of these 2 cases:
Expand Down
41 changes: 39 additions & 2 deletions website/openapi_v2.en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3031,6 +3031,27 @@ paths:
summary: Create invoice (CFDI 4.0)
description: |
Creates a new Invoice. If the invoice is created in the Live environment, it will be **stamped and sent to the SAT**.

### Recovery from intermittent stamping errors

If PADE returns a recognized intermittent error during stamping, Facturapi preserves the invoice with `status: "pending"`, no UUID, and its original CFD signature. The folio remains reserved for six hours, and the API responds with `202 Accepted`. This means that the request was accepted, but processing has not finished. Validation errors and other errors that are not eligible for recovery are returned normally.

Facturapi saves a snapshot of the data needed to validate a recovery: issuer and receiver RFCs, series, folio, total, currency, CFDI type, signature, and issuance date. Ten minutes later, it asks PADE for CFDIs matching the issuer RFC, series, and folio, then validates every result against the snapshot to prevent associating the wrong CFDI.

Facturapi makes up to five recovery attempts, one every 10 minutes. The original request at minute 0 does not count as an attempt:

| Time | Action |
| --- | --- |
| Minute 0 | The original stamping request fails intermittently; the API returns `202` |
| Minute 10 | Recovery attempt 1 |
| Minute 20 | Recovery attempt 2 |
| Minute 30 | Recovery attempt 3 |
| Minute 40 | Recovery attempt 4 |
| Minute 50 | Recovery attempt 5 |

If a valid CFDI is recovered, Facturapi stores the UUID, stamp, and XML; marks the invoice as `valid`; consumes the reserved folio; records usage and accounting relationships; marks the recovery task as succeeded; emits the invoice status update event; and stops retrying.

If the fifth attempt does not recover a valid CFDI, Facturapi marks the recovery task and the invoice as `failed`, releases the reserved folio, and stops retrying. The `202` status code only communicates that the recovery task will continue in the background; the status code itself does not initiate retries.
Comment thread
raul-facturapi marked this conversation as resolved.
x-codeSamples:
- lang: Bash
label: cURL
Expand Down Expand Up @@ -3196,6 +3217,21 @@ paths:
mapping:
pending: "#/components/schemas/Invoice"
draft: "#/components/schemas/InvoiceDraft"
"202":
description: Request accepted; Facturapi will try to recover the CFDI up to five times, once every 10 minutes
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Invoice"
- type: object
required:
- status
properties:
status:
type: string
enum: [pending]
example: pending
"400":
$ref: "#/components/responses/BadRequest"
"401":
Expand Down Expand Up @@ -10826,7 +10862,7 @@ x-webhooks:
description: |
Notifies about changes in the `status` field of an invoice.

Used in the case of having created the invoice asynchronously (`async` param).
Used when an invoice is created asynchronously (`async` param) or when a recovery task for an intermittent stamping error changes its status.
tags:
- events
requestBody:
Expand Down Expand Up @@ -14614,8 +14650,9 @@ components:
- valid
- canceled
- draft
- failed
description: |
Current status of the invoice.
Current status of the invoice. `failed` means that background stamping or automatic CFDI recovery ended unsuccessfully.
example: valid
cancellation_status:
type: string
Expand Down
41 changes: 39 additions & 2 deletions website/openapi_v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3257,6 +3257,27 @@ paths:
summary: Crear factura (CFDI 4.0)
description: |
Crea una nueva Factura. Si la factura es creada en ambiente Live, ésta será **timbrada y enviada al SAT**.

### Recuperación ante intermitencias de timbrado

Si PADE devuelve un error intermitente reconocido durante el timbrado, Facturapi conserva la factura con `status: "pending"`, sin UUID y con su sello CFD original. El folio queda reservado durante seis horas y la API responde con `202 Accepted`. Esto significa que la solicitud fue aceptada, pero su procesamiento todavía no termina. Los errores de validación y demás errores que no sean elegibles para recuperación se devuelven de forma normal.

Facturapi guarda una copia de los datos necesarios para validar la recuperación: RFC del emisor y receptor, serie, folio, total, moneda, tipo de CFDI, sello y fecha de emisión. Diez minutos después consulta a PADE por los CFDI que coincidan con el RFC del emisor, la serie y el folio, y valida cada resultado contra esos datos para evitar asociar un CFDI incorrecto.

Se realizan hasta cinco intentos de recuperación, uno cada 10 minutos. La solicitud original del minuto 0 no cuenta como intento:

| Tiempo | Acción |
| --- | --- |
| Minuto 0 | El timbrado original falla por una intermitencia; la API responde `202` |
| Minuto 10 | Intento de recuperación 1 |
| Minuto 20 | Intento de recuperación 2 |
| Minuto 30 | Intento de recuperación 3 |
| Minuto 40 | Intento de recuperación 4 |
| Minuto 50 | Intento de recuperación 5 |

Si se recupera un CFDI válido, Facturapi guarda el UUID, el timbre y el XML; marca la factura como `valid`; consume el folio reservado; registra el uso y las relaciones contables; marca la tarea de recuperación como exitosa; emite el evento de actualización de estado de la factura; y detiene los reintentos.

Si el quinto intento no recupera un CFDI válido, Facturapi marca la tarea de recuperación y la factura como `failed`, libera el folio reservado y deja de reintentar. El código `202` solo comunica que la tarea de recuperación continuará en segundo plano; el código de estado no es lo que inicia los reintentos.
Comment thread
raul-facturapi marked this conversation as resolved.
x-codeSamples:
- lang: Bash
label: cURL
Expand Down Expand Up @@ -3422,6 +3443,21 @@ paths:
mapping:
pending: "#/components/schemas/Invoice"
draft: "#/components/schemas/InvoiceDraft"
"202":
description: Solicitud aceptada; Facturapi intentará recuperar el CFDI hasta cinco veces, una cada 10 minutos
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Invoice"
- type: object
required:
- status
properties:
status:
type: string
enum: [pending]
example: pending
"400":
$ref: "#/components/responses/BadRequest"
"401":
Expand Down Expand Up @@ -11032,7 +11068,7 @@ x-webhooks:
description: |
Notifica acerca del cambio del campo `status` de una factura.

Se utiliza en el caso de haber creado la factura de manera asíncrona.
Se utiliza cuando la factura se crea de manera asíncrona o cuando una tarea de recuperación por intermitencia de timbrado cambia su estado.
tags:
- events
requestBody:
Expand Down Expand Up @@ -14838,8 +14874,9 @@ components:
- valid
- canceled
- draft
- failed
description: |
Estado actual de la factura.
Estado actual de la factura. `failed` indica que el timbrado en segundo plano o la recuperación automática del CFDI terminó sin éxito.
example: valid
cancellation_status:
type: string
Expand Down