StabisoDocs

API reference

Approvals

The queue of actions waiting for a second person, and the decision.

Operation Summary Scope Idempotency-Key
GET /v1/approvals List Approvals read
POST /v1/approvals/{approval_id}/approve Approve a request approve
POST /v1/approvals/{approval_id}/reject Reject a request approve
POST /v1/approvals/{approval_id}/withdraw Withdraw one's own request admin
GET /v1/approvers The approver register read
POST /v1/me/approver-acknowledgement Confirm that my approvals are recorded in my name read

GET /v1/approvals

List Approvals

The approval requests of the organization with the given status, oldest first. A request is created when the approval policy sends an action to a second person, e.g. a journal entry or a new vendor; object_type and object_id say what it is about. With for_me, also one's own steps the AI reviewer held that one releases oneself, being the only person who could (ADR-0101).

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

Parameters

In Name Type Required Description
query status "approved" | "pending" | "rejected" or null pending (default), approved or rejected. (default "pending")
query entity string or null Only the requests of this entity (code), the ones the Role Center counts for it. Default: the whole organization.
query for_me boolean Only the requests the caller may decide under the four-eyes principle: not requested, created, prepared or submitted by the caller (whichever key or session); none for a key the system issued. The Role Center's pending_for_me. (default false)

Example request

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

Responses

Status Meaning Body
200 OK array of ApprovalOut
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",
    "object_type": "string",
    "object_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "action": "string",
    "status": "string",
    "requested_by": {},
    "reason": "string",
    "summary": {},
    "decided_by": {},
    "decided_at": "2026-09-21T09:30:00Z",
    "comment": "string",
    "created_at": "2026-09-21T09:30:00Z",
    "withdrawn": false,
    "reason_words": {
      "words": "string",
      "code": "string",
      "details": null,
      "text": "string"
    }
  }
]

POST /v1/approvals/{approval_id}/approve

Approve a request

Approve a pending request; the action it asked for then happens (a journal entry is posted, a vendor activated). The body, with an optional comment, may be left out.

Refused with 403 when the caller is the person or key that made the request (four_eyes: a second person must approve, whatever the caller's scopes), lacks the approve scope or is denied by the policy (agents are); with 409 when the request is no longer pending, or when the action fails, e.g. the period is closed or a credit note now exceeds what is left to credit on the document it credits (another credit note took it while this one waited; 422 when its lines without an obligation would credit more than the invoice's other lines left).

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

Parameters

In Name Type Required Description
path approval_id string (uuid) yes Approval request id (GET /v1/approvals).

Request body

application/json (optional): Decision or null

Example request

curl -X POST "$ERP_URL/v1/approvals/3fa85f64-5717-4562-b3fc-2c963f66afa6/approve" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "comment": "Checked against the vendor statement"
}'

Responses

Status Meaning Body
200 OK ApprovalOut
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

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "object_type": "string",
  "object_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "action": "string",
  "status": "string",
  "requested_by": {},
  "reason": "string",
  "summary": {},
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "comment": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "withdrawn": false,
  "reason_words": {
    "words": "string",
    "code": "string",
    "details": {},
    "text": "string"
  }
}

POST /v1/approvals/{approval_id}/reject

Reject a request

Reject a pending request; the action does not happen (a journal entry becomes rejected and is never posted). The body, with an optional comment saying why, may be left out. The same refusals as approving apply: four-eyes (403), scope or policy (403), already decided (409). The person who asked to raise or remove a posting approval limit or to relax the approval policy takes her request back with POST .../withdraw instead.

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

Parameters

In Name Type Required Description
path approval_id string (uuid) yes Approval request id (GET /v1/approvals).

Request body

application/json (optional): Decision or null

Example request

curl -X POST "$ERP_URL/v1/approvals/3fa85f64-5717-4562-b3fc-2c963f66afa6/reject" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "comment": "Amount does not match the bill"
}'

Responses

Status Meaning Body
200 OK ApprovalOut
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

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "object_type": "string",
  "object_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "action": "string",
  "status": "string",
  "requested_by": {},
  "reason": "string",
  "summary": {},
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "comment": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "withdrawn": false,
  "reason_words": {
    "words": "string",
    "code": "string",
    "details": {},
    "text": "string"
  }
}

POST /v1/approvals/{approval_id}/withdraw

Withdraw one's own request

Take back one's own pending request to raise or remove a company's posting approval limit (object_type posting_limit) or to relax the approval policy (approval_policy). Four eyes is about approving: withdrawing relaxes nothing, and the limit or policy in force stays. Without it, a request nobody else may decide (an administrator whose colleagues are not administrators) would wait forever and block every later one (409 posting_limit_change_waiting, approval_policy_change_waiting).

The request becomes rejected, decided by the person who asked, with the comment withdrawn (followed by the body's optional comment) and withdrawn true, and is audited as approval.withdrawn. When her next change of the same kind applies at once (she is alone in the organization; for the posting limit also when she lowers it or the policy allows the change), it withdraws her older request the same way, without this call.

Refused with 403 approval_withdraw_own_only for anyone but the person who asked (whichever key or session she uses, and whatever its scopes; a second person rejects instead) and for an agent; 403 approval_withdraw_admin_only for her without the admin scope; 409 approval_not_withdrawable for another kind of request and approval_decided when it is no longer pending.

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

Parameters

In Name Type Required Description
path approval_id string (uuid) yes Approval request id (GET /v1/approvals).

Request body

application/json (optional): Decision or null

Example request

curl -X POST "$ERP_URL/v1/approvals/3fa85f64-5717-4562-b3fc-2c963f66afa6/withdraw" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "comment": "Asked too early"
}'

Responses

Status Meaning Body
200 OK ApprovalOut
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

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "object_type": "string",
  "object_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "action": "string",
  "status": "string",
  "requested_by": {},
  "reason": "string",
  "summary": {},
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "comment": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "withdrawn": false,
  "reason_words": {
    "words": "string",
    "code": "string",
    "details": {},
    "text": "string"
  }
}

GET /v1/approvers

The approver register

Everyone who may approve (or could, before being disabled), ordered by email, with what the administrator declared when giving them the right (who the person is, their relationship to the company and firm, confirmed to be a different person), who declared it and when, when the person confirmed that their approvals are recorded in their name, and every change of their access. For an administrator and an auditor (the admin or audit scope; 403 otherwise). See ADR-0098.

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

Example request

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

Responses

Status Meaning Body
200 OK array of ApproverOut
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)

[
  {
    "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "email": "string",
    "name": "string",
    "status": "string",
    "scopes": [
      "string"
    ],
    "declaration": {},
    "declared_by": {},
    "declared_at": "2026-09-21T09:30:00Z",
    "acknowledged_at": "2026-09-21T09:30:00Z",
    "changes": [
      {}
    ]
  }
]

POST /v1/me/approver-acknowledgement

Confirm that my approvals are recorded in my name

A person an administrator named an approver confirms, once, that their approvals and rejections are recorded in their name for the company's auditor; they decide nothing before (409 approver_acknowledgement_needed). GET /v1/me says when it is due (approver_acknowledgement_needed). Recorded in the audit trail (approver.acknowledge); nothing changes when it is not due. 409 approver_not_a_person for an API key or agent.

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

Example request

curl -X POST "$ERP_URL/v1/me/approver-acknowledgement" \
  -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
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

Schemas

ApproverOut

One person who may approve, in the approver register.

Field Type Required Description
user_id string (uuid) yes Id (uuid) of the user.
email string yes The person's sign-in address.
name string or null yes The person's display name.
status string yes invited, active or disabled.
scopes array of string yes The rights the person holds now.
declaration object or null yes What was declared when the person got the right to approve (person_name, relationship, organisation, note); null for a person who got it before declarations or through the platform's provisioning.
declared_by object or null yes The administrator who declared it (type, id, name).
declared_at string (date-time) or null yes When it was declared.
acknowledged_at string (date-time) or null yes When the person confirmed that their approvals are recorded in their name; null until they do (they decide nothing before).
changes array of object yes The changes of the person's access, oldest first: action (user.invite, user.update, approver.declare, approver.acknowledge), at, by and data.

Decision

Field Type Required Description
comment string or null Why you approve or reject; stored with the decision and the audit event. Null when omitted.
Stabiso, the Enterprise System of Record · generated from the code and its guides · release d6b82bcf059d