Public API v1

Fiscalise receipts from any system

One documented JSON contract for any POS, ERP or e-commerce platform. Post a completed sale and get back the ZIMRA fiscal data plus a print-ready receipt — 80mm/58mm ESC/POS bytes or an A4 PDF. You write no receipt layout code.

Point-of-sale system sending a JSON receipt to the QRFRS API, fiscalised with ZIMRA and returned as a printed receipt with a QR code

Any system

Canonical schema, plus a lenient resolver for common field-name variants.

Print-ready

80mm / 58mm ESC/POS bytes and an A4 PDF, built on our side.

Secure

Per-merchant revocable API tokens and HMAC-SHA256 signed webhooks.

Start here: request an FDMS test device

Test devices are provisioned by QRFRS, not self-service. Email support@qrfiscalreceipt.com with the merchant name and TIN, and we set up a ZIMRA test device, portal account and API token for you before you start coding.

Integration in six steps

From first email to a live merchant. Most integrations take two to five working days.

  1. 1

    Contact QRFRS for a test device

    Before you write any code, email us and we set up an FDMS test device for you on the ZIMRA test environment. Self-service test devices are not available yet — every sandbox is provisioned by our team so the certificate, taxes and fiscal day are correct from day one.

    • Tell us the merchant name, TIN and VAT number (or use ours for a pure sandbox).
    • We register the test device, open a fiscal day and create the merchant portal account.
    • You receive the portal login plus a test API token — usually within one business day.
    Contact QRFRS for a test device
  2. 2

    Get the API token

    The merchant opens their QRFRS portal, goes to Settings → External API and generates a token. One token per merchant, revocable at any time. Send it on every request.

    Authorization: Bearer <api_token>
  3. 3

    Post each completed sale

    One HTTP call per sale, and one per refund (a CREDIT_NOTE referencing original_receipt_number). We fiscalise with ZIMRA and answer in the same response.

    curl -X POST https://qrfiscalreceipt.com/api/public/v1/receipts?formats=escpos80 \
      -H "Authorization: Bearer <api_token>" \
      -H "Content-Type: application/json" \
      -d '{
        "receipt_number": "INV-000123",
        "receipt_date": "2026-08-28T10:15:00Z",
        "receipt_type": "SALE",
        "currency": "USD",
        "branch_id": "SHOP-01",
        "device_id": "TILL-2",
        "lines": [
          { "description": "Baby food puree", "hs_code": "21041000",
            "quantity": 5, "unit_price": 1.00, "line_total": 5.00,
            "tax_code": "A", "tax_rate": 15 }
        ],
        "payments": [{ "method": "Cash", "amount": 5.00 }],
        "total": 5.00
      }'
  4. 4

    Print what comes back

    Write the ESC/POS bytes straight to the thermal printer, or open the A4 PDF. If you prefer your own layout, print our qr_url and verification_code on it — both are mandatory on a ZIMRA fiscal receipt.

    Print what comes back
    const bytes = Buffer.from(res.escpos_80_base64, "base64");
    socket.write(bytes);                       // network printer on port 9100
    // Windows: fs.writeFileSync("\\\\.\\USB001", bytes)
  5. 5

    Certify on the test device

    Run your full flow against the test device: a normal sale, a multi-line sale, a zero-rated and an exempt line, a multi-currency sale, a credit note, and a retry of the same receipt_number. Send us the receipt numbers and we verify them against ZIMRA with you.

  6. 6

    Go live

    The merchant registers their production ZIMRA device in the portal and generates a production token. Nothing changes in your code — swap the token, keep the same endpoints.

What the response looks like

{
  "receipt_id": "0f0b…",
  "receipt_number": "INV-000123",
  "status": "fiscalised",
  "fiscal_day_no": 42,
  "receipt_global_no": 1187,
  "verification_code": "A1B2-C3D4-E5F6",
  "qr_url": "https://receipt.zimra.org/…",
  "pdf_url": ".../api/public/v1/receipts/0f0b…/pdf",
  "escpos_url": ".../api/public/v1/receipts/0f0b…/escpos",
  "escpos_80_base64": "G0AbYQE…"
}

Re-posting the same receipt_number returns the original result with status: "duplicate" — retries never double-fiscalise.

Endpoints

POST/api/public/v1/receiptsFiscalise a receipt, get the result in the same response
GET/api/public/v1/receipts/{id}Fiscal metadata for a receipt
GET/api/public/v1/receipts/{id}/escpos?width=80|58Raw ESC/POS bytes (or base64 with format=json)
GET/api/public/v1/receipts/{id}/pdfA4 PDF tax invoice

Error codes

UNAUTHORIZEDMissing or invalid API token
INVALID_PAYLOADSchema violation — issues[] names the fields
DEVICE_NOT_CONFIGUREDMerchant has no active ZIMRA device
DAY_CLOSEDNo fiscal day is open
MISSING_ORIGINAL_RECEIPTCredit note without a valid original receipt
INVALID_TAXTax code or rate not registered on the device
DEVICE_BUSYAnother receipt is submitting — retry shortly
ZIMRA_UNAVAILABLEZIMRA unreachable — safe to retry

What you need from the merchant

  • An API token from their QRFRS portal.
  • Their branch and till identifiers, if they run more than one.
  • HS codes and tax codes for their products — ZIMRA requires them per line.
  • An open fiscal day, or the automatic day cycle switched on.

Common blockers

  • Missing HS codes — the single most frequent cause of a failed go-live.
  • Tax codes that are not registered on the merchant's device.
  • Reusing a receipt_number for a different sale.
  • Posting to a branch that has fiscalisation switched off in the portal.

Ready to start?

Email us for an FDMS test device and we will have your sandbox, portal account and API token ready, then walk your team through certification.