# Sending Receipts

Use the SDK-level complete-receipt flow where available. It coordinates the
merchant side of the current digital route:

1. resolve the customer and owner devices
2. inspect the explicit `deliveryRouteType`
3. serialize the definitive receipt payload locally
4. encrypt that payload independently for every matched device
5. submit the complete device set to `/receipt/encrypted`


The accepted submission becomes durable device work. Final CHEQI and UBL
documents are generated later on owner devices, not by the backend.

## Complete receipt flow

```java Java
ReceiptResult result = sdk.getReceiptService()
    .issueReceipt(identificationDetails, receiptPayload);

if (result.isAccepted()) {
    System.out.println(result.getDeliveryRouteType());
    System.out.println(result.getCheqiReceiptId());
} else if (result.isEmailReceiptRequired()) {
    // Generate and submit the email receipt explicitly.
} else if (result.isDownloadEnvelopeRequired()) {
    // Build a final ReceiptEnvelope and call completeDownloadFallback(...).
}
```

```javascript JavaScript
const result = await sdk.receiptService.issueReceipt(
  identificationDetails,
  receiptPayload
);

if (result.isAccepted()) {
  console.log(result.deliveryRouteType);
  console.log(result.cheqiReceiptId);
} else if (result.isEmailReceiptRequired()) {
  // Generate and submit the email receipt explicitly.
} else if (result.isDownloadEnvelopeRequired()) {
  // Build a final ReceiptEnvelope and call completeDownloadFallback(...).
}
```

Both current SDKs accept a definitive `ReceiptPayload`. For digital delivery,
the SDK encrypts that input locally; it does not send it to a plaintext
template-generation endpoint. A missing route is raised as `CUSTOMER_NOT_FOUND`
by the high-level method rather than returned as a `ReceiptResult` state.

## Low-level encrypted submission

If you implement the low-level flow yourself, submit the match and one
independently encrypted copy per matched device:

```json
{
  "matchId": "match_opaque",
  "storeId": "4ce2f858-d57d-4fde-a7ce-2e93f165494b",
  "deviceDeliveries": [
    {
      "deviceRecipientId": "rcpt_temporary_1",
      "encryptedContent": "base64-aes-gcm-ciphertext",
      "encryptedAesKey": "base64-wrapped-content-key"
    },
    {
      "deviceRecipientId": "rcpt_temporary_2",
      "encryptedContent": "base64-aes-gcm-ciphertext",
      "encryptedAesKey": "base64-wrapped-content-key"
    }
  ]
}
```

`storeId` is optional. When present, it must identify a store directly owned by
the company in the authenticated token. The authenticated company remains the
legal issuer.

A successful request returns `202 Accepted`:

```json
{
  "cheqiReceiptId": "CHQ-20260802-143502-A1B2C3",
  "matchId": "match_opaque",
  "status": "PENDING",
  "createdAt": "2026-08-02T14:35:02Z"
}
```

Acceptance means Cheqi durably queued the encrypted work. It does not mean a
phone has generated or acknowledged the receipt yet.

## Submission rules

- Treat `matchId` as single-use, short-lived flow state.
- Submit exactly the temporary device recipient IDs returned by that match.
- Encrypt the same definitive payload independently with a fresh symmetric key
for every device.
- Preserve the returned `cheqiReceiptId`; it identifies the durable receipt job
after the match has been consumed.
- A retry with the same match and identical request is idempotent. A conflicting
replay is rejected.
- Do not put plaintext receipt contents, final CHEQI JSON, or UBL XML in the
encrypted-submission request outside the device ciphertext.


## Legal issuer and store

Determine the legal issuer before recipient resolution and use the same
company-bound access token through resolution and encrypted submission.

For company groups, obtain a delegated token for the company that legally made
the sale. A parent-company token cannot select a child as issuer. The deprecated
`childCompanyId` field does not grant parent-to-child issuance.

See [Companies, Child Companies, and Stores](/companies/structure) for
retail, group, and franchise mappings.

## Delivery routes

Follow `deliveryRouteType`; do not infer the route from recipient count.

- `DIGITAL`: encrypt the definitive payload for every matched owner device and
submit `/receipt/encrypted`.
- `DOWNLOAD_FALLBACK`: build and encrypt the download envelope locally, then use
`/receipt/download`. The response URL carries the content key only in its
fragment.
- `routeFound: false`: stop with customer-not-found in the healthy online flow.


Download fallback does not return a Cheqi-held recipient key and does not go
through `/receipt/encrypted`. See [Download Links](/receipts/download-fallback).

## After submission

Cheqi creates durable encrypted generation work for each assigned device and an
outbox entry for its opaque push hint. A phone may process the job from that
hint or during foreground catch-up sync. Only device generation,
acknowledgement, and encrypted downstream delivery advance the durable job;
provider acceptance of a push does not.

See [Mobile Delivery](/receipts/mobile-delivery) for APNs, FCM, notification
permission, retry, and force-quit behavior.