Send a finalized sale's receipt to the customer
Delivers the receipt for one FINALIZED sale to a real person over email or sms. This leaves the system: it hands the message to the ESP and, for SMS, DEBITS the account credit wallet. It cannot be recalled and there is no delete endpoint, so only send on an explicit request naming the sale and the channel. Identify the sale with saleId (the finalized-sale id) or transactionId — at least one is required. recipient overrides the customer’s on-file contact; omit it to use the contact attached to the ticket, and expect a 400 no recipient when the ticket is anonymous or the contact is missing. A receipt is transactional (lawful basis: the sale contract), so it is NOT gated on marketing consent. Read status: sent (the ESP accepted it, providerMessageId carries the correlation id), failed (rejected; nothing was charged) or dry_run (no ESP key on this deployment — the customer received NOTHING). NOT IDEMPOTENT at the boundary. A repeat of the same (saleId, channel, recipient) is normally deduped against the usage log and returns the earlier outcome without re-sending, BUT that log row is written AFTER the ESP call: a request that times out mid-send leaves no row, so a blind retry sends and charges a SECOND time. On a timeout, do not retry — check with the operator. Changing recipient also defeats the dedup, and a dry_run row still counts as a prior send, so a receipt dry-run on a keyless environment permanently suppresses the real send for that triple. ERROR-VOCABULARY TRAP: the specific failure is carried in error.message, not in the error.code enum. 422 sms-unavailable (SMS switched off network-wide), 422 insufficient-credits, 422 email-quota-exceeded, 422 rate-limited (the per-account 300-per-minute or per-shop 120-per-minute ceiling — semantically a 429, but this API has no 429), 404 sale not found, 400 no recipient. Branch on the message token.
Authorizations
Authorization: Bearer <token>. Accepts EITHER a Keycloak access token (scopes-in-token) OR an opaque POS session token; both resolve to the same pos.* scope vocabulary the route guards enforce.
Body
Response
The delivery outcome. A 200 means the request was PROCESSED, not that the customer received anything — read status.

