Every Cheqi webhook has an event and a data property. The key inside data identifies the object delivered by that event.
{
"event": "EVENT_NAME",
"data": {
"eventSpecificObject": {}
}
}The event-specific object may contain this routing metadata:
| Field | Description |
|---|---|
clientId | Public OAuth client ID for the webhook subscription, when owned by a client application. |
companyId | Customer-company context, when applicable. |
userId | Customer-user context, when applicable. |
created_at | Time Cheqi created the webhook event. |
cheqiReceiptId | Cheqi identifier of the receipt or credit note. |
Null and empty fields are omitted. Do not assume both companyId and userId are present.
data.encryptedReceipt contains the encrypted document envelope produced by the elected owner device for your integration.
{
"event": "RECEIPT_CREATED",
"data": {
"encryptedReceipt": {
"clientId": "your-public-client-id",
"companyId": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-08-03T13:30:00Z",
"cheqiReceiptId": "CHQ-20260803-ABC123",
"encryptedEnvelope": "base64-ciphertext...",
"encryptedEnvelopeKey": "base64-wrapped-aes-key...",
"publicKey": "base64-recipient-public-key-snapshot...",
"recipientKeyAlgorithm": "RSA_2048",
"envelopeVersion": 1,
"receiptGeneratorVersion": "0.3.0",
"finalHash": "sha256-cheqi-document-hash..."
}
}
}Use publicKey and recipientKeyAlgorithm to select the corresponding private key and unwrap encryptedEnvelopeKey. Decrypt encryptedEnvelope, then deserialize its UTF-8 plaintext as a ReceiptEnvelope.
finalHash is the definitive CHEQI document hash submitted by the elected device together with the encrypted deliveries. There is no separate receipt-finalization webhook.
The customer's device encrypts a CreditNoteInitiationRequest for the original issuer. Cheqi routes that ciphertext without decrypting it.
{
"event": "RETURN_REQUESTED",
"data": {
"creditNoteInitiationRequest": {
"clientId": "issuer-public-client-id",
"companyId": "550e8400-e29b-41d4-a716-446655440000",
"userId": "8ccf09a7-d969-4a09-880f-42d520a0999a",
"created_at": "2026-08-03T14:00:00Z",
"cheqiReceiptId": "CHQ-20260803-ABC123",
"publicKey": "base64-issuer-public-key...",
"encryptedCreditNoteInitiationRequest": "base64-ciphertext...",
"encryptedSymmetricKey": "base64-wrapped-aes-key..."
}
}
}After decryption, the plaintext is a CreditNoteInitiationRequest containing the original receipt identifiers, requested return line items, the customer's note, and refund preference. It is not a ReceiptEnvelope.
Credit notes use the same encrypted document-bundle contract as receipts. The event-specific key is encryptedCreditNote.
{
"event": "CREDIT_NOTE_CREATED",
"data": {
"encryptedCreditNote": {
"clientId": "your-public-client-id",
"companyId": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-08-03T15:00:00Z",
"cheqiReceiptId": "CHQ-CN-20260803-DEF456",
"encryptedEnvelope": "base64-ciphertext...",
"encryptedEnvelopeKey": "base64-wrapped-aes-key...",
"publicKey": "base64-recipient-public-key-snapshot...",
"recipientKeyAlgorithm": "RSA_2048",
"envelopeVersion": 1,
"receiptGeneratorVersion": "0.3.0",
"finalHash": "sha256-cheqi-credit-note-hash..."
}
}
}Decrypt encryptedEnvelope as a ReceiptEnvelope. Its documents map contains CHEQI and, when requested by the recipient, UBL_CREDIT_NOTE.
finalHash is the definitive CHEQI credit-note hash submitted by the elected device together with the encrypted deliveries. There is no separate credit-note finalization webhook.
The plaintext obtained by decrypting a receipt or credit-note encryptedEnvelope has this versioned structure:
{
"envelopeVersion": 1,
"receiptUuid": "6f9619ff-8b86-d011-b42d-00cf4fc964ff",
"cheqiReceiptId": "CHQ-20260803-ABC123",
"receiptGeneratorVersion": "0.3.0",
"documents": {
"CHEQI": {
"mediaType": "application/json",
"content": "{...}"
},
"UBL_INVOICE": {
"mediaType": "application/xml",
"content": "<Invoice>...</Invoice>"
}
}
}| Property | Description |
|---|---|
envelopeVersion | Version of the encrypted plaintext-envelope contract. |
receiptUuid | Shared UUID for the locally generated document bundle. |
cheqiReceiptId | Cheqi receipt or credit-note identifier. |
receiptGeneratorVersion | Rust receipt-engine version used to generate the documents. |
documents | Map keyed by CHEQI, UBL_INVOICE, UBL_PURCHASE_RECEIPT, or UBL_CREDIT_NOTE. |
Each document has a mediaType and string content. The keys actually present in documents are authoritative; there is no separate receiptFormats property in the webhook.
The envelope already contains the complete generated documents, including their supplier, customer, and payment context. There is no separate encryptedCustomerDetails or encryptedCustomerAesKey payload.
- Verify the webhook signature against the raw HTTP body.
- Persist the event or establish idempotency before returning success.
- Select the private key corresponding to the supplied public-key snapshot.
- Unwrap the AES key from
encryptedEnvelopeKeyaccording torecipientKeyAlgorithm. - Decrypt
encryptedEnvelopeusing the envelope encryption implementation in the Cheqi SDK. - Deserialize the plaintext as
ReceiptEnvelope. - Read the required representation from
ReceiptEnvelope.documents. - Canonicalize and hash the CHEQI document when independently verifying
finalHash.
Receipt and return contents can contain personal and financial data. Do not log ciphertext keys, decrypted envelopes, or document contents.
Cheqi can retry a webhook when delivery fails. Process events idempotently. A practical idempotency key is event + cheqiReceiptId; if your application can receive the same receipt for multiple authorization contexts, include the applicable clientId, companyId, or userId as well.
Always return 2xx only after the raw event has been durably accepted. Perform decryption and downstream synchronization asynchronously.