Deliver a receipt in one request.
Receipt Out gives retailers and POS platforms a small, authenticated API for sending an itemized digital receipt directly to a customer's Receipt Out account after checkout.
POST https://receiptout.com/api/v1/receiptsQuickstart
A retailer integration needs an API key, an activated Receipt Out customer email, and a unique transaction identifier from the retailer's POS system.
curl https://receiptout.com/api/v1/receipts \
-X POST \
-H "Authorization: Bearer ro_test_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sale-12345" \
--data @receipt.jsonAuthentication
Every API request uses a retailer-specific Bearer key in the Authorization header.
Authorization: Bearer ro_test_...Keys are shown only once when created. Receipt Out stores a cryptographic hash of the key rather than the full credential. A revoked key immediately stops authenticating.
Test and Live modes
Test keys
Keys beginning with ro_test_ exercise the real receipt pipeline but mark every resulting receipt DEMO.
Live keys
Keys beginning with ro_live_ create normal customer receipts. Live keys should only be issued for an approved production integration.
Receipt request
The API accepts a JSON object with a customer and receipt. The customer must already have an activated Receipt Out account matching the submitted email address.
{
"customer": {
"email": "customer@example.com"
},
"receipt": {
"transaction_id": "SALE-12345",
"purchased_at": "2026-09-27T13:45:00-05:00",
"store": {
"location": "Jackson, Missouri"
},
"currency": "USD",
"payment": {
"label": "Visa ending 4242"
},
"items": [
{
"description": "Milk",
"quantity": 1,
"unit_price_cents": 348,
"line_total_cents": 348
},
{
"description": "Laundry detergent",
"quantity": 1,
"unit_price_cents": 1297,
"line_total_cents": 1297
}
],
"subtotal_cents": 1645,
"discount_cents": 100,
"tax_cents": 124,
"total_cents": 1669
}
}| Field | Type | Required | Notes |
|---|---|---|---|
customer.email | string | Yes | Must match an activated Receipt Out account. |
receipt.transaction_id | string | Yes | Retailer's unique transaction ID, max 120 characters. |
receipt.purchased_at | ISO-8601 | Yes | Purchase time; cannot be more than 24 hours in the future. |
receipt.store.location | string | No | Store/location description, max 160 characters. |
receipt.currency | string | No | Three-letter ISO code. Defaults to USD. |
receipt.payment.label | string | No | Display-only payment description. Do not send full card numbers. |
receipt.items | array | Yes | 1–200 line items. |
items[].description | string | Yes | Item description, max 140 characters. |
items[].quantity | number | No | Defaults to 1. Greater than 0, up to 9999. |
items[].unit_price_cents | integer | Yes | Non-negative integer cents. |
items[].line_total_cents | integer | Yes | Non-negative integer cents. |
receipt.subtotal_cents | integer | Yes | Must equal the sum of all line totals. |
receipt.discount_cents | integer | No | Defaults to 0 and cannot exceed subtotal. |
receipt.tax_cents | integer | Yes | Non-negative integer cents. |
receipt.total_cents | integer | Yes | Must equal subtotal − discounts + tax. |
Money and totals
All monetary values are integer minor units—cents for USD. Do not send floating-point dollar values.
12993480Receipt Out validates both equations before accepting a receipt:
subtotal_cents = Σ line_total_centstotal_cents = subtotal_cents − discount_cents + tax_centsIdempotency and retries
Every delivery requires an Idempotency-Key header. Use a value tied to the retailer transaction or delivery attempt, such as store-104-sale-882741.
- If the exact same request is retried with the same key, Receipt Out returns the original delivery with
idempotent_replay: true. - If the same idempotency key is reused with different JSON, the API returns
409 idempotency_conflict. - If the same retailer
transaction_idis sent again under a different idempotency key, the API returns409 transaction_exists.
This lets POS software safely retry after network timeouts without creating duplicate customer receipts.
Successful responses
{
"object": "receipt_delivery",
"status": "delivered",
"receipt_id": 42,
"transaction_id": "SALE-12345",
"customer": {
"email": "c******@example.com"
},
"mode": "test",
"demo": true,
"idempotent_replay": false,
"request_id": "req_..."
}A successful replay returns HTTP 200 with the original receipt_id and idempotent_replay: true.
Error responses
Errors are JSON and include a stable error code plus a request ID that can be used during integration support.
| HTTP | Code | Meaning |
|---|---|---|
| 400 | invalid_idempotency_key | Missing or malformed Idempotency-Key. |
| 400 | invalid_json | Missing or malformed JSON. |
| 401 | invalid_api_key | Missing, invalid, revoked, or inactive API key. |
| 404 | customer_not_found | No activated Receipt Out account matches the email. |
| 409 | idempotency_conflict | Same idempotency key, different request body. |
| 409 | transaction_exists | That retailer transaction ID was already delivered. |
| 413 | payload_too_large | Request body exceeded 512 KB. |
| 415 | unsupported_media_type | Content-Type is not application/json. |
| 422 | validation_error | One or more receipt fields failed validation. |
| 429 | rate_limited | Current delivery rate limit exceeded. |
| 500 | internal_error | Receipt Out could not complete the delivery. |
Current v1 limits
These are initial pilot limits and can evolve as Receipt Out moves into larger retailer integrations.
OpenAPI specification
The machine-readable OpenAPI 3.1 document describes the current v1 endpoint, schemas, authentication, and response models.
View openapi.json