Nota de Crédito (05)
La Nota de Crédito (tipo 05) se emite para ajustar un CCF (tipo 03) ya emitido. Se usa típicamente para:
- Devoluciones de mercadería.
- Correcciones de precio o cantidad posteriores a la emisión.
- Anulación de un CCF que ya salió de la ventana de 3 meses donde la invalidación directa todavía es posible.
- Descuentos aplicados después de la facturación.
La NC no aplica a Facturas (01), Exportaciones (11), ni SUEX (14). Para esos tipos se usa Anulación directa.
/credit-memo vs /invalidate — cuándo usar cada uno
Cuando necesitas revertir un CCF tienes dos caminos:
| Situación | Endpoint | Qué hace |
|---|---|---|
| Ajuste parcial (p.ej. devolución de 2 items de 10) | POST /credit-memo con lines | Genera NC parcial, deja el CCF vigente. |
| Ajuste total intencional | POST /credit-memo con full_adjustment: true | Genera NC completa, marca el CCF como invalidado. |
| Anular CCF completo con menos de 3 meses | POST /invalidate/{id} | Anulación directa en MH. Más simple. |
| Anular CCF completo con más de 3 meses | POST /invalidate/{id} | El API crea automáticamente una NC porque el MH ya no permite anulación directa. |
Si solo quieres anular un CCF completo, usa /invalidate — el API decide solo si hacer anulación directa o generar una NC automática. Usa /credit-memo cuando necesitas control explícito sobre las líneas ajustadas.
Detalles técnicos
| Método | POST |
| URL | https://ocote.io/api/connect/credit-memo |
| Content-Type | application/json |
| Autenticación | Header Authorization: Bearer odt_... |
Request
Campos
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
document_id | string | Sí | UUID o external_ref del CCF a ajustar. |
full_adjustment | bool | No (default false) | Si true, genera NC completa (clona todas las líneas del CCF) e invalida el CCF. |
lines | array | No | Líneas específicas a ajustar. Solo válido con full_adjustment: false. Si no envías líneas y full_adjustment: false, el API clona todas las del CCF pero no invalida. |
external_ref | string | No | Tu identificador idempotente para la NC. |
Líneas (lines)
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
description | string | Sí | Descripción del ítem ajustado (típicamente igual al del CCF original). |
quantity | float | Sí | Cantidad a ajustar. |
unit_price | float | Sí | Precio unitario con IVA incluido. |
discount_percentage | float | No (default 0) | Descuento. |
Los tres modos de emisión
Modo 1: Ajuste completo explícito (full_adjustment: true)
El API copia todas las líneas del CCF original a la NC y marca el CCF como invalidado. Equivale a una anulación pero genera un documento fiscal explícito de ajuste.
{
"document_id": "CCF-ENLACES-001",
"full_adjustment": true,
"external_ref": "NC-DEV-TOTAL-001"
}
Úsalo cuando quieres un documento de anulación formal con todas las líneas visibles.
Modo 2: Ajuste parcial (full_adjustment: false + lines)
Solo ajustas lo que especificas en lines. El CCF queda vigente pero con monto ajustado.
{
"document_id": "CCF-ENLACES-001",
"full_adjustment": false,
"lines": [
{
"description": "Laptop Dell XPS devuelta",
"quantity": 1,
"unit_price": 1200.00
}
],
"external_ref": "NC-DEV-PARCIAL-001"
}
Úsalo para devoluciones reales (regresó 1 item de 10) o descuentos post-facturación.
Modo 3: Clonación sin invalidar (sin lines, full_adjustment: false)
El API clona todas las líneas del CCF pero no lo invalida. Es útil para "anotar" un ajuste completo sin anular fiscalmente el CCF.
{
"document_id": "CCF-ENLACES-001",
"external_ref": "NC-AJUSTE-001"
}
Es el modo menos común. Si necesitas anular completamente, prefiere Modo 1 o el endpoint /invalidate.
Validaciones
El API valida sobre el CCF antes de procesar:
| Validación | HTTP | Mensaje |
|---|---|---|
| CCF no existe o no es tuyo | 404 | Documento no encontrado |
| Documento referenciado no es CCF | 400 | Solo se pueden emitir Notas de Credito sobre CCF (tipo 03) |
| CCF ya invalidado | 400 | Este CCF ya fue invalidado |
| CCF ya completamente ajustado | 400 | Este CCF ya fue completamente ajustado |
| CCF con más de 90 días | 400 | Este CCF tiene mas de 90 dias y no puede ser ajustado |
La NC directa vía /credit-memo solo aplica mientras el CCF tiene menos de 90 días. Después de eso, el API rechaza el ajuste y debes usar otros mecanismos contables. Si tu intención era simplemente anular un CCF antiguo completo, usa POST /invalidate/{ccf_id} — ese endpoint sí maneja automáticamente el caso de fuera de ventana generando la NC equivalente.
Response
Además de los flags estándar (Respuestas y estados), la NC devuelve campos específicos:
| Campo | Tipo | Descripción |
|---|---|---|
document_id | string (UUID) | ID de la NC creada. |
external_ref | string | El que enviaste (o autogenerado). |
control_number | string | Correlativo de la NC (DTE-05-...). |
generation_code | string | Código de generación MH. |
reception_stamp | string | Sello MH de la NC. |
ccf_control_number | string | Correlativo del CCF ajustado. |
ccf_adjusted_amount | float | Monto total ajustado (suma de líneas de la NC). |
ccf_fully_adjusted | bool | true si el CCF quedó completamente ajustado tras esta NC. |
dte | object | Schema firmado completo de la NC (solo si dte_success: true). |
Ejemplo: devolución parcial
Escenario: emitiste un CCF por tres productos ($500, $300, $200 = $1 000 gravable). El cliente devuelve solo el producto de $300. Emites NC parcial:
curl -X POST https://ocote.io/api/connect/credit-memo \
-H "Authorization: Bearer odt_xxx" \
-H "Content-Type: application/json" \
-d '{
"document_id": "CCF-ENLACES-TEST-001",
"external_ref": "NC-ENLACES-TEST-001",
"full_adjustment": false,
"lines": [
{
"description": "Silla ergonómica de oficina",
"quantity": 1,
"unit_price": 339.00
}
]
}'
const { data } = await axios.post(
'https://ocote.io/api/connect/credit-memo',
{
document_id: 'CCF-ENLACES-TEST-001',
external_ref: 'NC-ENLACES-TEST-001',
full_adjustment: false,
lines: [
{ description: 'Silla ergonómica de oficina',
quantity: 1, unit_price: 339.00 },
],
},
{ headers: { Authorization: `Bearer ${process.env.OCOTE_API_KEY}` } }
);
r = requests.post(
"https://ocote.io/api/connect/credit-memo",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"document_id": "CCF-ENLACES-TEST-001",
"external_ref": "NC-ENLACES-TEST-001",
"full_adjustment": False,
"lines": [
{
"description": "Silla ergonómica de oficina",
"quantity": 1,
"unit_price": 339.00,
},
],
},
)
Respuesta exitosa
{
"success": true,
"dte_success": true,
"contingency": false,
"rejected": false,
"posted": true,
"is_duplicate": false,
"document_id": "63e82935-9d98-43db-b2b8-bc669364885c",
"external_ref": "NC-ENLACES-TEST-001",
"control_number": "DTE-05-M001P002-000000000000001",
"generation_code": "63E82935-9D98-43DB-B2B8-BC669364885C",
"reception_stamp": "20260421145823...",
"ccf_control_number": "DTE-03-M001P002-000000000000008",
"ccf_adjusted_amount": 339.00,
"ccf_fully_adjusted": false,
"observaciones": [],
"dte_state": "",
"dte_message": ""
}
Observa:
ccf_control_numberapunta al CCF ajustado — útil para tu conciliación.ccf_adjusted_amount: 339.00es el monto que esta NC descuenta del CCF.ccf_fully_adjusted: falseindica que el CCF aún tiene monto vigente — podrías emitir más NC si siguen llegando devoluciones, siempre dentro de los 90 días.
Ajuste total
Para anular un CCF completo vía NC (dentro de ventana):
curl -X POST https://ocote.io/api/connect/credit-memo \
-H "Authorization: Bearer odt_xxx" \
-H "Content-Type: application/json" \
-d '{
"document_id": "CCF-ENLACES-TEST-002",
"full_adjustment": true,
"external_ref": "NC-ANULA-CCF-002"
}'
Este modo es equivalente a lo que hace POST /invalidate/{id} internamente cuando el CCF está fuera de ventana. La diferencia es que aquí tú tomas la decisión explícita.
Idempotencia y retry
Aplican las mismas reglas de Retry e idempotencia:
- Mismo
external_refdevuelto con éxito →is_duplicate: true, la NC original. - Mismo
external_refque falló previamente → reutiliza registro con el correlativo original, actualizando datos del payload nuevo. - El
document_iddel CCF no se puede cambiar en retry. El API ignoradocument_iddel payload nuevo y conserva el CCF vinculado originalmente. Si quieres emitir NC sobre otro CCF, usa otroexternal_ref.
Ver también
- Crédito Fiscal (03) — emisión del CCF original.
- Anular DTE — incluye la creación automática de NC para CCF fuera de ventana.
- Retry e idempotencia — comportamiento en reintentos de NC.
- Respuestas y estados — semántica de flags.