StabisoDocs

API reference

Organization

The organization of the credential, its API keys and your own profile.

Operation Summary Scope Idempotency-Key
GET /v1/api-keys List API keys admin
POST /v1/api-keys Issue an API key admin
GET /v1/api-keys/{key_id} Get an API key admin
DELETE /v1/api-keys/{key_id} Revoke an API key admin
GET /v1/connectors/authorization Check an app's request to connect read
POST /v1/connectors/authorization Allow or refuse an app read
GET /v1/me Me
PATCH /v1/me Update my preferences
GET /v1/organization Get Organization read
PATCH /v1/organization Rename the organization admin

GET /v1/api-keys

List API keys

The organization's API keys: name, actor type, scopes, created, last used, revoked.

Never the secret or its hash; prefix identifies a key in the audit trail (key:<prefix>).

Required scope admin (admin implies every scope)
Operation id list_keys_v1_api_keys_get

Parameters

In Name Type Required Description
query status "active" | "revoked" or null Only active or only revoked keys; omit for all of them.

Example request

curl "$ERP_URL/v1/api-keys" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK array of KeyInfo
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

[
  {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "name": "string",
    "prefix": "string",
    "actor_type": "string",
    "scopes": [
      "string"
    ],
    "created_at": "2026-09-21T09:30:00Z",
    "last_used_at": "2026-09-21T09:30:00Z",
    "revoked_at": "2026-09-21T09:30:00Z",
    "status": "active",
    "bound_to": "string"
  }
]

POST /v1/api-keys

Issue an API key

Issue an API key for a person or a system, e.g. a scoped key for an AI agent.

The secret is in key of the response and is shown only now: the server keeps just its hash. Send it as Authorization: Bearer <key> or X-API-Key. Only a human with the admin scope may issue keys (403 otherwise). An agent key may not hold the approve or admin scope (422), so an agent can never approve its own work. Issuing a key is an audit event.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id create_key_v1_api_keys_post

Request body

application/json (required): KeyRequest

Field Type Required Description
name string yes A label for the key, e.g. bookkeeping agent; shown in listings.
actor_type "agent" | "human" yes human or agent; system actors exist only inside the server (CLI, jobs).
scopes array of "admin" | "approve" | "audit" | "draft" | "post" | "read" yes What the key may do: read, draft, post, approve, admin, audit (the archive export). admin includes the others; agent keys may not hold approve, admin or audit.

Example request

curl -X POST "$ERP_URL/v1/api-keys" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "actor_type": "agent",
  "name": "bookkeeping agent",
  "scopes": [
    "read",
    "draft",
    "post"
  ]
}'

Responses

Status Meaning Body
201 Created KeyOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "key": "string",
  "actor_type": "string",
  "scopes": [
    "string"
  ],
  "bound_to": "string"
}

GET /v1/api-keys/{key_id}

Get an API key

One API key of the organization, without its secret. A key of another organization, or an unknown id, answers 404.

Required scope admin (admin implies every scope)
Operation id get_key_v1_api_keys__key_id__get

Parameters

In Name Type Required Description
path key_id string (uuid) yes API key id (GET /v1/api-keys), not the secret.

Example request

curl "$ERP_URL/v1/api-keys/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK KeyInfo
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "string",
  "prefix": "string",
  "actor_type": "string",
  "scopes": [
    "string"
  ],
  "created_at": "2026-09-21T09:30:00Z",
  "last_used_at": "2026-09-21T09:30:00Z",
  "revoked_at": "2026-09-21T09:30:00Z",
  "status": "active",
  "bound_to": "string"
}

DELETE /v1/api-keys/{key_id}

Revoke an API key

Revoke a key. From then on requests with it answer 401; the key stays listed with status revoked. Only a human with the admin scope may revoke keys (403 otherwise). The response has no body. Revoking is an audit event.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id revoke_key_v1_api_keys__key_id__delete

Parameters

In Name Type Required Description
path key_id string (uuid) yes API key id (GET /v1/api-keys), not the secret.

Example request

curl -X DELETE "$ERP_URL/v1/api-keys/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
204 No content
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

GET /v1/connectors/authorization

Check an app's request to connect

What the connect page (/app/connect) shows before the person allows an app such as Claude's custom connector: the app's name, the host it returns to, the organization it would work in (the person's own) and what it may do. Refused with 422 (connector_unknown_app, connector_redirect_not_allowed) for a request that does not come from an app registered with this server or would return elsewhere than its own address.

Required scope read (admin implies every scope)
Operation id check_authorization_v1_connectors_authorization_get

Parameters

In Name Type Required Description
query client_id string yes The app's client id. (max length 4000)
query redirect_uri string yes Where the app wants the answer. (max length 2000)

Example request

curl "$ERP_URL/v1/connectors/authorization?client_id=value&redirect_uri=value" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK ConnectorRequest
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "app_name": "Claude",
  "can_post": true,
  "organization": "Demo AG",
  "returns_to": "claude.ai",
  "scopes": [
    "read",
    "draft"
  ]
}

POST /v1/connectors/authorization

Allow or refuse an app

The person's answer to an app's request to connect (OAuth 2.1 authorization code with PKCE, ADR-0082). Allowed, the app gets an agent key of this organization bound to the person: read and draft, with post when post is true and the person may post, never approve or admin, so everything it prepares still waits for a person and it can never be the second person on what this person started. The key waits for the app to exchange the one-time code in redirect_to within ten minutes, then appears among the API keys, where it is revoked. Only a person signed in to the web UI may answer (403 connector_needs_session for an API key); 422 for a request that is not an app's of this server (connector_unknown_app, connector_redirect_not_allowed) or not made with PKCE S256 (connector_request_invalid). Recorded as api_key.create with connected_app.

Required scope read (admin implies every scope)
Idempotency-Key not used
Operation id decide_authorization_v1_connectors_authorization_post

Request body

application/json (required): ConnectorDecision

Field Type Required Description
client_id string yes The app's client id, as it sent it. (max length 4000)
redirect_uri string yes Where the app wants the answer. (max length 2000)
app_state string or null The app's state, returned to it unchanged. (max length 2000)
code_challenge string The PKCE challenge. (max length 200; default "")
code_challenge_method string Must be S256. (max length 20; default "")
allow boolean yes true allows the app; false refuses it.
post boolean Also let it post routine entries within the approval policy. (default false)

Example request

curl -X POST "$ERP_URL/v1/connectors/authorization" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "allow": true,
  "app_state": "af0ifjsldkj",
  "client_id": "erpapp_eyJuIjoiQ2xhdWRlIn0",
  "code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
  "code_challenge_method": "S256",
  "post": false,
  "redirect_uri": "https://claude.ai/api/mcp/auth_callback"
}'

Responses

Status Meaning Body
200 OK ConnectorAnswer
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "redirect_to": "https://claude.ai/api/mcp/auth_callback?code=erpcode_…&state=af0"
}

GET /v1/me

Me

Who the credential is: actor type (human or agent), id, name, organization and scopes.

auth says whether the request was authenticated with an api_key or a web UI session. locale is the UI language of a signed-in person and null for API keys. Any valid credential may call this; it is the quickest check that a key works and what it may do.

Required scope any valid credential
Operation id me_v1_me_get

Example request

curl "$ERP_URL/v1/me" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK Me
401 Missing or invalid API key (unauthorized). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "type": "string",
  "id": "string",
  "name": "string",
  "org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "scopes": [
    "string"
  ],
  "areas": [
    "string"
  ],
  "org_name": "string",
  "auth": "string",
  "locale": "string",
  "bound_to": "string",
  "test_organization": false,
  "test_copy": "masked",
  "other_approvers": 0,
  "approver_acknowledgement_needed": false
}

PATCH /v1/me

Update my preferences

Change your own preferences: the web UI language (locale; null follows the browser).

Only the fields sent change. Preferences belong to people signed in to the web UI: a call with an API key answers 409. Returns the updated identity, as GET /v1/me.

Required scope any valid credential
Idempotency-Key not used
Operation id update_me_v1_me_patch

Request body

application/json (required): Preferences

Field Type Required Description
locale "de-CH" | "en" | "fr-CH" | "it-CH" | "sv-SE" or null UI language; null follows the browser's language.

Example request

curl -X PATCH "$ERP_URL/v1/me" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "locale": "de-CH"
}'

Responses

Status Meaning Body
200 OK Me
401 Missing or invalid API key (unauthorized). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "type": "string",
  "id": "string",
  "name": "string",
  "org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "scopes": [
    "string"
  ],
  "areas": [
    "string"
  ],
  "org_name": "string",
  "auth": "string",
  "locale": "string",
  "bound_to": "string",
  "test_organization": false,
  "test_copy": "masked",
  "other_approvers": 0,
  "approver_acknowledgement_needed": false
}

GET /v1/organization

Get Organization

The organization the credential belongs to: id, name, creation time and whether the default approval policy or the organization's own applies (approval_policy; the policy itself is GET /v1/approval-policy).

Required scope read (admin implies every scope)
Operation id get_organization_v1_organization_get

Example request

curl "$ERP_URL/v1/organization" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK OrganizationOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "approval_policy": "default"
}

PATCH /v1/organization

Rename the organization

Rename the organization and return it. Only a human with the admin scope may do this (agent keys and other scopes get 403); a blank name is refused with 422, an omitted or unchanged name changes nothing. The change is an audit event, org.update. The approval policy has its own resource, /v1/approval-policy.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id update_organization_v1_organization_patch

Request body

application/json (required): OrganizationUpdate

Field Type Required Description
name string or null The new name, 1 to 200 characters; omit to change nothing. (min length 1; max length 200)

Example request

curl -X PATCH "$ERP_URL/v1/organization" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Demo Group AG"
}'

Responses

Status Meaning Body
200 OK OrganizationOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "approval_policy": "default"
}

Schemas

ConnectorAnswer

Field Type Required Description
redirect_to string yes Where the browser goes next: the app's address with a one-time code (code, good for ten minutes), or with error=access_denied when refused.

ConnectorDecision

The person's answer, with the app's request as it came (OAuth 2.1 authorization request).

Field Type Required Description
client_id string yes The app's client id, as it sent it. (max length 4000)
redirect_uri string yes Where the app wants the answer. (max length 2000)
app_state string or null The app's state, returned to it unchanged. (max length 2000)
code_challenge string The PKCE challenge. (max length 200; default "")
code_challenge_method string Must be S256. (max length 20; default "")
allow boolean yes true allows the app; false refuses it.
post boolean Also let it post routine entries within the approval policy. (default false)

ConnectorRequest

What the person is asked to allow.

Field Type Required Description
app_name string yes The name the app registered with.
returns_to string yes The host the person is sent back to once they answer.
organization string yes The organization the app would work in: the person's.
scopes array of string yes What the app may always do: read and draft.
can_post boolean yes Whether the person may also let it post (they hold post themselves).

KeyInfo

An API key without its secret: neither the key nor its hash ever leaves the server.

Field Type Required Description
id string (uuid) yes Id (uuid) of the key.
name string yes The label given when the key was issued.
prefix string yes Identifies the key; audit events name it as key:.
actor_type string yes human or agent.
scopes array of string yes The scopes the key holds: some of read, draft, post, approve, admin, audit.
created_at string (date-time) yes When the key was issued (ISO 8601, UTC).
last_used_at string (date-time) or null yes When a request last used the key; null if never.
revoked_at string (date-time) or null yes When the key was revoked; null while active.
status "active" | "revoked" yes active or revoked.
bound_to string or null The person the key acts for: the actor id of a user (user:<id>). Four eyes and segregation of duties compare people, and a key counts as the person who created it, so a key a signed-in person created is bound to that person. Null when it belongs to no known person (the organization's first key, issued by the system, and every key created with it): it can request but never approve, activate, release or finalise.

KeyOut

Field Type Required Description
id string (uuid) yes Id (uuid) of the key, for GET and DELETE /v1/api-keys/{key_id}.
key string yes Shown once. Store it securely; only its hash is kept.
actor_type string yes human or agent.
scopes array of string yes The scopes the key holds.
bound_to string or null The person the key acts for: the actor id of a user (user:<id>). Four eyes and segregation of duties compare people, and a key counts as the person who created it, so a key a signed-in person created is bound to that person. Null when it belongs to no known person (the organization's first key, issued by the system, and every key created with it): it can request but never approve, activate, release or finalise.

KeyRequest

Field Type Required Description
name string yes A label for the key, e.g. bookkeeping agent; shown in listings.
actor_type "agent" | "human" yes human or agent; system actors exist only inside the server (CLI, jobs).
scopes array of "admin" | "approve" | "audit" | "draft" | "post" | "read" yes What the key may do: read, draft, post, approve, admin, audit (the archive export). admin includes the others; agent keys may not hold approve, admin or audit.

Me

Field Type Required Description
type string yes human or agent.
id string yes Actor id: key:<prefix> for an API key, user:<id> for a signed-in person.
name string yes The key's label or the person's email address.
org_id string (uuid) yes Id (uuid) of the organization the credential belongs to.
scopes array of string yes The scopes held: some of read, draft, post, approve, admin, audit; admin implies the others.
areas array of string or null The areas of the app the person's role opens (ledger, sales, purchasing, bank, assets, close, reports, stock, service; GET /v1/roles/areas); null: every area. An operation outside them answers 403 area_closed.
org_name string or null Name of the organization; null if it cannot be read.
auth string or null api_key or session
locale string or null The user's UI language; null follows the browser.
bound_to string or null The person the credential acts for: the actor id of a user (user:<id>). Four eyes and segregation of duties compare people, and a key counts as the person who created it, so a key a signed-in person created is bound to that person. Null when it belongs to no known person (the organization's first key, issued by the system, and every key created with it): it can request but never approve, activate, release or finalise.
test_organization boolean true for a test organization (a demo, or a test copy made with POST /v1/test-copies): its books are not real and it may be removed. A test copy never e-mails customers. (default false)
test_copy "masked" | "unmasked" or null A test copy (POST /v1/test-copies): masked (its people, customers and vendors are made up; its company's bank accounts are real) or unmasked; null for any other organization. A test copy makes no payment files, sends no e-mail and puts no QR-bill on its invoices.
other_approvers integer How many people besides this one may approve (users with the approve scope, active or invited). 0 means nobody else can approve what this person requests: one's own request always needs a second person, so invite one (POST /v1/users, role controller), such as the company's accountant. (default 0)
approver_acknowledgement_needed boolean True when an administrator named this person an approver and they have not yet confirmed that their approvals are recorded in their name (POST /v1/me/approver-acknowledgement); they decide nothing before. (default false)

OrganizationOut

Field Type Required Description
id string (uuid) yes Id (uuid) of the organization.
name string yes The organization's name.
created_at string (date-time) yes When the organization was created (ISO 8601, UTC).
approval_policy "default" | "organization" yes Whether the default approval policy or the organization's own applies (GET /v1/approval-policy).

OrganizationUpdate

Field Type Required Description
name string or null The new name, 1 to 200 characters; omit to change nothing. (min length 1; max length 200)

Preferences

Field Type Required Description
locale "de-CH" | "en" | "fr-CH" | "it-CH" | "sv-SE" or null UI language; null follows the browser's language.
Stabiso, the Enterprise System of Record · generated from the code and its guides · release 20b7dd5b0c4d