# Receiving Return Requests

Customer return requests are encrypted for the original receipt issuer. Receive them in real time through a `RETURN_REQUESTED` webhook or retrieve outstanding requests from `GET /credit-note`.

Encryption key required
API-key integrations use the company's registered encryption key. OAuth integrations use the client application's registered key. Decrypt with the private key that matches the registered public key.

## Webhook delivery

Subscribe to `RETURN_REQUESTED`. The event-specific payload is under `data.creditNoteInitiationRequest`:

```json
{
  "event": "RETURN_REQUESTED",
  "data": {
    "creditNoteInitiationRequest": {
      "companyId": "550e8400-e29b-41d4-a716-446655440000",
      "userId": "8c669f72-65bb-4788-9ab1-5289452e05d1",
      "cheqiReceiptId": "CHQ-20260805-000001",
      "publicKey": "base64-public-key...",
      "encryptedCreditNoteInitiationRequest": "base64-ciphertext...",
      "encryptedSymmetricKey": "base64-wrapped-aes-key...",
      "clientId": null,
      "created_at": "2026-08-05T12:30:00Z"
    }
  }
}
```

`companyId`, `userId`, and `clientId` identify the applicable routing context and can be absent when they do not apply. Verify the webhook signature before processing the request, acknowledge promptly with a 2xx response, and make processing idempotent because delivery can be retried.

See [Webhook Setup](/webhooks/setup) and [Webhook Security](/webhooks/security).

## Polling

Use the authenticated company or client-application credential:

```bash
curl https://api.cheqi.io/credit-note \
  -H "Authorization: Bearer <token>"
```

The endpoint returns an array of outstanding requests:

```json
[
  {
    "requestId": "d79f71c6-ef10-4cd1-92ca-750bfdb31a0c",
    "cheqiReceiptId": "CHQ-20260805-000001",
    "encryptedSymmetricKey": "base64-wrapped-aes-key...",
    "publicKey": "base64-public-key...",
    "status": "PENDING",
    "createdAt": "2026-08-05T12:30:00Z",
    "encryptedCreditNoteInitiationRequest": "base64-ciphertext..."
  }
]
```

| Property | Type | Description |
|  --- | --- | --- |
| `requestId` | UUID | Queue request identifier; use it for status updates. |
| `cheqiReceiptId` | string | Cheqi identifier of the original receipt. |
| `encryptedSymmetricKey` | string | AES content key wrapped for the issuer's registered RSA public key. |
| `publicKey` | string | Public key associated with the encrypted request. |
| `status` | enum | `PENDING`, `ACCEPTED`, `PARTIALLY_ACCEPTED`, `REJECTED`, `COMPLETED`, or `CANCELLED`. |
| `createdAt` | ISO 8601 date-time | Time at which the request entered the queue. |
| `encryptedCreditNoteInitiationRequest` | string | Base64-encoded encrypted plaintext request. |


The polling response does not contain `recipientId`, `returnRequestId`, `receiverType`, `encryptedCreditNote`, customer-detail ciphertext fields, or `supplierPartyId`.

## Decrypting with an SDK

Pass either the polling object or `webhook.data.creditNoteInitiationRequest` to the decryption service.

```java
CreditNoteInitiationRequest request = sdk.getDecryptionService()
    .decryptCreditNoteInitiationRequest(encryptedRequest, privateKeyBase64);

String parentCheqiReceiptId = request.getCheqiReceiptId();
for (ReturnLineItem item : request.getLineItems()) {
    String productId = item.getProductId();
    BigDecimal quantity = item.getQuantity();
    ReturnReasonCode reason = item.getReasonCode();
    String explanation = item.getReasonDescription();
}
```

```javascript
const request = sdk.decryptionService.decryptCreditNoteInitiationRequest(
  encryptedRequest,
  privateKeyBase64
);

const parentCheqiReceiptId = request.cheqiReceiptId;
for (const item of request.lineItems) {
  const { productId, quantity, reasonCode, reasonDescription } = item;
}
```

The SDK decrypts, deserializes, and validates the plaintext contract. If you decrypt manually, validate it against the model below before acting on it.

## Decrypted request properties

```json
{
  "cheqiReceiptId": "CHQ-20260805-000001",
  "receiptId": "INV-2026-0042",
  "customerNote": "The left shoe is damaged.",
  "lineItems": [
    {
      "productId": "SKU-AIRMAX-BLK-42",
      "quantity": 1,
      "reasonCode": "DAMAGED",
      "reasonDescription": "Damage near the heel"
    }
  ],
  "refundPreference": "ORIGINAL_PAYMENT_METHOD"
}
```

| Property | Rules |
|  --- | --- |
| `cheqiReceiptId` | Required, non-empty. |
| `receiptId` | Required, non-empty merchant receipt identifier. |
| `customerNote` | Optional; maximum 1,000 characters. |
| `lineItems` | Required and non-empty. |
| `refundPreference` | `ORIGINAL_PAYMENT_METHOD`, `BANK_TRANSFER`, or `STORE_CREDIT`. |
| `refundBankAccount` | Required only for `BANK_TRANSFER`; forbidden for the other preferences. |


Each line needs a non-empty `productId` of at most 255 characters, a positive `quantity`, and a valid `reasonCode`. `reasonDescription` is optional and limited to 1,000 characters. Multiple lines may use the same `productId` when different quantities have different reasons.

The `productId` is the `identifier` supplied on the original receipt product. Use it with `receiptId` or `cheqiReceiptId` to look up the authoritative original line, price, tax, and quantity in your own system.

## Merchant validation

The return request describes what the customer wants; it does not authorize or calculate a refund. Before issuing a credit note:

- Confirm that the receipt and product belong to the requesting customer.
- Check the return window and your policy.
- Confirm that the requested quantity does not exceed the remaining returnable quantity.
- Calculate the accepted credit and tax from your authoritative transaction data.
- Treat the refund preference as a request, not an instruction to move money automatically.
- Validate bank details before using them.


## Updating request status

After making a decision, update the queue item using its `requestId`:

```bash
curl -X PATCH https://api.cheqi.io/credit-note/requests/d79f71c6-ef10-4cd1-92ca-750bfdb31a0c/status \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"status":"ACCEPTED"}'
```

Use `PARTIALLY_ACCEPTED` when only part of the requested return is approved, `REJECTED` when none is approved, and `COMPLETED` after the accepted return and refund workflow is finished.

## Next step

Build the definitive merchant payload and issue the linked document in [Issuing Credit Notes](/creditnote/issuing-credit-notes).