Developers

API reference

One request per completed sale. Safe to retry.

Base URL: https://receiptter.eu/api/v1. All bodies are JSON. All amounts are integers in minor units (cents).

Authentication

Create a key in the dashboard under Integrations. It is shown once. Send it as a bearer token. Keys can be revoked at any time and are limited to 600 requests per minute.

Authorization: Bearer rk_…

Create a receipt

Call this when the payment is complete. The receipt waits for the customer's tap on that till for the claim window you set (default 3 minutes).

POST /api/v1/receipts
{
  "register": "KASA-02",
  "external_id": "TX-10045",
  "receipt_number": "0000452",
  "currency": "EUR",
  "total_minor": 1100,
  "items": [
    { "name": "Flat white", "qty": 2, "unit_price_minor": 370, "total_minor": 740, "vat_rate": 20 },
    { "name": "Almond croissant", "qty": 1, "unit_price_minor": 360, "total_minor": 360, "vat_rate": 20 }
  ],
  "vat": [{ "rate": 20, "base_minor": 917, "amount_minor": 183 }],
  "payment_method": "Card",
  "fiscal": { "unp": "DT123456-0001-0000452", "device": "DT123456" }
}

Fields

FieldRequiredDescription
registerYesThe till's POS ID (set in the dashboard) or its internal ID.
external_idYesYour transaction ID. Unique per merchant; used for idempotency.
total_minorYesTotal in cents. Negative for refunds.
items[]Yesname, qty, unit_price_minor, total_minor; optional discount_minor, vat_rate.
currencyNoISO 4217, default EUR.
vat[]Norate, base_minor, amount_minor.
payment_methodNoA category like Card or Cash. Never send card numbers; digit runs of 6+ are masked.
fiscalNounp, device, qr from your fiscal device.
receipt_number, issued_at, timezoneNoShown on the receipt. issued_at is ISO 8601.

Response

201 Created
{
  "id": "rc_9f2c…",
  "status": "pending",
  "ready_until": 1793960000,
  "claim_url": "https://receiptter.eu/c/rc_9f2c….Kq3…"
}

claim_url is a one-time link. Show it as a QR code on your customer display for phones without NFC. Whoever opens it first collects the receipt.

Retries and duplicates

Sending the same external_id again returns 200 with "duplicate": true and the existing receipt. Retry freely on timeouts; you will never create two receipts for one sale.

Get status

GET /api/v1/receipts/{id or external_id}

Status is one of pending, claimed, expired, void. claim_via is tap or qr.

Void or refund

POST /api/v1/receipts/{id or external_id}/void

The receipt page and the authenticity check then show it as cancelled or refunded. For a partial refund, send a new receipt with a negative total.

List tills

GET /api/v1/registers

Webhooks

Receiptter can tell your own system what happens. Add an HTTPS address in the dashboard under Integrations, Webhooks, and choose the events. Every event is one POST with a JSON body. Answer with any 2xx within 5 seconds.

EventWhen
receipt.createdA sale arrived from the till.
receipt.collectedThe customer collected the receipt. via is tap or qr.
receipt.cancelledThe receipt was cancelled or refunded.
feedback.receivedA customer left a rating and a comment.
subscriber.addedSomeone joined your newsletter. The consent text they agreed to is included.
invoice.requestedA company asked for an invoice under its receipt.
POST https://your-system.example/receiptter
Receiptter-Event: receipt.collected
Receiptter-Delivery: evt_4c1d…
Receiptter-Signature: t=1793960000,v1=5f2a…

{
  "id": "evt_4c1d…",
  "type": "receipt.collected",
  "created_at": 1793960000,
  "data": { "id": "rc_9f2c…", "register_id": "reg_71b0…", "total_minor": 1100, "via": "tap", "collected_at": 1793960000 }
}

Check the signature before you trust a request. v1 is the HMAC-SHA256 of "<t>.<body>" with the secret you were shown when you added the address.

const [t, v1] = header.split(',').map(p => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex');
const ok = v1.length === expected.length
  && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
  && Math.abs(Date.now() / 1000 - Number(t)) < 300;

If your side does not answer with 2xx, the event is sent again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours, with the same id, so you can drop repeats. After 20 failed deliveries in a row the address is switched off until you switch it on again in the dashboard.

Errors

StatusMeaning
400A field is missing or invalid. The error text names it.
401Missing, wrong or revoked API key.
402The plan's receipts for this month are used up. New receipts pause until the 1st.
403The merchant account is suspended.
404Unknown till or receipt for this key.
409The receipt is archived and can no longer be cancelled.
429Rate limit. Retry after a short pause.