Hierarchies
A hierarchy is an ordered list of sets the platform takes a value from: the first set with a value wins. Price hierarchies apply to every time series of an asset (prices, rates, factors), exchange-rate hierarchies to exchange rates, and basic-data hierarchies to an asset's basic data, field by field. A list applies to one scope (your whole firm, a client, an entity, an asset or, for exchange rates, a currency pair) and one period; the most specific list that covers a day is the one that counts, and on prices and exchange rates Aleta's default list (platform) stands behind them all. Reading requires the market_data:read scope and the market data module at read level, writing market_data:write and the module at read-write level.
The three resources share one shape and one set of routes: /v1/price-hierarchies, /v1/exchange-rate-hierarchies and /v1/basic-data-hierarchies.
Data model
The fields in the order the API writes them. An exchange-rate list adds from_currency_code and to_currency_code at the end, a basic-data list adds fields; a price list has neither.
idstringA UUID. A list the legacy platform keeps per entity has an opaque id that stays the same while legacy rebuilds it. Aleta's default list has the id
platform.namestring | nullThe list's name; it defaults to its scope type (
EUR/DKKfor a currency pair). Aleta's default list isAleta default.nullfor a list from the legacy platform, which has none there.scope_typestringtenant,client,entityandassetfor prices and basic data;tenant,client,entityandcurrency_pairfor exchange rates;platformfor Aleta's default list.scope_ref_idstring | nullThe client, entity or asset;
nullfortenant,currency_pairandplatform.sourcesarray of objectThe sets in priority order, each
{set, provider}.setisglobal(Aleta's shared data),manual(Aleta's manual set),external(your own),custodianor a custodian's code (that bank's own set). On a list from the legacy platformcustodiankeeps legacy's meaning: any bank that holds the position, in the bank order legacy uses.providernames the source insideglobalon lists from the legacy platform and on Aleta's default exchange-rate list while the legacy platform is master for you, and isnullotherwise. A legacy price list shows legacy's own sources, one per legacy source; a legacy exchange-rate list shows the stored ones (Danmarks Nationalbank's rates show asdanmarks_nationalbank, legacy's own ascalculated).valid_fromstring | nullFirst day, inclusive;
nullwhen open.valid_tostring | nullLast day, inclusive;
nullwhen open.statusstringactiveorarchived.archived_atstring | nullWhen the list was archived, RFC 3339;
nullwhile active.archived_bystring | nullThe user who archived it: a person, or an API key's machine user;
nullwhile active.archive_reasonstring | nullWhy, when given.
originstringlegacyorv1. Alegacylist is read-only until your move to v1 is done (409 legacy_managed).ownerstringtenantfor your lists,aletafor Aleta's default list, which is read-only for you.created_atstringRFC 3339 timestamp.
updated_atstringRFC 3339 timestamp.
from_currency_codestring | nullExchange rates only: set on a
currency_pairlist,nullotherwise.to_currency_codestring | nullExchange rates only: set on a
currency_pairlist,nullotherwise.fieldsarray of string | nullBasic data only: the fields the list is for, among
name,isin,figi,cusip,valoren,currency_codeandcountry_code.nullis every field.
List hierarchies
Returns the lists you can see in the standard v1 list envelope, in a stable order. On prices and exchange rates the first page starts with Aleta's default list (platform: global, manual; on exchange rates global/six, global/ecb while the legacy platform is master for you); it is left out when you filter on a scope other than platform or on scope_ref_id. Basic data has no default list. A list on a client, entity or asset is shown only when you see that client, entity or asset. The envelope carries as_of (the day you filtered on) only when you pass as_of. The same query works on /v1/exchange-rate-hierarchies and /v1/basic-data-hierarchies.
Query parameters
scope_typestringOnly this scope, or
platformfor Aleta's default list alone (an empty page on basic data). Optional; a scope the resource does not take is a400problem.scope_ref_idstringOnly lists on this client, entity or asset. Optional.
as_ofstringOnly lists whose period covers this day (
YYYY-MM-DD). Optional; without it every period is listed.fieldstringBasic data only: lists that cover this field. Optional.
include_archivedbooleanInclude archived lists. Optional, default
false.limitintegerPage size. Optional, default
100, at most1000.cursorstringnext_cursorof the previous page. Optional.
Errors
400 bad_request for an unknown scope_type, a scope_ref_id that is no UUID, an invalid as_of or cursor, or field outside basic data or not a basic-data field; 403 forbidden without the scope or the module.
curl -G https://api.aleta.ai/v1/price-hierarchies \
-H "Authorization: Bearer {access_token}" \
-d scope_type=client
{
"data": [
{
"id": "019a1f4e-6c1d-7b42-9d3e-2f6a0c8b1e55",
"name": "client",
"scope_type": "client",
"scope_ref_id": "019a1f4e-0000-7b42-9d3e-2f6a0c8b1e01",
"sources": [
{ "set": "danske-bank-dk", "provider": null },
{ "set": "global", "provider": null }
],
"valid_from": "2026-01-01",
"valid_to": null,
"status": "active",
"archived_at": null,
"archived_by": null,
"archive_reason": null,
"origin": "v1",
"owner": "tenant",
"created_at": "2026-10-16T09:00:00.000000Z",
"updated_at": "2026-10-16T09:00:00.000000Z"
}
],
"next_cursor": null,
"limit": 100
}
Get a hierarchy
Returns one list, an archived one too. On prices and exchange rates platform returns Aleta's default list; basic data has none, so platform is a 404 there. An unknown id, or a list you cannot see, is a 404 not_found problem. GET /v1/exchange-rate-hierarchies/{id} and GET /v1/basic-data-hierarchies/{id} work the same way.
curl https://api.aleta.ai/v1/exchange-rate-hierarchies/019a1f4e-7d2e-7b42-9d3e-2f6a0c8b1e66 \
-H "Authorization: Bearer {access_token}"
{
"id": "019a1f4e-7d2e-7b42-9d3e-2f6a0c8b1e66",
"name": "EUR/DKK",
"scope_type": "currency_pair",
"scope_ref_id": null,
"sources": [
{ "set": "external", "provider": null },
{ "set": "global", "provider": null }
],
"valid_from": null,
"valid_to": null,
"status": "active",
"archived_at": null,
"archived_by": null,
"archive_reason": null,
"origin": "v1",
"owner": "tenant",
"created_at": "2026-10-16T09:00:00.000000Z",
"updated_at": "2026-10-16T09:00:00.000000Z",
"from_currency_code": "EUR",
"to_currency_code": "DKK"
}
Create a hierarchy
Creates one list for one scope and period. Supports ?dry_run=true and an Idempotency-Key header, and every write is kept in the change log. Returns 201 with the list.
- The body is checked first:
400 bad_requestfor ascope_typethe resource does not take, a missing or extrascope_ref_id, a missing currency pair, an invalid currency code or the same currency twice, emptysources, a set named twice, aproviderthat is notnull, asetthat is a source (ecb,six, ...) or that the resource does not take,fieldsoutside basic data or empty or with an unknown field, a day that is notYYYY-MM-DDor is on or before1900-01-01,valid_fromaftervalid_to, a missingnameon basic data, an empty name or one over 200 characters, or a field the body does not take. - A client, entity or asset you cannot see is a
404 not_found, as is an asset that does not exist. - A
tenant,assetorcurrency_pairlist applies to the whole firm and needs a firm administrator; aclientorentitylist needs write access on its client (a read-only grant is not enough). Otherwise403 forbidden. - A custodian code the custodian register does not know is a
400 bad_request. - Two active lists of the same scope (and pair, and for basic data a shared field) cannot share a day: an overlap is a
409 in_usenaming the lists, withnext_actionending the first one of yours (PATCHitsvalid_to). A list on an entity whose legacy list covers the days is a409 legacy_managedwhile the legacy platform is master for you; after your move it is a409 in_use, as the legacy list cannot be changed through v1. The defaults set for an entity made through the API do not count: your list goes before them. - A price or exchange-rate list of a scope runs without gaps: a list that leaves a gap between two lists of its scope is a
409 conflict.
Body parameters
scope_typestringRequired.
scope_ref_idstringA UUID. Required for
client,entityandasset; left out otherwise.from_currency_codestringExchange rates,
currency_paironly. Required there.to_currency_codestringExchange rates,
currency_paironly. Required there; differs fromfrom_currency_code.fieldsarray of stringBasic data only. Optional; left out or
null, the list covers every field.sourcesarray of objectThe sets in priority order, each
{set}:global,manual,external,custodianor a custodian's code (basic data takesglobal,custodianand custodian codes). At least one, no set twice.providermay be left out ornull: the sources insideglobalare Aleta's choice. A source such asecborsixis not a set: it is a400problem. Required.valid_fromstringFirst day, inclusive, after
1900-01-01. Optional, open when left out.valid_tostringLast day, inclusive. Optional, open when left out.
namestringAt most 200 characters. Required for basic data; optional for prices and exchange rates, where it defaults to the scope type (
EUR/DKKfor a currency pair).
curl -X POST https://api.aleta.ai/v1/exchange-rate-hierarchies \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"scope_type": "currency_pair",
"from_currency_code": "EUR",
"to_currency_code": "DKK",
"sources": [{ "set": "external" }, { "set": "global" }]
}'
curl -X POST https://api.aleta.ai/v1/basic-data-hierarchies \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"name": "Bank ISINs first",
"scope_type": "client",
"scope_ref_id": "019a1f4e-0000-7b42-9d3e-2f6a0c8b1e01",
"fields": ["isin"],
"sources": [{ "set": "danske-bank-dk" }, { "set": "global" }]
}'
Update a hierarchy
Changes one list. Without effective_from it is a correction: the list is rewritten in place. With effective_from it is a change from that day: the list ends the day before, and a new list with the changes runs from that day to the old end; the answer is the new list. A list with an open start can change from any day; effective_from on the list's own first day is a correction. The scope cannot change: create a new list for another scope. A body that changes nothing answers 200 with the list and writes nothing. Supports ?dry_run=true and Idempotency-Key. Returns 200. PATCH /v1/exchange-rate-hierarchies/{id} and PATCH /v1/basic-data-hierarchies/{id} work the same way.
- An unknown id or a list you cannot see is a
404 not_found; Aleta's default list (platform) is a403 aleta_owned; a list you may not write (see create) a403 forbidden. - A list from the legacy platform is a
409 legacy_managedwhile the legacy platform is master for you; after your move a legacy list kept per entity is a409 conflict, as it cannot be changed through v1. - Then the body:
400 bad_requestas on create, forfieldson a price or exchange-rate list, foreffective_fromtogether withvalid_fromorvalid_to, forstatus: archived(archive withDELETE), or foreffective_fromoutside the list's period. - An archived list takes only
status: active, which brings it back; any other change is a409 in_usewithnext_actionrestore(PATCHstatus). - An overlap or a gap as on create is a
409 in_useor409 conflict.
Body parameters
sourcesarray of objectAs on create. Optional.
namestringOptional.
fieldsarray of string | nullBasic data only;
nullcovers every field. Optional.valid_fromstring | nullCorrections only;
nullopens the start. Optional.valid_tostring | nullCorrections only;
nullopens the end. Optional.effective_fromstringThe first day of a change, within the list's period. Optional.
statusstringactivebrings an archived list back. Optional.reasonstringWhy, kept in the change log. Optional.
Archive a hierarchy
Archives the list: it stops being listed (unless you pass include_archived=true), and the record stays. Optional query parameter reason. Supports ?dry_run=true and Idempotency-Key. Returns 200 with the archived list; an archived list answers the same and logs nothing. Bring it back with PATCH and status: active. The refusals and their order are as on update: 404 not_found, 403 (403 aleta_owned on platform), then 409 legacy_managed or, for a legacy list after your move, 409 conflict. DELETE /v1/exchange-rate-hierarchies/{id} and DELETE /v1/basic-data-hierarchies/{id} work the same way.
List exchange-rate hierarchies
As List hierarchies, for exchange rates. scope_type takes tenant, client, entity, currency_pair and platform; asset is a 400, as is field. Every list carries from_currency_code and to_currency_code. The first page starts with Aleta's default list: global/six, global/ecb while the legacy platform is master for you, global, manual after your move.
Create an exchange-rate hierarchy
As Create a hierarchy. A currency_pair list needs from_currency_code and to_currency_code, two different currencies, and no scope_ref_id; the pair on any other scope, or fields, is a 400. Sets: global, manual, external, custodian or a custodian's code. A currency_pair list, like a tenant list, needs a firm administrator. Returns 201 with the list.
Get an exchange-rate hierarchy
As Get a hierarchy; platform returns Aleta's default exchange-rate list.
Update an exchange-rate hierarchy
As Update a hierarchy. The pair cannot change: the body does not take from_currency_code or to_currency_code, and those and fields are a 400; platform is a 403 aleta_owned.
Archive an exchange-rate hierarchy
As Archive a hierarchy; platform is a 403 aleta_owned.
List basic-data hierarchies
As List hierarchies, for basic data. scope_type takes tenant, client, entity, asset and platform (always an empty page: basic data has no default list). field keeps the lists that cover one of name, isin, figi, cusip, valoren, currency_code and country_code; another value is a 400. Every list carries fields.
Create a basic-data hierarchy
As Create a hierarchy. name is required. Sets: global, custodian or a custodian's code; manual and external are a 400. fields is optional (left out or null: every field); an empty list, an unknown field or a field named twice is a 400. Returns 201 with the list.
Get a basic-data hierarchy
As Get a hierarchy. There is no default list: platform is a 404.
Update a basic-data hierarchy
As Update a hierarchy; fields changes the fields the list is for (null: every field). platform is a 404.
Archive a basic-data hierarchy
As Archive a hierarchy. platform is a 404.
Related
- Exchange rates — the sets an exchange-rate list chooses between.
- Assets — the time series and basic data the lists apply to.