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:
- 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.
- 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.
- 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.
- Secrets shown once. API keys are returned exactly once at creation time. There is no “retrieve key” endpoint — reducing the window for interception.
- Safe error responses. Authentication failures return identical error structures regardless of whether the credential exists or is simply wrong — preventing credential enumeration.
Credential types
| Credential | Format | Lifetime | Scope | Use case |
|---|
| Platform API key | ofk_platform_... | Long-lived (until rotated/revoked) | All organizations, all registers | Server-side integration |
| Register API key | ofk_reg_... | Long-lived (until rotated) | Single register only | Device-direct integration |
| Merchant credentials | Login-based | Session-scoped | Single organization, read-mostly | Merchant 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 authenticate server-side integrations. The key goes directly in the Authorization: Bearer header on every request — no token exchange needed.
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:
- Create a new key via
POST /auth/api-keys.
- Deploy the new key to your servers.
- 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:
| Endpoint | Purpose |
|---|
POST /auth/api-keys | Issue a new platform API key |
GET /auth/api-keys | List 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:
- Heartbeats —
POST /registers/:id/heartbeat
- Sales, refunds, and other operations — start, mutate, complete, cancel
- Cash drawer openings —
POST /registers/:id/cash-drawer-openings
- Daily closings —
POST /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
| Tier | Credential | Scope | Typical actions |
|---|
| Platform | Platform API key | All organizations | Create orgs, provision registers, process operations, manage keys |
| Merchant | Portal login | Single organization | View receipts, monitor register health, access reports |
| Device | Register API key | Single register | Process 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