API keys — creation, transport and limits

The key is the only credential for a third party — 40 hex characters shown once and stored as a hash. It has a name, a company, an optional expiry and a per-minute limit. Send it in a header; rotate by creating a new one and deleting the old.

Last updated: 2026-10-09

On this page

No session tokens

A third party has exactly one credential: the API key. No Sanctum tokens, no OAuth — and whatever needs an employee session (the two Dynamics print steps) is not callable from a server.

Creating a key

Settings → Integration → API keys → Add key:

Field Label Rule
name Client name required — “SAP — Finance”
company_id Company required — the company this key writes into; a system serving two companies gets two keys
expires_at Expiry (optional) empty = never expires; a past date is rejected
rate_limit_per_minute Requests / minute 1–100000, default 60

After saving, the key is shown once: “Copy the key now — it won’t be shown again.” Only its SHA-256 hash is stored; there is no retrieval. The key is 40 hex characters (160 random bits).

Columns: ID · Name · Company · Status · Expiry · Requests / minute · Created · Actions.

The Status column reports the effective state, not the key's own flag:

Badge When
Active the key is enabled and its company is active and not deleted — the only state that works
Stopped the key is enabled but its company doesn't accept writes: no company · company disabled · company in the recycle bin
Inactive the key itself is disabled
Expired past its expiry date

The Company column gives the reason: a key with no company carries a No company — stopped badge and a banner with the count; a key stopped because of its company shows the company name with Company disabled or deleted — stopped; and a company in the recycle bin is shown marked (in the recycle bin).

Assigning or moving a key's company from the picker in its row: when its current company isn't an option (none · disabled · in the bin), the picker starts on a non-selectable “— choose a company —”, and the ✓ button stays disabled until the choice differs from the current one. The confirmation names the new company and states that previously stamped cheques keep their company, the old audit rows keep theirs, and the change applies going forward only.

A key follows its company's state

A key stops by itself when its company is disabled or deleted, and comes back automatically when the company is re-enabled or restored — nothing is written to the key itself:

  • External-path and Dynamics calls are refused with 422 “the key's company is disabled or deleted”, and completing a Dynamics draft is refused too.
  • A key with no company is refused with 422 “the API key is not assigned to a company” until one is assigned from this screen.
  • Assigning a key to a disabled or deleted company is refused with “choose an active company” — the list only offers active, non-deleted companies.

Force-deleting a company that still has keys is refused with a message that names the way out: move its keys to another company or delete them first. (Recycle bin → Companies → force delete.)

Transport

Three forms, in precedence order — use the first:

# Form Note
1 Header X-API-KEY: <key> use this
2 ?api_key=<key> in the URL leaks into server logs and Referer — avoid
3 secret field in the body redacted from the audit log, but still transmitted

Rejection ladder

The first matching condition ends the request:

# Condition Status Body
1 Integrations disabled 503 {"error":"Integrations are currently disabled"}
2 No key 401 {"error":"Unauthorized"}
3 Key unknown or inactive 401 same text — deliberately indistinguishable
4 Key expired 403 {"error":"API key expired"}
5 Over the limit 429 {"error":"Too many requests","retry_after_seconds":N}

Rate limiting

A fixed 60-second window per key (not per IP). With a limit of 60, requests 1–60 pass and 61 is the first rejected; retry_after_seconds says when to retry. The limit applies after authentication — a wrong key is not rate-limited (see Security).

Rotation and deletion

There is no “revoke” button: deleting a key is final (“systems using it will no longer be able to connect”). Rotation = create a new key → swap it in the external system → delete the old one. Automatic expiry via expires_at. Deleting a key keeps its audit rows, unattributed.

Key scope

A key opens every endpoint (no partial scopes) inside its company: every cheque created with it is stamped with the key’s company; company_id in the payload is optional — if sent and different ⟹ 422 naming both companies. A reference from another company (bank_account_id · cheque_book_id · cheque_leaf_id · partner_id) ⟹ 422. A key without a company ⟹ an explicit 422 until an administrator assigns one. See Security.

Get your free trial