View as Markdown

Corporate actions

A corporate action is an event on an asset that changes what its holders own, such as a stock split, a merger, a spin-off, a demerger, an ISIN change or a currency change. When one of those events is active, the platform books one transaction on every custody account that held the asset the banking day before, as the legacy platform's transaction generator did, and the calculation reads the event through it. Reversing or superseding an event counter-posts those transactions; nothing is deleted.

Reading requires market_data:read and shows the events your role may see. Registering and correcting events requires market_data:write and an Aleta staff role (403 otherwise). Until your move to v1 is done, the legacy platform owns your events: registering answers 409 legacy_managed, and so does any change to an event that comes from the legacy platform. Every write takes dry_run and Idempotency-Key, and a body field a write does not know answers 400.

Data model

idstring

Stable identifier for the event. A UUID; treat it as opaque.

legacy_idstring or null

The event's id on the legacy platform. null for an event made in v1 or by Aleta's operations. A list filter, never an id in the path.

asset_idstring

The asset the event is registered on.

affected_asset_idsstring[]

Every asset the event touches, asset_id first: merger and spin-off targets, the new line of an ISIN or currency change.

kindstring

dividend, stock_split, reverse_split, share_class_split, spinoff, merger, demerger, acquisition, rights_issue, bonus_issue, tender_offer, drip, name_change, isin_change, currency_change, delist, relist or other.

categorystring or null

mandatory, voluntary or mandatory_with_choice.

statusstring

pending (effective in the future), active, reversed or superseded.

effective_dateISO 8601 date

The date the event takes effect.

ex_dateISO 8601 date or null
The ex-date.
record_dateISO 8601 date or null
The record date.
pay_dateISO 8601 date or null
The pay date.
announcement_dateISO 8601 date or null
The announcement date.
settlement_dateISO 8601 date or null
The settlement date.
detailsobject

The kind's payload in its v1 form, also on an event from the legacy platform. Asset ids in it are v1 asset ids, null when the asset is not known. Numbers are decimal strings.

overrideobject or null

Aleta's correction over the event as registered: patches, reason, author_user_id and at, each null when not recorded. null when there is none. Your own overrides are in the override endpoint's answer and in the effects.

sourcestring

Where the event comes from: manual for one registered on v1, else the legacy or vendor source.

ownerstring

aleta for a shared event (from the legacy platform or Aleta's operations), tenant for one made on v1.

originstring

legacy or v1.

superseded_by_idstring or null

The event that replaced this one, when superseded.

created_atRFC 3339 timestamp
When the event was registered.
updated_atRFC 3339 timestamp
When the event last changed.

POST/v1/corporate-actions

Register corporate actions

Registers one event, or an array of up to 100 events in one transaction. A single object answers 201 with the event, an array with the array. The whole body is validated first: an invalid row (also an unknown field) is a 400 naming it ([2] kind must be one of: ... for an array), never a partial success. An asset that does not exist or that you can't see is a 404.

One asset has at most one live split, merger, spin-off, demerger, ISIN change or currency change per date: two in the same body are a 400, and one that meets a live event already registered is a 409.

An event whose effective date has come is active; one dated in the future is pending and books nothing yet. An active split, merger, spin-off, demerger, ISIN change or currency change books its transactions in the same transaction: one per custody account that held the asset (or one of the assets the event names) the Danish banking day before the effective date, on the custody account's default cash account. They stay unsettled until the bank's own transaction replaces them. Nothing is written, and the answer is 409, when a custody account holding the asset has no default cash account, or when the period is closed on the effective date for a custody account or the cash account it books on (the entity's closed_through). The detail names each locked custody or cash account with its entity and closing date. The other kinds book nothing.

A merger, a spin-off and a demerger book from their first two sources or targets, as the legacy platform did; an active one with fewer is a 400.

Body

asset_idstring

Required. The asset the event is registered on.

kindstring

Required. One of the kinds in the data model.

categorystring or null

mandatory, voluntary or mandatory_with_choice.

effective_dateISO 8601 date

Required.

ex_dateISO 8601 date or null
The ex-date.
record_dateISO 8601 date or null
The record date.
pay_dateISO 8601 date or null
The pay date.
announcement_dateISO 8601 date or null
The announcement date.
settlement_dateISO 8601 date or null
The settlement date.
detailsobject or null

Typed for seven kinds, and required for them; unknown keys answer 400. Numbers may be JSON numbers or decimal strings. stock_split, reverse_split: ratio_numerator, ratio_denominator (both above 0). isin_change: to_asset_id. currency_change: to_asset_id, from_currency_price, to_currency_price. merger: resulting_asset_id, sources (asset_id, ratio of 0 or more). spinoff: source_numerator, spin_offs (asset_id, denominator, value_share). demerger: sources (asset_id, holding_ratio, value_ratio). Any other kind takes an open object.

Request
POST/v1/corporate-actions
curl -X POST https://api.aleta.ai/v1/corporate-actions \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "asset_id": "019e0424-187a-72c0-93c0-a79c006bcc08",
    "kind": "stock_split",
    "effective_date": "2026-05-04",
    "details": { "ratio_numerator": 1, "ratio_denominator": 2 }
  }'
201 Created
{
  "id": "019e0424-3a00-7f11-8e2b-5d3c9a1b0c42",
  "legacy_id": null,
  "asset_id": "019e0424-187a-72c0-93c0-a79c006bcc08",
  "affected_asset_ids": ["019e0424-187a-72c0-93c0-a79c006bcc08"],
  "kind": "stock_split",
  "category": null,
  "status": "active",
  "effective_date": "2026-05-04",
  "ex_date": null,
  "record_date": null,
  "pay_date": null,
  "announcement_date": null,
  "settlement_date": null,
  "details": { "ratio_numerator": "1", "ratio_denominator": "2", "split_ratio": "2" },
  "override": null,
  "source": "manual",
  "owner": "tenant",
  "origin": "v1",
  "superseded_by_id": null,
  "created_at": "2026-05-04T08:12:31.402117Z",
  "updated_at": "2026-05-04T08:12:31.402117Z"
}

GET/v1/corporate-actions

List corporate actions

The events you may see, newest effective_date first, in the standard v1 list envelope with as_of. Any other query field answers 400.

Query parameters

asset_idstring

Events registered on or affecting the asset.

legacy_idstring

The event with this id on the legacy platform.

kindstring

One of the kinds in the data model.

statusstring

pending, active, reversed or superseded.

from_dateISO 8601 date

Earliest effective_date, inclusive.

to_dateISO 8601 date

Latest effective_date, inclusive. Not before from_date.

as_ofISO 8601 date

No event effective after it. The envelope shows it, today when left out, where every event is listed, pending ones too.

limitinteger

Page size, 100 by default, at most 1000.

cursorstring

The previous page's next_cursor.


GET/v1/corporate-actions/{id}

Retrieve a corporate action

One event in the data model's form. An unknown id, or an event you can't see, is a 404.


GET/v1/corporate-actions/{id}/effects

List what an event did

One row per custody account the event touched, in custody account order, paged with cursor and limit. A row shows the transactions the event booked there (the legacy platform's row, or the platform's own and their counter-postings) and the override you set for that custody account. Only custody accounts you can see are listed. An unknown event is a 404. An unknown query parameter is a 400.

Response

custody_account_idstring
The custody account.
cash_account_idstring or null
The cash account the transactions are booked on.
statusstring
applied, adjusted (an override for the custody account), reversed or superseded.
adjustmentobject or null
The override's patches when it has one.
notestring or null
The override's reason when it has one.
transaction_idsstring[]
The transactions, in booking order: a reversal adds the counter-postings.
kindstring or null
The legacy platform's row kind.
effective_dateISO 8601 date or null
The date the transactions are booked on.
detailsobject or null
The legacy platform's generated payload.
sourcestring
Where the row comes from: the legacy source, generated or override.
originstring
legacy or v1.
Response
{
  "data": [
    {
      "custody_account_id": "019e0424-2a10-7b51-9c1e-4c55d7e0a311",
      "cash_account_id": "019e0424-2a10-7b51-9c1e-4c55d7e0a2f0",
      "status": "applied",
      "adjustment": null,
      "note": null,
      "transaction_ids": ["019e0424-3b80-70d2-a1a4-0f6d1c2e9b01"],
      "kind": null,
      "effective_date": "2026-05-04",
      "details": null,
      "source": "generated",
      "origin": "v1"
    }
  ],
  "next_cursor": null,
  "limit": 100
}

POST/v1/corporate-actions/{id}/override

Override an event

Your own layer on top of a live event; the event itself is never rewritten. patches holds the fields to change (category, effective_date, ex_date, record_date, pay_date, announcement_date, settlement_date, details; a patched details is checked like a new event's), reason is required, and custody_account_ids limits the override to some custody accounts (leave it out for the whole tenant; an empty list is a 400). A new override for the same scope replaces the one before, and the change log keeps it. The transactions in scope are counter-posted and booked again under the override; the custody account's override wins over the tenant's.

Answers 200 with the event and every override (overrides: custody_account_id, null for the whole tenant, patches, reason, author_user_id, at). A custody account you can't see is a 404; an ended event is a 409.

Request
POST/v1/corporate-actions/{id}/override
curl -X POST https://api.aleta.ai/v1/corporate-actions/019e0424-3a00-7f11-8e2b-5d3c9a1b0c42/override \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "patches": { "details": { "ratio_numerator": 1, "ratio_denominator": 3 } },
    "reason": "vendor typo",
    "custody_account_ids": ["019e0424-2a10-7b51-9c1e-4c55d7e0a311"]
  }'

POST/v1/corporate-actions/{id}/reverse

Reverse an event

Ends a live event: its status becomes reversed, and every transaction it booked gets a counter-posting on the same date, linked both ways. The body is reason, which is required. Answers 200 with the event and counter_postings (custody_account_id, transaction_id, linked_transaction_id, trade_date). A 409 when the event has ended already, or when the period is closed on a transaction's date.

Response
{
  "id": "019e0424-3a00-7f11-8e2b-5d3c9a1b0c42",
  "legacy_id": null,
  "asset_id": "019e0424-187a-72c0-93c0-a79c006bcc08",
  "affected_asset_ids": ["019e0424-187a-72c0-93c0-a79c006bcc08"],
  "kind": "stock_split",
  "category": null,
  "status": "reversed",
  "effective_date": "2026-05-04",
  "ex_date": null,
  "record_date": null,
  "pay_date": null,
  "announcement_date": null,
  "settlement_date": null,
  "details": { "ratio_numerator": "1", "ratio_denominator": "2", "split_ratio": "2" },
  "override": null,
  "source": "manual",
  "owner": "tenant",
  "origin": "v1",
  "superseded_by_id": null,
  "created_at": "2026-05-04T08:12:31.402117Z",
  "updated_at": "2026-05-06T09:40:02.118540Z",
  "counter_postings": [
    {
      "custody_account_id": "019e0424-2a10-7b51-9c1e-4c55d7e0a311",
      "transaction_id": "019e0424-4c11-7a03-b2c5-1e7f2d3a8c02",
      "linked_transaction_id": "019e0424-3b80-70d2-a1a4-0f6d1c2e9b01",
      "trade_date": "2026-05-04"
    }
  ]
}

POST/v1/corporate-actions/{id}/supersede

Supersede an event

Registers the corrected event and ends the old one (superseded, pointing at the new one) in one transaction. The body is the new event, with the register body's fields; asset_id and kind default to the old event's, effective_date and reason are required. The old event's transactions are counter-posted and the new event books its own. Answers 201 with the new event. An event that has ended already is a 409.

Request
POST/v1/corporate-actions/{id}/supersede
curl -X POST https://api.aleta.ai/v1/corporate-actions/019e0424-3a00-7f11-8e2b-5d3c9a1b0c42/supersede \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "effective_date": "2026-05-05",
    "details": { "ratio_numerator": 1, "ratio_denominator": 2 },
    "reason": "wrong date"
  }'
  • Transactions: the ledger the generated transactions are booked in.
  • Assets: the assets an event is registered on.