API-Schlüssel

API-Schlüssel ausstellen, rotieren, sperren und widerrufen – mit den Scopes, die jede Operation verlangt.

Issue, rotate, suspend, and revoke programmatic API keys.

8 Operationen · Basis-URL https://api.fortlauf.de/v1

Operationen

  1. GET /tenants/{tenantId}/api-key-status Whether the tenant has ≥1 usable live API key
  2. GET /tenants/{tenantId}/api-keys List the tenant's API keys (masked)
  3. POST /tenants/{tenantId}/api-keys Issue an API key
  4. POST /tenants/{tenantId}/api-keys/{id}/rotate Rotate a key
  5. POST /tenants/{tenantId}/api-keys/{id}/revoke Revoke a key immediately
  6. POST /tenants/{tenantId}/api-keys/{id}/suspend Suspend a key (operator-driven, reversible)
  7. POST /tenants/{tenantId}/api-keys/{id}/unsuspend Clear a key's suspension
  8. DELETE /tenants/{tenantId}/api-keys/{id} Revoke a key (REST alias for /revoke)

Whether the tenant has ≥1 usable live API key

GET /tenants/{tenantId}/api-key-status

A boolean signal the dashboard reads during session-mode resolution to pick a fresh session's default (Live vs Test). Open to every active member (no requireRole) and any valid credential; returns only the boolean, no key material.

Erforderlicher Scope
keiner
Zugang
angemeldete Sitzung oder API-Schlüssel (authenticated)

Parameter

Parameter In Typ Beschreibung
tenantId erforderlich Pfad string (uuid)

The tenant the key belongs to. Must equal the caller's active tenant (the resolved auth context) — a mismatch is 403 tenant_mismatch.

Antworten

  • 200

    The live-key presence signal.

    application/json ApiKeyStatus

  • 403

    tenant_mismatch — the path tenantId is not the caller's active tenant.

    application/json ValidationError

List the tenant's API keys (masked)

GET /tenants/{tenantId}/api-keys

Admin/owner only (role-gated; a capability scope for key management is not offered on this route). Returns MASKED keys only: the plaintext is NEVER retrievable after issuance (reveal-once, unlike webhook secrets). Each key carries its displayPrefix, scopes, mode, derived status, and lastUsedAt.

Erforderlicher Scope
keiner
Zugang
nur mit angemeldeter Sitzung – ein API-Schlüssel wird abgelehnt (oidc-only)

Parameter

Parameter In Typ Beschreibung
tenantId erforderlich Pfad string (uuid)

The tenant the key belongs to. Must equal the caller's active tenant (the resolved auth context) — a mismatch is 403 tenant_mismatch.

Antworten

  • 200

    The tenant's API keys (masked).

    application/json object

  • 403

    Caller lacks the admin role, or path tenantId ≠ active tenant.

Issue an API key

POST /tenants/{tenantId}/api-keys

Mints a key and returns the full plaintext EXACTLY ONCE (key) in this 201 body — it is never stored (only its SHA-256 hash) and never retrievable again (genuine reveal-once, contrast webhook secrets which are encrypted-and-retrievable). mode is REQUIRED (live/test) and permanently fixes the key's mode and its sk_live_/sk_test_ prefix — the one place mode comes from request input. entityId is optional (omitted = full tenant scope; must belong to the tenant). scopes optional, defaults to the full 8-scope set. Honors the optional Idempotency-Key header so a retried issuance does not mint a SECOND active credential. A retry of an issuance that already succeeded answers 409 idempotency_replay_secret_withheld with the key it created, minus the plaintext key: the secret is shown once and never stored for a replay.

Erforderlicher Scope
keiner
Zugang
nur mit angemeldeter Sitzung – ein API-Schlüssel wird abgelehnt (oidc-only)

Parameter

Parameter In Typ Beschreibung
tenantId erforderlich Pfad string (uuid)

The tenant the key belongs to. Must equal the caller's active tenant (the resolved auth context) — a mismatch is 403 tenant_mismatch.

Idempotency-Key Header string

Optional client-supplied key that makes this mutating request safe to retry. The FIRST request runs and its outcome is recorded keyed by (tenant, route, key); a REPLAY (same key + byte-identical body, after the first completed) returns the recorded response verbatim WITHOUT re-executing — so a network-blip retry never creates a duplicate customer/subscription/key. A key reused with a DIFFERENT body is 409 IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_BODY; a key whose first request is still in flight is 409 REQUEST_IN_PROGRESS (retryable). Keys expire after 24h — a key reused after expiry is a fresh request, not a replay. Transparent when omitted (the request runs normally).

Länge: max. 255

Request-Body erforderlich

application/json CreateApiKey

Antworten

  • 201

    Created. Body is the masked key PLUS the one-time plaintext key.

    application/json ApiKeyWithSecret

  • 400

    Validation error or invalid_entity (entityId not in the tenant).

  • 402

    The plan tier does not include LIVE API keys. Evaluated on the RESOLVED mode, so omitting mode on a live session is refused too. No key row and no key event are written. A test key is never refused, at any tier and at any usage level — the sandbox is unlimited, permanent and card-free.

    application/json EntitlementRequiredError

  • 403

    Caller lacks the admin role, or path tenantId ≠ active tenant.

  • 409

    Idempotency-Key conflict, on a route whose success body carries a one-time secret. IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_BODY — the key was already used with a different request body; REQUEST_IN_PROGRESS — the first request bearing this key is still in flight (retryable); idempotency_replay_secret_withheld — the first request bearing this key already SUCCEEDED. Nothing new was created. The secret it returned is shown only once and is never stored for a replay, so this answer carries the created resource in resource without the secret. Do not retry it.

Rotate a key

POST /tenants/{tenantId}/api-keys/{id}/rotate

Mints a SUCCESSOR key (fresh secret) carrying the predecessor's mode, entity, scopes, and name, with rotatedFrom linking the pair. The old key is NOT auto-revoked (no forced overlap window — the customer revokes it on their own schedule; staleOverlapWarning nudges them if they keep using it). Returns the new plaintext ONCE (201).

Erforderlicher Scope
keiner
Zugang
nur mit angemeldeter Sitzung – ein API-Schlüssel wird abgelehnt (oidc-only)

Parameter

Parameter In Typ Beschreibung
tenantId erforderlich Pfad string (uuid)

The tenant the key belongs to. Must equal the caller's active tenant (the resolved auth context) — a mismatch is 403 tenant_mismatch.

id erforderlich Pfad string (uuid)

API key id.

Antworten

  • 201

    The new (masked) key PLUS its one-time plaintext key.

    application/json ApiKeyWithSecret

  • 404

    Not found / not visible to the caller.

Revoke a key immediately

POST /tenants/{tenantId}/api-keys/{id}/revoke

Permanent. Rejected by api's own verification on the very next request (immediate at the api layer, independent of any gateway cache). Idempotent — revoking an already-revoked key returns it unchanged. revoked_at never clears (contrast suspension).

Erforderlicher Scope
keiner
Zugang
nur mit angemeldeter Sitzung – ein API-Schlüssel wird abgelehnt (oidc-only)

Parameter

Parameter In Typ Beschreibung
tenantId erforderlich Pfad string (uuid)

The tenant the key belongs to. Must equal the caller's active tenant (the resolved auth context) — a mismatch is 403 tenant_mismatch.

id erforderlich Pfad string (uuid)

API key id.

Antworten

  • 200

    The revoked (masked) key.

    application/json ApiKey

  • 404

    Not found / not visible.

Suspend a key (operator-driven, reversible)

POST /tenants/{tenantId}/api-keys/{id}/suspend

Same immediate rejection as revoke, but REVERSIBLE (suspended_at, distinct from revoked_at) — for abuse / non-payment hard-blocks ("never a quota, always a suspension"). Idempotent.

Erforderlicher Scope
keiner
Zugang
nur mit angemeldeter Sitzung – ein API-Schlüssel wird abgelehnt (oidc-only)

Parameter

Parameter In Typ Beschreibung
tenantId erforderlich Pfad string (uuid)

The tenant the key belongs to. Must equal the caller's active tenant (the resolved auth context) — a mismatch is 403 tenant_mismatch.

id erforderlich Pfad string (uuid)

API key id.

Antworten

  • 200

    The suspended (masked) key.

    application/json ApiKey

  • 404

    Not found / not visible.

Clear a key's suspension

POST /tenants/{tenantId}/api-keys/{id}/unsuspend

Reverses a suspension so the key verifies again. Idempotent — a non-suspended key returns unchanged. A revoked key stays revoked (revocation is not clearable). A key the platform operator suspended cannot be reactivated here; it can still be rotated, or a new key created.

Erforderlicher Scope
keiner
Zugang
nur mit angemeldeter Sitzung – ein API-Schlüssel wird abgelehnt (oidc-only)

Parameter

Parameter In Typ Beschreibung
tenantId erforderlich Pfad string (uuid)

The tenant the key belongs to. Must equal the caller's active tenant (the resolved auth context) — a mismatch is 403 tenant_mismatch.

id erforderlich Pfad string (uuid)

API key id.

Antworten

  • 200

    The re-activated (masked) key.

    application/json ApiKey

  • 404

    Not found / not visible.

  • 409

    api_key_held_by_suspension — the platform operator suspended this key; nothing was changed.

    application/json ValidationError

Revoke a key (REST alias for /revoke)

DELETE /tenants/{tenantId}/api-keys/{id}

Idiomatic-DELETE alias of POST /revoke — same permanent revocation, returns 204.

Erforderlicher Scope
keiner
Zugang
nur mit angemeldeter Sitzung – ein API-Schlüssel wird abgelehnt (oidc-only)

Parameter

Parameter In Typ Beschreibung
tenantId erforderlich Pfad string (uuid)

The tenant the key belongs to. Must equal the caller's active tenant (the resolved auth context) — a mismatch is 403 tenant_mismatch.

id erforderlich Pfad string (uuid)

API key id.

Antworten

  • 204

    Revoked.

  • 404

    Not found / not visible.

Datenmodelle

Jedes Datenmodell dieser Gruppe einmal, mit allen Feldern – auch den verschachtelten. Verschachtelte Objekte sind aufklappbar; ein benannter Typ verweist auf seinen eigenen Abschnitt.

ApiKeyStatus object

Whether the tenant has at least one usable live API key.

Feld Typ Beschreibung
hasLiveKey erforderlich boolean

Verwendet von: GET /tenants/{tenantId}/api-key-status

ValidationError object

A refused request body. error is validation_error when the body was read and failed validation (each problem is listed in issues), or invalid_json when the body is not valid JSON at all (then issues is absent). An unparsable body is answered 400 invalid_json on every operation that takes a JSON body. The submitted body is never echoed.

Feld Typ Beschreibung
error string
issues Array<object>
Felder
Feld Typ Beschreibung
path string
message string
code string

Optional machine-readable reason for the refusal, for a client that shows its own wording instead of message. Present only where an endpoint documents it; an issue without it is refused for the reason message states. Today: invalid_address (an entry of a copy list is not one valid address), too_many_addresses (more than 5 entries in one copy list), copies_require_email (copy recipients are set but email is empty), and service_timing_conflict (POST /invoices, PUT /invoices/{id}: the invoice would state both a single service date and a service period; the issue's path is deliveryDate). On the invoice saves and POST /invoices/preview-totals also reverse_charge_buyer_vat_id_required (a reverse-charge line needs the buyer's VAT identification number), vat_category_unavailable (the line's VAT category is not available), vat_rate_category_mismatch (the rate does not fit the line's VAT category), vat_exemption_reason_required / vat_exemption_reason_not_allowed (the exemption reason is missing, or not allowed for that category), and, only where the deployment enforces the check, unit_code_unknown, currency_code_unknown and vat_id_prefix_unknown (the same three also appear on quote and recurring-template saves, with the same paths). The set may grow, so treat an unknown value like an absent one. Never carries a submitted value.

Verwendet von: GET /tenants/{tenantId}/api-key-status , POST /tenants/{tenantId}/api-keys/{id}/unsuspend

ApiKey object

A customer-issuable API key, MASKED. The plaintext is NEVER present here — only the safe-to-display displayPrefix (sk_live_a1b2c3d4).

Feld Typ Beschreibung
id erforderlich string (uuid)
name erforderlich string
mode erforderlich string

Set at creation, permanently fixed; also determines the key prefix.

Werte: "live", "test"
entityId erforderlich string (uuid) | null

NULL = full tenant scope; otherwise the key is restricted to this entity.

displayPrefix erforderlich string

Masked key identifier (sk_live_ + first 8 base62 chars).

scopes erforderlich Array<ApiKeyScope>
status erforderlich string

Derived. revoked (permanent) > suspended (reversible) > expired > active.

Werte: "active", "revoked", "suspended", "expired"
createdAt erforderlich string (date-time)
expiresAt erforderlich string (date-time) | null
lastUsedAt erforderlich string (date-time) | null
revokedAt erforderlich string (date-time) | null
suspendedAt erforderlich string (date-time) | null
rotatedFrom erforderlich string (uuid) | null

The predecessor key this one was rotated from; NULL if first-issued.

staleOverlapWarning erforderlich boolean

Rotation nudge: true when this key was rotated away from but is still valid AND still recently used (the customer forgot to retire it). Computed at read time; dashboard-facing only.

Verwendet von: GET /tenants/{tenantId}/api-keys , POST /tenants/{tenantId}/api-keys/{id}/revoke , POST /tenants/{tenantId}/api-keys/{id}/suspend , POST /tenants/{tenantId}/api-keys/{id}/unsuspend

CreateApiKey object

Feld Typ Beschreibung
name erforderlich string
Länge: min. 1, max. 200
mode erforderlich string

Required. Permanently fixes the key's mode and prefix.

Werte: "live", "test"
entityId string (uuid)

Optional; omitted = full tenant scope. Must be an entity of the tenant (else 400).

scopes Array<ApiKeyScope>

Optional; defaults to the full 8-scope set when omitted.

Elemente: min. 1
expiresAt string (date-time)

Optional expiry; must be in the future. Verification rejects the key past it.

Verwendet von: POST /tenants/{tenantId}/api-keys

ApiKeyWithSecret object

An ApiKey plus the full plaintext key, returned ONCE at issuance.

Feld Typ Beschreibung
id erforderlich string (uuid)
name erforderlich string
mode erforderlich string

Set at creation, permanently fixed; also determines the key prefix.

Werte: "live", "test"
entityId erforderlich string (uuid) | null

NULL = full tenant scope; otherwise the key is restricted to this entity.

displayPrefix erforderlich string

Masked key identifier (sk_live_ + first 8 base62 chars).

scopes erforderlich Array<ApiKeyScope>
status erforderlich string

Derived. revoked (permanent) > suspended (reversible) > expired > active.

Werte: "active", "revoked", "suspended", "expired"
createdAt erforderlich string (date-time)
expiresAt erforderlich string (date-time) | null
lastUsedAt erforderlich string (date-time) | null
revokedAt erforderlich string (date-time) | null
suspendedAt erforderlich string (date-time) | null
rotatedFrom erforderlich string (uuid) | null

The predecessor key this one was rotated from; NULL if first-issued.

staleOverlapWarning erforderlich boolean

Rotation nudge: true when this key was rotated away from but is still valid AND still recently used (the customer forgot to retire it). Computed at read time; dashboard-facing only.

key erforderlich string

The full plaintext key. Shown ONCE here and never again — not retrievable, not logged, not stored (only its hash is).

Verwendet von: POST /tenants/{tenantId}/api-keys , POST /tenants/{tenantId}/api-keys/{id}/rotate

EntitlementRequiredError object

402 body when the tenant's plan tier does not include the capability the request needs. Cleared by an upgrade, never by the caller — which is what distinguishes it from entity_limit_reached (409), a conflict with current state that the same caller clears by deactivating an entity. Nothing already issued is affected: an entitlement change never touches a document, never touches the archive and never revokes a read.

Feld Typ Beschreibung
error erforderlich string
Werte: "entitlement_required"
message string
capability erforderlich string

The capability the current plan does not include.

Werte: "live_issuance", "live_api_keys"
planTier erforderlich string
Werte: "launch", "entry", "platform", "sovereign"
upgradeUrl string

Relative dashboard hint to the plan surface.

Verwendet von: POST /tenants/{tenantId}/api-keys

IdempotencyConflictOrSecretWithheldError object

Feld Typ Beschreibung
error erforderlich string
Werte: "IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_BODY", "REQUEST_IN_PROGRESS", "idempotency_replay_secret_withheld"
message string
resource object

Only with idempotency_replay_secret_withheld: the resource the first request created, as it answered then but WITHOUT the one-time secret field.

Verwendet von: POST /tenants/{tenantId}/api-keys

ApiKeyScope string

The canonical customer-issuable capability set. This enum is the authoritative published list: a key-issuance request is validated against exactly the same vocabulary the server enforces, so a scope listed here is a scope the server accepts, and a scope absent here is refused. The equality is asserted by an automated check on every build, so the published list cannot drift from the enforced one.

  • Werte: "documents:write", "documents:read", "customers:write", "customers:read", "subscriptions:write", "subscriptions:read", "archive:read", "webhooks:manage", "entities:read", "entities:write", "validation:run", "sandbox:write", "payments:write", "payments:read"

Verwendet von: GET /tenants/{tenantId}/api-keys , POST /tenants/{tenantId}/api-keys , POST /tenants/{tenantId}/api-keys/{id}/rotate , POST /tenants/{tenantId}/api-keys/{id}/revoke , POST /tenants/{tenantId}/api-keys/{id}/suspend , POST /tenants/{tenantId}/api-keys/{id}/unsuspend

Nach oben · Alle Ressourcengruppen · OpenAPI-Spezifikation (YAML)