Tags
Tags are the classification system: named nodes in a taxonomy (asset classes, regions, sectors, custom groupings) that get attached to targets through tag assignments. The tag defines what a classification is; the assignment says which target carries it, from when, and at what scope. Reading requires the reporting:read scope, and so does previewing a tag's rule, which writes nothing; every other write requires reporting:write.
There are two layers, told apart by owner. Aleta's shared tags (owner: "aleta") are the same for every tenant: the GICS sectors and industries, and the standard asset classification with its default assignments. You read them and assign them, but you cannot change or remove them (403 aleta_owned). Your own tags and assignments (owner: "tenant") sit on top: a tag of your own, or an override of a shared tag named in base_tag_id, and the assignments you make, which take precedence over the default ones.
Data model
idstringStable identifier for the tag. A UUID — treat it as opaque.
base_tag_idstring or nullThe Aleta reference tag this tag overrides, when it overrides one.
taxonomy_codestring or nullThe taxonomy the tag belongs to, e.g.
legacy_classification.codestring or nullMachine-readable code within the taxonomy.
parent_tag_idstring or nullParent tag in the hierarchy.
nullfor roots.namestringDisplay name shown in the product.
patternstring or nullHow a tag of yours relates to Aleta's:
tenant_onlyfor a tag of its own,extend,override_subtreeorreplace_whole_treefor an override of the shared tag inbase_tag_id.nullon Aleta's tags.matchobject, array or nullOptional match rule for automatic assignment, as it was written: one predicate or an array of them.
valid_fromISO 8601 date or nullFirst date the tag applies.
nullmeans no start.valid_toISO 8601 date or nullLast date the tag applies.
nullmeans open-ended.statusstringactivewhen the tag applies on the day read (as_ofin a list, today otherwise), elseinactive. An Aleta reference tag (GICS) has no dates: it isactiveuntil Aleta retires it, and a list shows only active ones.ownerstringaletafor Aleta's shared tags,tenantfor yours.originstringlegacyfor a tag that came from the legacy platform (Aleta's shared tags among them),v1for one made through this API.metadataobjectFree-form metadata carried on the tag.
balance_post_id, the accounting balance post of a legacy classification, is a string like every other id ("7").
List tags
Returns tags in the standard v1 list envelope: Aleta's reference tags (GICS) first, then the tags kept for your tenant (Aleta's standard classification and your own) in the order they were created; one page can hold both. A reference tag has no dates and is listed while it is active. Any other tag is listed on the days it applies: from valid_from through valid_to, read on as_of. One that ended before as_of is left out unless include_expired=true; one that starts after it is always left out.
Query parameters
as_ofISO 8601 dateThe day the tags are read on, shown in
as_of. Defaults to today.ownerstringaletaortenantfor one layer. Both when left out. Anything else is a400.include_expiredbooleanAlso list the tags that ended before
as_of, withstatus: "inactive". Defaults tofalse.limitintegerPage size. Defaults to 100, capped at 1 000.
cursorstringOpaque cursor from the previous page's
next_cursor. Omit for the first page. A cursor that cannot be read is a400.
Any other parameter is a 400.
curl -G https://api.aleta.ai/v1/tags \
-H "Authorization: Bearer {access_token}" \
-d limit=2
{
"data": [
{
"id": "019f106f-59a2-71dc-9d28-e9069a50a4ea",
"base_tag_id": null,
"taxonomy_code": "gics",
"code": "10",
"parent_tag_id": null,
"name": "Energy",
"pattern": null,
"match": null,
"valid_from": null,
"valid_to": null,
"status": "active",
"owner": "aleta",
"origin": "legacy",
"metadata": {
"gics_level": "sector"
}
},
{
"id": "019e93ad-6ed9-72fa-b817-e463aaee1662",
"base_tag_id": null,
"taxonomy_code": "legacy_classification",
"code": "6a0443600f1c409c6873c522",
"parent_tag_id": null,
"name": "Indlån DKK",
"pattern": null,
"match": null,
"valid_from": "2024-06-01",
"valid_to": null,
"status": "active",
"owner": "aleta",
"origin": "legacy",
"metadata": {
"last_modified": null
}
}
],
"next_cursor": "dGVuYW50fDIwMjYtMDYtMDQgMTc6Mjc6NTcuMzAwNTA3KzAwfDAxOWU5M2FkLTZlZDktNzJmYS1iODE3LWU0NjNhYWVlMTY2Mg",
"limit": 2,
"as_of": "2026-10-18"
}
Create a tag
Creates a tag of your own. Only name is required. Returns 201 with the created tag. Takes ?dry_run=true and Idempotency-Key. A base_tag_id that is not one of Aleta's reference tags, or a parent_tag_id that is not one of the tags kept for your tenant, answers 404. An unknown field is a 400.
Body parameters
namestringDisplay name. Required; an empty name is a
400.taxonomy_codestringTaxonomy to file the tag under. Optional. The taxonomies of Aleta's shared tags (
legacy_classification,asset_class,asset_class_detailed,gics,morningstar,risk_country) are refused with400; to override one of those tags, name it inbase_tag_id.codestringMachine-readable code within the taxonomy. Optional.
parent_tag_idstringParent tag for hierarchy placement. Optional.
base_tag_idstringThe Aleta reference tag this one overrides. Optional; required with an override
pattern.patternstringtenant_only(the default) for a tag of its own, which must omitbase_tag_id, orextend,override_subtreeorreplace_whole_treefor an override, which needsbase_tag_id. Anything else is a400.matchobject or arrayMatch rule for automatic assignment. Optional. One predicate or an array of them, all of which must hold:
{"field": "currency_code", "op": "=", "value": "DKK"}.opis=,!=,in,not_in,>,>=,<or<=;fieldis an asset field (name,kind,isin,currency_code,country_code, ...) ormetadata.<key>. A rule the preview cannot read is refused with400and the reason.valid_fromISO 8601 dateFirst date the tag applies; today when omitted. Optional, like
valid_to(the last date it applies). Avalid_fromaftervalid_tois a400.metadataobjectFree-form metadata. Optional.
curl -X POST https://api.aleta.ai/v1/tags \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"name": "Core holdings",
"taxonomy_code": "house_view",
"code": "core"
}'
{
"id": "01a14e88-af2b-7298-b166-842476dd361f",
"base_tag_id": null,
"taxonomy_code": "house_view",
"code": "core",
"parent_tag_id": null,
"name": "Core holdings",
"pattern": "tenant_only",
"match": null,
"valid_from": "2026-10-18",
"valid_to": null,
"status": "active",
"owner": "tenant",
"origin": "v1",
"metadata": {}
}
Retrieve a tag
Fetch a single tag by id, one of yours or one of Aleta's shared tags. status is read on today. Returns the bare object, or a 404 problem when the id is neither.
curl https://api.aleta.ai/v1/tags/019e93ad-6ed9-72fa-b817-e463aaee1662 \
-H "Authorization: Bearer {access_token}"
{
"id": "019e93ad-6ed9-72fa-b817-e463aaee1662",
"base_tag_id": null,
"taxonomy_code": "legacy_classification",
"code": "6a0443600f1c409c6873c522",
"parent_tag_id": null,
"name": "Indlån DKK",
"pattern": null,
"match": null,
"valid_from": "2024-06-01",
"valid_to": null,
"status": "active",
"owner": "aleta",
"origin": "legacy",
"metadata": {
"last_modified": null
}
}
Update a tag
Partially updates a tag and returns 200 with it. Every field from create is patchable; base_tag_id, taxonomy_code, code, parent_tag_id, match and valid_to can be cleared by passing null explicitly. Omitted fields are unchanged; metadata, when sent, replaces the tag's metadata. The same rules as on create apply to name, pattern, taxonomy_code, match and the dates. Takes ?dry_run=true and Idempotency-Key.
An update corrects the tag for its whole life and is logged as a change with an optional reason (a string in the body). A parent_tag_id that is the tag itself or would make it its own ancestor answers 400; an unknown id answers 404. One of Aleta's shared tags answers 403 aleta_owned, before and after your move, and so do Aleta's reference tags (GICS) always; any other tag from the legacy platform answers 409 legacy_managed until your move to v1 is done. An unknown field is a 400.
curl -X PATCH https://api.aleta.ai/v1/tags/01a14e88-af2b-7298-b166-842476dd361f \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{ "valid_to": "2026-12-31", "reason": "House view ends this year" }'
Delete a tag
Removes a tag nothing uses (204). A tag that is assigned, has child tags or is the target of a limit, benchmark, hierarchy, document or lens rule is ended instead: its valid_to and that of its open assignments are set to ?valid_to= (today when left out), and the answer is 200 with the tag. Pass ?valid_to= to end an unused tag too. A date before the tag's valid_from is a 400; a tag that already ends on or before the day answers 200 unchanged.
A child tag that runs past the day, or an assignment that starts after it, answers 409 in_use: next_action points to the child (DELETE /v1/tags/{id} with valid_to) or the assignment (DELETE /v1/tag-assignments/{id}). Ending a tag that has open assignments you cannot see also answers 409 in_use, without next_action. An open assignment that would end up the same as one already ending on that day answers 409 conflict. One of Aleta's shared tags answers 403 aleta_owned, before and after your move, and so do Aleta's reference tags (GICS) always; any other tag from the legacy platform answers 409 legacy_managed until your move to v1 is done. Takes ?dry_run=true and Idempotency-Key; 404 if the id doesn't exist for this tenant.
curl -X DELETE "https://api.aleta.ai/v1/tags/01a14e88-af2b-7298-b166-842476dd361f?valid_to=2026-12-31" \
-H "Authorization: Bearer {access_token}"
Preview a tag's assignments
Runs the tag's match rule over the assets you hold, without writing anything; it needs reporting:read. The assets are those held on the newest holdings date on or before as_of (today when left out) that you can see. Each match comes with one reason per predicate and whether the tag is already assigned on that day. The tag must be one kept for your tenant: Aleta's reference tags (GICS) and unknown ids answer 404. A tag without a rule answers 400.
Body parameters (optional)
as_ofISO 8601 dateThe day to read holdings on; the newest holdings date on or before it is used. Today when left out.
limitintegerHow many matches
dataholds;matchedcounts them all. Defaults to 100, capped at 1 000.
An empty body is the same as {}. Any other field, or a body that is not JSON, is a 400.
Response fields
tag_idstringThe tag previewed.
as_ofISO 8601 date or nullThe holdings date used; null when you hold nothing.
evaluated, matchedintegerAssets the rule ran over, and how many matched.
limitintegerThe page size applied to
data.dataarrayPer match:
asset_id,name,isin(string or null),already_assigned(boolean) andreasons, one line per predicate.
curl -X POST https://api.aleta.ai/v1/tags/01a14e8c-3d2e-7c41-8f0a-5b6e7d8c9a01/preview \
-H "Authorization: Bearer {access_token}"
{
"tag_id": "01a14e8c-3d2e-7c41-8f0a-5b6e7d8c9a01",
"as_of": "2026-10-17",
"evaluated": 214,
"matched": 1,
"limit": 100,
"data": [
{
"asset_id": "019e2c98-b0ea-7c57-81b7-c0c3fac715e7",
"name": "Danske Bank A/S",
"isin": "DK0010274414",
"already_assigned": false,
"reasons": [
"currency_code = \"DKK\" (holds: 'DKK')"
]
}
]
}
List tag assignments
Returns tag assignments — the edges connecting tags to their targets — in effect on as_of (a date, today when left out, shown in the envelope), in the standard v1 list envelope. An assignment whose valid_from is after as_of is left out, and so is one whose valid_to is before it unless include_expired=true. Aleta's default assignment of an asset and one you made are both listed; where both apply, yours takes precedence.
You see the assignments of what you have access to: one scoped to an entity needs that entity, and one on a holding, transaction, document or lens needs that target. An asset is shared, so an asset assignment without a scope shows to everyone with reporting:read, except on an asset a client owns, which shows only to those who see that asset. Tenant owners and tenant admins see every assignment.
Query parameters
as_of, limit and cursor as on every v1 list, and:
tag_id, target_kind, target_idstringOnly the assignments of this tag, this kind of target, or this target.
tag_idandtarget_idmust be UUIDs, else400.ownerstringaletafor Aleta's default assignments,tenantfor yours. Both when left out. Anything else is a400.include_expiredbooleanAlso list the assignments that ended before
as_of. Defaults tofalse.
Any other parameter is a 400.
Assignment fields
idstringStable identifier for the assignment.
target_kindstringWhat kind of thing is tagged:
asset,holding,transaction,documentorlens.target_idstringIdentifier of the tagged target.
tag_idstringThe tag being assigned.
sourcestringHow the assignment came to be:
defaultfor Aleta's standard classification,manualfor one made by hand,ruleoraifor a derived one.ownerstringaletafor adefaultassignment,tenantfor the rest.originstringlegacyorv1, as on a tag.scope_entity_idstring or nullRestricts the assignment to one entity's view, when set.
confidencedecimal string or nullConfidence score for rule-derived assignments.
valid_fromISO 8601 date or nullFirst date the assignment applies.
nullmeans no start.valid_toISO 8601 date or nullLast date the assignment applies.
nullmeans open-ended.metadataobjectFree-form metadata carried on the assignment.
curl -G https://api.aleta.ai/v1/tag-assignments \
-H "Authorization: Bearer {access_token}" \
-d limit=2
{
"data": [
{
"id": "019e96e3-8d71-7296-bf6f-39e5fbe1f25b",
"target_kind": "asset",
"target_id": "019e2c98-b0ea-7c57-81b7-c0c3fac715e7",
"tag_id": "019e93fd-66c1-7a7d-bf11-f35ee5d22ecb",
"source": "default",
"owner": "aleta",
"origin": "legacy",
"scope_entity_id": null,
"confidence": null,
"valid_from": null,
"valid_to": null,
"metadata": {}
},
{
"id": "01a14e89-1050-7dea-9609-99a3b1fc77f3",
"target_kind": "asset",
"target_id": "019e2c98-b0ea-7c57-81b7-c0c3fac715e7",
"tag_id": "01a14e88-af2b-7298-b166-842476dd361f",
"source": "manual",
"owner": "tenant",
"origin": "v1",
"scope_entity_id": null,
"confidence": null,
"valid_from": null,
"valid_to": null,
"metadata": {}
}
],
"next_cursor": "MjAyNi0xMC0xOCAxMDoyMTo0MC4xMjM0NTYrMDB8MDFhMTRlODktMTA1MC03ZGVhLTk2MDktOTlhM2IxZmM3N2Yz",
"limit": 2,
"as_of": "2026-10-18"
}
Create a tag assignment
Attaches a tag to a target: one of yours, or an active one of Aleta's reference tags (a GICS sector, say), which keys on its taxonomy as yours do. Its source is manual. A target holds one manual tag per taxonomy (a tag without a taxonomy only itself) and entity scope at a time, so a new tag in the same taxonomy replaces the one there:
- without
effective_fromit replaces it in its whole period (a correction), and the answer is200with that assignment; - with
effective_fromthe old assignment ends the day before and a new one runs from that day (a change), answered201; - when no assignment of the taxonomy is in force on that day (today, or
effective_from), a new one runs fromeffective_from, elsevalid_from, else from where the last one ended, else from the beginning, answered201.
The same tag already in force answers 200 and writes nothing. An effective_from equal to the start of the assignment in force is a correction (200). A new period that runs into another assignment of the taxonomy answers 409 conflict. Defaults set by Aleta's classification are never replaced. An unknown tag, a retired reference tag, or a target or scope_entity_id you cannot see, answers 404. A tag of Aleta's standard classification, or replacing an assignment from the legacy platform, answers 409 legacy_managed until your move to v1 is done. Takes ?dry_run=true and Idempotency-Key; each write is logged as a change with reason. An unknown field is a 400. The answer carries the assignment.
Body parameters
target_kindstringWhat kind of thing to tag:
asset,holding,transaction,documentorlens. Required; anything else is a400.target_idstringIdentifier of the target, a UUID. Required.
tag_idstringThe tag to assign, a UUID. Required.
scope_entity_idstringRestrict the assignment to one entity's view. Optional.
valid_fromISO 8601 dateFirst date a new assignment applies. Optional, like
valid_to, its last day. Avalid_from(oreffective_from) aftervalid_tois a400.effective_fromISO 8601 dateThe day the tag replaces the one in force. Optional; sending it with
valid_fromis a400.reasonstringWhy, for the change list. Optional.
metadataobjectFree-form metadata. Optional.
curl -X POST https://api.aleta.ai/v1/tag-assignments \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"target_kind": "asset",
"target_id": "019e2c98-b0ea-7c57-81b7-c0c3fac715e7",
"tag_id": "01a14e88-af2b-7298-b166-842476dd361f"
}'
{
"id": "01a14e89-1050-7dea-9609-99a3b1fc77f3",
"target_kind": "asset",
"target_id": "019e2c98-b0ea-7c57-81b7-c0c3fac715e7",
"tag_id": "01a14e88-af2b-7298-b166-842476dd361f",
"source": "manual",
"owner": "tenant",
"origin": "v1",
"scope_entity_id": null,
"confidence": null,
"valid_from": null,
"valid_to": null,
"metadata": {}
}
Delete a tag assignment
Detaches a tag from its target by removing the assignment. The tag itself is untouched. Returns 204 on success, 404 if the id doesn't exist for this tenant or hangs on a target or entity you cannot see. One of Aleta's default assignments answers 403 aleta_owned; any other assignment from the legacy platform answers 409 legacy_managed until your move to v1 is done. Takes ?dry_run=true and Idempotency-Key.
curl -X DELETE https://api.aleta.ai/v1/tag-assignments/01a14e89-1050-7dea-9609-99a3b1fc77f3 \
-H "Authorization: Bearer {access_token}"