SettleMint
API referenceGuides

Corporate actions

Declare a corporate action, preview its effect on holders, then confirm or abort. Covers chunked mint or burn, the in-flight limit, and redemption approval.

Overview

The corporate actions catalog is one collection, /tokens/{assetId}/corporate-actions, that records a named lifecycle event on an issued security, then runs the mechanism that applies it to holders. type names the legal event: a cash dividend, a bond coupon, a bonus issue, a rights issue, a capital decrease, a stock split, a spin-off, a maturity redemption, a distribution, a partial redemption, a conversion, a buyback, or a coordinated distribution across share classes. Read this page before you call any corporate-actions route; it covers the sequence every type follows and the behavior specific to this catalog. For the status lifecycle and the in-flight rule across all thirteen types, read the corporate actions catalog reference. For the request and response fields of each route, use the v2 API explorer.

Create checks what the token carries before it records anything; the asset type plays no part. A type that needs an attached feature is refused with CORPORATE_ACTION_PROGRAM_MISSING when the feature is absent: dividend-yield for a cash dividend or distribution, fixed-treasury-yield for a cash bond coupon, maturity-redemption for a maturity redemption, a partial redemption, or a bond coupon paid in kind, conversion for a conversion, and both distribution-waterfall and dividend-yield for a coordinated distribution. A type that changes issued capital is refused with CORPORATE_ACTION_CAPABILITY_MISSING when the token lacks the capital it needs: a bonus issue and a rights issue need share-capital; a capital decrease, a stock split, a spin-off, a buyback, and a dividend or distribution paid in kind need share-capital or unit-capital. The token read's capabilities lists what a token carries. A token linked to a template keeps the capabilities that template declared when the token was issued. A token with no template link gets them derived when it is read, from its asset type and its attached features: equity without an attached conversion feature has share-capital, fund and real-estate have unit-capital, and any other type has none. cashDividend and distribution are the same payout and are both accepted on any token with dividend-yield.

Creating a cashDividend record, or a distribution paid in cash, already declares the dividend on chain through the dividend-yield feature. Review the approved terms before create; Preview and Confirm happen after that declaration. Declaration is refused with CORPORATE_ACTION_PAYOUT_INELIGIBLE (DALP-14021) when a claim would fail because the payout token refuses the transfer from the treasury to a holder with a positive payout. A holder fails when it is not verified in the payout token's identity registry (on a V1 payout token, or a registry that only records membership, when it is not registered), or when the payout token's compliance refuses that holder's payout. When the payout token charges a transfer fee, the net payout and the fee leg to the fee recipient are each checked. When the payout token charges an external transfer fee in a separate fee token, the treasury pays it on every claim, so its fee-token balance and its allowance to the fee feature must cover one fee per paying holder, including paying holders beyond the 250 the check lists; the claims beyond that capacity are refused. What is left for the new dividend excludes what the same treasury's other open dividends still need. An open dividend that charges its fee in the same fee token reduces the available balance, unless the treasury itself receives that fee. One that uses the same fee feature reduces the available allowance. When an open dividend pays out in the fee token itself, its unclaimed payouts reduce the available balance. The treasury and venue fee accounts block a declaration only when they also hold the dividend token at the record date and so receive a payout; otherwise the denomination eligibility read still lists them, but they do not block. A record date in the future is checked against today's holders and balances, and a past record date against the holders and balances on that date. Holders whose identity is marked lost are skipped, because the snapshot pays them nothing. The refusal's ineligibleCount says how many holders failed, and the denomination eligibility read gives each listed party's reason. The read lists the largest 250 holders, by balance and then address, and sets holdersTruncated when there are more; the probes cover those 250, while the fee count covers every paying holder, so ineligibleCount can exceed the parties the read lists. A probe that gets no answer in time does not block. A declared dividend can still be cancelled until its record-date snapshot is recorded.

Three types name an attached feature program (bond coupon, maturity redemption, conversion): a holder claims yield, redemption, or conversion on that feature's own routes afterward, not through this catalog. A bond coupon and a conversion complete the moment you create them. A maturity redemption calls the bond's mature() on create. The record stays executing until that transaction lands, then reads completed; replay the same Idempotency-Key to resume a record whose transaction did not land. Create is refused with wrong-status before the maturity date on chain and once the bond has already matured. Abort of an executing maturity redemption is refused while its transaction may still land, and returns the record completed when the bond has matured. Holders then redeem their own tokens; nothing is paid to them automatically.

The remaining seven types use stored holder allocations: bonus issue, rights issue, capital decrease, stock split, spin-off, partial redemption, and buyback. Most apply mint or burn batches; a rights issue can instead add entitled holders to an existing token sale whitelist. A bonus issue, an allotment rights issue, and a spin-off mint the floored ratio amount itself. A spin-off adjusts the ratio for any difference between parent and child token decimals. A stock split mints or burns the difference between that amount and the holder's current balance. A capital decrease, a partial redemption, and a buyback burn the amount you announce per holder. This page calls that group the composition types.

A bonus issue, a rights issue, a stock split, and a spin-off count holders when preview runs, and accept an optional recordDate. Without it, preview reads the holder book at the moment it runs. With one, preview is refused while that date is still ahead of the chain time the indexer has reached. Once the date is reached, preview reads the holders at that date from the token's historical-balances feature; a date before the feature began is refused. For a token without that feature, preview reads the current holder book while the indexed chain time is within one minute after the date, and is refused later. A preview that reads the current holder book waits until the indexer has caught up with chain finality as it stood when the preview started, and is refused as unavailable only if that takes longer than 30 seconds. The stored snapshot recordDate is the instant whose holders were counted: the record date when preview read the holders at it, otherwise the instant preview read the book.

A cashDividend, bondCoupon or distribution sent with paymentKind: "inKind" pays holders in new units of the token instead of cash. An in-kind cashDividend is a stock dividend. It carries numerator, denominator, and an optional recordDate, and none of the cash terms. It then runs as a composition type: it mints the floored ratio amount to every snapshot holder, takes the token's in-flight slot, and needs a second operator's approval before confirm. Its recordDate follows the same rule as a bonus issue. Omitting paymentKind means cash, which keeps the behavior described above.

Create, preview, confirm

A composition type follows four steps: create, preview, confirm, and, if needed, abort. A partial redemption, and a dividend, bond coupon or distribution paid in kind, add a fifth between preview and confirm: a second operator approves or rejects the previewed plan, as Approve or reject a partial redemption describes.

curl -X POST "https://your-instance.example.com/api/v2/tokens/0x8a791620dd6260079bf849dc5567adc3f2fdc318/corporate-actions" \
  -H "X-Api-Key: sm_dalp_..." \
  -H "Idempotency-Key: split-2026-09-03-001" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "stockSplit",
    "direction": "forward",
    "numerator": 3,
    "denominator": 1
  }'

The returned record includes the fields below. This excerpt omits timestamps and related identifiers:

{
  "id": "0198f1a2-3c4d-7e8f-9a0b-1c2d3e4f5a6b",
  "tokenAddress": "0x8a791620dd6260079bf849dc5567adc3f2fdc318",
  "type": "stockSplit",
  "status": "drafted",
  "terms": {
    "type": "stockSplit",
    "direction": "forward",
    "numerator": 3,
    "denominator": 1
  },
  "snapshot": null,
  "holders": [],
  "transactionHashes": []
}

For a composition type, create stores the event as drafted without applying the supply change. A composition type takes only one in-flight record per token: creating a second composition record while the first is not yet completed or aborted fails, and the same limit blocks primitive mint and burn on that token while the record is in flight. Send Idempotency-Key on create so a retried request replays the same record instead of declaring the event twice. See Idempotency and async settlement for the header contract that every mutating route on this catalog shares.

Preview computes and stores the allocation snapshot without applying anything:

curl -X POST "https://your-instance.example.com/api/v2/tokens/0x8a791620dd6260079bf849dc5567adc3f2fdc318/corporate-actions/0198f1a2-3c4d-7e8f-9a0b-1c2d3e4f5a6b/preview" \
  -H "X-Api-Key: sm_dalp_..." \
  -H "Content-Type: application/json" \
  -d '{}'

The preview response contains corporateAction, including its snapshot and holders array. Each holder entry has an address, amount, and state. Nonzero allocations start as remaining; amounts that floor to zero start as zeroAfterFloor. Per-holder amounts round down: a ratio type computes floor(balance × numerator / denominator) for each holder, and the record's remainder is the sum over holders of (balance × numerator) mod denominator, counted in denominator-ths of a base unit rather than in whole base units. A holder whose amount floors to zero is stored as zeroAfterFloor and never receives a chunk. Read the computed snapshot against the approved terms before you confirm; nothing has moved yet.

Confirm applies the stored preview. It fails without one for a composition type. A supply change can require multiple on-chain batches. Follow an accepted transaction handle and read the corporate-action record to reconcile the holder states. A completed mint or burn composition has applied every snapshot holder or recorded a zero allocation. A pause or missing historical-balance watermark blocks confirmation. If remaining holder balances have changed since preview, snapshot drift aborts the run. The aborted record cannot resume; review any applied holders before starting another action.

Abort stops a drafted, previewed, approved, or executing record. Status becomes aborted, and no later chunk submits. Holders already applied when abort lands keep their change: abort does not roll back. Replaying the same confirm Idempotency-Key after an abort returns the aborted record, not a retry.

Approve or reject a partial redemption

A partialRedemption, and a cashDividend, bondCoupon or distribution paid in kind, need a second operator before confirm, because none of them can be undone after confirm. Every record carries an approval block. Its policy reads required when the record waits for that decision. Its state reads pending until someone decides. Its planDigest identifies the plan the record holds now. Other types read not_required or not_applicable and confirm exactly as the sequence above describes.

After preview, a different person reads the stored plan and sends its planDigest back as reviewedDigest to POST .../corporate-actions/{corporateActionId}/approve:

{ "reviewedDigest": "3b8f1d2c4a5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8" }

Approve moves the record to approved, and confirm can then run it. Reject, on .../reject, takes the same digest and a required reason of up to 500 characters. It ends the record aborted with approval state denied and keeps the reason on the record. Neither call signs or queues a transaction.

The rules a decision must pass:

  • The checker is an organization owner or admin on a signed-in session. An API key or an impersonated session cannot decide.
  • The checker did not create the record and did not run its last preview.
  • The digest still matches the stored plan. A plan that moved since the checker read it is refused.
  • Approve is refused when the stored preview shows the paying treasury short of the cash leg.

A refused decision answers DALP-12165, and data.violation names the rule that failed.

A decision stays open for 72 hours from the last preview. Each new preview replaces the plan, clears any earlier decision, and restarts that window. A late decision ends the record aborted with approval state expired. A create on the same token does the same to a record whose window has closed, so a forgotten decision does not hold the in-flight slot.

If the plan no longer matches the approved digest at confirm, confirm clears the approval and returns the record to previewed. It answers DALP-12052. Preview again, then have a second operator approve the new plan.

An organization can switch the gate off with the CORPORATE_ACTION_MAKER_CHECKER_ENABLED setting, which defaults to true. An organization with no owner or admin other than the author cannot staff the gate either. In both cases the record opens with policy not_required and state not_required, and waiver names the reason: organization-disabled or too-few-approvers. Confirm checks that answer again. When the organization can now staff the gate, confirm returns the record to previewed with a pending decision instead of running it.

A partial redemption can also declare a cash leg. Send denominationAsset, pricePerUnit, and payingTreasury together on create, and confirm from the paying treasury. Scope this catalog does not cover describes how the cash and the burn settle.

Cash dividends and distributions

Create already declares the dividend on chain. Preview then stores the holder allocations and returns dividendConfirmation: merkleRoot, totalEntitled, and totalRequired. Send all three values together when confirming the reviewed allocation. totalEntitled is a holder count; totalRequired is an amount in denomination-asset base units.

Confirm rejects a missing or mismatched confirmation group. A completed corporate action means the dividend snapshot was committed; holders still claim payment through the dividend-yield feature.

Coordinated distributions across share classes

A coordinatedDistribution pays every share class of a multi-class instrument from one pool, at one record date. Raise it on the class token that carries the distribution-waterfall feature. A token that is not a class of a multi-class instrument, or a class without that feature, is refused. The instrument's waterfall decides the split from each class's rank and contribution basis; classes at the same rank share their part of the pool pro rata.

Create takes poolAmount in base units of the waterfall's denomination asset, denominationAsset (the address of that asset; preview refuses any other), and exDate, recordDate and paymentDate in that order. claimDeadline, when set, falls on or after the payment date. determinationKind is optional: it asks for the waterfall's priority order for that issuer event, and the waterfall applies it only when a valid determination claim of that kind is in force. Create stores the record as drafted and moves no money.

Preview is refused until the record date has passed on chain. It is also refused with waterfall-cannot-receive (DALP-14016) when the denomination asset refuses a transfer from the executing wallet into the waterfall, for example a paused asset or an identity-gated asset and a waterfall that has no registered identity. The distribution-waterfall factory registers an identity for each waterfall it deploys; for a waterfall deployed before that, the token's governance calls ensurePoolReceiver with the class token on that factory. Preview returns coordinatedSplit: the ordinary legs, and the priority legs when you named a determinationKind. Each leg names the class token, its role, its rank and its amount. Preview then declares one dividend per class the pool reaches, at a rate of that class's leg divided by its supply on the record date, rounded down, and waits for each class's record-date snapshot. Until every class is declared and its snapshot is computed, preview is refused with snapshot-not-ready; send it again. Once they are, the record is previewed and its holders list every holder of every class with the amount they can claim, each entry tagged with its classToken. Declaring moves no money. A second operator then approves the plan, as Approve or reject a partial redemption describes; the approval covers the split, each class's dividend and rate, and every holder amount. If the split moved since an earlier preview, preview cancels the class dividends it declared for the old split before declaring new ones.

Confirm is refused until the record date has been reached on chain. It is also refused before any money moves when the claim deadline has passed, the waterfall or its split changed since the approved preview, a class can no longer serve the record date, a class dividend is no longer declared or its snapshot no longer matches the approved holder amounts, a class carries another declared dividend that was never recorded, a class's dividend-yield feature pays in another asset than the pool, the denomination asset refuses a transfer into the waterfall, or the confirming wallet lacks governance on any class. When the approval no longer covers the run, abort it and declare a new one. The executing wallet must hold the pool and approve the waterfall to spend it; POST /api/v2/tokens/{assetId}/features/distribution-waterfall/treasury-allowance sends that approval. Its amount is in base units of the denomination asset, like poolAmount, and sets the whole approval, so send at least the pool. Confirm then funds the waterfall once, bound to the approved split, and records each class's approved snapshot in rank order. The record is completed when every class it pays is recorded, which is when its holders can claim through that class's dividend-yield feature.

The record stays executing while the funding or a class record is still queued. Send confirm again to resume it; it records each class once its transaction lands. A funding transaction that fails, for example because it reverts, moves no money. Confirm returns that failure and puts the record back to approved, so the next confirm funds the pool again. When confirm finds that a queued funding failed, it funds the pool again in the same call. Abort cancels every class dividend the run declared and did not record, including on a record a reject or an approval expiry already ended: abort that record again to cancel its class dividends. The record's appliedSplit.classDividendsOpen is true while any class dividend of the run is still declared or being cancelled. Its appliedSplit.fundingSent is true once the run sent a funding transaction, so an aborted run with it set had a funding that failed and moved no money. To know when to send preview or confirm again, read the record: each appliedSplit.legs[] entry carries requestId, the transaction request its class is waiting on, and appliedSplit.fundingRequestId is the latest funding attempt's transaction request. Wait on each one through the transaction status route or its stream, then send the call again. A funding request that ended FAILED is answered by sending confirm again, which funds the pool in a new attempt. Abort is allowed only while no funding was sent, or when the funding request failed. It is refused while a sent funding may still land and once the pool is in the waterfall, so a funded pool is never left without its class snapshots.

Continuing a partial run

Abort and snapshot drift leave applied holders unchanged. When a new composition continues an earlier run, set followsUpOn to the earlier corporate-action identifier. Preview excludes holders already applied by that linked sequence. Review the remaining allocations before confirming the new run.

Stock split: chunked mint or burn, never an atomic rebase

A stock split (direction: "forward") or reverse split (direction: "reverse") is not a single on-chain rebase of every balance. It applies the ratio through the same chunked mint or burn every other composition type uses: forward mints floor(balance × numerator / denominator) − balance to each holder when that delta is positive, and reverse burns balance − floor(balance × numerator / denominator).

A split processes holders in chunks, so some balances can remain unchanged while the run advances. Inspect entries whose state is remaining to identify holders whose chunks have not completed.

Next steps

On this page