StabisoDocs

API reference

Journals

Manual journal entries: create, submit through the approval policy, reverse.

Operation Summary Scope Idempotency-Key
GET /v1/journal-entries List Entries read
POST /v1/journal-entries Create a journal entry draft required
GET /v1/journal-entries/{entry_id} Get a journal entry read
PUT /v1/journal-entries/{entry_id} Change a draft entry draft required
DELETE /v1/journal-entries/{entry_id} Delete a draft entry draft
POST /v1/journal-entries/{entry_id}/reverse Reverse a posted entry post required
POST /v1/journal-entries/{entry_id}/submit Submit a draft entry draft

GET /v1/journal-entries

List Entries

Journal entries of all statuses with their lines, newest first (by entry date, then by creation time). All filters are optional and combine; account needs entity, else 422.

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

Parameters

In Name Type Required Description
query entity string or null Only entries of this entity (code or id).
query status string or null Only entries with this status: draft, pending_approval, posted or rejected.
query source string or null Only entries created by this source: manual or the subledger or process that made them, e.g. ar, ap, fa, bank, fx, close.
query start string (date) or null Only entries dated on or after this day (ISO 8601).
query end string (date) or null Only entries dated on or before this day (ISO 8601).
query q string or null Keep entries whose description or entry number contains this text (case-insensitive).
query account string or null Only entries with a line on this account code (needs entity).
query min_amount decimal string or null Only entries whose debits total at least this amount, in the entity's functional currency (decimal, e.g. 1000.00). (min 0)
query limit integer How many entries to return, newest first; at most 1000. (max 1000; default 100)

Example request

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

Responses

Status Meaning Body
200 OK array of JournalEntryOut
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",
    "entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string",
    "entry_date": "2026-09-21",
    "description": "string",
    "source": "string",
    "source_ref": "string",
    "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reversed_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "auto_reverse_on": "2026-09-21",
    "adjustment_period": 1,
    "provenance": {},
    "hash": "string",
    "prev_hash": "string",
    "created_by": {},
    "created_at": "2026-09-21T09:30:00Z",
    "posted_by": {},
    "posted_at": "2026-09-21T09:30:00Z",
    "lines": [
      {
        "line_no": 1,
        "account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "description": null,
        "currency": "string",
        "debit": "string",
        "credit": "string",
        "fx_rate": "string",
        "functional_debit": "string",
        "functional_credit": "string",
        "tax_code": null,
        "tax": null,
        "fx": null,
        "counterparty_type": null,
        "counterparty_id": null,
        "subledger_type": null,
        "subledger_id": null,
        "dimensions": {}
      }
    ]
  }
]

POST /v1/journal-entries

Create a journal entry

Create a manual journal entry and, unless submit is false, submit it.

The entry must balance (debits equal credits; on a multi-currency entry an FX rounding difference of up to 0.05 in functional currency is booked to the rounding account; a larger one answers 422 entry_off_in_currency with the limit) and its date must fall in an open period. A rate typed on a line (fx_rate) more than 10% away from the reference rate (ECB) of the entry date answers 422 fx_rate_implausible naming both rates and the date, unless confirm_rate is true; without a reference rate for the currency or day it is not checked. Control accounts (bank, receivables, payables, fixed assets) cannot be used: they are posted through their subledgers. source and provenance are set by the server; sending others answers 422. The approval policy decides (result.policy): allow posts it at once (result.status posted, with its number), require_approval leaves it pending_approval with an approval_id, and deny answers 403. An entry made by a key without the post scope always waits for approval. With submit false the status stays draft and policy shows what would happen on submit.

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id create_journal_entry_v1_journal_entries_post

Parameters

In Name Type Required Description
query submit boolean true (default): submit the entry through the approval policy at once. false: keep it as a draft; submit it later with POST /v1/journal-entries/{entry_id}/submit. (default true)

Request body

application/json (required): EntryInput

Field Type Required Description
entity string yes Entity code or id.
entry_date string (date) yes The posting date, ISO 8601 (e.g. 2026-09-30); it decides the fiscal period, which must be open.
description string yes What the entry is for; shown in the journal and the general ledger. At least one character. (min length 1)
lines array of LineInput yes The lines of the entry, at least two. Debits and credits must balance. (min items 2)
source string Where the entry comes from. Over the API it is always manual: omit it. Subledger entries are created by their own endpoints. (default "manual")
source_ref string or null Your own reference for the entry, e.g. a document number in another system; free text, null when omitted.
provenance object Free-form JSON that records where the entry came from. Over the API it must be empty: omit it.
auto_reverse_on string (date) or null Reverse the entry on this date (e.g. the first day of the next period for an accrual). The reversal is posted when the entry is posted, also after an approval; the date must be after entry_date, in an open period.
confirm_rate boolean Use the fx_rate typed on a line even when it differs by more than 10% from the reference rate (ECB) of the entry date. Without it such a line is refused (422 fx_rate_implausible); with it the line's fx records the confirmation and the reference rate. (default false)
adjustment_period integer or null Post into this adjustment period (e.g. 13) instead of the regular period; entry_date must be the last day of the fiscal year. (min 1; max 15)

Example request

curl -X POST "$ERP_URL/v1/journal-entries" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "Accrued audit fee September 2026",
  "entity": "CH01",
  "entry_date": "2026-09-30",
  "lines": [
    {
      "account": "6500",
      "debit": "1500.00",
      "description": "Audit fee"
    },
    {
      "account": "2300",
      "credit": "1500.00",
      "description": "Accrual"
    }
  ]
}'

Responses

Status Meaning Body
201 Created SubmitOut
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)

{
  "result": {
    "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "string",
    "number": "string",
    "policy": {
      "decision": "allow",
      "reason": "string",
      "rule_id": "string",
      "table_id": "string"
    },
    "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reason": "string"
  },
  "entry": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string",
    "entry_date": "2026-09-21",
    "description": "string",
    "source": "string",
    "source_ref": "string",
    "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reversed_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "auto_reverse_on": "2026-09-21",
    "adjustment_period": 1,
    "provenance": {},
    "hash": "string",
    "prev_hash": "string",
    "created_by": {},
    "created_at": "2026-09-21T09:30:00Z",
    "posted_by": {},
    "posted_at": "2026-09-21T09:30:00Z",
    "lines": [
      {
        "line_no": null,
        "account_id": null,
        "description": null,
        "currency": null,
        "debit": null,
        "credit": null,
        "fx_rate": null,
        "functional_debit": null,
        "functional_credit": null,
        "tax_code": null,
        "tax": null,
        "fx": null,
        "counterparty_type": null,
        "counterparty_id": null,
        "subledger_type": null,
        "subledger_id": null,
        "dimensions": null
      }
    ]
  }
}

GET /v1/journal-entries/{entry_id}

Get a journal entry

One journal entry with its lines, in any status. A posted entry carries its number and its place in the hash chain (hash, prev_hash).

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

Parameters

In Name Type Required Description
path entry_id string (uuid) yes Journal entry id.

Example request

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

Responses

Status Meaning Body
200 OK JournalEntryOut
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",
  "entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "number": "string",
  "status": "string",
  "entry_date": "2026-09-21",
  "description": "string",
  "source": "string",
  "source_ref": "string",
  "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "reversed_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "auto_reverse_on": "2026-09-21",
  "adjustment_period": 1,
  "provenance": {},
  "hash": "string",
  "prev_hash": "string",
  "created_by": {},
  "created_at": "2026-09-21T09:30:00Z",
  "posted_by": {},
  "posted_at": "2026-09-21T09:30:00Z",
  "lines": [
    {
      "line_no": 1,
      "account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "description": "string",
      "currency": "string",
      "debit": "string",
      "credit": "string",
      "fx_rate": "string",
      "functional_debit": "string",
      "functional_credit": "string",
      "tax_code": "string",
      "tax": {},
      "fx": {},
      "counterparty_type": "string",
      "counterparty_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "subledger_type": "string",
      "subledger_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "dimensions": {}
    }
  ]
}

PUT /v1/journal-entries/{entry_id}

Change a draft entry

Replace the content of a draft entry: its date, description, lines, auto_reverse_on, adjustment_period (the body is the same as on create, and the whole entry is sent). It is checked exactly as a new entry is (balance, open period, accounts, reversal date). The entry keeps its id, created_by and created_at; the audit trail records the change (journal.draft_updated, before and after), and whoever changed it cannot approve its posting (four eyes).

Only a draft entered by hand (source manual) is changed: 409 draft_not_editable when the entry waits for approval, is posted or rejected, and 409 draft_from_process for a draft that a process made (FX revaluation, depreciation, revenue recognition and others), which is corrected by that process.

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id update_draft_v1_journal_entries__entry_id__put

Parameters

In Name Type Required Description
path entry_id string (uuid) yes Journal entry id.
query submit boolean false (default): keep the changed entry as a draft. true: submit it through the approval policy at once, as POST /v1/journal-entries/{entry_id}/submit. (default false)

Request body

application/json (required): EntryInput

Field Type Required Description
entity string yes Entity code or id.
entry_date string (date) yes The posting date, ISO 8601 (e.g. 2026-09-30); it decides the fiscal period, which must be open.
description string yes What the entry is for; shown in the journal and the general ledger. At least one character. (min length 1)
lines array of LineInput yes The lines of the entry, at least two. Debits and credits must balance. (min items 2)
source string Where the entry comes from. Over the API it is always manual: omit it. Subledger entries are created by their own endpoints. (default "manual")
source_ref string or null Your own reference for the entry, e.g. a document number in another system; free text, null when omitted.
provenance object Free-form JSON that records where the entry came from. Over the API it must be empty: omit it.
auto_reverse_on string (date) or null Reverse the entry on this date (e.g. the first day of the next period for an accrual). The reversal is posted when the entry is posted, also after an approval; the date must be after entry_date, in an open period.
confirm_rate boolean Use the fx_rate typed on a line even when it differs by more than 10% from the reference rate (ECB) of the entry date. Without it such a line is refused (422 fx_rate_implausible); with it the line's fx records the confirmation and the reference rate. (default false)
adjustment_period integer or null Post into this adjustment period (e.g. 13) instead of the regular period; entry_date must be the last day of the fiscal year. (min 1; max 15)

Example request

curl -X PUT "$ERP_URL/v1/journal-entries/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "Accrued audit fee September 2026",
  "entity": "CH01",
  "entry_date": "2026-09-30",
  "lines": [
    {
      "account": "6500",
      "debit": "1500.00",
      "description": "Audit fee"
    },
    {
      "account": "2300",
      "credit": "1500.00",
      "description": "Accrual"
    }
  ]
}'

Responses

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

{
  "result": {
    "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "string",
    "number": "string",
    "policy": {
      "decision": "allow",
      "reason": "string",
      "rule_id": "string",
      "table_id": "string"
    },
    "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reason": "string"
  },
  "entry": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string",
    "entry_date": "2026-09-21",
    "description": "string",
    "source": "string",
    "source_ref": "string",
    "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reversed_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "auto_reverse_on": "2026-09-21",
    "adjustment_period": 1,
    "provenance": {},
    "hash": "string",
    "prev_hash": "string",
    "created_by": {},
    "created_at": "2026-09-21T09:30:00Z",
    "posted_by": {},
    "posted_at": "2026-09-21T09:30:00Z",
    "lines": [
      {
        "line_no": null,
        "account_id": null,
        "description": null,
        "currency": null,
        "debit": null,
        "credit": null,
        "fx_rate": null,
        "functional_debit": null,
        "functional_credit": null,
        "tax_code": null,
        "tax": null,
        "fx": null,
        "counterparty_type": null,
        "counterparty_id": null,
        "subledger_type": null,
        "subledger_id": null,
        "dimensions": null
      }
    ]
  }
}

DELETE /v1/journal-entries/{entry_id}

Delete a draft entry

Delete a draft entered by hand. A draft has no entry number yet, so no number is lost; the audit trail keeps its date, description, amount and lines (journal.draft_deleted). The same refusals as for a change: 409 draft_not_editable (not a draft; a posted entry is reversed instead) and 409 draft_from_process (made by a process).

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

Parameters

In Name Type Required Description
path entry_id string (uuid) yes Journal entry id.

Example request

curl -X DELETE "$ERP_URL/v1/journal-entries/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

POST /v1/journal-entries/{entry_id}/reverse

Reverse a posted entry

Reverse a posted manual entry or opening entry: the same lines with debit and credit swapped, dated on (default: the entry's own date). The reason is required (at most 500 characters) and recorded on the reversal entry and in the audit trail. It goes through the approval policy (action reverse_entry): only a person reverses, never an agent (403).

A person who created, posted or approved the entry reverses it at once and gets the reversal entry (200; reverses_id names the original, whose reversed_by_id is now set). If another person created, posted or approved the entry, or the policy limits what one person posts, the reversal waits for a second person: answer 202 with the approval request, decided with POST /v1/approvals/{approval_id}/approve (four eyes; everything is checked again then). A request that nobody decides blocks the close of the entry's period (the close checklist counts it) until it is approved or rejected.

Only manual entries and opening entries are reversed here. The opening entry of a data import is the import's correction path (reverse it, then import the balances again) and always waits for a second person (202); it leaves the open items and the clearing account in place, so the close check opening_clearing fails until the balances are imported again. Entries of a subledger or process (invoices, credit notes, bills, payments, depreciation, FX revaluation, bank postings, VAT settlement, intercompany, revenue recognition, year-end close) are refused with 409, and the message names where the correction belongs, so that documents, payments and assets never drift from the ledger. Also 409 when the entry is not posted, is already reversed, already has a reversal waiting for approval, or the period of the reversal date is closed; 422 when no fiscal period covers that date or the reason is missing.

Required scope post (admin implies every scope)
Idempotency-Key required
Operation id reverse_entry_v1_journal_entries__entry_id__reverse_post

Parameters

In Name Type Required Description
path entry_id string (uuid) yes Journal entry id.

Request body

application/json (required): ReverseRequest

Field Type Required Description
on string (date) or null Date of the reversal, ISO 8601; its period must exist and be open. Default: the date of the entry being reversed.
reason string yes Why it is reversed (required, at most 500 characters): appended to the description of the reversal entry and recorded in the audit trail and, when the reversal needs approval, in the approval request. (min length 1; max length 500)

Example request

curl -X POST "$ERP_URL/v1/journal-entries/3fa85f64-5717-4562-b3fc-2c963f66afa6/reverse" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "on": "2026-10-01",
  "reason": "Accrual released"
}'

Responses

Status Meaning Body
200 OK JournalEntryOut
202 The reversal needs a second person: the approval request (object_type journal_reversal). The entry stays posted until it is approved. 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",
  "entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "number": "string",
  "status": "string",
  "entry_date": "2026-09-21",
  "description": "string",
  "source": "string",
  "source_ref": "string",
  "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "reversed_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "auto_reverse_on": "2026-09-21",
  "adjustment_period": 1,
  "provenance": {},
  "hash": "string",
  "prev_hash": "string",
  "created_by": {},
  "created_at": "2026-09-21T09:30:00Z",
  "posted_by": {},
  "posted_at": "2026-09-21T09:30:00Z",
  "lines": [
    {
      "line_no": 1,
      "account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "description": "string",
      "currency": "string",
      "debit": "string",
      "credit": "string",
      "fx_rate": "string",
      "functional_debit": "string",
      "functional_credit": "string",
      "tax_code": "string",
      "tax": {},
      "fx": {},
      "counterparty_type": "string",
      "counterparty_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "subledger_type": "string",
      "subledger_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "dimensions": {}
    }
  ]
}

POST /v1/journal-entries/{entry_id}/submit

Submit a draft entry

Submit a draft entry through the approval policy: it is posted if the policy allows it for the caller (result.status posted), otherwise it becomes pending_approval with an approval_id for a second person. Only drafts can be submitted (409 otherwise). A policy decision deny answers 403. A key without the post scope always sends it to approval.

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

Parameters

In Name Type Required Description
path entry_id string (uuid) yes Journal entry id.

Example request

curl -X POST "$ERP_URL/v1/journal-entries/3fa85f64-5717-4562-b3fc-2c963f66afa6/submit" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

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

{
  "result": {
    "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "string",
    "number": "string",
    "policy": {
      "decision": "allow",
      "reason": "string",
      "rule_id": "string",
      "table_id": "string"
    },
    "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reason": "string"
  },
  "entry": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string",
    "entry_date": "2026-09-21",
    "description": "string",
    "source": "string",
    "source_ref": "string",
    "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reversed_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "auto_reverse_on": "2026-09-21",
    "adjustment_period": 1,
    "provenance": {},
    "hash": "string",
    "prev_hash": "string",
    "created_by": {},
    "created_at": "2026-09-21T09:30:00Z",
    "posted_by": {},
    "posted_at": "2026-09-21T09:30:00Z",
    "lines": [
      {
        "line_no": null,
        "account_id": null,
        "description": null,
        "currency": null,
        "debit": null,
        "credit": null,
        "fx_rate": null,
        "functional_debit": null,
        "functional_credit": null,
        "tax_code": null,
        "tax": null,
        "fx": null,
        "counterparty_type": null,
        "counterparty_id": null,
        "subledger_type": null,
        "subledger_id": null,
        "dimensions": null
      }
    ]
  }
}

Schemas

EntryInput

Field Type Required Description
entity string yes Entity code or id.
entry_date string (date) yes The posting date, ISO 8601 (e.g. 2026-09-30); it decides the fiscal period, which must be open.
description string yes What the entry is for; shown in the journal and the general ledger. At least one character. (min length 1)
lines array of LineInput yes The lines of the entry, at least two. Debits and credits must balance. (min items 2)
source string Where the entry comes from. Over the API it is always manual: omit it. Subledger entries are created by their own endpoints. (default "manual")
source_ref string or null Your own reference for the entry, e.g. a document number in another system; free text, null when omitted.
provenance object Free-form JSON that records where the entry came from. Over the API it must be empty: omit it.
auto_reverse_on string (date) or null Reverse the entry on this date (e.g. the first day of the next period for an accrual). The reversal is posted when the entry is posted, also after an approval; the date must be after entry_date, in an open period.
confirm_rate boolean Use the fx_rate typed on a line even when it differs by more than 10% from the reference rate (ECB) of the entry date. Without it such a line is refused (422 fx_rate_implausible); with it the line's fx records the confirmation and the reference rate. (default false)
adjustment_period integer or null Post into this adjustment period (e.g. 13) instead of the regular period; entry_date must be the last day of the fiscal year. (min 1; max 15)

JournalEntryOut

Field Type Required Description
id string (uuid) yes Id (uuid) of the entry; the {entry_id} of other operations.
entity_id string (uuid) yes Id (uuid) of the entity.
number string or null yes Entry number such as 2026-000042, assigned when it is posted; null before.
status string yes draft, pending_approval (waits for a second person), posted (final) or rejected.
entry_date string (date) yes The posting date (ISO 8601).
description string yes What the entry is for.
source string yes manual, or the subledger or process that created it: ar, ap, fa, bank, fx, close (the year-end closing entry), appropriation (a resolution on the result), ic (intercompany), rev (revenue recognition) or opening (a data import's opening entry).
source_ref string or null yes The caller's own reference; null if none.
reverses_id string (uuid) or null yes For a reversal, the id of the entry it reverses; else null.
reversed_by_id string (uuid) or null yes Once reversed, the id of the reversal entry; else null.
auto_reverse_on string (date) or null The reversal is posted on this date when the entry posts.
adjustment_period integer or null The adjustment period the entry is in (e.g. 13); null in a regular period.
provenance object yes Free-form JSON recording where the entry came from, e.g. the FX rates used or the document it was built from.
hash string or null yes SHA-256 hash of the posted entry's content and prev_hash; null before posting.
prev_hash string or null yes The hash of the previous posted entry of the entity (the hash chain); null for the first and before posting.
created_by object yes The actor, as stored in the audit trail: an object with type (human, agent or system), id (user:<id>, key:<prefix> or system:<name>) and name.
created_at string (date-time) yes When the entry was created (ISO 8601, UTC).
posted_by object or null yes The actor that posted it, as created_by; null before posting.
posted_at string (date-time) or null yes When it was posted (ISO 8601, UTC); null before.
lines array of JournalLineOut The lines of the entry.

JournalLineOut

Field Type Required Description
line_no integer yes Position of the line in the entry, from 1.
account_id string (uuid) yes Id (uuid) of the account (GET /v1/entities/{entity}/accounts).
description string or null yes Text of the line; null if none.
currency string yes ISO 4217 currency of the line.
debit string yes Debit amount in the line's currency; 0 on a credit line.
credit string yes Credit amount in the line's currency; 0 on a debit line.
fx_rate string yes Units of the entity's functional currency per unit of currency; 1 when the line is in the functional currency.
functional_debit string yes The debit in the entity's functional currency.
functional_credit string yes The credit in the entity's functional currency.
tax_code string or null yes VAT treatment code of the line; null if none.
tax object or null yes VAT tag the VAT report reads (kind base or tax, direction, jurisdiction, treatment, rate); kind foreign_vat on a bill's lines that book the net amount and the foreign VAT the company cannot reclaim as cost, and non_deductible on the line that books the part of a bill line's input VAT that is not deductible as cost (recovery_percent), which no VAT report reads; null on lines without VAT.
fx object or null yes Where the exchange rate came from (source, rate, fixing date); null in the functional currency.
counterparty_type string or null yes customer or vendor; null if none.
counterparty_id string (uuid) or null yes Id (uuid) of the customer or vendor; null if none.
subledger_type string or null yes Kind of subledger record the line belongs to; null for manual lines.
subledger_id string (uuid) or null yes Id (uuid) of that subledger record; null for manual lines.
dimensions object yes The line's cost centre, profit centre, project ...: dimension code to value code.

LineInput

Field Type Required Description
account string yes Account code (or id) in the entry's entity.
debit decimal string Debit amount in the line's currency, a decimal string such as 1500.00; zero or omitted on a credit line. Exactly one of debit and credit must be above zero. (min 0; default "0")
credit decimal string Credit amount in the line's currency, a decimal string such as 1500.00; zero or omitted on a debit line. (min 0; default "0")
currency string or null Defaults to functional currency.
fx_rate decimal string or null Units of functional currency per unit; default the ECB rate of the entry date. On a manual entry a rate more than 10% from that reference rate needs confirm_rate. (greater than 0)
fx_meta object or null Provenance of a supplied fx_rate (e.g. an ECB quote).
description string or null Text of this line, shown in the general ledger; null when omitted.
tax_code string or null VAT treatment code recorded on the line for reporting. Manual entries normally leave it out; the sales and purchase documents set it.
tax object or null VAT tag the VAT report reads: an object with kind (base or tax), direction, jurisdiction, treatment and rate. Set by the sales and purchase documents; leave it out on manual entries.
counterparty_type "customer" | "entity" | "vendor" or null customer or vendor, when the line concerns a customer or vendor; use with counterparty_id. entity (another entity of the organization) is set by the intercompany postings only; a manual entry is refused with it.
counterparty_id string (uuid) or null Id (uuid) of the customer or vendor the line concerns; null when omitted.
subledger_type string or null Kind of subledger record the line belongs to. Set by the subledger services; leave it out on manual entries.
subledger_id string (uuid) or null Id (uuid) of the subledger record the line belongs to. Set by the subledger services; leave it out on manual entries.
dimensions map of string Dimension code to value code, e.g. {"cost_centre": "CC-100", "project": "P-17"} (GET /v1/dimensions); empty when omitted. Each must be an active value of an active dimension, valid on the entry date in its company (dimension_unknown, dimension_value_unknown); a manual entry gives every dimension its accounts require (dimension_required).

SubmitOut

Field Type Required Description
result SubmitResult yes What the approval policy decided and where the entry stands now.
entry JournalEntryOut yes The journal entry in its new state, with its lines.
Stabiso, the Enterprise System of Record · generated from the code and its guides · release d6b82bcf059d