View as Markdown

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.

idstring

A 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 | null

The list's name; it defaults to its scope type (EUR/DKK for a currency pair). Aleta's default list is Aleta default. null for a list from the legacy platform, which has none there.

scope_typestring

tenant, client, entity and asset for prices and basic data; tenant, client, entity and currency_pair for exchange rates; platform for Aleta's default list.

scope_ref_idstring | null

The client, entity or asset; null for tenant, currency_pair and platform.

sourcesarray of object

The sets in priority order, each {set, provider}. set is global (Aleta's shared data), manual (Aleta's manual set), external (your own), custodian or a custodian's code (that bank's own set). On a list from the legacy platform custodian keeps legacy's meaning: any bank that holds the position, in the bank order legacy uses. provider names the source inside global on lists from the legacy platform and on Aleta's default exchange-rate list while the legacy platform is master for you, and is null otherwise. 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 as danmarks_nationalbank, legacy's own as calculated).

valid_fromstring | null

First day, inclusive; null when open.

valid_tostring | null

Last day, inclusive; null when open.

statusstring

active or archived.

archived_atstring | null

When the list was archived, RFC 3339; null while active.

archived_bystring | null

The user who archived it: a person, or an API key's machine user; null while active.

archive_reasonstring | null

Why, when given.

originstring

legacy or v1. A legacy list is read-only until your move to v1 is done (409 legacy_managed).

ownerstring

tenant for your lists, aleta for Aleta's default list, which is read-only for you.

created_atstring

RFC 3339 timestamp.

updated_atstring

RFC 3339 timestamp.

from_currency_codestring | null

Exchange rates only: set on a currency_pair list, null otherwise.

to_currency_codestring | null

Exchange rates only: set on a currency_pair list, null otherwise.

fieldsarray of string | null

Basic data only: the fields the list is for, among name, isin, figi, cusip, valoren, currency_code and country_code. null is every field.


GET/v1/price-hierarchies

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_typestring

Only this scope, or platform for Aleta's default list alone (an empty page on basic data). Optional; a scope the resource does not take is a 400 problem.

scope_ref_idstring

Only lists on this client, entity or asset. Optional.

as_ofstring

Only lists whose period covers this day (YYYY-MM-DD). Optional; without it every period is listed.

fieldstring

Basic data only: lists that cover this field. Optional.

include_archivedboolean

Include archived lists. Optional, default false.

limitinteger

Page size. Optional, default 100, at most 1000.

cursorstring

next_cursor of 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.

Request
GET/v1/price-hierarchies
curl -G https://api.aleta.ai/v1/price-hierarchies \
  -H "Authorization: Bearer {access_token}" \
  -d scope_type=client
Response
{
  "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/v1/price-hierarchies/{id}

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.

Request
GET/v1/exchange-rate-hierarchies/{id}
curl https://api.aleta.ai/v1/exchange-rate-hierarchies/019a1f4e-7d2e-7b42-9d3e-2f6a0c8b1e66 \
  -H "Authorization: Bearer {access_token}"
Response
{
  "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"
}

POST/v1/price-hierarchies

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_request for a scope_type the resource does not take, a missing or extra scope_ref_id, a missing currency pair, an invalid currency code or the same currency twice, empty sources, a set named twice, a provider that is not null, a set that is a source (ecb, six, ...) or that the resource does not take, fields outside basic data or empty or with an unknown field, a day that is not YYYY-MM-DD or is on or before 1900-01-01, valid_from after valid_to, a missing name on 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, asset or currency_pair list applies to the whole firm and needs a firm administrator; a client or entity list needs write access on its client (a read-only grant is not enough). Otherwise 403 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_use naming the lists, with next_action ending the first one of yours (PATCH its valid_to). A list on an entity whose legacy list covers the days is a 409 legacy_managed while the legacy platform is master for you; after your move it is a 409 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_typestring

Required.

scope_ref_idstring

A UUID. Required for client, entity and asset; left out otherwise.

from_currency_codestring

Exchange rates, currency_pair only. Required there.

to_currency_codestring

Exchange rates, currency_pair only. Required there; differs from from_currency_code.

fieldsarray of string

Basic data only. Optional; left out or null, the list covers every field.

sourcesarray of object

The sets in priority order, each {set}: global, manual, external, custodian or a custodian's code (basic data takes global, custodian and custodian codes). At least one, no set twice. provider may be left out or null: the sources inside global are Aleta's choice. A source such as ecb or six is not a set: it is a 400 problem. Required.

valid_fromstring

First day, inclusive, after 1900-01-01. Optional, open when left out.

valid_tostring

Last day, inclusive. Optional, open when left out.

namestring

At most 200 characters. Required for basic data; optional for prices and exchange rates, where it defaults to the scope type (EUR/DKK for a currency pair).

Request
POST/v1/exchange-rate-hierarchies
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" }]
  }'
Request
POST/v1/basic-data-hierarchies
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" }]
  }'

PATCH/v1/price-hierarchies/{id}

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 a 403 aleta_owned; a list you may not write (see create) a 403 forbidden.
  • A list from the legacy platform is a 409 legacy_managed while the legacy platform is master for you; after your move a legacy list kept per entity is a 409 conflict, as it cannot be changed through v1.
  • Then the body: 400 bad_request as on create, for fields on a price or exchange-rate list, for effective_from together with valid_from or valid_to, for status: archived (archive with DELETE), or for effective_from outside the list's period.
  • An archived list takes only status: active, which brings it back; any other change is a 409 in_use with next_action restore (PATCH status).
  • An overlap or a gap as on create is a 409 in_use or 409 conflict.

Body parameters

sourcesarray of object

As on create. Optional.

namestring

Optional.

fieldsarray of string | null

Basic data only; null covers every field. Optional.

valid_fromstring | null

Corrections only; null opens the start. Optional.

valid_tostring | null

Corrections only; null opens the end. Optional.

effective_fromstring

The first day of a change, within the list's period. Optional.

statusstring

active brings an archived list back. Optional.

reasonstring

Why, kept in the change log. Optional.


DELETE/v1/price-hierarchies/{id}

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.


GET/v1/exchange-rate-hierarchies

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.

POST/v1/exchange-rate-hierarchies

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/v1/exchange-rate-hierarchies/{id}

Get an exchange-rate hierarchy

As Get a hierarchy; platform returns Aleta's default exchange-rate list.

PATCH/v1/exchange-rate-hierarchies/{id}

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.

DELETE/v1/exchange-rate-hierarchies/{id}

Archive an exchange-rate hierarchy

As Archive a hierarchy; platform is a 403 aleta_owned.


GET/v1/basic-data-hierarchies

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.

POST/v1/basic-data-hierarchies

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/v1/basic-data-hierarchies/{id}

Get a basic-data hierarchy

As Get a hierarchy. There is no default list: platform is a 404.

PATCH/v1/basic-data-hierarchies/{id}

Update a basic-data hierarchy

As Update a hierarchy; fields changes the fields the list is for (null: every field). platform is a 404.

DELETE/v1/basic-data-hierarchies/{id}

Archive a basic-data hierarchy

As Archive a hierarchy. platform is a 404.

  • Exchange rates — the sets an exchange-rate list chooses between.
  • Assets — the time series and basic data the lists apply to.