# Issuing Credit Notes

After validating a customer return, calculate the accepted credit from your authoritative transaction data and submit a definitive credit-note generation input. The SDK resolves the original receipt's owner devices, encrypts the same input independently for each device, and submits it through the credit-note endpoint.

Cheqi does not calculate or move the refund
Your system owns eligibility, accepted quantity, financial and tax calculations, payment processing, reconciliation, and customer communication. The SDK serializes the values you provide without recalculating them.

## Amount signs

Supply positive quantities and positive credit amounts. A credit note represents the reversal through its document type; negative quantities or totals are not required and produce incorrect UBL semantics for the current generator.

For example, a full credit of a €100.00 net line with 21% VAT uses:

- `quantity`: `1`
- `subtotal`: `100.00`
- `totalTaxAmount`: `21.00`
- `totalAmount`: `121.00`


## Generation input

The plaintext encrypted for each owner device has two top-level properties:

| Property | Type | Required | Description |
|  --- | --- | --- | --- |
| `creditNoteTemplateRequest` | object | Yes | Definitive merchant-supplied credit-note values. |
| `taxesApplied` | boolean | Yes | Set to `false` when no tax applies; otherwise provide tax entries with values. |


```json
{
  "creditNoteTemplateRequest": {
    "documentNumber": "CN-2026-0042",
    "originatorDocumentReference": "INV-2026-0042",
    "identifiers": [
      { "type": "RETURN_AUTHORIZATION", "value": "RMA-91827" }
    ],
    "issueDate": "2026-08-05T14:30:22Z",
    "currency": "EUR",
    "creditNoteSubtotal": 100.00,
    "totalBeforeTax": 100.00,
    "totalTaxAmount": 21.00,
    "totalAmount": 121.00,
    "products": [
      {
        "identifier": "SKU-AIRMAX-BLK-42",
        "brandName": "Nike",
        "name": "AirMax",
        "quantity": 1,
        "baseQuantity": 1,
        "unitCode": "C62",
        "unitPrice": 100.00,
        "subtotal": 100.00,
        "total": 121.00,
        "taxes": [
          {
            "rate": 21,
            "type": "VAT",
            "taxableAmount": 100.00,
            "amount": 21.00,
            "category": "STANDARD"
          }
        ]
      }
    ],
    "taxes": [
      {
        "rate": 21,
        "type": "VAT",
        "taxableAmount": 100.00,
        "amount": 21.00,
        "label": "VAT 21%",
        "category": "STANDARD"
      }
    ]
  },
  "taxesApplied": true
}
```

`C62` is the UN/ECE unit code for one item. Use the unit and tax category appropriate to the original transaction.

### Required credit-note properties

| Property | Description |
|  --- | --- |
| `documentNumber` | Your unique credit-note number. |
| `originatorDocumentReference` | Your document number for the original receipt. |
| `issueDate` | ISO 8601 issue date and time. |
| `currency` | ISO 4217 code. |
| `creditNoteSubtotal` | Line subtotal before document-level adjustments and tax. |
| `totalBeforeTax` | Total after document-level adjustments, excluding tax. |
| `totalTaxAmount` | Total tax credit. |
| `totalAmount` | Tax-inclusive total credit. |
| `products` | At least one credited product. |


Every product needs `name`, `identifier`, `quantity`, `unitCode`, `unitPrice`, `subtotal`, and `total`. The `identifier` should match the return request's `productId` and the original receipt product's `identifier`.

When `taxesApplied` is `true`, include document-level tax entries and the relevant product-level entries. Each tax needs `rate`, `type`, and `taxableAmount`; include `amount` for the calculated tax credit. When tax does not apply, set `taxesApplied` to `false` and the tax arrays may be empty.

Optional document properties are `identifiers`, `discounts`, `charges`, `period`, and `jurisdictionalData`. The legacy `note` property is ignored by the current document generator; configure the company's receipt text when a seller note should appear.

See [Credit Notes](/creditnote/credit-notes#merchant-credit-note-input) for the full property summary.

## Build from a return request

Use `request.productId` and the parent receipt identifiers to look up the original line in your own system. Do not trust prices or derive refund eligibility from customer input.

```javascript
function buildCreditNoteGenerationInput(returnRequest, originalReceipt) {
  const acceptedLines = validateAndPriceReturn(returnRequest, originalReceipt);

  const products = acceptedLines.map(({ originalLine, acceptedQuantity }) => {
    const subtotal = calculateNetCredit(originalLine, acceptedQuantity);
    const tax = calculateTaxCredit(originalLine, acceptedQuantity);

    return {
      identifier: originalLine.identifier,
      brandName: originalLine.brandName,
      name: originalLine.name,
      quantity: acceptedQuantity,
      baseQuantity: originalLine.baseQuantity ?? 1,
      unitCode: originalLine.unitCode,
      unitPrice: originalLine.unitPrice,
      subtotal,
      total: subtotal + tax,
      taxes: [{
        rate: originalLine.taxRate,
        type: originalLine.taxType,
        taxableAmount: subtotal,
        amount: tax,
        category: originalLine.taxCategory
      }]
    };
  });

  const totalBeforeTax = products.reduce((sum, line) => sum + line.subtotal, 0);
  const totalTaxAmount = products.reduce(
    (sum, line) => sum + line.taxes.reduce((taxSum, tax) => taxSum + tax.amount, 0),
    0
  );

  return {
    creditNoteTemplateRequest: {
      documentNumber: generateCreditNoteNumber(),
      originatorDocumentReference: returnRequest.receiptId,
      issueDate: new Date().toISOString(),
      currency: originalReceipt.currency,
      creditNoteSubtotal: totalBeforeTax,
      totalBeforeTax,
      totalTaxAmount,
      totalAmount: totalBeforeTax + totalTaxAmount,
      products,
      taxes: aggregateTaxes(products)
    },
    taxesApplied: originalReceipt.taxesApplied
  };
}
```

Use decimal-safe arithmetic in production; the example leaves those business-specific functions to your implementation.

## Issue with JavaScript or TypeScript

Identify the recipient through the parent `cheqiReceiptId`, then pass that same ID separately as `parentCheqiReceiptId`:

```javascript
const parentCheqiReceiptId = returnRequest.cheqiReceiptId;
const identification = { cheqiReceiptId: parentCheqiReceiptId };
const generationInput = buildCreditNoteGenerationInput(
  returnRequest,
  originalReceipt
);

const result = await sdk.creditNoteService.issueCreditNote(
  identification,
  parentCheqiReceiptId,
  generationInput
);

if (result.isAccepted()) {
  console.log("Credit note accepted", result.cheqiReceiptId);
}
```

For OAuth, pass the access token as the fourth argument. To specify a store and OAuth token, pass `storeId` as the fourth argument and the token as the fifth.

## Issue with Java

The current Java method accepts the generation input as an object while the public typed schema is being finalized:

```java
Map<String, Object> generationInput = Map.of(
    "creditNoteTemplateRequest", creditNoteTemplateRequest,
    "taxesApplied", taxesApplied
);

IdentificationDetails identification = new IdentificationDetails()
    .cheqiReceiptId(returnRequest.getCheqiReceiptId());

CreditNoteResult result = sdk.getCreditNoteService().issueCreditNote(
    identification,
    returnRequest.getCheqiReceiptId(),
    generationInput
);

if (result.isAccepted()) {
    log.info("Credit note accepted: {}", result.getCheqiReceiptId());
}
```

The OAuth overload takes `accessToken` after the generation input. An overload with `storeId` and `accessToken` is also available.

## Result and lifecycle

An accepted SDK result contains:

| Property | Description |
|  --- | --- |
| `cheqiReceiptId` | Identifier assigned to the new credit-note generation job/document. |
| `parentCheqiReceiptId` | Original receipt ID supplied by the merchant. |
| `matchId` | Recipient-resolution match identifier. |
| `status` | Initial submission status. |
| `createdAt` | Submission timestamp. |


Acceptance means the encrypted generation job was queued; it does not mean a payment refund has been completed. Update the original return request's status separately using its `requestId`, and only mark it `COMPLETED` when your return workflow has actually finished.

## Related

- [Receiving Return Requests](/creditnote/receiving-returns)
- [Credit Notes property reference](/creditnote/credit-notes)
- [UBL XML Credit Note Format](/creditnote/ubl-format)
- [Java SDK](/sdk/java)
- [JavaScript SDK](/sdk/javascript)
- [Request another SDK language](/sdk/overview)