<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ledger17 on Ledger17 | Headless Ledger</title><link>/</link><description>Recent content in Ledger17 on Ledger17 | Headless Ledger</description><generator>Hugo</generator><language>en</language><lastBuildDate>Fri, 14 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml"/><item><title>The MCP server</title><link>/docs/ai-agents/mcp-server/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0000</pubDate><guid>/docs/ai-agents/mcp-server/</guid><description>&lt;h2 id="endpoint"&gt;Endpoint &lt;a href="#endpoint" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Endpoint&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;https://mcp.ledger17.com/mcp&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Transport&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Streamable HTTP&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Authorization server&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;https://auth2.sirktek.com&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="authorization"&gt;Authorization &lt;a href="#authorization" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The server is an OAuth2 &lt;strong&gt;protected resource&lt;/strong&gt; — it issues nothing and stores nothing.
Every request must carry a bearer JWT, which the server validates (issuer, signature,
expiry, audience) before any tool runs. The token is then forwarded &lt;strong&gt;verbatim&lt;/strong&gt; to the
command and query APIs together with the resolved tenant as &lt;code&gt;X-Tenant-ID&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Permissions are enforced upstream, exactly as for direct API calls: reads require
&lt;code&gt;{orgId}:read:ledger&lt;/code&gt;, writes require &lt;code&gt;{orgId}:write:ledger&lt;/code&gt; — see
&lt;a href="/docs/authentication/"&gt;Authentication&lt;/a&gt;. A &lt;code&gt;403&lt;/code&gt; from the APIs is
surfaced to the model unchanged. The MCP server adds no permission logic of its own.&lt;/p&gt;</description></item><item><title>Balances</title><link>/docs/query-api/balances/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/query-api/balances/</guid><description>&lt;h2 id="single-account-balance"&gt;Single account balance &lt;a href="#single-account-balance" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="f3ed32a" class="language-text "&gt;
 &lt;code&gt;GET /ledger/v1/balances&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;



 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="ecffa2f" class="language-bash "&gt;
 &lt;code&gt;curl &amp;#34;http://localhost:8081/ledger/v1/balances?accountReference=1500-receivables&amp;amp;timePeriodType=MONTHLY&amp;amp;timePeriodValue=2026-07&amp;amp;dimensionFilters=CUSTOMER:cust-001&amp;#34; \
 -H &amp;#34;X-Tenant-ID: org-84120557&amp;#34;&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;h3 id="query-parameters"&gt;Query parameters &lt;a href="#query-parameters" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Parameter&lt;/th&gt;
 &lt;th&gt;Required&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;accountReference&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;Must match &lt;code&gt;NNNN-slug&lt;/code&gt;, e.g. &lt;code&gt;1500-receivables&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;timePeriodType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;YEAR&lt;/code&gt;, &lt;code&gt;TERTIAL&lt;/code&gt;, &lt;code&gt;QUARTER&lt;/code&gt;, &lt;code&gt;BI_MONTHLY&lt;/code&gt;, &lt;code&gt;MONTHLY&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;timePeriodValue&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;e.g. &lt;code&gt;2026-07&lt;/code&gt;, &lt;code&gt;2026-Q1&lt;/code&gt;, &lt;code&gt;2026&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;dimensionFilters&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;A &lt;code&gt;TYPE:value&lt;/code&gt; filter — one dimension type per query&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="response--200-ok"&gt;Response — &lt;code&gt;200 OK&lt;/code&gt; &lt;a href="#response--200-ok" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="03e2759" class="language-json "&gt;
 &lt;code&gt;{
 &amp;#34;tenantId&amp;#34;: &amp;#34;org-84120557&amp;#34;,
 &amp;#34;accountReference&amp;#34;: &amp;#34;1500-receivables&amp;#34;,
 &amp;#34;timePeriod&amp;#34;: &amp;#34;2026-07&amp;#34;,
 &amp;#34;dimensionFiltersApplied&amp;#34;: { &amp;#34;CUSTOMER&amp;#34;: &amp;#34;cust-001&amp;#34; },
 &amp;#34;balanceMinorUnits&amp;#34;: 125000,
 &amp;#34;currencyUnit&amp;#34;: &amp;#34;EUR&amp;#34;,
 &amp;#34;transactionCount&amp;#34;: 3,
 &amp;#34;queryTimestamp&amp;#34;: &amp;#34;2026-07-23T10:00:00.000Z&amp;#34;,
 &amp;#34;cacheHit&amp;#34;: true
}&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;&lt;code&gt;balanceMinorUnits&lt;/code&gt; is signed and debit-positive: Σ debits − Σ credits.&lt;/p&gt;</description></item><item><title>Core concepts</title><link>/docs/concepts/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/concepts/</guid><description>&lt;h2 id="transaction-blocks-and-entries"&gt;Transaction blocks and entries &lt;a href="#transaction-blocks-and-entries" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The unit of writing in Ledger17 is the &lt;strong&gt;transaction block&lt;/strong&gt;: a named set of two or more &lt;strong&gt;accounting entries&lt;/strong&gt; that must balance.&lt;/p&gt;
&lt;p&gt;Each entry has:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Meaning&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;accountReference&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;The account the entry posts to, e.g. &lt;code&gt;1920-bank&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;entryType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;DEBIT&lt;/code&gt; or &lt;code&gt;CREDIT&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;amountMinorUnits&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;A &lt;strong&gt;signed integer&lt;/strong&gt; amount at the unit&amp;rsquo;s scale — see &lt;a href="/docs/concepts/#amounts"&gt;Amounts and units&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;dimensions&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Optional analytical tags, e.g. &lt;code&gt;{&amp;quot;CUSTOMER&amp;quot;: &amp;quot;cust-001&amp;quot;}&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;lineDescription&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Optional free-text line description&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;quantity&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Optional decimal quantity (e.g. number of shares)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A block is accepted only when the sum of its debit amounts equals the sum of its credit amounts. The accounting side of an entry is expressed through &lt;code&gt;entryType&lt;/code&gt;, not through the sign of the amount.&lt;/p&gt;</description></item><item><title>Record transactions</title><link>/docs/command-api/transactions/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/command-api/transactions/</guid><description>&lt;h2 id="record-a-transaction-batch"&gt;Record a transaction batch &lt;a href="#record-a-transaction-batch" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="c2fe61d" class="language-text "&gt;
 &lt;code&gt;POST /ledger/v1/transactions&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;The request body is an &lt;strong&gt;array&lt;/strong&gt; of 1–1000 transaction commands. Even a single transaction is sent as a one-element array.&lt;/p&gt;
&lt;h3 id="headers"&gt;Headers &lt;a href="#headers" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Header&lt;/th&gt;
 &lt;th&gt;Required&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;X-Tenant-ID&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;Tenant the transactions belong to&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;X-Idempotency-Key&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;Missing key → &lt;code&gt;400 VALIDATION_ERROR&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;X-Correlation-ID&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;Optional UUID&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="request-body"&gt;Request body &lt;a href="#request-body" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="7f4731e" class="language-json "&gt;
 &lt;code&gt;[{
 &amp;#34;name&amp;#34;: &amp;#34;Sales invoice #42&amp;#34;,
 &amp;#34;description&amp;#34;: &amp;#34;July invoice for cust-001&amp;#34;,
 &amp;#34;documentReference&amp;#34;: &amp;#34;INV-2026-0042&amp;#34;,
 &amp;#34;transactionDate&amp;#34;: &amp;#34;2026-07-01&amp;#34;,
 &amp;#34;unit&amp;#34;: &amp;#34;EUR&amp;#34;,
 &amp;#34;entries&amp;#34;: [
 { &amp;#34;accountReference&amp;#34;: &amp;#34;1500-receivables&amp;#34;, &amp;#34;entryType&amp;#34;: &amp;#34;DEBIT&amp;#34;, &amp;#34;amountMinorUnits&amp;#34;: 125000,
 &amp;#34;lineDescription&amp;#34;: &amp;#34;Invoice total incl. VAT&amp;#34;,
 &amp;#34;dimensions&amp;#34;: { &amp;#34;CUSTOMER&amp;#34;: &amp;#34;cust-001&amp;#34; } },
 { &amp;#34;accountReference&amp;#34;: &amp;#34;3000-sales&amp;#34;, &amp;#34;entryType&amp;#34;: &amp;#34;CREDIT&amp;#34;, &amp;#34;amountMinorUnits&amp;#34;: 100000 },
 { &amp;#34;accountReference&amp;#34;: &amp;#34;2700-vat-out&amp;#34;, &amp;#34;entryType&amp;#34;: &amp;#34;CREDIT&amp;#34;, &amp;#34;amountMinorUnits&amp;#34;: 25000 }
 ],
 &amp;#34;dimensions&amp;#34;: [
 { &amp;#34;dimensionType&amp;#34;: &amp;#34;PROJECT&amp;#34;, &amp;#34;valueReference&amp;#34;: &amp;#34;0b9f3c1e-…&amp;#34; }
 ],
 &amp;#34;applicationMetadata&amp;#34;: { &amp;#34;source&amp;#34;: &amp;#34;billing-service&amp;#34; }
}]&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Constraints&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;required, ≤ 200 chars&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;≤ 2000 chars&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;documentReference&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;≤ 100 chars&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;transactionDate&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;date&lt;/td&gt;
 &lt;td&gt;required, &lt;code&gt;yyyy-MM-dd&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;unit&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;required, ≤ 20 chars — a currency (&lt;code&gt;EUR&lt;/code&gt;, &lt;code&gt;JPY&lt;/code&gt;, …) or other registered unit (&lt;code&gt;kgCO2e&lt;/code&gt;, &lt;code&gt;hours&lt;/code&gt;, …)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;entries&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;array&lt;/td&gt;
 &lt;td&gt;required, &lt;strong&gt;minimum 2&lt;/strong&gt; entries&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;entries[].accountReference&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;required, ≤ 50 chars&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;entries[].amountMinorUnits&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;integer&lt;/td&gt;
 &lt;td&gt;required; signed, scaled by the unit (&lt;a href="/docs/concepts/#amounts"&gt;Amounts and units&lt;/a&gt;) — negative means &amp;ldquo;reduce this side&amp;rdquo;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;entries[].entryType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;enum&lt;/td&gt;
 &lt;td&gt;required, &lt;code&gt;DEBIT&lt;/code&gt; or &lt;code&gt;CREDIT&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;entries[].lineDescription&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;≤ 500 chars&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;entries[].dimensions&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;object&lt;/td&gt;
 &lt;td&gt;map of dimension type → value&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;entries[].quantity&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;decimal&lt;/td&gt;
 &lt;td&gt;optional, ≥ 0 (e.g. shares)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;dimensions&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;array&lt;/td&gt;
 &lt;td&gt;block-level &lt;code&gt;{dimensionType, valueReference}&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;applicationMetadata&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;object&lt;/td&gt;
 &lt;td&gt;free-form&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;enforceCapture&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;boolean&lt;/td&gt;
 &lt;td&gt;optional; enforce dimension-capture validation&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Every block must balance: the signed sum of &lt;code&gt;DEBIT&lt;/code&gt; amounts must equal the signed sum of &lt;code&gt;CREDIT&lt;/code&gt; amounts. See &lt;a href="/docs/concepts/#negative-amounts"&gt;Negative amounts&lt;/a&gt; for what a negative entry means.&lt;/p&gt;</description></item><item><title>Connect a client</title><link>/docs/ai-agents/connect-a-client/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0000</pubDate><guid>/docs/ai-agents/connect-a-client/</guid><description>&lt;h2 id="choose-the-right-credential"&gt;Choose the right credential &lt;a href="#choose-the-right-credential" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Situation&lt;/th&gt;
 &lt;th&gt;Credential&lt;/th&gt;
 &lt;th&gt;Tenant&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;You are talking to the assistant yourself&lt;/td&gt;
 &lt;td&gt;Your own Ledger17 login&lt;/td&gt;
 &lt;td&gt;Chosen in conversation if you belong to several&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;An agent runs unattended, without you&lt;/td&gt;
 &lt;td&gt;An &lt;a href="/docs/api-keys/"&gt;API key&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Bound to one tenant, never chosen&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If in doubt: when a person is driving the assistant, the assistant should use that
person&amp;rsquo;s login.&lt;/p&gt;
&lt;h2 id="claudeai"&gt;Claude.ai &lt;a href="#claudeai" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings → Connectors → Add custom connector&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Paste the endpoint: &lt;code&gt;https://mcp.ledger17.com/mcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Complete the login in the window that opens.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The client discovers the authorization server itself from the protected-resource
metadata — nothing else to configure.&lt;/p&gt;</description></item><item><title>Authentication</title><link>/docs/authentication/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/authentication/</guid><description>&lt;h2 id="bearer-tokens"&gt;Bearer tokens &lt;a href="#bearer-tokens" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In production both services require an &lt;strong&gt;OIDC JWT bearer token&lt;/strong&gt; on every request — there are no anonymous endpoints:&lt;/p&gt;



 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="4ea5034" class="language-bash "&gt;
 &lt;code&gt;curl https://api.example.com/ledger/v1/balances?… \
 -H &amp;#34;Authorization: Bearer &amp;lt;JWT&amp;gt;&amp;#34; \
 -H &amp;#34;X-Tenant-ID: org-84120557&amp;#34;&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;In local development (&lt;code&gt;dev&lt;/code&gt;/&lt;code&gt;test&lt;/code&gt; profiles) OIDC is disabled and the tenant falls back to a configured development tenant — you can call the API without a token.&lt;/p&gt;
&lt;h2 id="tenant-scoping"&gt;Tenant scoping &lt;a href="#tenant-scoping" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Send the organization id in the &lt;code&gt;X-Tenant-ID&lt;/code&gt; header on every request. If the header is missing or blank, the API responds &lt;code&gt;400&lt;/code&gt;. The token must grant access to that same organization, otherwise the API responds &lt;code&gt;403&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Business events</title><link>/docs/command-api/events/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/command-api/events/</guid><description>&lt;h2 id="submit-a-business-event"&gt;Submit a business event &lt;a href="#submit-a-business-event" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="e737b4b" class="language-text "&gt;
 &lt;code&gt;POST /ledger/v1/events&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;The event is validated against the applicable rule sets&amp;rsquo; ontologies, entries are derived, conflicts between rule sets are detected — and only then is the event appended. Processing is asynchronous: the API answers &lt;code&gt;202 Accepted&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="headers"&gt;Headers &lt;a href="#headers" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Header&lt;/th&gt;
 &lt;th&gt;Required&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;X-Tenant-ID&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;Authorization&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;in production&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="request-body"&gt;Request body &lt;a href="#request-body" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="c9e9b7b" class="language-json "&gt;
 &lt;code&gt;{
 &amp;#34;eventType&amp;#34;: &amp;#34;SalesInvoice&amp;#34;,
 &amp;#34;transactionDate&amp;#34;: &amp;#34;2026-07-01&amp;#34;,
 &amp;#34;accountingEntries&amp;#34;: [
 { &amp;#34;accountCode&amp;#34;: &amp;#34;1500-receivables&amp;#34;, &amp;#34;entryType&amp;#34;: &amp;#34;DEBIT&amp;#34;, &amp;#34;amount&amp;#34;: 125000,
 &amp;#34;currency&amp;#34;: &amp;#34;EUR&amp;#34;, &amp;#34;description&amp;#34;: &amp;#34;Invoice #42&amp;#34;,
 &amp;#34;dimensions&amp;#34;: { &amp;#34;CUSTOMER&amp;#34;: &amp;#34;cust-001&amp;#34; } },
 { &amp;#34;accountCode&amp;#34;: &amp;#34;3000-sales&amp;#34;, &amp;#34;entryType&amp;#34;: &amp;#34;CREDIT&amp;#34;, &amp;#34;amount&amp;#34;: 100000, &amp;#34;currency&amp;#34;: &amp;#34;EUR&amp;#34; },
 { &amp;#34;accountCode&amp;#34;: &amp;#34;2700-vat-out&amp;#34;, &amp;#34;entryType&amp;#34;: &amp;#34;CREDIT&amp;#34;, &amp;#34;amount&amp;#34;: 25000, &amp;#34;currency&amp;#34;: &amp;#34;EUR&amp;#34; }
 ],
 &amp;#34;metadata&amp;#34;: {
 &amp;#34;idempotencyKey&amp;#34;: &amp;#34;f4b6a2d8-1c3e-4f5a-9b7d-8e2f0a1c3b5d&amp;#34;,
 &amp;#34;tags&amp;#34;: { &amp;#34;channel&amp;#34;: &amp;#34;webshop&amp;#34; }
 }
}&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Constraints&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;eventType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;required, e.g. &lt;code&gt;SalesInvoice&lt;/code&gt;, &lt;code&gt;PayrollRun&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;transactionDate&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;date&lt;/td&gt;
 &lt;td&gt;optional &lt;code&gt;yyyy-MM-dd&lt;/code&gt;; defaults to today&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;accountingEntries&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;array&lt;/td&gt;
 &lt;td&gt;required, non-empty&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;accountingEntries[].accountCode&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;required&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;accountingEntries[].entryType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;enum&lt;/td&gt;
 &lt;td&gt;required, &lt;code&gt;DEBIT&lt;/code&gt; or &lt;code&gt;CREDIT&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;accountingEntries[].amount&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;integer&lt;/td&gt;
 &lt;td&gt;scaled by the unit — see &lt;a href="/docs/concepts/#amounts"&gt;Amounts and units&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;accountingEntries[].currency&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;optional — a currency or other registered unit&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;accountingEntries[].dimensions&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;object&lt;/td&gt;
 &lt;td&gt;optional&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;metadata.idempotencyKey&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;optional; parsed as a correlation UUID when valid&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;metadata.tags&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;object&lt;/td&gt;
 &lt;td&gt;optional string map&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="response--202-accepted"&gt;Response — &lt;code&gt;202 Accepted&lt;/code&gt; &lt;a href="#response--202-accepted" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="8dec3fa" class="language-json "&gt;
 &lt;code&gt;{ &amp;#34;eventId&amp;#34;: &amp;#34;9c2e4f6a-…&amp;#34;, &amp;#34;message&amp;#34;: &amp;#34;Event accepted for processing&amp;#34; }&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;h3 id="errors"&gt;Errors &lt;a href="#errors" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;th&gt;Code&lt;/th&gt;
 &lt;th&gt;Cause&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;400&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;VALIDATION_ERROR&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Bad &lt;code&gt;entryType&lt;/code&gt;, missing fields&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;400&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;Ontology violations: &lt;code&gt;{&amp;quot;error&amp;quot;: &amp;quot;Event validation failed against ontology constraints&amp;quot;, &amp;quot;violations&amp;quot;: [...]}&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;403&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;FORBIDDEN&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;No write access to tenant&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;409&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;Conflicting rule-set derivations (body lists &lt;code&gt;conflictingEntries&lt;/code&gt; per domain)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="inquiry"&gt;Inquire events &lt;a href="#inquiry" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="0d1a312" class="language-text "&gt;
 &lt;code&gt;GET /ledger/v1/events&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;A lightweight read on the command side, useful for reconciliation and for finding events that still lack a correction.&lt;/p&gt;</description></item><item><title>Transaction lines</title><link>/docs/query-api/transaction-lines/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/query-api/transaction-lines/</guid><description>&lt;h2 id="list-transaction-lines"&gt;List transaction lines &lt;a href="#list-transaction-lines" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="7f7b9dd" class="language-text "&gt;
 &lt;code&gt;GET /ledger/v1/transactions&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;



 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="b379e23" class="language-bash "&gt;
 &lt;code&gt;curl &amp;#34;http://localhost:8081/ledger/v1/transactions?from=2026-07-01&amp;amp;to=2026-07-31&amp;amp;account=1500-receivables&amp;amp;dimension=CUSTOMER:cust-001&amp;#34; \
 -H &amp;#34;X-Tenant-ID: org-84120557&amp;#34;&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;h3 id="query-parameters"&gt;Query parameters &lt;a href="#query-parameters" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Parameter&lt;/th&gt;
 &lt;th&gt;Required&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;from&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;Accounting-date lower bound, &lt;code&gt;yyyy-MM-dd&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;to&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;Upper bound; defaults to &lt;code&gt;from&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;account&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;Exact account reference&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;dimension&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;One &lt;code&gt;TYPE:value&lt;/code&gt; pair&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;limit&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;Default 1000, capped at 5000&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="response--200-ok"&gt;Response — &lt;code&gt;200 OK&lt;/code&gt; &lt;a href="#response--200-ok" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;One element per entry line:&lt;/p&gt;



 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="5e72f78" class="language-json "&gt;
 &lt;code&gt;[
 {
 &amp;#34;transactionId&amp;#34;: &amp;#34;7d9e1a4c-…&amp;#34;,
 &amp;#34;transactionDate&amp;#34;: &amp;#34;2026-07-01&amp;#34;,
 &amp;#34;accountReference&amp;#34;: &amp;#34;1500-receivables&amp;#34;,
 &amp;#34;entryType&amp;#34;: &amp;#34;DEBIT&amp;#34;,
 &amp;#34;amountMinorUnits&amp;#34;: 125000,
 &amp;#34;lineDescription&amp;#34;: &amp;#34;Invoice total incl. VAT&amp;#34;,
 &amp;#34;dimensions&amp;#34;: { &amp;#34;CUSTOMER&amp;#34;: &amp;#34;cust-001&amp;#34; },
 &amp;#34;quantity&amp;#34;: null,
 &amp;#34;recordedAt&amp;#34;: &amp;#34;2026-07-01T09:14:03.512Z&amp;#34;
 }
]&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;th&gt;Cause&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;400&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Invalid date or dimension syntax&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;403&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;No read access to tenant&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>API keys</title><link>/docs/api-keys/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0000</pubDate><guid>/docs/api-keys/</guid><description>&lt;h2 id="when-you-need-one"&gt;When you need one &lt;a href="#when-you-need-one" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Only when something works with the ledger &lt;strong&gt;without a signed-in user&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;another system integrating against the Ledger17 APIs — an invoicing system posting
sales, a payroll run posting salaries, a bank feed posting settlements&lt;/li&gt;
&lt;li&gt;an &lt;a href="/docs/ai-agents/"&gt;AI agent&lt;/a&gt; running unattended, with nobody present to
approve a login&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a person uses Ledger17 — or an assistant works on behalf of a signed-in person —
no API key is involved: the person&amp;rsquo;s own login is the credential.&lt;/p&gt;</description></item><item><title>Batch import</title><link>/docs/command-api/batch-events/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/command-api/batch-events/</guid><description>&lt;h2 id="submit-a-batch"&gt;Submit a batch &lt;a href="#submit-a-batch" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="82c9099" class="language-text "&gt;
 &lt;code&gt;POST /ledger/v1/events/batch&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;h3 id="headers"&gt;Headers &lt;a href="#headers" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Header&lt;/th&gt;
 &lt;th&gt;Required&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;X-Tenant-ID&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="request-body"&gt;Request body &lt;a href="#request-body" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;events&lt;/code&gt; holds 1–10 000 items:&lt;/p&gt;



 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="e5c9036" class="language-json "&gt;
 &lt;code&gt;{
 &amp;#34;events&amp;#34;: [
 {
 &amp;#34;eventType&amp;#34;: &amp;#34;SalesInvoice&amp;#34;,
 &amp;#34;date&amp;#34;: &amp;#34;2026-01-31&amp;#34;,
 &amp;#34;documentReference&amp;#34;: &amp;#34;INV-2026-0001&amp;#34;,
 &amp;#34;description&amp;#34;: &amp;#34;January invoice&amp;#34;,
 &amp;#34;accountingEntries&amp;#34;: [
 { &amp;#34;accountCode&amp;#34;: &amp;#34;1500-receivables&amp;#34;, &amp;#34;entryType&amp;#34;: &amp;#34;DEBIT&amp;#34;, &amp;#34;amount&amp;#34;: 125000, &amp;#34;currency&amp;#34;: &amp;#34;EUR&amp;#34; },
 { &amp;#34;accountCode&amp;#34;: &amp;#34;3000-sales&amp;#34;, &amp;#34;entryType&amp;#34;: &amp;#34;CREDIT&amp;#34;, &amp;#34;amount&amp;#34;: 100000, &amp;#34;currency&amp;#34;: &amp;#34;EUR&amp;#34; },
 { &amp;#34;accountCode&amp;#34;: &amp;#34;2700-vat-out&amp;#34;, &amp;#34;entryType&amp;#34;: &amp;#34;CREDIT&amp;#34;, &amp;#34;amount&amp;#34;: 25000, &amp;#34;currency&amp;#34;: &amp;#34;EUR&amp;#34; }
 ]
 }
 ]
}&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Constraints&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;events&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;array&lt;/td&gt;
 &lt;td&gt;required, 1–10 000 items&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;events[].eventType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;required&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;events[].date&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;date&lt;/td&gt;
 &lt;td&gt;required, &lt;code&gt;yyyy-MM-dd&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;events[].documentReference&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;≤ 255 chars; unique per tenant&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;events[].description&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;optional&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;events[].accountingEntries&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;array&lt;/td&gt;
 &lt;td&gt;required, non-empty; same shape as &lt;a href="/docs/command-api/events/"&gt;business events&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;events[].metadata&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;object&lt;/td&gt;
 &lt;td&gt;optional&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="responses"&gt;Responses &lt;a href="#responses" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The response always reports how many items were accepted and lists per-item errors:&lt;/p&gt;</description></item><item><title>Errors &amp; status codes</title><link>/docs/errors/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/errors/</guid><description>&lt;h2 id="the-error-envelope"&gt;The error envelope &lt;a href="#the-error-envelope" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Errors are returned as JSON with a stable shape. &lt;code&gt;violations&lt;/code&gt; and &lt;code&gt;timestamp&lt;/code&gt; are omitted when empty:&lt;/p&gt;



 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="7c8244c" class="language-json "&gt;
 &lt;code&gt;{
 &amp;#34;error&amp;#34;: &amp;#34;VALIDATION_ERROR&amp;#34;,
 &amp;#34;message&amp;#34;: &amp;#34;Transaction block must balance&amp;#34;,
 &amp;#34;violations&amp;#34;: [&amp;#34;entries: sum of debits (125000) != sum of credits (100000)&amp;#34;],
 &amp;#34;timestamp&amp;#34;: &amp;#34;2026-07-01T09:14:03.512Z&amp;#34;
}&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;Two write-path errors use specialized shapes:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Batch size exceeded&lt;/strong&gt; (&lt;code&gt;413&lt;/code&gt;):&lt;/p&gt;



 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="625b5b5" class="language-json "&gt;
 &lt;code&gt;{
 &amp;#34;error&amp;#34;: &amp;#34;BATCH_SIZE_EXCEEDED&amp;#34;,
 &amp;#34;message&amp;#34;: &amp;#34;Batch size 1500 exceeds maximum allowed size of 1000&amp;#34;,
 &amp;#34;maxBatchSize&amp;#34;: 1000,
 &amp;#34;requestedSize&amp;#34;: 1500
}&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Idempotency / conflict responses&lt;/strong&gt; on event submission (&lt;code&gt;409&lt;/code&gt;) include the conflicting entries per domain:&lt;/p&gt;</description></item><item><title>Subledger rollups</title><link>/docs/query-api/subledger/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/query-api/subledger/</guid><description>&lt;h2 id="subledger-rollup"&gt;Subledger rollup &lt;a href="#subledger-rollup" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="0b8501c" class="language-text "&gt;
 &lt;code&gt;GET /ledger/v1/subledger&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;Rolls an account range up per dimension value — e.g. receivables per &lt;code&gt;CUSTOMER&lt;/code&gt;. Rollups are served from views that are pre-aggregated for every dimension type as transactions post, so the query is a lookup regardless of ledger size; each request filters on &lt;strong&gt;one&lt;/strong&gt; dimension type:&lt;/p&gt;



 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="4a6c404" class="language-bash "&gt;
 &lt;code&gt;curl &amp;#34;http://localhost:8081/ledger/v1/subledger?accounts=1500&amp;amp;dimensionType=CUSTOMER&amp;amp;timePeriodType=YEAR&amp;amp;timePeriodValue=2026&amp;#34; \
 -H &amp;#34;X-Tenant-ID: org-84120557&amp;#34;&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;h3 id="query-parameters"&gt;Query parameters &lt;a href="#query-parameters" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Parameter&lt;/th&gt;
 &lt;th&gt;Required&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;accounts&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;4-digit codes and/or ranges, comma-separated: &lt;code&gt;1500&lt;/code&gt; or &lt;code&gt;5000-5999&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;dimensionType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;CUSTOMER&lt;/code&gt;, &lt;code&gt;SUPPLIER&lt;/code&gt;, &lt;code&gt;EMPLOYEE&lt;/code&gt;, &lt;code&gt;COUNTERPARTY&lt;/code&gt;, &lt;code&gt;ASSET&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;timePeriodType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;YEAR&lt;/code&gt;, &lt;code&gt;TERTIAL&lt;/code&gt;, &lt;code&gt;QUARTER&lt;/code&gt;, &lt;code&gt;BI_MONTHLY&lt;/code&gt;, &lt;code&gt;MONTHLY&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;timePeriodValue&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;e.g. &lt;code&gt;2026&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="response--200-ok"&gt;Response — &lt;code&gt;200 OK&lt;/code&gt; &lt;a href="#response--200-ok" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Debit-positive minor units. A &lt;code&gt;dimensionValue: null&lt;/code&gt; row collects entries in the range that carry no tag of the requested type:&lt;/p&gt;</description></item><item><title>Party exposure</title><link>/docs/query-api/party-exposure/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/query-api/party-exposure/</guid><description>&lt;h2 id="get-a-partys-exposure"&gt;Get a party&amp;rsquo;s exposure &lt;a href="#get-a-partys-exposure" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="4029df8" class="language-text "&gt;
 &lt;code&gt;GET /ledger/v1/parties/{partyId}/exposure&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;&lt;code&gt;partyId&lt;/code&gt; is the party&amp;rsquo;s UUID. The same party may appear as customer, supplier and owner — the endpoint sums each role separately and nets AR against AP.&lt;/p&gt;



 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="ea3f4e5" class="language-bash "&gt;
 &lt;code&gt;curl &amp;#34;http://localhost:8081/ledger/v1/parties/6f1c…/exposure?receivableAccounts=1500&amp;amp;payableAccounts=2400&amp;amp;timePeriodType=YEAR&amp;amp;timePeriodValue=2026&amp;#34; \
 -H &amp;#34;X-Tenant-ID: org-84120557&amp;#34;&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;h3 id="query-parameters"&gt;Query parameters &lt;a href="#query-parameters" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Parameter&lt;/th&gt;
 &lt;th&gt;Required&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;receivableAccounts&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;Codes/ranges for accounts receivable&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;payableAccounts&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;Codes/ranges for accounts payable&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ownerAccounts&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;Codes/ranges for owner/equity accounts&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;timePeriodType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;Default &lt;code&gt;YEAR&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;timePeriodValue&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;Default: current year&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="response--200-ok"&gt;Response — &lt;code&gt;200 OK&lt;/code&gt; &lt;a href="#response--200-ok" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="f4ff7c6" class="language-json "&gt;
 &lt;code&gt;{
 &amp;#34;partyId&amp;#34;: &amp;#34;6f1c…&amp;#34;,
 &amp;#34;timePeriodType&amp;#34;: &amp;#34;YEAR&amp;#34;,
 &amp;#34;timePeriodValue&amp;#34;: &amp;#34;2026&amp;#34;,
 &amp;#34;receivable&amp;#34;: 50000,
 &amp;#34;payable&amp;#34;: 20000,
 &amp;#34;net&amp;#34;: 30000,
 &amp;#34;roles&amp;#34;: [
 { &amp;#34;role&amp;#34;: &amp;#34;CUSTOMER&amp;#34;, &amp;#34;accountCodes&amp;#34;: [&amp;#34;1500&amp;#34;], &amp;#34;balance&amp;#34;: 50000 },
 { &amp;#34;role&amp;#34;: &amp;#34;SUPPLIER&amp;#34;, &amp;#34;accountCodes&amp;#34;: [&amp;#34;2400&amp;#34;], &amp;#34;balance&amp;#34;: -20000 }
 ]
}&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;&lt;code&gt;net = receivable − payable&lt;/code&gt;. An owner/&lt;code&gt;COUNTERPARTY&lt;/code&gt; balance appears in &lt;code&gt;roles&lt;/code&gt; but is &lt;strong&gt;excluded&lt;/strong&gt; from &lt;code&gt;net&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Dead-letter queue</title><link>/docs/command-api/failed-events/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/command-api/failed-events/</guid><description>&lt;p&gt;The dead-letter queue is an operations surface: listing can span tenants, but everything requires the &lt;strong&gt;&lt;code&gt;{orgId}:admin:ledger&lt;/code&gt;&lt;/strong&gt; permission — the list only shows tenants you hold &lt;code&gt;admin:ledger&lt;/code&gt; for, and reading, reprocessing or discarding a record requires &lt;code&gt;admin:ledger&lt;/code&gt; on that record&amp;rsquo;s tenant. Operations against another tenant&amp;rsquo;s records answer &lt;code&gt;403 FORBIDDEN&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="list-failed-events"&gt;List failed events &lt;a href="#list-failed-events" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="d48504c" class="language-text "&gt;
 &lt;code&gt;GET /ledger/v1/failed-events&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Parameter&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;tenantId&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Filter by tenant (requires read access to it)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;eventType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Filter by event type&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;reprocessed&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt; — filter by reprocessing state&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;200&lt;/code&gt; response:&lt;/p&gt;</description></item><item><title>Journal &amp; event stream</title><link>/docs/query-api/journal/</link><pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate><guid>/docs/query-api/journal/</guid><description>&lt;h2 id="paginated-journal"&gt;Paginated journal &lt;a href="#paginated-journal" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="75d882c" class="language-text "&gt;
 &lt;code&gt;GET /ledger/v1/journal&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;Newest-first pages of posted transaction events. Use it to load history before subscribing to the live stream.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Parameter&lt;/th&gt;
 &lt;th&gt;Required&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;limit&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;Default 50, hard cap 200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;before&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;Global-sequence cursor; returns older events. Omit for the latest page&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="response--200-ok"&gt;Response — &lt;code&gt;200 OK&lt;/code&gt; &lt;a href="#response--200-ok" class="anchor" aria-hidden="true"&gt;&lt;i class="material-icons align-middle"&gt;link&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;


 
 
 

 
 
 
 

 

 &lt;div class="prism-codeblock "&gt;
 &lt;pre id="225db10" class="language-json "&gt;
 &lt;code&gt;{
 &amp;#34;events&amp;#34;: [ { &amp;#34;…&amp;#34;: &amp;#34;TransactionEventDto — see below&amp;#34; } ],
 &amp;#34;maxGlobalSeq&amp;#34;: 1042
}&lt;/code&gt;
 &lt;/pre&gt;
 &lt;/div&gt;
&lt;p&gt;Pass &lt;code&gt;maxGlobalSeq&lt;/code&gt; as the WebSocket &lt;code&gt;fromSequence&lt;/code&gt; to continue seamlessly from where the history ends.&lt;/p&gt;</description></item></channel></rss>