Skip to main content

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:

SituationEndpointWhat it does
Partial adjustment (e.g. 2 items returned out of 10)POST /credit-memo with linesGenerates partial NC, leaves the CCF valid.
Intentional total adjustmentPOST /credit-memo with full_adjustment: trueGenerates full NC, marks the CCF as invalidated.
Cancel a complete CCF within invalidation windowPOST /invalidate/{id}Direct MH cancellation. Simpler.
Cancel a complete CCF outside invalidation windowPOST /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

MethodPOST
URLhttps://ocote.io/api/connect/credit-memo
Content-Typeapplication/json
AuthenticationHeader Authorization: Bearer odt_...

Request

Fields

FieldTypeRequiredDescription
document_idstringYesUUID or external_ref of the CCF to adjust.
full_adjustmentboolNo (default false)If true, generates full NC (clones all CCF lines) and invalidates the CCF.
linesarrayNoSpecific 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_refstringNoYour idempotent identifier for the NC.

Lines (lines)

FieldTypeRequiredDescription
descriptionstringYesDescription of the adjusted item (typically matching the CCF original).
quantityfloatYesQuantity to adjust.
unit_pricefloatYesUnit price VAT-included.
discount_percentagefloatNo (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:

ValidationHTTPMessage
CCF doesn't exist or isn't yours404Documento no encontrado
Referenced document isn't a CCF400Solo se pueden emitir Notas de Credito sobre CCF (tipo 03)
CCF already invalidated400Este CCF ya fue invalidado
CCF already fully adjusted400Este CCF ya fue completamente ajustado
CCF more than 90 business days old400Este CCF tiene mas de 90 dias y no puede ser ajustado
90-business-day window

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:

FieldTypeDescription
document_idstring (UUID)ID of the created NC.
external_refstringThe one you sent (or auto-generated).
control_numberstringNC correlative (DTE-05-...).
generation_codestringMH generation code.
reception_stampstringMH stamp of the NC.
ccf_control_numberstringCorrelative of the adjusted CCF.
ccf_adjusted_amountfloatTotal amount adjusted (sum of NC lines).
ccf_fully_adjustedbooltrue if the CCF became fully adjusted after this NC.
dteobjectFull 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
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
}
]
}'
Node.js (axios)
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}` } }
);
Python (requests)
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_number points to the adjusted CCF — useful for your reconciliation.
  • ccf_adjusted_amount: 339.00 is the amount this NC deducts from the CCF.
  • ccf_fully_adjusted: false indicates 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
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_ref previously successful → returned with is_duplicate: true, the original NC.
  • Same external_ref previously failed → reuses the record with the original correlative, updating data from the new payload.
  • The document_id of the CCF cannot be changed on retry. The API ignores the document_id from the new payload and preserves the originally linked CCF. If you want to issue an NC against a different CCF, use a different external_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
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