Skip to main content
This guide walks the v1 integration contract for Germany. You create a deu API key, onboard a merchant with a German fiscal_identity, register a location and terminal, fiscalize the register under KassenSichV, open a register session, start an operation, and complete it with the typed payment contract.
Examples use https://sandbox.api.openfiskal.com/v1. Replace with https://api.openfiskal.com/v1 when you go live.

Prerequisites

  • An OpenFiskal tenant
  • A tenant-scoped, country-scoped API key (of_test_deu_โ€ฆ or of_live_deu_โ€ฆ)
  • curl or an HTTP client
  • A backend service / database to store your API key, entity IDs and ETags

Authentication model

Use the standard bearer header on every request. The key encodes the environment and country โ€” of_test_deu_โ€ฆ for the German sandbox, of_live_deu_โ€ฆ for German production. A deu key rejects payloads for any other country.
Every merchant-scoped request must also include:
This header value is the merchant ID returned by POST /merchants, not your API key.

Create resources

Create these resources in order. The register cannot fiscalize until the merchant has a DEU fiscal_identity.
1

Create your API key

Generate a deu key at console.openfiskal.com. Store it in your secrets manager. Format: of_{env}_deu_{random}, e.g. of_test_deu_abcdefgh12345678.
2

Create a merchant

country_code and every address.country_code must be DEU (ISO 3166-1 alpha-3). The German fiscal_identity requires both tax_number (Steuernummer) and vat_id (USt-IdNr).
The returned id becomes the X-OpenFiskal-Merchant header on every merchant-scoped request that follows.
3

Create a location

A location is a physical point of sale. timezone is a required IANA zone string.
4

Create a register

A register is the logical checkout or terminal that creates operations.
5

Fiscalize the register

Before the register can accept operations, call POST /registers/{id}/fiscalize. This provisions the KassenSichV components โ€” the TSS and POS client โ€” using the merchantโ€™s German fiscal_identity and the locationโ€™s country. The response is 202 Accepted; poll GET /registers/{id} until fiscalizedAt is populated.
6

Verify your saved IDs

Persist the returned merchant, location, and register IDs. You will use them for every later operation request.

Perform a fiscalized sale

Once your register is fiscalized, you can create and complete sale operations.
1

Open a session

Every POS sale, return, or exchange must bind to an open register session. Send a session_open operation with the register, currency, and counted opening cash float. Without an open session, POST /operations with source: POS is rejected.
Persist the returned session_id. See Sessions for cash adjustments, end-of-shift counting, and closing.
2

Start an operation

POST /operations is a single-shot create. All monetary fields are decimal strings, not integers. pretax_amount + tax_amount + tip_amount must equal total_amount. line_items is required with at least one entry.
Sending line_items[].taxes[] is required.
Response:
3

Complete the operation

Completion includes typed payment legs. Use one entry per tender leg and include processor references where available. Send the latest ETag in If-Match. The completed operation returns fiscal_information with the KassenSichV regime.
Operation bodies are immutable after creation โ€” there is no endpoint to amend them. Build the full line-item and amount set before calling POST /operations. Open operations only accept POST .../complete and POST .../void.
Response:
A completed operation returns the receipt data under fiscal_information. Print verification.qr_data as a QR code on your receipt, exactly as returned, byte for byte. That code carries the full TSE data, including both serial numbers, so it covers the TSE requirements on its own. For the required fields, where each comes from, and example layouts, see German receipt requirements.

Void an open operation

If the sale is abandoned before completion, void the open operation:

Next steps