Notas crédito y débito
Una factura validada no se edita ni se borra
Sección titulada «Una factura validada no se edita ni se borra»No existe PUT /v1/invoices/:id, y tampoco un DELETE. No es un hueco del
API: un documento que la DIAN validó ya está en su poder y no se deshace
desde aquí. Lo que se hizo mal se corrige emitiendo otro documento que
apunta al primero.
| Lo que necesitas | Emites | Ruta | type |
operation_type |
|---|---|---|---|---|
| Anular la factura completa, devolver mercancía, rebajar, descontar | Nota crédito | /v1/credit-notes |
91 |
20 |
| Cobrar de más: intereses, gastos por cobrar, un valor que subió | Nota débito | /v1/debit-notes |
92 |
30 |
La regla mental es corta: la crédito baja, la débito sube.
Una nota es un documento electrónico completo
Sección titulada «Una nota es un documento electrónico completo»No es una marca sobre la factura. Pasa por exactamente lo mismo que ella:
- Consume su propio rango de numeración (
CREDIT_NOTEoDEBIT_NOTE), y con él un consecutivo que ya no vuelve. - Ese rango no lleva resolución ni vigencia: el consecutivo de una nota es del emisor, no de un acto de la DIAN. Ver rangos de numeración.
- Se numera, se arma en UBL, se firma y se transmite en segundo plano.
- Gasta cupo del plan, igual que una factura.
- Tiene su propio código único. Es un CUDE, no un CUFE — aunque el
campo de la respuesta se siga llamando
cufe. - Necesita su propio
reference_code. Reutilizar el de la factura da un409(ver Errores).
Nota crédito
Sección titulada «Nota crédito»POST/v1/credit-notes
Nota débito
Sección titulada «Nota débito»POST/v1/debit-notes
Las dos reciben el mismo cuerpo y devuelven la misma forma. Lo
único que cambia entre ellas es la lista de valores válidos de
discrepancy_code.
Es el cuerpo de la factura más dos campos obligatorios. Nada se quita.
Los dos campos que solo tienen las notas
Sección titulada «Los dos campos que solo tienen las notas»| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
related_document_id |
string (UUID) | Sí | El documento que se corrige. Es el id de esta API, no su full_number ni su CUFE. |
discrepancy_code |
string | Sí | Concepto de corrección. Las listas de crédito y débito son distintas: ver abajo. |
discrepancy_description |
string | No | Se acepta, pero no se usa. La descripción sale siempre del catálogo. |
Los campos que hereda de la factura
Sección titulada «Los campos que hereda de la factura»| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
reference_code |
string | Sí | Tu identificador, máximo 100 caracteres. Es la clave de idempotencia. |
numbering_range_id |
string (UUID) | Sí | Rango de la nota, no el de la factura. |
customer |
objeto | Sí | El adquirente. Mismos campos que en la factura. |
items |
array | Sí | Al menos una línea. Es lo que se está acreditando o cobrando. |
payment |
objeto | No | Por defecto { "form_code": "1", "means_code": "10" } — contado, efectivo. |
notes |
string | No | Observación libre. Va al UBL y a la representación gráfica. |
send_email |
boolean | No | Por defecto true: la nota le sale por correo al adquirente. |
El detalle campo por campo de customer, items y payment está en
Facturas; aquí se comportan idéntico.
En qué se diferencia del cuerpo de la factura
Sección titulada «En qué se diferencia del cuerpo de la factura»El rango es otro. Cada tipo de documento consume su propia serie y la
DIAN no deja mezclarlas. Mandar el numbering_range_id de facturas a
/v1/credit-notes responde 422:
El rango 6f0a9c2e-4b1d-4a77-9f3e-2c8d5b71e004 es de tipo INVOICE, no aplica para este documentooperation_type no se envía. Lo pone el servidor: 20 en la crédito,
30 en la débito. Aparece en la respuesta, no en la petición.
Los montos siguen siendo del servidor. No hay campo de totales, ni
aquí ni en la factura, y un campo de más es un 400, no un aviso. La nota
vale lo que sumen sus propias líneas, con las mismas fórmulas.
Cómo se referencia el documento afectado
Sección titulada «Cómo se referencia el documento afectado»related_document_id es el UUID interno que devolvió la creación de la
factura en el campo id, el mismo que usas en GET /v1/documents/:id. No
es full_number (SETP990000841) ni el CUFE.
Tres condiciones, y las tres las revisa el API antes de crear nada:
- El documento existe y es de tu emisor. Si no:
422related_document_id no existe. - Está en
VALIDATED. Si no:422Solo se pueden emitir notas sobre documentos VALIDATED. - El
discrepancy_codeestá en la lista de esa nota.
En la respuesta de la nota, related_document_id vuelve poblado. Es el
único tipo de documento en el que ese campo no es null.
discrepancy_code
Sección titulada «discrepancy_code»El código dice por qué se corrige, y de él sale la descripción que viaja en el XML.
Nota crédito
Sección titulada «Nota crédito»| Código | Concepto |
|---|---|
1 |
Devolución parcial de los bienes y/o no aceptación parcial del servicio |
2 |
Anulación de factura electrónica |
3 |
Rebaja o descuento parcial o total |
4 |
Ajuste de precio |
5 |
Descuento comercial por pronto pago |
6 |
Descuento comercial por volumen de ventas |
Nota débito
Sección titulada «Nota débito»| Código | Concepto |
|---|---|
1 |
Intereses |
2 |
Gastos por cobrar |
3 |
Cambio del valor |
4 |
Otros |
Un código fuera de la lista responde 422 y te dice cuáles valen:
{ "status": 422, "code": "VALIDATION", "message": "discrepancy_code 9 no existe. Válidos: 1, 2, 3, 4, 5, 6", "errors": []}Por qué discrepancy_description se ignora
Sección titulada «Por qué discrepancy_description se ignora»La regla CBF04 de la DIAN compara la descripción con su lista oficial y rechaza cualquier texto que no esté ahí. Con el texto libre del cliente se caían todas las notas. Por eso el API arma la descripción a partir del código y copia la cadena literal de la tabla oficial, espacios incluidos. El campo se sigue aceptando, pero no llega a ninguna parte.
Ejemplo — nota crédito que anula una factura
Sección titulada «Ejemplo — nota crédito que anula una factura»Anula por completo la factura SETP990000841, cuyo id es
b1f3a7d0-9c42-4e18-8a5b-6d0e2f4c9a11. Las líneas son las mismas de la
factura, así que la nota vale exactamente lo mismo.
curl -X POST https://sandbox.facturacion.misofk.com/v1/credit-notes \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "reference_code": "NC-2026-000129", "numbering_range_id": "8d5f2c60-71ab-4e09-b3c7-4f9e1d802a75", "related_document_id": "b1f3a7d0-9c42-4e18-8a5b-6d0e2f4c9a11", "discrepancy_code": "2", "customer": { "identification_type": "31", "identification": "901234567", "dv": "8", "person_type": "1", "name": "Distribuidora El Poblado S.A.S.", "tax_regime": "48", "responsibilities": ["O-15"], "email": "facturacion@elpoblado.com.co", "address": "Carrera 43A 5A-113 Oficina 502", "municipality_code": "05001" }, "items": [ { "code": "SKU-001", "name": "Camiseta algodón talla M", "quantity": 2, "unit_measure": "94", "price": 45000, "discount_rate": 10, "taxes": [{ "code": "01", "rate": 19 }] }, { "code": "SKU-045", "name": "Gorra bordada", "quantity": 1, "unit_measure": "94", "price": 32000, "taxes": [{ "code": "01", "rate": 19 }] } ], "payment": { "form_code": "1", "means_code": "45" }, "notes": "Anulación total de SETP990000841 por devolución completa del pedido 4471." }'La cuenta que hace el servidor:
línea 1 2 × 45.000 = 90.000 −10% → 81.000,00 IVA 19% 15.390,00línea 2 1 × 32.000 → 32.000,00 IVA 19% 6.080,00───────────────────────────────────────────────────────────────────────subtotal 113.000,00IVA 21.470,00total 134.470,00Respuesta 201, recortada:
{ "id": "7c2e91a4-0f38-4b6d-a15c-3e8b0d6f2419", "type": "91", "reference_code": "NC-2026-000129", "full_number": null, "prefix": "NC", "number": null, "status": "QUEUED", "cufe": null, "operation_type": "20", "currency": "COP", "related_document_id": "b1f3a7d0-9c42-4e18-8a5b-6d0e2f4c9a11", "totals": { "lineExtensionAmount": "113000.00", "taxExclusiveAmount": "113000.00", "taxInclusiveAmount": "134470.00", "allowanceTotalAmount": "0.00", "payableAmount": "134470.00", "taxTotals": { "01": { "taxableAmount": "113000.00", "taxAmount": "21470.00" } } }}La respuesta completa tiene la misma forma que la de una factura
—customer, items recalculados, payment, dian_status_code…— con
type: "91", operation_type: "20" y el related_document_id poblado.
Ejemplo — nota débito por un cobro que faltó
Sección titulada «Ejemplo — nota débito por un cobro que faltó»El flete no entró en la factura. Se cobra aparte, contra el mismo documento.
curl -X POST https://sandbox.facturacion.misofk.com/v1/debit-notes \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "reference_code": "ND-2026-000034", "numbering_range_id": "e21b4f09-8c73-4a55-b0d6-9f14c7e35a80", "related_document_id": "b1f3a7d0-9c42-4e18-8a5b-6d0e2f4c9a11", "discrepancy_code": "2", "customer": { "identification_type": "31", "identification": "901234567", "dv": "8", "person_type": "1", "name": "Distribuidora El Poblado S.A.S.", "tax_regime": "48", "responsibilities": ["O-15"], "email": "facturacion@elpoblado.com.co", "address": "Carrera 43A 5A-113 Oficina 502", "municipality_code": "05001" }, "items": [ { "code": "FLETE-ITG", "name": "Flete a bodega Itagüí no incluido en SETP990000841", "quantity": 1, "unit_measure": "94", "price": 60000, "taxes": [{ "code": "01", "rate": 19 }] } ], "payment": { "form_code": "2", "means_code": "45", "due_date": "2026-10-04" }, "notes": "Cobro de transporte pactado por fuera del pedido 4471." }'Aquí discrepancy_code: "2" es Gastos por cobrar, no «Anulación»: es
la tabla de la nota débito.
línea 1 × 60.000 → 60.000,00 IVA 19% 11.400,00─────────────────────────────────────────────────────────total 71.400,00Respuesta 201, recortada:
{ "id": "0a4d7b13-56ce-42f7-9c80-b8e5f1a20d67", "type": "92", "reference_code": "ND-2026-000034", "status": "QUEUED", "operation_type": "30", "due_date": "2026-10-04", "related_document_id": "b1f3a7d0-9c42-4e18-8a5b-6d0e2f4c9a11", "totals": { "lineExtensionAmount": "60000.00", "taxExclusiveAmount": "60000.00", "taxInclusiveAmount": "71400.00", "allowanceTotalAmount": "0.00", "payableAmount": "71400.00", "taxTotals": { "01": { "taxableAmount": "60000.00", "taxAmount": "11400.00" } } }}Errores
Sección titulada «Errores»Los propios de las notas:
| HTTP | code |
Mensaje |
|---|---|---|
422 |
VALIDATION |
related_document_id no existe |
422 |
VALIDATION |
Solo se pueden emitir notas sobre documentos VALIDATED |
422 |
VALIDATION |
discrepancy_code {valor} no existe. Válidos: {lista} |
422 |
VALIDATION |
El rango {uuid} es de tipo {tipo}, no aplica para este documento |
404 |
NOT_FOUND |
Rango de numeración no encontrado |
409 |
IDEMPOTENCY_CONFLICT |
reference_code {valor} ya existe con otro tipo de documento |
Los demás son los de cualquier emisión —401, 403, el 400 de
validación y los de plan— y están en Errores.
Y ojo con el otro lado de la idempotencia: repetir un reference_code del
mismo tipo no falla — devuelve 201 con la nota que ya existía y
descarta en silencio el cuerpo nuevo. Si te equivocaste de
discrepancy_code, cambiarlo y reenviar con el mismo reference_code no
corrige nada.
Después de emitirla
Sección titulada «Después de emitirla»El 201 solo dice que se aceptó. El número, el CUDE, el XML y el PDF
llegan después, igual que en una factura:
- Escucha
document.validatedodocument.rejecteden tus webhooks. - O consulta
GET /v1/documents/{id}hasta verstatus: "VALIDATED". - Los archivos salen por
GET /v1/documents/{id}/xmlyGET /v1/documents/{id}/pdf.
Para listar solo notas, filtra el listado por tipo:
curl "https://sandbox.facturacion.misofk.com/v1/documents?type=91&status=VALIDATED" \ -H "Authorization: Bearer $TOKEN"