Skip to main content

Security model

Fiscalization data is legally sensitive — signatures, transaction counters, and audit trails that tax authorities rely on. The authentication model is designed around four principles:
  1. TLS required. All API traffic must use HTTPS (TLS 1.2 or higher). Plain HTTP connections are rejected. Credentials and fiscal data are never transmitted in cleartext.
  2. Static keys over TLS. Platform API keys are long-lived and sent directly in the Authorization header. With TLS enforced on every request, static keys provide the same wire-level protection as short-lived tokens — without the complexity of token exchange, caching, and expiry handling.
  3. Scoped credentials for devices. Register API keys are locked to a single register. A compromised device cannot access other registers, other merchants, or administrative endpoints.
  4. Secrets shown once. API keys are returned exactly once at creation time. There is no “retrieve key” endpoint — reducing the window for interception.
  5. Safe error responses. Authentication failures return identical error structures regardless of whether the credential exists or is simply wrong — preventing credential enumeration.

Credential types

CredentialFormatLifetimeScopeUse case
Platform API keyofk_platform_...Long-lived (until rotated/revoked)All organizations, all registersServer-side integration
Register API keyofk_reg_...Long-lived (until rotated)Single register onlyDevice-direct integration
Merchant credentialsLogin-basedSession-scopedSingle organization, read-mostlyMerchant portal (coming soon)

Credential bootstrapping

Before you can authenticate, you need your first credential. This happens once, out-of-band. After signing a partnership agreement, OpenFiskal emails a one-time setup token (ofk_setup_...) to your technical contact. Exchange it for your first platform API key:
curl -X POST https://api.openfiskal.com/v1/auth/bootstrap \
  -H "Content-Type: application/json" \
  -d '{
    "setup_token": "ofk_setup_abc123def456",
    "label": "Production"
  }'
{
  "object": "platform_api_key",
  "id": "key_01HXYZ",
  "api_key": "ofk_platform_live_abc123...",
  "label": "Production",
  "created_at": "2026-02-26T10:00:00Z"
}
Store api_key immediately in a secrets manager. It is returned once and cannot be retrieved again. The setup token is consumed on use — it cannot be reused.
Why this is secure: The setup token is delivered out-of-band (email to a known contact), is single-use, and expires after 48 hours. The bootstrap endpoint is the only unauthenticated credential endpoint. After bootstrap, all credential management requires an existing API key — preventing unauthorized credential creation. If a setup token is not consumed within the expiry window, contact OpenFiskal to issue a replacement.

Platform API keys

Platform API keys authenticate server-side integrations. The key goes directly in the Authorization: Bearer header on every request — no token exchange needed.

Using a platform API key

curl https://api.openfiskal.com/v1/registers \
  -H "Authorization: Bearer ofk_platform_live_abc123..." \
  -H "OpenFiskal-Organization: org_01HXYZ"
That’s it. No token endpoint, no expiry handling, no refresh logic.
A platform API key has access to all organizations and registers under your platform account. Protect it accordingly: store it in a secrets manager, never expose it to client-side code, device firmware, or version control. If the key is compromised, revoke it immediately and create a replacement.

Key rotation

To rotate a platform API key without downtime:
  1. Create a new key via POST /auth/api-keys.
  2. Deploy the new key to your servers.
  3. Revoke the old key via DELETE /auth/api-keys/{id}.
Revocation is instant — any request using the old key will return 401 immediately.
curl -X POST https://api.openfiskal.com/v1/auth/api-keys/key_01HXYZ/rotate \
  -H "Authorization: Bearer ofk_platform_live_abc123..."
The rotation endpoint atomically issues a new key and revokes the old one in a single call. Use the create-then-revoke pattern above if you need overlap.

Organization scoping

A platform API key can act on behalf of any merchant organization under your platform account. Include the OpenFiskal-Organization header to scope each request:
curl https://api.openfiskal.com/v1/registers \
  -H "Authorization: Bearer ofk_platform_live_abc123..." \
  -H "OpenFiskal-Organization: org_01HXYZ"
There is no need to obtain a separate key per organization. The same key works across all merchants. Why explicit scoping: Requiring the organization header on every request prevents accidental cross-tenant operations. Unlike implicit scoping (where a credential is silently bound to one tenant), explicit scoping makes the target organization visible in every request — easier to audit, harder to misroute.

API key management

Platform API keys can be created, listed, and revoked:
EndpointPurpose
POST /auth/api-keysIssue a new platform API key
GET /auth/api-keysList all active keys (key values are masked)
DELETE /auth/api-keys/{id}Permanently revoke a key
Issue separate keys per environment (production, staging). Rotate periodically — always create the replacement before revoking the old one, so there is no gap in access.

Register API keys

Register API keys are long-lived, device-scoped credentials for hardware that calls the API directly — without routing through a backend server.

When to use register API keys

Use register API keys only for device-direct integrations where the hardware cannot route through a backend:
  • EV chargers
  • Unattended kiosks and vending machines
  • Embedded POS terminals without server connectivity
Server-side integrations should use platform API keys exclusively. The platform API key already authorizes all register operations — there is no reason to distribute device-level keys if your backend is making the calls.

What a register API key can do

A register API key is scoped to a single register and authorizes only register-level operations:
  • HeartbeatsPOST /registers/:id/heartbeat
  • Sales, refunds, and other operations — start, mutate, complete, cancel
  • Cash drawer openingsPOST /registers/:id/cash-drawer-openings
  • Daily closingsPOST /registers/:id/closings
It cannot access other registers, list organizations, manage credentials, or perform any administrative action. If a device is compromised, the blast radius is limited to that single register.

Issuing a register API key

Pass issue_register_credential: true when creating a fiscal unit:
curl -X POST https://api.openfiskal.com/v1/registers/reg_01HXYZ/fiscal-units \
  -H "Authorization: Bearer ofk_platform_live_abc123..." \
  -H "OpenFiskal-Organization: org_01HXYZ" \
  -H "Content-Type: application/json" \
  -d '{ "issue_register_credential": true }'
{
  "object": "fiscal_unit_response",
  "fiscal_unit": { "id": "fu_01HXYZ", "state": "active" },
  "register_api_key": "ofk_reg_live_...",
  "credential_issued": true
}
The API key is returned once. Store it immediately and deliver it to the device over a secure channel (MDM, provisioning API, factory burn). It cannot be retrieved again.
Register API keys do not expire automatically — they remain valid until explicitly rotated or the register is archived. For devices deployed in physically accessible locations (EV chargers, outdoor kiosks), implement a periodic rotation schedule and store the key in encrypted storage or a secure enclave where the hardware supports it.

Using a register API key

The device includes the key in the X-Register-Api-Key header:
curl -X POST https://api.openfiskal.com/v1/registers/reg_01HXYZ/sales \
  -H "X-Register-Api-Key: ofk_reg_live_..." \
  -H "Idempotency-Key: sale-charger42-20260226-001" \
  -H "Content-Type: application/json" \
  -d '{ "...": "..." }'
No OpenFiskal-Organization header is needed — the key is already scoped to a specific register and organization.

Key rotation

If a device is compromised, lost, or replaced, rotate the key immediately from your backend:
curl -X POST https://api.openfiskal.com/v1/registers/reg_01HXYZ/credentials/rotate \
  -H "Authorization: Bearer ofk_platform_live_abc123..." \
  -H "OpenFiskal-Organization: org_01HXYZ"
The old key is revoked instantly — any request using it will return 401. Push the new key to the replacement device before it attempts any API calls.
Key rotation requires a platform API key, not the register API key itself. A compromised device cannot rotate its own key to lock out the legitimate owner.

Merchant credentials

Merchant credentials are coming soon. This section describes the planned model.
Merchant credentials are login-based credentials for the OpenFiskal merchant portal. They are scoped to a single organization and provide read-mostly access — merchants can view receipts, monitor registers, and access reports, but cannot modify fiscal configuration or manage platform-level resources.

Three-tier permission model

TierCredentialScopeTypical actions
PlatformPlatform API keyAll organizationsCreate orgs, provision registers, process operations, manage keys
MerchantPortal loginSingle organizationView receipts, monitor register health, access reports
DeviceRegister API keySingle registerProcess operations, send heartbeats
Platform credentials are for POS vendors building integrations. Merchant credentials are for the merchants themselves. Device credentials are for autonomous hardware.

Idempotency

All mutating endpoints accept an Idempotency-Key header. Including one is strongly recommended for start and complete actions — retrying a fiscalization event without an idempotency key risks creating duplicate fiscal records.
curl -X POST https://api.openfiskal.com/v1/registers/reg_01HXYZ/sales \
  -H "Authorization: Bearer ofk_platform_live_abc123..." \
  -H "OpenFiskal-Organization: org_01HXYZ" \
  -H "Idempotency-Key: sale_abc123_start" \
  -H "Content-Type: application/json" \
  -d '{ "...": "..." }'
If a request with the same idempotency key has already been processed, the original response is returned without creating a new fiscal event. This makes retries safe — critical for offline replay where the same queue entry may be sent multiple times. Idempotency keys are retained for 7 days. Requests replayed within this window are guaranteed to be deduplicated. For offline scenarios exceeding 7 days, generate a new idempotency key before retrying — the operation ID (client-generated UUID) still prevents duplicate fiscal records.

Rate limiting

All endpoints are rate-limited. Authentication endpoints (/auth/bootstrap) enforce stricter limits to prevent brute-force attacks on credentials. Repeated failed authentication attempts from the same source trigger progressive backoff. When a rate limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header indicating when the next request will be accepted.

Audit trail

OpenFiskal logs all authentication events:
  • API key usage (first use, last use per key)
  • Failed authentication attempts (platform API key and register API key)
  • Key creation and revocation
  • Register API key rotation
These events are available through the platform audit log. Failed authentication attempts are surfaced in monitoring — a spike in failures for a specific key or register may indicate a compromise attempt.

Topology reference

For guidance on choosing between server-side and device-direct integration architectures, see Integration Topologies.

Next steps