Entities
Entities capture who legally owns the assets under a client — the holding companies, trusts, foundations, funds and natural persons in the ownership tree. Cash accounts and custody accounts attach to an entity, so the legal structure has to exist before custody data can land anywhere. Who owns an entity is an ownership, from a party or another entity.
Data model
idstringStable identifier for the entity. A UUID — treat it as opaque.
client_idstringThe client this entity belongs to. Set at creation, immutable afterwards.
namestringDisplay name shown in the product.
kindstringperson,company,foundation,trust,fundorother.primary_currencystring or nullISO 4217 code of the entity's primary currency today: the one set through the API, else the one the entity was imported with, else its client's.
base_currenciesarray of stringsThe primary currency first, then the entity's alternative currencies from the legacy platform, without duplicates.
valid_fromstring or nullThe entity's first day (
YYYY-MM-DD),nullwhen it has always held.valid_tostring or nullThe entity's last day (
YYYY-MM-DD),nullwhile it is open. A closed client ends its entities.closed_throughstring or nullThe entity's period is closed up to and including this date (
YYYY-MM-DD): a write dated on or before it, on a cash or custody account the lock covers, answers409 period_locked.nullwhen nothing is closed. Set it with the period lock. Until your move to v1 is done the legacy platform's monthly lock is the one that counts, and this field staysnull.originstringlegacyfor an entity mirrored from the legacy platform,v1for one made through this API.
Every write on this page takes ?dry_run=true and an Idempotency-Key header, and logs each changed row in the change list. A row that comes from the legacy platform answers 409 legacy_managed until your move to v1 is done. An unknown body field is 400. Writing an entity takes a grant that covers it whole — the tenant, its client or a client above it, or the entity itself (a new entity: its client) — and is not read-only: anyone else gets 403. Admins and Aleta write everywhere. A refusal comes in that order: 404, then 403, then 409.
List entities
Returns every entity visible to the authenticated credential, across all clients it can reach, in the standard v1 list envelope.
Query parameters
client_idstringOnly the entities of this client. A malformed id is a
400; a client you cannot see lists nothing.limitintegerPage size. Defaults to 100, capped at 1 000.
cursorstringOpaque cursor from the previous page's
next_cursor. Omit for the first page.
curl -G https://api.aleta.ai/v1/entities \
-H "Authorization: Bearer {access_token}" \
-d client_id=019dfeb0-0511-77a7-a8be-a911ab988a0a \
-d limit=2
{
"data": [
{
"id": "019dfed3-168e-7cdc-83b2-7598704282f0",
"client_id": "019dfeb0-0511-77a7-a8be-a911ab988a0a",
"name": "LE renamed",
"kind": "company",
"primary_currency": "DKK",
"base_currencies": ["DKK", "EUR"],
"valid_from": null,
"valid_to": null,
"closed_through": null,
"origin": "legacy"
},
{
"id": "019eb3d2-fc2e-7782-973e-af78d0c10fa6",
"client_id": "019dfeb0-0511-77a7-a8be-a911ab988a0a",
"name": "Testing LegalEntity1",
"kind": "person",
"primary_currency": "DKK",
"base_currencies": ["DKK"],
"valid_from": null,
"valid_to": null,
"closed_through": null,
"origin": "v1"
}
],
"next_cursor": "MjAyNi0wNi0xMCAyMzoxNjo0OS4zMjY1MDQrMDB8MDE5ZWIzZDItZmMyZS03NzgyLTk3M2UtYWY3OGQwYzEwZmE2",
"limit": 2
}
Create an entity
Creates an entity under an existing client. Returns 201 with the created resource.
Body parameters
client_idstringThe client to create the entity under. Required.
namestringDisplay name. Required, not blank.
kindstringperson,company,foundation,trust,fundorother. Required.primary_currencystringISO 4217 code, three letters A-Z. Optional,
DKKwhen left out.
curl -X POST https://api.aleta.ai/v1/entities \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"client_id": "019dfeb0-0511-77a7-a8be-a911ab988a0a",
"name": "Testing LegalEntity1",
"kind": "person"
}'
{
"id": "019eb3d2-fc2e-7782-973e-af78d0c10fa6",
"client_id": "019dfeb0-0511-77a7-a8be-a911ab988a0a",
"name": "Testing LegalEntity1",
"kind": "person",
"primary_currency": "DKK",
"base_currencies": ["DKK"],
"valid_from": null,
"valid_to": null,
"closed_through": null,
"origin": "v1"
}
Retrieve an entity
Fetch a single entity by id. Returns the bare object, or a 404 problem when the id doesn't exist for this tenant.
curl https://api.aleta.ai/v1/entities/019dfed3-168e-7cdc-83b2-7598704282f0 \
-H "Authorization: Bearer {access_token}"
{
"id": "019dfed3-168e-7cdc-83b2-7598704282f0",
"client_id": "019dfeb0-0511-77a7-a8be-a911ab988a0a",
"name": "LE renamed",
"kind": "company",
"primary_currency": "DKK",
"base_currencies": ["DKK", "EUR"],
"valid_from": null,
"valid_to": null,
"closed_through": null,
"origin": "legacy"
}
Update an entity
Partially updates an entity. Only the fields present in the body are touched. client_id is immutable — an entity cannot move between clients.
Without effective_from the call is a correction: the entity was wrong all along. With effective_from it is a change of the primary currency from that day: the currency in force on the date ends the day before, the new one starts on it, and what stood before stays. A recalculation of the entity's figures from that day is queued. Both are logged with reason.
Body parameters
namestringNew display name. Must not be blank.
kindstringperson,company,foundation,trust,fundorother.primary_currencystringISO 4217 code, three letters A-Z.
nullis a422: an entity always has one.effective_fromstringYYYY-MM-DD, today or earlier (a later day is422 effective_from_in_future): the day the newprimary_currencystarts. Only withprimary_currencyand noname,kindorvalid_to; otherwise422 effective_from_currency_only.valid_tostring or nullThe entity's last day, or
nullto open it again. Not beforevalid_from(400).reasonstringWhy, for the change list. Optional, not blank.
A body without name, kind, primary_currency or valid_to is 422 empty_patch; primary_currency: null is 422 currency_required.
Ending an entity changes nothing else: its cash and custody accounts, the economic ownerships it is owner or owned in, and its tag assignments keep their own periods. The answer is the entity plus warnings: for a valid_to date, each of those rows that still holds after that day, as {code: "open_after_end", kind, id, valid_to}, with kind cash_account, custody_account, ownership or tag_assignment and the row's own last day (null when open), ordered by kind and id. End each one on its own route. Any other PATCH answers warnings: [].
curl -X PATCH https://api.aleta.ai/v1/entities/019dfed3-168e-7cdc-83b2-7598704282f0 \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{ "primary_currency": "EUR", "effective_from": "2026-01-01", "reason": "moved to Germany" }'
{
"id": "019dfed3-168e-7cdc-83b2-7598704282f0",
"client_id": "019dfeb0-0511-77a7-a8be-a911ab988a0a",
"name": "LE renamed",
"kind": "company",
"primary_currency": "EUR",
"base_currencies": ["EUR"],
"valid_from": null,
"valid_to": null,
"closed_through": null,
"origin": "v1",
"warnings": []
}
Delete an entity
Removes an entity that nothing uses. Returns 204 on success, 404 if the id doesn't exist for this tenant.
An entity with cash or custody accounts, transactions, holdings, figures computed on it, custodian positions, reconciliation breaks, ownerships, tags, custodian authorities, access grants, dated currency versions, price, exchange-rate or basic-data hierarchies set on it, documents, lens rules that name it, or assets built on it is not deleted: the answer is 409 in_use, and next_action points to PATCH /v1/entities/{id} with valid_to, which ends it instead.
A lens that only holds the entity through a broader rule, such as the client's structure lens (include client), does not block the delete. Neither do cash and custody accounts that were deleted before it. The entity leaves those lenses when it is deleted.
curl -X DELETE https://api.aleta.ai/v1/entities/019eb3d2-fc2e-7782-973e-af78d0c10fa6 \
-H "Authorization: Bearer {access_token}"
List an entity's owners
Who owns the entity on as_of (default today), with the owner's name, in the standard v1 list envelope with as_of. Same fields as an ownership, with ownership_id and owner_name. 404 if you cannot see the entity. Any other query parameter is a 400.
Query parameters
as_ofstringYYYY-MM-DD. Defaults to today.limitintegerPage size. Defaults to 100, capped at 1 000.
cursorstringOpaque cursor from the previous page's
next_cursor.
curl https://api.aleta.ai/v1/entities/019eb3d2-fc2e-7782-973e-af78d0c10fa6/owners \
-H "Authorization: Bearer {access_token}"
Ownerships
An ownership says that a party or an entity owns a share of an entity for a period. v1 records economic ownership only: there is no basis, and a voting-only row from the legacy platform is not shown.
idstringStable identifier for the ownership.
owner_kindstringpartyorentity.owner_idstringThe owning party or entity.
owned_entity_idstringThe entity owned.
percentagestringThe share, a decimal string from 0 to 100.
valid_fromstring or nullThe first day,
nullwhen it has always held.valid_tostring or nullThe last day,
nullwhile it is open.custody_account_idsarray or nullnullwhen the ownership covers the whole entity; else the custody accounts it covers that are open on the day read (as_of, else today).cash_account_idsarray or nullnullwhen the ownership covers the whole entity; else the cash accounts it covers that are open on the day read (as_of, else today).created_atstringWhen it was recorded.
originstringlegacyorv1.
List ownerships
Every ownership in force on as_of (default today) that you can see, ordered by owned entity, start and id, in the standard v1 list envelope with as_of. Takes as_of, limit and cursor; any other query parameter is a 400.
curl -G https://api.aleta.ai/v1/ownerships \
-H "Authorization: Bearer {access_token}" \
-d as_of=2026-10-18
Retrieve an ownership
One ownership by id, whatever its period. 404 when it doesn't exist, you cannot see it, or it is a voting-only row.
curl https://api.aleta.ai/v1/ownerships/019f9a10-6b2c-7d11-a0f4-3c9e2b8d4e55 \
-H "Authorization: Bearer {access_token}"
Create an ownership
Records that a party or an entity owns a share of an entity from valid_from (and up to valid_to, the last day, when it ends).
The economic shares of an entity may not pass 100 % on any of its cash or custody accounts on any day: that answers 400 ownership_over_100. Shares under 100 % are allowed while you build the structure, and the answer then carries a warning in warnings. An ownership that would make an entity own itself answers 400 ownership_cycle. The same owner owning the same entity in an overlapping period answers 409 ownership_overlap, with next_action on the existing ownership (PATCH, effective_from): change that one instead. A voting-only ownership does not count.
Body parameters
owner_kindstringpartyorentity. Required.owner_idstringThe owning party or entity. Required.
owned_entity_idstringThe entity owned. Required.
percentagestringThe share, a decimal string from 0 to 100. Required.
valid_fromstringThe first day. Required.
valid_tostringThe last day. Optional.
Response fields
warningsarrayshares_below_100when the cash and custody accounts the ownership covers hold less than 100 % on some day of its period:allocated_percentageis the lowest sum in the period andonthe first day it holds. Empty when they hold 100 %.
curl -X POST https://api.aleta.ai/v1/ownerships \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"owner_kind": "party",
"owner_id": "019eb3d2-fc2e-7782-973e-af78d0c10f01",
"owned_entity_id": "019eb3d2-fc2e-7782-973e-af78d0c10fa6",
"percentage": "40",
"valid_from": "2026-01-01"
}'
{
"id": "019f9a10-6b2c-7d11-a0f4-3c9e2b8d4e55",
"owner_kind": "party",
"owner_id": "019eb3d2-fc2e-7782-973e-af78d0c10f01",
"owned_entity_id": "019eb3d2-fc2e-7782-973e-af78d0c10fa6",
"percentage": "40.0000000000",
"valid_from": "2026-01-01",
"valid_to": null,
"custody_account_ids": null,
"cash_account_ids": null,
"created_at": "2026-10-16T12:00:00Z",
"origin": "v1",
"warnings": [
{ "code": "shares_below_100", "on": "2026-01-01", "allocated_percentage": "40.0000000000" }
]
}
Change an ownership
Corrects an ownership, or changes its share from a date. Returns 200 with the resulting ownership and warnings as on create, 404 if you cannot see the ownership, its owner, a new owner or the entity it owns.
Without effective_from the call is a correction: the ownership was wrong all along, and it is rewritten in its whole period under the same id. You may send percentage, owner_kind with owner_id, valid_from and valid_to (null for open-ended).
With effective_from the call is a change: the share is new from that day. The ownership ends the day before, and a new ownership with the new percentage runs from effective_from to the old end; the answer is the new one. Only percentage goes with effective_from, and the date must fall inside the ownership's period.
Both are logged as a change with reason. The share and cycle rules of create apply (400 ownership_over_100, 400 ownership_cycle), and a second ownership of the same owner starting the same day is 409 ownership_overlap. The owned entity cannot be changed.
Request fields
percentagestringThe share, a decimal string from 0 to 100.
owner_kindstringpartyorentity, withowner_id. Correction only.valid_fromstringThe first day. Correction only.
valid_tostringThe last day, or
null. Correction only.effective_fromstringThe day the new share starts.
reasonstringWhy, for the change list.
curl -X PATCH https://api.aleta.ai/v1/ownerships/019f9a10-6b2c-7d11-a0f4-3c9e2b8d4e55 \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{ "percentage": "50", "effective_from": "2027-01-01", "reason": "share purchase" }'
{
"id": "019f9a10-7c3d-7e22-b1a5-4d0f3c9e5f66",
"owner_kind": "party",
"owner_id": "019eb3d2-fc2e-7782-973e-af78d0c10f01",
"owned_entity_id": "019eb3d2-fc2e-7782-973e-af78d0c10fa6",
"percentage": "50.0000000000",
"valid_from": "2027-01-01",
"valid_to": null,
"custody_account_ids": null,
"cash_account_ids": null,
"created_at": "2026-10-17T09:00:00Z",
"origin": "v1",
"warnings": [
{ "code": "shares_below_100", "on": "2027-01-01", "allocated_percentage": "50.0000000000" }
]
}
Delete an ownership
Removes an ownership no figure ever rested on, for a typing mistake. Returns 204, 404 if you cannot see the ownership, its owner or the entity it owns.
An ownership is used once its period holds data in the cash and custody accounts it reaches: transactions before it ends, or holdings, net asset values, custodian positions or manual holdings inside it, also further down through entities the owned entity owns. A used ownership answers 409 in_use with next_action end_date on PATCH /v1/ownerships/{id} (valid_to).
curl -X DELETE https://api.aleta.ai/v1/ownerships/019f9a10-6b2c-7d11-a0f4-3c9e2b8d4e55 \
-H "Authorization: Bearer {access_token}"
Set the period lock
Closes an entity's period up to and including closed_through, or opens it again. The date is stored on the entity only. Two more routes take the same body and set the same date on several entities in one call: PUT /v1/clients/{id}/period-lock (the client's own entities) and PUT /v1/tenants/current/period-lock (every entity). There is no DELETE: an earlier date opens the days after it, and null opens everything.
Once a period is closed, a write dated on or before the date on a cash or custody account the lock covers answers 409 period_locked. To correct something in a closed period: unlock, register, then lock again. Each move is logged as a change (lock or unlock) with its reason.
Only an admin sets the lock (Aleta too, when every cash and custody account the call covers is one Aleta runs); anyone else gets 403 forbidden. Until your move to v1 is done the legacy platform's monthly lock is the one that counts, so the routes answer 409 legacy_managed. A refusal comes in that order: 400 for the body, 404 for an entity or client you cannot see, then 403, then 409. Takes dry_run and Idempotency-Key: a dry run answers what the call would lock and open, and changes nothing.
Body parameters
closed_throughstring or nullYYYY-MM-DD, today or earlier, ornullto open everything. Required. A date in the future is a400.reasonstringWhy, for the change log. Optional.
Response
closed_throughstring or nullThe date the call set.
entitiesarrayEvery entity the call covers, by id, with
entity_id,previous_closed_throughandclosed_through.lockedarrayThe covered cash and custody accounts (
kind,id,entity_id) of the entities whose closed period grew.unlockedarrayThe covered cash and custody accounts of the entities whose closed period shrank.
curl -X PUT https://api.aleta.ai/v1/entities/019dfed3-168e-7cdc-83b2-7598704282f0/period-lock \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{ "closed_through": "2026-09-30", "reason": "Q3 closed" }'
{
"closed_through": "2026-09-30",
"entities": [
{
"entity_id": "019dfed3-168e-7cdc-83b2-7598704282f0",
"previous_closed_through": null,
"closed_through": "2026-09-30"
}
],
"locked": [
{
"kind": "cash_account",
"id": "019dfed4-2a1b-7c3d-9e4f-5a6b7c8d9e0f",
"entity_id": "019dfed3-168e-7cdc-83b2-7598704282f0"
}
],
"unlocked": []
}
Related
- Parties — the persons and organisations that own entities.
- Cash accounts — the cash accounts attached to an entity.
- Custody accounts — where an entity's assets physically sit.
- Clients — the client an entity belongs to.