Credit Note (05)
The Credit Note (type 05) is issued to adjust an already-issued CCF (type 03). Typical uses:
- Merchandise returns.
- Price or quantity corrections after issuance.
- Cancellation of a CCF past the direct-invalidation window.
- Discounts applied after invoicing.
The Credit Note does not apply to Invoices (01), Export Invoices (11), or SUEX (14). For those types use Direct invalidation.
/credit-memo vs /invalidate — when to use each
When you need to reverse a CCF you have two paths:
| Situation | Endpoint | What it does |
|---|---|---|
| Partial adjustment (e.g. 2 items returned out of 10) | POST /credit-memo with lines | Generates partial NC, leaves the CCF valid. |
| Intentional total adjustment | POST /credit-memo with full_adjustment: true | Generates full NC, marks the CCF as invalidated. |
| Cancel a complete CCF within invalidation window | POST /invalidate/{id} | Direct MH cancellation. Simpler. |
| Cancel a complete CCF outside invalidation window | POST /invalidate/{id} | The API automatically creates a Credit Note since MH no longer allows direct cancellation. |
If you just want to cancel a complete CCF, use /invalidate — the API decides whether to do direct cancellation or generate an automatic NC. Use /credit-memo when you need explicit control over which lines are adjusted.
Technical details
| Method | POST |
| URL | https://ocote.io/api/connect/credit-memo |
| Content-Type | application/json |
| Authentication | Header Authorization: Bearer odt_... |
Request
Fields
| Field | Type | Required | Description |
|---|---|---|---|
document_id | string | Yes | UUID or external_ref of the CCF to adjust. |
full_adjustment | bool | No (default false) | If true, generates full NC (clones all CCF lines) and invalidates the CCF. |
lines | array | No | Specific lines to adjust. Only valid with full_adjustment: false. If you send neither lines nor full_adjustment: true, the API clones all CCF lines but does not invalidate. |
external_ref | string | No | Your idempotent identifier for the NC. |
Lines (lines)
| Field | Type | Required | Description |
|---|---|---|---|
description | string | Yes | Description of the adjusted item (typically matching the CCF original). |
quantity | float | Yes | Quantity to adjust. |
unit_price | float | Yes | Unit price VAT-included. |
discount_percentage | float | No (default 0) | Discount. |
The three issuance modes
Mode 1: Explicit full adjustment (full_adjustment: true)
The API copies all lines of the original CCF into the NC and marks the CCF as invalidated. Equivalent to a cancellation but producing an explicit fiscal adjustment document.
{
"document_id": "CCF-ENLACES-001",
"full_adjustment": true,
"external_ref": "NC-RETURN-TOTAL-001"
}
Use it when you want a formal cancellation document with all lines visible.
Mode 2: Partial adjustment (full_adjustment: false + lines)
You only adjust what you specify in lines. The CCF remains valid but with an adjusted amount.
{
"document_id": "CCF-ENLACES-001",
"full_adjustment": false,
"lines": [
{
"description": "Returned Dell XPS laptop",
"quantity": 1,
"unit_price": 1200.00
}
],
"external_ref": "NC-RETURN-PARTIAL-001"
}
Use it for real returns (1 of 10 items came back) or post-invoice discounts.
Mode 3: Clone without invalidating (no lines, full_adjustment: false)
The API clones all CCF lines but does not invalidate it. Useful to "record" a complete adjustment without fiscally cancelling the CCF.
{
"document_id": "CCF-ENLACES-001",
"external_ref": "NC-ADJUSTMENT-001"
}
This is the least common mode. If you need full cancellation, prefer Mode 1 or the /invalidate endpoint.
Validations
The API validates the CCF before processing:
| Validation | HTTP | Message |
|---|---|---|
| CCF doesn't exist or isn't yours | 404 | Documento no encontrado |
| Referenced document isn't a CCF | 400 | Solo se pueden emitir Notas de Credito sobre CCF (tipo 03) |
| CCF already invalidated | 400 | Este CCF ya fue invalidado |
| CCF already fully adjusted | 400 | Este CCF ya fue completamente ajustado |
| CCF more than 90 business days old | 400 | Este CCF tiene mas de 90 dias y no puede ser ajustado |
Direct NC via /credit-memo only works while the CCF is less than 90 business days old since its issuance. After that, the API rejects the adjustment and you must use other accounting mechanisms. If your intent was to cancel an entire old CCF, use POST /invalidate/{ccf_id} — that endpoint does handle the out-of-window case automatically by generating the equivalent NC.
Response
In addition to standard flags (Responses and states), the NC returns specific fields:
| Field | Type | Description |
|---|---|---|
document_id | string (UUID) | ID of the created NC. |
external_ref | string | The one you sent (or auto-generated). |
control_number | string | NC correlative (DTE-05-...). |
generation_code | string | MH generation code. |
reception_stamp | string | MH stamp of the NC. |
ccf_control_number | string | Correlative of the adjusted CCF. |
ccf_adjusted_amount | float | Total amount adjusted (sum of NC lines). |
ccf_fully_adjusted | bool | true if the CCF became fully adjusted after this NC. |
dte | object | Full signed schema of the NC (only if dte_success: true). |
Example: partial return
Scenario: you issued a CCF for three products ($500, $300, $200 = $1,000 taxable). The customer returns only the $300 product. You issue a partial NC:
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": "Ergonomic office chair",
"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: 'Ergonomic office chair',
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": "Ergonomic office chair",
"quantity": 1,
"unit_price": 339.00,
},
],
},
)
Successful response
{
"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": ""
}
Notice:
ccf_control_numberpoints to the adjusted CCF — useful for your reconciliation.ccf_adjusted_amount: 339.00is the amount this NC deducts from the CCF.ccf_fully_adjusted: falseindicates the CCF still has active amount — you could issue more NCs if more returns come in, always within the 90-business-day window from CCF issuance.
Total adjustment
To cancel a complete CCF via NC (within window):
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-CANCEL-CCF-002"
}'
This mode is equivalent to what POST /invalidate/{id} does internally when the CCF is out of window. The difference is that here you make the decision explicitly.
Idempotency and retry
The same rules from Retry and idempotency apply:
- Same
external_refpreviously successful → returned withis_duplicate: true, the original NC. - Same
external_refpreviously failed → reuses the record with the original correlative, updating data from the new payload. - The
document_idof the CCF cannot be changed on retry. The API ignores thedocument_idfrom the new payload and preserves the originally linked CCF. If you want to issue an NC against a different CCF, use a differentexternal_ref.
Downloading files
The POST /credit-memo endpoint does not return invoice_url or json_url in the response — only fiscal data. To get the download URLs for an issued NC, query the status endpoint:
curl -H "Authorization: Bearer odt_xxx" \
https://ocote.io/api/connect/credit-memo/status/NC-ENLACES-TEST-001
The response includes invoice_url (letter-sized PDF) and json_url (signed DTE). NCs do not generate a thermal ticket; only the two files above. See Downloading files for details on dual auth and the full table by document type.
See also
- Tax Credit Voucher (03) — issuance of the original CCF.
- Invalidate DTE — includes automatic NC creation for CCFs out of window.
- Retry and idempotency — retry behavior on NCs.
- Responses and states — flags semantics.