{"openapi":"3.1.0","info":{"title":"Ledger Core","version":"0.1.0"},"paths":{"/healthz":{"get":{"summary":"Healthz","operationId":"healthz","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/transactions":{"post":{"summary":"Create Transaction","operationId":"create_transaction_v1_transactions_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Transaction V1 Transactions Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/transactions/{transaction_id}/commit":{"post":{"summary":"Commit Transaction","operationId":"commit_transaction_v1_transactions__transaction_id__commit_post","parameters":[{"name":"transaction_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Transaction Id"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Commit Transaction V1 Transactions  Transaction Id  Commit Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/transactions/{transaction_id}/void":{"post":{"summary":"Void Transaction","operationId":"void_transaction_v1_transactions__transaction_id__void_post","parameters":[{"name":"transaction_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Transaction Id"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Void Transaction V1 Transactions  Transaction Id  Void Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/balance":{"get":{"summary":"Get Balance","operationId":"get_balance_v1_accounts__account_id__balance_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Balance V1 Accounts  Account Id  Balance Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/transactions/{transaction_id}":{"get":{"summary":"Get Transaction","operationId":"get_transaction_v1_transactions__transaction_id__get","parameters":[{"name":"transaction_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Transaction Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Transaction V1 Transactions  Transaction Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts":{"get":{"summary":"List Accounts","description":"The caller's accounts, newest first (#69).\n\nReported by an integrator: without this, a client that loses local state\ncannot rebuild its view, and \"persist every id we return\" is a requirement\nimposed on every consumer by omission.\n\nKeyset pagination on the UUIDv7 id, never OFFSET (LGR-A4): ids sort by time,\nso a page boundary is stable even while rows are being inserted.","operationId":"list_accounts_v1_accounts_get","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"UUIDv7 cursor (exclusive)","title":"Cursor"},"description":"UUIDv7 cursor (exclusive)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Accounts V1 Accounts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"summary":"Create Account","operationId":"create_account_v1_accounts_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Payload"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Account V1 Accounts Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}":{"get":{"summary":"Get Account","description":"One account, including the balance_floor it actually carries (#69/#68).\n\nThis did not exist: an account id served /balance and /entries but not the\naccount, so GET /v1/accounts/{id} answered 405 for ANY credential and a\ncaller's first reading was \"my key is broken\". Being able to READ the floor\nmatters more since #68 — an omitted floor means unbounded, and this is how a\ncaller confirms which regime they got.","operationId":"get_account_v1_accounts__account_id__get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Account V1 Accounts  Account Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"summary":"Update Account","description":"Update name/metadata/status/balance_floor only.\n\nGuard errors mapped: LG012 close-with-balance -> 409; LG005 terminal ->\n409; floor-raise CHECK (23514) -> 422. Cross-tenant id -> 404.","operationId":"update_account_v1_accounts__account_id__patch","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Account V1 Accounts  Account Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/balance-as-of":{"get":{"summary":"Get Balance As Of","description":"The account's balance as it stood at a past instant (#67).\n\nDERIVED FROM THE JOURNAL, never from the materialised balance — that row\nholds only the CURRENT value and has moved since, so reading it would answer\na different question and look right.\n\nRequested by an integrator with the argument that made the case: the absence\nof this endpoint does not remove the requirement, it relocates it into every\nconsumer's database, where a second and divergent notion of balance grows next\nto ours. Replaying entries client-side to answer \"what did I have on the 1st\"\nis exactly the thing the model tells them not to do.\n\nInclusion is by the transaction's finalized_at, not the entry's created_at: an\nentry exists from the moment its transaction is created, but it does not COUNT\nuntil that transaction commits. Using created_at would include the legs of\nholds that were later voided.","operationId":"get_balance_as_of_v1_accounts__account_id__balance_as_of_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}},{"name":"at","in":"query","required":true,"schema":{"type":"string","description":"ISO-8601 instant WITH an explicit UTC offset","title":"At"},"description":"ISO-8601 instant WITH an explicit UTC offset"},{"name":"basis","in":"query","required":false,"schema":{"type":"string","description":"'effective' (when it happened) or 'recorded' (when we learned it)","default":"effective","title":"Basis"},"description":"'effective' (when it happened) or 'recorded' (when we learned it)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Balance As Of V1 Accounts  Account Id  Balance As Of Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/entries":{"get":{"summary":"Get Account Entries","operationId":"get_account_entries_v1_accounts__account_id__entries_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"UUIDv7 cursor (exclusive)","title":"Cursor"},"description":"UUIDv7 cursor (exclusive)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":50,"title":"Limit"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"serve only entries whose transaction is in this state: pending | committed | voided | expired (default: all)","title":"State"},"description":"serve only entries whose transaction is in this state: pending | committed | voided | expired (default: all)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Account Entries V1 Accounts  Account Id  Entries Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/asset-types":{"get":{"summary":"List Asset Types","description":"The caller's asset types (#69).\n\nRead-role, not admin: knowing which units exist is a read, and a reporting\ncredential needs it to interpret any amount it is shown. There is no\npagination because an asset catalogue is small and bounded by the tenant's\nown currency set; if that ever stops being true this gains a cursor like\nthe account listing.","operationId":"list_asset_types_v1_asset_types_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Asset Types V1 Asset Types Get"}}}}}},"post":{"summary":"Create Asset Type","operationId":"create_asset_type_v1_asset_types_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Asset Type V1 Asset Types Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sub-tenants":{"get":{"summary":"List Sub Tenants","operationId":"list_sub_tenants_v1_sub_tenants_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Sub Tenants V1 Sub Tenants Get"}}}}}},"post":{"summary":"Create Sub Tenant","description":"Create a client of the calling partner.\n\n`kind` is `production` or `sandbox` and is IMMUTABLE afterwards — a sandbox\nexists so an integrator can make permanent mistakes somewhere that is not\nproduction, and letting it be reclassified later would destroy exactly that.","operationId":"create_sub_tenant_v1_sub_tenants_post","responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Sub Tenant V1 Sub Tenants Post"}}}}}}},"/v1/sub-tenants/{sub_tenant_id}":{"patch":{"summary":"Update Sub Tenant","description":"Suspend, reactivate or rename a client. Never reclassify or reparent it.","operationId":"update_sub_tenant_v1_sub_tenants__sub_tenant_id__patch","parameters":[{"name":"sub_tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Sub Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Sub Tenant V1 Sub Tenants  Sub Tenant Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sub-tenants/{sub_tenant_id}/keys":{"post":{"summary":"Mint Sub Tenant Key","description":"Mint a credential scoped to one client. The secret is returned once.","operationId":"mint_sub_tenant_key_v1_sub_tenants__sub_tenant_id__keys_post","parameters":[{"name":"sub_tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Sub Tenant Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Mint Sub Tenant Key V1 Sub Tenants  Sub Tenant Id  Keys Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"List Sub Tenant Keys","operationId":"list_sub_tenant_keys_v1_sub_tenants__sub_tenant_id__keys_get","parameters":[{"name":"sub_tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Sub Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Sub Tenant Keys V1 Sub Tenants  Sub Tenant Id  Keys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sub-tenants/{sub_tenant_id}/keys/{key_id}":{"delete":{"summary":"Revoke Sub Tenant Key","operationId":"revoke_sub_tenant_key_v1_sub_tenants__sub_tenant_id__keys__key_id__delete","parameters":[{"name":"sub_tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Sub Tenant Id"}},{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Revoke Sub Tenant Key V1 Sub Tenants  Sub Tenant Id  Keys  Key Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}