Ciclo de vida de una factura
Qué pasa después del 202, qué significa cada estado, cada cuánto consultar y cuándo todavía se puede cancelar.
Los campos del cuerpo están en Crear una factura. Esta página es lo que pasa alrededor: cómo revisarla antes de emitirla, qué significan sus estados, cada cuánto preguntar por ella y qué puedes hacer cuando ya salió.
El recorrido completo
Revísala sin emitirla. POST /v1/invoice-validations recibe exactamente el mismo cuerpo que la emisión y te dice si cumple las reglas, sin numerar, firmar ni enviar nada.
curl -s -X POST https://api-sandbox.cufy.co/v1/invoice-validations \
-H "X-API-Key: TU_API_KEY" \
-H "Content-Type: application/json" \
-d @factura.jsonResponde valid: true o valid: false con la lista de errors, campo por campo. No gasta numeración ni cupo diario, así que úsala todo lo que quieras mientras desarrollas.
Guarda lo que vas a enviar antes de enviarlo: los bytes exactos del cuerpo, tu external_reference y la Idempotency-Key. Si la respuesta no llega, eso es lo único que te permite reintentar sin duplicar (idempotencia).
Emítela con POST /v1/invoices. Responde 202 con document_id, operation_id y status_url. Guarda el document_id en tu base de datos junto al pedido.
Espera el resultado. Consulta GET /v1/documents/{id} o, mejor, deja que te avisemos con un webhook y consulta solo cuando llegue el aviso.
Prevalidar no es permiso para emitir
La prevalidación comprueba las reglas fiscales del cuerpo, no el estado de tu empresa ni el de tu rango. Una factura puede prevalidar como valid: true y luego no emitirse porque el rango se agotó, la empresa no está lista o gastaste la cuota del día. Lee siempre el code de la respuesta.
202 no es «aprobada»
202 significa que recibimos y guardamos tu factura. A partir de ahí trabajamos en segundo plano: le asignamos el número, la firmamos, la enviamos a la DIAN y generamos sus archivos. Todo eso tarda segundos, pero no ocurre dentro de tu petición.
La única fuente de verdad es el documento, y tiene dos estados que hay que leer por separado.
processing_status: en qué vamos nosotros
| Valor | Qué significa |
|---|---|
queued | La recibimos. Está en fila. |
processing | La estamos numerando, firmando o enviando. |
retry_scheduled | Algo falló de forma pasajera y lo reintentamos solos. No hagas nada. |
action_required | Se quedó atascada y necesita que intervengas. Lee blocked_reason. |
completed | Terminamos. Ojo: también termina en completed una factura rechazada. |
cancelled | La cancelaste antes de que saliera. |
dian.status: qué dijo la DIAN
| Valor | Qué significa | Qué haces |
|---|---|---|
not_sent | Todavía no ha salido. | Esperar. |
pending | Está en la DIAN, sin respuesta aún. | Esperar. |
validated | La DIAN la aceptó. | Ya tienes factura. Puede traer avisos en dian.warnings. |
rejected | La DIAN la rechazó. | Lee dian.errors, corrige y emite otra. Esta no sirve. |
unknown | No sabemos qué pasó. | No la vuelvas a emitir. Sigue consultando el mismo documento. |
En el sandbox verás casi siempre avisos de la DIAN sobre el emisor (FAJ43b, FAJ44b, RUT01): son del emisor de pruebas compartido, no de tu factura. La tabla de reglas está en Errores.
Consultar el documento
curl -s https://api-sandbox.cufy.co/v1/documents/DOCUMENT_ID \
-H "X-API-Key: TU_API_KEY"Lo que te interesa de la respuesta:
| Campo | Qué es |
|---|---|
processing_status y dian | Los dos estados de arriba. |
fiscal_identity.prefix y fiscal_identity.number | El número que se le asignó, por ejemplo SETP y 990000258. |
signature.cufe | El CUFE. Está aquí, no en fiscal_identity. |
delivery_status | Si se le envió al comprador (Entrega). |
allowed_actions | Qué puedes hacerle ahora mismo: cancel, correct, resume, deliver, download, issue_credit_note, issue_debit_note. |
blocked_reason | Por qué se quedó quieta, cuando processing_status es action_required. |
billing_status | El consumo comercial. En el sandbox siempre not_billable. |
related_note_ids | Las notas crédito y débito que ya la referencian. |
allowed_actions es la forma más cómoda de saber si todavía puedes cancelar o si ya toca emitir una nota: dilo con ese campo en lugar de deducirlo de los estados.
Cada cuánto preguntar
No consultes en un bucle cerrado: tienes 60 peticiones por minuto y las consultas cuentan.
- Espera unos 3 segundos antes de la primera consulta.
- Si sigue en
queuedoprocessing, duplica la espera en cada intento hasta un máximo de un minuto. - Ríndete después de unos minutos y déjalo para un proceso en segundo plano: el documento no se pierde.
- Mejor aún, registra un webhook y consulta solo cuando te avisemos.
Un documento que quedó en unknown no se resuelve consultando más rápido. Se resuelve consultándolo más tarde.
Ver lo que has emitido
GET /v1/documents lista tus documentos, del más reciente al más antiguo, con paginación. Si guardaste tu external_reference, esa es la forma de encontrar una factura sin depender de nuestros identificadores.
El listado recorre una instantánea: lo que emitas después de pedir la primera página no aparece hasta que reinicies el recorrido.
Cancelar
Una factura solo se cancela antes de que empiece a firmarse: dian.status en not_sent y procesamiento sin terminar. Es una ventana de segundos, útil sobre todo cuando te das cuenta del error nada más enviarla.
Consulta el documento y guarda el ETag de la respuesta.
Llama POST /v1/documents/{id}/cancellation con If-Match, Idempotency-Key y el motivo:
{ "reason": "El pedido se anuló antes de enviarse a la DIAN" }Responde con cancelled: true y la versión nueva. Si ya había empezado la firma, responde 409 CANCELLATION_NOT_ALLOWED.
Cancelar antes de numerar devuelve la capacidad al rango. Un número ya asignado no se recicla: ese consecutivo queda gastado.
Después ya no se cancela
Una factura que la DIAN validó no se borra ni se edita. Para deshacerla o rebajarla, emites una nota crédito; para cobrar de más, una nota débito.
Si algo sale mal
| Respuesta | Qué pasó | Qué hacer |
|---|---|---|
422 al emitir lo que había prevalidado | Una regla del ambiente, no del cuerpo. | Lee code. La prevalidación no cubre estas reglas. |
422 NUMBERING_TYPE_MISMATCH | Usaste el rango de otro tipo de documento. | Filtra los rangos por document_type (Empresa). |
409 IDEMPOTENCY_CONFLICT | Repetiste la clave con un cuerpo distinto. | Compara con lo que guardaste. Si es otra factura, usa otra clave y otra referencia. |
429 SANDBOX_DAILY_LIMIT | Gastaste tus 50 facturas del día. | Retry-After te dice cuánto falta para la medianoche de Colombia. |
processing_status: action_required | Se bloqueó antes de llegar a la DIAN. | Lee blocked_reason y ve a Corregir y reanudar. |
No llegó la respuesta del 202 | No sabes si se creó. | Reintenta el mismo cuerpo con la misma clave. Nunca cambies nada (reintentar sin duplicar). |
Los archivos (PDF, XML firmado, respuesta de la DIAN) pueden tardar un poco más que el resultado fiscal: ver Archivos descargables.