Skip to main content
May 19, 2026
Addition
Fiscalization API
Cart-level discounts on SALEPOST /operations for type: "sale" now accepts an optional cart_level_discounts[] array — discounts applied to the order as a whole, not apportioned onto specific line items.
  • Each entry carries an optional description (free-text label, e.g. promo code) and a required positive amount decimal string.
  • line_items[].total_amount values must already reflect their post-discount totals — cart_level_discounts[] is documentation of the order-level reduction, not a re-pricing instruction.
  • The same array is returned on sale operation responses, with server-issued id per entry.
  • For German fiscalization: each cart-level discount emits a separate Rabatt business case in the DSFinV-K cashpoint-closing archive, slotted between the line-item Umsatz lines and any gift-card MehrzweckgutscheinEinloesung lines. Descriptions over 255 characters are trimmed for the DSFinV-K text field; the original is preserved in the operation response.
May 16, 2026
Addition
Fiscalization API
Gift card redemptionspayments[] on POST /operations/{id}/complete now accepts an optional gift_card_id field — the integrator-supplied identifier of the voucher being redeemed.
  • gift_card_id is required when method: "gift_card". Sending a gift_card payment without it (or with empty/whitespace) returns 422 gift_card_id_required.
  • The same field is returned on payments in the operation response.
  • For German fiscalization: each gift_card payment on a sale now emits a MehrzweckgutscheinEinloesung line in the DSFinV-K cashpoint-closing archive, carrying the redeemed amount (negative) and voucher_id. Per §3 Abs. 15 Satz 2 UStG, the redemption is anti-payment rather than revenue, so the gift-card leg is no longer reported under payment_types; the SALE’s full_amount_incl_vat is reduced by the redeemed total and a balancing entry is added to amounts_per_vat_id at the non-taxable bucket. No integrator action needed beyond setting gift_card_id.
May 12, 2026
Addition
Fiscalization API
Gift card line itemsLine items on POST /operations (for sale, return, exchange) now accept an optional type field with values item (default) or gift_card.
  • gift_card lines represent multi-purpose voucher sales (Mehrzweckgutschein). All taxes on a gift_card line must carry rate: "0" and tax_amount: "0" — non-zero rates are rejected with 422 gift_card_tax_rate_must_be_zero. VAT on multi-purpose vouchers is deferred to redemption.
  • An optional gift_card_id field carries the integrator-supplied gift card identifier.
  • The same type and gift_card_id fields are returned on line items in operation responses.
  • For German fiscalization: gift_card lines emit MehrzweckgutscheinKauf (instead of Umsatz) in the DSFinV-K cashpoint-closing archive. No integrator action needed beyond setting type.
May 7, 2026
Addition
Fiscalization API
tax_amount consistency checkEach line_items[].taxes[].tax_amount is now checked against total_amount × rate_i / (1 + Σ rate_j) at 8 dp; mismatches return 422 tax_amount_precision_invalid. Submit tax_amount rounded to 8 dp. See Decimal precision.
May 3, 2026
BreakingAddition
Fiscalization API
Line items now require sku_identifierEach entry in line_items[] on POST /operations (for sale, return, exchange) now requires a sku_identifier field — the merchant’s stable variant identifier (e.g. "TSHIRT-BLUE-M").Identifies a unique number used to maintain and manage the item, product, or merchandise category in the company’s systems. Example: A Tshirt has one sku for each size + color combination.Breaking — sku_identifier is mandatory, as it’s required for regulatory exportsThe same field is returned on every line item in the operation response.
May 2, 2026
BreakingAddition
Fiscalization API
Session close split into count + closeEnd-of-shift is now a two-step flow: the merchant counts the drawer first (session_cash_count), then seals the session (session_close). The two events attest to different fiscal facts and produce up to two TSE signatures from one end-of-shift sequence.
  • session_cash_count — new operation type. Carries counted_closing_amount (and optional discrepancy_note). Server returns expected_closing_amount and signed closing_cashcount_diff (counted − expected). The variance is fiscally attested.
  • session_close — slimmed. Now carries only the optional closing pull/add (closing_adjustment_amount signed: negative = cash out, positive = cash in; plus closing_adjustment_note). Fiscally signed only when the pull is non-zero; null/zero is a state-transition close with no Fiskaly signature.
Breaking — session_close no longer accepts counted_closing_amount or discrepancy_note. Those fields moved to session_cash_count. Bodies that include them on a close return 400.Breaking — session_close requires a preceding session_cash_count. A close on an un-counted session returns 409 session_not_counted.Breaking — once counted, the session is frozen. Posting sale / return / exchange / session_cash_adjustment / a second session_cash_count against a counted session returns 409 session_already_counted. Only session_close is accepted from the counted state.Breaking — SessionCloseOperation response shape narrowed. counted_closing_amount and discrepancy_note are no longer on the close response (they’re on the SessionCashCountOperation response).New session lifecycle state. Sessions transition open → counted → closed (was open → closed). The counted state is short-lived in the happy path — most integrators will POST count then close back-to-back.Update integrator end-of-shift flows to count first, then close, before your next release. See Sessions for the walkthrough.
April 30, 2026
BreakingAddition
Fiscalization API
Register sessions: new operation types + session-bound POS operationsThree new operation types model the cash-drawer shift on a fiscalized register:
  • session_open — start a shift with a counted opening float
  • session_cash_adjustment — record mid-shift cash movement (drop, pay-in, correction)
  • session_close — end the shift with a merchant-counted closing balance
All three flow through POST /operations (no separate sessions endpoint) and carry the same resource_version / ETag mechanics as goods-movement operations. They are born status: "completed" — there is no POST .../complete step for session events.Breaking — POS goods-movement now requires an open session. Sending POST /operations with source: "POS" on a register that has no open session is rejected with 409 no_open_session. Open the register’s session with session_open before posting POS sales, returns, or exchanges. At most one open session per register; opening a second while one is open returns 409 session_invalid_state.New field — session_id on operation responses. Every operation response now carries session_id on the envelope. For POS goods-movement, it’s the session the operation was rung up during. For session-event variants, it’s the session being opened/adjusted/closed. For ONLINE goods-movement (no register), it’s null.Breaking — GET /operations/{id} is now a discriminated union on type. Six variants: SaleOperation, ReturnOperation, ExchangeOperation, SessionOpenOperation, SessionCashAdjustmentOperation, SessionCloseOperation. Each variant carries only the fields meaningful to its type — SESSION_* responses no longer include zero-padded pretax_amount / total_amount / line_items / payments / fiscal_information, and goods-movement responses no longer include null session_*_details placeholders. SALE responses also drop the always-null related_operation_id and external_related_operation.See Sessions for the full walkthrough.Update integrators that issue POS operations to bracket each shift with session_open / session_close before your next release.
April 30, 2026
Breaking
Fiscalization API
Merchant tax_id removed; Italian fiscal_identity reshapedThe top-level Merchant.tax_id field has been removed from create, update, and response shapes. Tax identifiers now live exclusively on fiscal_identity, where they can vary by country.The Italian fiscal_identity object (country_code: "ITA") has been replaced:
  • Removed (both optional): vat_id, codice_fiscale.
  • Added (all required): tax_number (Codice Fiscale — 11 numeric digits or 16 alphanumeric characters), vat_number (Partita IVA — 11 numeric digits), fisconline_user (16 alphanumeric characters; the Codice Fiscale of the person delegated to access the Agenzia delle Entrate portal), fisconline_password, fisconline_pin.
  • legal_entity_type (COMPANY | INDIVIDUAL) is unchanged.
The German (DEU) and Austrian (AUT) fiscal_identity shapes are unchanged.Update integrator code that creates or updates Italian merchants, and stop sending the top-level tax_id field for all merchants, before your next release.
April 29, 2026
Addition
Fiscalization API
New endpoints: POST /exports, GET /exports/{exportId}Two new endpoints expose asynchronous fiscal export jobs.POST /exports enqueues a job and returns 202 Accepted with { id, type, status: "pending", register_id?, from, to, created_at }. Required body fields: type (dsfinvk is the first available type), from and to (ISO 8601 date-times). Optional register_id scopes the export to a single register; when omitted, the export covers all eligible registers under the merchant for the matching fiscal regime.GET /exports/{exportId} returns the job. Once status is completed, download_url is a signed URL to fetch the artifact. When status is failed, error.message (and optional error.details) describes why.Both endpoints require the X-OpenFiskal-Merchant header.
April 29, 2026
Addition
Fiscalization API
external_related_operation accepted on returns/exchangesPOST /operations with type: "return" or type: "exchange" now accepts a new optional field, external_related_operation, for cases where the original sale was never ingested into OpenFiskal (typically during platform migration before backfill).
{
  "type": "return",
  "external_related_operation": {
    "description": "Return against order from external platform.",
    "external_operation_id": "shopify-order-4711"
  }
}
  • Mutually exclusive with related_operation_id — exactly one must be set on a return or exchange.
  • Both fields on the nested object are required strings.
  • The same field is echoed on the operation response.
Use related_operation_id when the original sale exists in OpenFiskal; use external_related_operation otherwise.
April 23, 2026
Breaking
Fiscalization API
Sign convention enforced on total_amount and line_items[].total_amountPOST /operations now rejects payloads whose amount signs do not match Operation.type:
  • sale: total_amount and every line_items[].total_amount must be >= 0.
  • return: total_amount and every line_items[].total_amount must be <= 0.
  • exchange: no sign constraint (an exchange may net positive, negative, or zero).
Zero is permitted on both sale (freebie) and return (zero-value return). Violations return 422 Unprocessable Entity.Sign is the only on-wire signal Fiskaly’s TSE has to distinguish a sale from a return signature, so OpenFiskal enforces it before signing.Update return flows that previously sent positive amounts on type: "return" to send non-positive amounts before your next release.
April 23, 2026
Breaking
Fiscalization API
fiscal_identities[].country renamed to country_code; DE fields now requiredThe discriminator field on every fiscal_identity entry has been renamed from country to country_code. This applies to all three country variants (DEU, AUT, ITA) on both the create/update payload and the merchant response. Payloads that send country will reject.For DEU fiscal identities, tax_number (Steuernummer) and vat_id (USt-IdNr) are now both required. Previously both were optional. AUT and ITA requirements are unchanged.Update integrator code that creates or parses merchant fiscal_identities before your next release.
April 23, 2026
Addition
Fiscalization API
OpenAPI JSON now served at /openapi-jsonThe current production OpenAPI spec is now served at:
  • Production: https://api.openfiskal.com/openapi-json
  • Sandbox: https://sandbox.api.openfiskal.com/openapi-json
The previous Swagger-default path /docs-json is preserved as a 301 redirect to the new path. Import the new URL into Postman, Insomnia, or an OpenAPI code generator to scaffold a client.The OpenAPI spec always matches the live API implementation.
April 23, 2026
Breaking
Fiscalization API
Operation.status enum corrected; 412 payload includes expected_resource_versionThe Operation.status enum has been corrected from 'open' | 'completed' | 'cancelled' to 'open' | 'completed' | 'voided'. Operations that you POST /operations/{id}/void resolve to status: "voided", not cancelled. There were never any operations with status: "cancelled" in production — the OpenAPI value was wrong.412 Precondition Failed error bodies now include details.expected_resource_version alongside details.current_resource_version. The message field is the literal string "Resource version mismatch." (previously "The supplied If-Match value is stale.").Update Operation.status parsers to accept voided, and update any UI that surfaces the precondition_failed error message string.
April 23, 2026
Breaking
Fiscalization API
Operation type refund renamed to returnThe Operation.type enum now uses return instead of refund for operations where goods come back from the customer. The valid values are sale | return | exchange.OpenFiskal now distinguishes goods movement from money movement: operations model goods (sale/return/exchange), refund is reserved for money movement (a negative payment transaction on an operation) and is not an operation type. This unlocks flows the old terminology could not express cleanly — return with store credit (goods back, no money), goodwill refund (money back, no goods), or partial refunds on kept items.
  • POST /operations with type: "refund" will now reject. Use type: "return".
  • Response shapes now return type: "return" for what was previously type: "refund".
  • Payment.status enum is unchanged — refunded is still the correct status for a payment that has been reversed, because it describes money movement.
Update integrator code that creates or parses return/refund operations before your next release.
April 23, 2026
Breaking
Fiscalization API
KassenSichV schema changes
  • FiscalInformationKassenSichVVerification: renamed tse_serial to tss_serial_number and client_id to pos_client_serial_number. Both remain required.
  • FiscalInformationKassenSichVEndEvent: added required public_key (string).
  • FiscalInformationKassenSichVStartEvent: removed transaction_counter and signature. signed_at is now the only required field.
Update integrations that submit or parse KassenSichV fiscal information before your next release.
April 23, 2026
Clarification
Fiscalization API
Country code casingClarified that the country field on Address and on the legal entity schema must be an ISO 3166-1 alpha-3 code in uppercase (for example, DEU). No behavior change; existing uppercase values continue to validate.
April 19, 2026
Addition
Fiscalization API
New endpoint: POST /registers/{registerId}/decommissionPermanently retires a fiscalized register. The endpoint calls the underlying TSE provider to decommission the register’s fiscal components and returns the updated register with decommissionedAt populated. Currently DE/KassenSichV only; AT/IT will follow as those regimes ship publicly.Conflicts return 409 decommission_conflict:
  • Register has not been fiscalized.
  • Register has already been decommissioned.
A decommissioned register cannot be re-fiscalized — create a new register for that location instead.