factadesarrolladores API 0.1.0-esqueleto

Anular un documento sellado

Anular no es borrar. POST /v1/dte/{codigoGeneracion}/invalidate produce un evento aparte: su propio documento firmado, con su propio código de generación y su propio sello del Ministerio. El documento original no desaparece de ningún registro — cambia de estado, y esta ruta devuelve el sello que lo prueba. No hay forma de deshacer una anulación.

Los tres tipos no son intercambiables

CAT-024 del catálogo oficial define tres motivos, y cada uno exige cosas distintas:

TipoQué esExige
1Error en el documentomotivo y codigoGeneracionReemplazo — emita primero el documento correcto
2Rescisión de la operaciónNinguno de los dos — nombrar un reemplazo aquí es un error
3OtroIgual que el 1: motivo y codigoGeneracionReemplazo
curlTerminal · error con reemplazo
curl -X POST "$FACTA_API_BASE_URL/v1/dte/7875BC7A-9580-441D-94E4-FA455E9D8BD0/invalidate" \
  -H "X-Facta-Key: $FACTA_API_KEY" \
  -H "X-Facta-Sign-Key: $FACTA_SIGN_KEY" \
  -H "Idempotency-Key: anulacion-venta-00417" \
  -H "Content-Type: application/json" \
  -d '{
        "tipoAnulacion": 1,
        "motivo": "El precio unitario iba sin el descuento pactado",
        "codigoGeneracionReemplazo": "9C1E77A2-0B44-4E0F-9E31-0C2A6F4D1B88",
        "responsable": { "nombre": "Ana Rivas", "tipoDocumento": "36", "numDocumento": "06142803901121" },
        "solicita": { "nombre": "Beto Cruz", "tipoDocumento": "13", "numDocumento": "012345678" }
      }'

responsable es quien responde por la anulación ante Hacienda; solicita es quien la pidió — pueden ser la misma persona, pero el esquema los pide siempre por separado.

Qué más hace Facta al anular

Cuando Hacienda acepta el evento, la respuesta trae dos cosas más que no debe ignorar.

archivoEvento es el evento sellado en el formato del Archivo DTE (el evento firmado más firmaElectronica y selloRecibido). Guárdelo en ruta, al lado del documento original, con el nombre <CODIGO-DEL-DOCUMENTO>.invalidacion.json. El JSON y el PDF originales no se tocan nunca. Vale null solo si no se pudo armar.

storage es el recibo de la copia en Almacenamiento Facta, si su empresa lo tiene: { destination, environment, invalidation, operationId }, o null. Es un solo archivo, el evento, y por eso no trae las partes json y pdf de una emisión. Facta lo sube como un archivo de tipo invalidation, guardado bajo el código del documento original. Si la escritura falla, el estado queda en invalidation.state (failed o pending) y la anulación sigue en pie. Más tarde lo ve en GET /v1/storage/copies (una fila con kind: invalidation) y lo descarga con GET /v1/dte/{codigoGeneracion}/file?kind=invalidation&source=managed.

En sus propios destinos el archivo es suyo de guardar, en la ruta que la respuesta indica. Para que Facta lo cuente como copia suya, avise con POST /v1/storage/copies/{codigoGeneracion}/byos y el cuerpo { "kind": "invalidation", "secretId": "…", "jsonPath": "<ruta>" }; no mande pdfPath. El código es el del documento anulado, no el del evento. El aviso solo existe si GET /v1/storage/status anuncia capabilities.byosCopyReport en 2 o más, o invalidation en supportedKinds. Facta lo registra como copia informada, nunca como verificada, y la copia del documento original tiene que haberse informado antes a ese mismo destino.

avisoCliente dice qué pasó con el aviso «Documento invalidado» al cliente: un correo con el PDF marcado «INVALIDADO» y el evento firmado. Sale solo, justo después de que Hacienda acepta la anulación, cuando la empresa tiene activo el envío automático de correos y el documento trae el correo del receptor. Su estado es enviado, en_proceso, fallido o no_enviado, y cuando no se envió, motivo dice por qué (por ejemplo envio_automatico_desactivado o sin_correo). Un aviso que falla nunca deshace la anulación. Si no salió, la empresa puede mandarlo desde Facta con «Enviar aviso al cliente».

Desde ese momento, reenviar el documento desde la app manda los archivos marcados como invalidados, no los originales.

Solo se anula lo que tiene sello

Un documento rechazado nunca existió para Hacienda, así que no hay nada que anular: su correlativo lo reutiliza el documento corregido, no un evento de anulación (§167 de la normativa).

Por qué exige Idempotency-Key sin gastar correlativo

Anular no reserva un número nuevo, pero sí exige Idempotency-Key igual que las rutas que sí gastan uno: un reintento por timeout mandaría un segundo evento de anulación contra un documento que el primero ya anuló, y eso es exactamente el tipo de duplicado que esta cabecera existe para prevenir en cualquier operación irreversible, gaste o no un correlativo.

Anular algo que ya estaba anulado

No es un error. La respuesta es 200 con yaEstabaInvalidado: true, y no se manda ningún evento nuevo al Ministerio. Un reintento que en cambio recibiera un error enseñaría a hacer algo peor: forzar una segunda anulación sobre un documento que ya la tiene.

Otras guías

Escriba para buscar Por ejemplo: idempotencia, contingencia, 422, emitir.

↑ ↓ moverse Enter abrir Esc cerrar