Skip to main content

Upgrade an integration to DALP 3.2

Every change in DALP 3.2 that can break a client built against 3.1 or 3.0: removed operations, new required fields, new refusals, the SDK and the CLI.

DALP 3.2 changes the Platform API, the TypeScript SDK and the CLI in ways that can break a client written for DALP 3.1 or 3.0. Each change lists what changed, who is affected, how to detect it and what to do. The steps for the people who run the platform, such as the Helm upgrade, are on Upgrade a deployment to DALP 3.2.

The v2 API is stable from DALP 3.2. Before 3.2, v2 could change between releases, and this page lists the changes it took up to 3.2. From 3.2 onward a breaking change to v2 ships only on a new API version, after a deprecation with at least 90 days' notice, as Versioning and deprecation describes.

Start here

Regenerate your client from the 3.2 OpenAPI document and compile it before you read the rest of this page. The V2 API reference serves the 3.2 document. A typed client then reports most of the changes below as compile errors: a removed operation, a field that is now required, a removed response field and a renamed path parameter. The changes in New refusals on existing operations do not show up at compile time, because they are new error responses on operations whose shape did not change. Read that section whatever your client is built with.

Change
Affects
Detected at
Every SDK user
Compile time
Scripts and agents that call dalp
Run time
Clients that read defined on an error
Run time
Clients that call one of them
Compile time
Freezes, user creation, invitations, organisations
Compile time
Strict response decoders
Compile time
v2 list calls that send limit, offset, sortBy, sortDirection or filters
Run time
Token and token-feature writes
Run time
Mint and burn callers
Run time
Burn and forced-transfer callers
Run time
Clients that change a management fee recipient
Run time
Clients that branch on DALP-1109 or DALP-9059
Run time
Price writes
Run time
Asset class create and rename
Run time
Token creation and factory installation
Run time
Token creation and issuance runs
Run time
v1 clients in an organisation that protects changes
Run time
Document replace and delete, template delete
Run time
Clients that read venue trades or session states
Run time
Clients that retry POST /api/v2/webhooks
Run time
v1 clients that read a user's wallet or an event's gas fields
Compile time
v1 clients
Run time
v1 clients that change roles, freezes, transfers or settings
Run time
Clients built against DALP 3.0
Both

A client without generated types meets every change in the table as a failing request.

Client libraries and tools

The TypeScript SDK

What changed. The 3.2 SDK, @settlemint/dalp-sdk, builds its client from the generated route tree:

  • You create the client with createDalpClient({ baseUrl, apiKey }). 3.1 took url and a top-level organizationId. 3.2 has no organisation option, because the credential names the organisation: an API key acts in the organisation it was issued for, and a session in its active organisation.
  • Routes sit under dalp.api.* and take one flat argument object instead of { params, query, body }.
  • Token routes name their path value assetId, where 3.1 named it tokenAddress.
  • Helpers that the 3.1 package exported from its root, such as waitForTransaction, the auth client and the webhook verifier, move to subpaths such as @settlemint/dalp-sdk/wait-for-transaction.
  • The requestValidation and responseValidation options are gone, because the client checks every success response against the route's schema.
  • A queue-backed write sends Prefer: wait=15 unless you set settlementTimeoutMs. The 3.1 client sent Prefer: wait=99 on every write.

Who is affected. Every application that imports @settlemint/dalp-sdk.

How to detect it. Upgrade the package and compile. Each changed call, import and option fails to type-check.

What to do. Follow the call-by-call TypeScript SDK migration guide. Set settlementTimeoutMs if your code relied on the longer 99-second wait, and handle the { transactionId, status, statusUrl } handle that a write returns when it has not settled within that wait. Calling routes covers the 3.2 client.

The CLI

What changed. The 3.1 CLI was a hand-written tree of noun-first commands, such as dalp contacts list. The 3.2 CLI generates its commands from the v2 API and puts the verb first: dalp get contacts. The verb comes from the HTTP method: get for GET, create for POST, patch for PATCH, replace for PUT and delete for DELETE. Only login, logout, whoami, config and org stay hand-written. dalp --mcp still starts the Model Context Protocol server, but it no longer registers one tool for each command. It offers four tools instead: search_tools, get_tool_details, call_read_tool and call_write_tool. call_read_tool runs only read commands, which are HTTP GETs, and refuses any other with Tool is not read-only: <tool name>. call_write_tool runs only the others and refuses a read command with Tool is read-only: <tool name>.

Who is affected. Shell scripts, CI jobs and AI agents that call dalp.

How to detect it. A 3.1 command name fails as an unknown command. An agent that calls a command through the wrong tool receives Tool is not read-only or Tool is read-only.

What to do. Run dalp --help and rewrite each command in the verb-first form. Point agents at call_read_tool for reads and call_write_tool for writes. CLI commands describes the pattern, and AI agents describes the MCP server.

Changes to every error response

Error bodies drop defined

What changed. A 3.1 error body carried five top-level fields: defined, code, status, message and data. The 3.2 body carries four: code, status, message and data. defined is gone from both v1 and v2 error responses. data always holds id, the DALP- identifier of the failure, together with why, fix, retryable and category, plus any fields specific to that failure.

Who is affected. Clients that branch on defined to tell a declared error from an unexpected one.

How to detect it. defined is missing from every error body.

What to do. Identify a failure by data.id, decide on a retry from data.retryable, and show data.fix to the person who has to act. Errors describes the envelope.

Removed v2 operations

What changed. 187 operations that the DALP 3.1 v2 API published are not in the 3.2 v2 API. 145 of them have a 3.2 successor. Most successors fold several narrow operations into one: role grants and revokes become one role-set call, state changes become one transitions call, smart wallet operations move under /custody/wallets, and statistics move behind one operation with a metric parameter. The other 42 have no v2 successor. A v1 operation still serves eight of those, and the other 34 have no replacement. The full list is in Removed v2 operations and their successors.

Who is affected. Any client that calls one of the removed operations.

How to detect it. A generated client no longer has the operation and fails to compile. A hand-written client receives HTTP 404 with code ROUTE_NOT_FOUND and data.id DALP-18235.

What to do. Move each call to the successor the table names, and read the successor's request schema in the API reference, because a successor often takes the old operation's intent as a field, such as to on a transitions call. Where the table names no successor, drop the call or move it to the v1 operation the table names.

Required fields added to v2 requests

What changed. These v2 operations refuse a request that leaves out a field 3.1 did not ask for:

Operation
Field now required
PUT /api/v2/tokens/{assetId}/address-freezes
reason
POST /api/v2/tokens/{assetId}/partial-freezes
reason, for holdings on an EVM network
POST /api/v2/tokens/{assetId}/partial-unfreezes
reason
POST /api/v2/invitations
custodyAccountId
POST /api/v2/invitation-acceptances
custodyAccountId
POST /api/v2/organizations
custodyAccountId. The 3.1 fields name and custody are refused
POST /api/v2/users
providerConfigurationId, the custody provider configuration for the new user, which GET /api/v2/custody/provider-configurations lists
PATCH /api/v2/users/me
language, which is now the only field the body takes

A freeze or unfreeze reason is an object with a code and an optional text. The code is one of aml-review, sanctions-screening, fraud-investigation, court-order, regulatory-request, client-request or other, and other needs a text of 1 to 500 characters. The platform records the reason in the audit trail beside the freeze. custodyAccountId binds the invitation, the acceptance or the new organisation to one exact custody account. PATCH /api/v2/users/me sets the person's language. The bodies of PATCH /api/v2/users/me and POST /api/v2/organizations accept no field beyond those they declare, so a 3.1 body that still carries onboardingKycVisited, or name and custody, is refused with REQUEST_INVALID.

Who is affected. Clients that freeze or unfreeze holdings, invite people or accept invitations, create organisations or users, or update the signed-in user.

How to detect it. A generated client fails to compile without the field, or with a field the 3.2 body does not take. At run time the platform answers HTTP 400 with code REQUEST_INVALID, and data.errors names the field.

What to do. Send the new field and drop the refused ones. For custodyAccountId, read the participant's custody accounts first, from GET /api/v2/participants/{participantId}/custody-accounts, and pass the one the action should use.

v2 response shapes that changed

What changed. Some v2 responses changed shape:

  • Several list rows became a union that a family field tells apart, among them the rows of GET /api/v2/transaction-requests, GET /api/v2/users/me/events and GET /api/v2/addons/xvp-settlements. A field such as transactionHash or createdAt no longer sits at the top level of the row schema. Every field is still present on a row whose family is evm. A GET /api/v2/tokens row is { ref, token }, and token._tag, which is evm, canton or deploying, tells its variants apart.
  • User wallet projections, executorAddress, signingAddress and, on GET /api/v2/users/me, effectiveWallet, are replaced by collections of the participant's custody accounts, and user asset balances no longer group by one wallet (byWallet).
  • dob leaves the rows of GET /api/v2/users and GET /api/v2/admin/users. GET /api/v2/users still accepts filter[dob]. An organisation that records a date of birth declares it as a profile field, which GET /api/v2/users/{userId}/profile returns.
  • The member list returns a union of the full row and a narrowed row when the request names a view=summary or a fields[user] projection.
  • An asset type template's featureConfig is null or an array with one attachment for each feature, on read and on create and update. It was an object.
  • A queued transaction write that answers 202 Accepted carries the queue handle, transactionId, status and statusUrl, instead of a copy of the 200 body. Other 202 responses have their own body, which each route's reference documents.
  • Some optional response fields are now declared nullable.
  • funding in the POST /api/v2/organizations/networks/bundler response is funded or ask-raised. It is no longer unknown.

Who is affected. Clients with strict response types or strict decoders, and clients that send featureConfig as an object.

How to detect it. A client generated from the 3.2 document fails to compile where it reads a moved field without first narrowing on family, or where it reads a removed field.

What to do. Narrow on family, or on token._tag for a token row, before reading an EVM row's fields. Read custody accounts instead of the removed wallet fields. Send featureConfig in the attachment form the API reference shows. On a 202, poll statusUrl until the transaction settles instead of reading the result from the body.

New refusals on existing operations

These operations keep their shape in 3.2 but refuse requests they used to accept, or answer a refusal with a different code. A typed client does not see them at compile time.

Flat collection keys are refused

What changed. v2 list operations that take the collection parameters page, sort and filter refuse the flat query keys limit, offset, sortBy, sortDirection and filters. 3.1 accepted them and converted them to the canonical form. A list that declares its own flat limit, with offset or cursor, such as GET /api/v2/transaction-requests, accepts the keys it declares. The platform answers HTTP 400 with code REQUEST_INVALID and data.id DALP-14261, and data.errors names the key: Collection query key "limit" is not supported: send sort, page[offset], page[limit], and filter[<field>]. The refusal is not retryable.

Who is affected. Any client that pages, sorts or filters such a list with the flat keys.

How to detect it. A list call answers HTTP 400 where it used to answer 200.

What to do. Send page[limit], page[offset], sort and filter[<field>], as Pagination describes. Correct the request before you send it again, because an unchanged retry fails the same way.

Gas refusals

What changed. When the platform refuses a token or token-feature write because the account that pays for it cannot pay the gas, the operation answers HTTP 400 with code TRANSACTION_QUEUE_FUNDING_REQUIRED and data.id DALP-14062. This covers a direct token or token-feature write, a token role change, a token creation or a token price update that waits for its result, and the coordinated corporate-action steps: preview, confirm, abort and leg release. data.refusal is FundingRequired, and data.transactionId names the transaction request when the platform recorded one. The new refusal is not retryable. A step whose smart wallet pays its own gas and holds too little answers HTTP 422 TRANSACTION_QUEUE_SMART_WALLET_GAS_REQUIRED (DALP-14034), with smartWalletAddress and shortfallWei in data.

Who is affected. Clients that write to tokens or token features, change token roles, create tokens, update prices or run coordinated corporate actions, and that handle a gas shortfall.

How to detect it. The write answers HTTP 400 with data.refusal set to FundingRequired.

What to do. Fund the paying account, then send the write again. If the retry answers TRANSACTION_QUEUE_DUPLICATE_SETTLED (DALP-14066), the idempotency key already names the refused transaction: read that transaction's outcome and send the write under a new Idempotency-Key. Do not retry this code automatically. Error handling covers recovery.

Bundler wallet without a fee quote

What changed. POST /api/v2/organizations/networks/bundler sizes the bundler wallet's funding request from the network's live fee quote. When the network has no fresh fee quote, it answers HTTP 503 with data.id DALP-12250, a retryable dependency failure. When the platform cannot record the funding request, it answers a retryable failure too. 3.1 answered both cases with HTTP 200 and funding set to unknown.

Who is affected. Clients that create or repair a bundler wallet and treat funding: "unknown" as a success.

How to detect it. The call answers HTTP 503 where it used to answer 200.

What to do. Retry the call later. The answer is retryable, and the call reuses the bundler wallet that exists.

Mint and burn during a corporate action

What changed. A mint or a burn on an asset is refused while a corporate action run is in flight on the same asset. A run is in flight from the moment it starts until it completes or someone aborts it. The platform answers HTTP 409 with code CORPORATE_ACTION_IN_FLIGHT and data.id DALP-12067, and data.corporateActionId names the run that holds the asset.

Who is affected. Every client that mints or burns, including one whose own operator did not start the corporate action.

How to detect it. The mint or burn answers HTTP 409 with CORPORATE_ACTION_IN_FLIGHT.

What to do. Wait until the run named in data.corporateActionId completes, or abort it, then send the mint or burn again. Corporate actions describes the run.

Burns and forced transfers on a frozen address

What changed. An asset on the DALP 3.2 token contract refuses a burn from an address frozen as a whole, with DALP-1076 (“Sender address frozen”), and a forced transfer into one, with DALP-1074 (“Recipient address frozen”). A request that names several holders fails as a whole when one of them is frozen. 3.1 burned from a frozen address and delivered a forced transfer into one. A freeze of part of a balance still stops neither: the write releases the frozen units it needs. An asset issued from an instrument template moves to the DALP 3.2 token contract when its organisation runs System updates. An asset of the seven original asset families, such as a bond or an equity from its type-specific factory, keeps the 3.1 behaviour.

Who is affected. Clients that call POST /api/v2/tokens/{assetId}/burns or POST /api/v2/tokens/{assetId}/forced-transfers for a holder whose address may be frozen.

How to detect it. The Platform API does not check the address freeze before it queues the write, so the request still answers 202 Accepted. Its transaction then ends FAILED, with contractError.id DALP-1076 or DALP-1074. A request sent with Prefer: wait answers HTTP 422 CONTRACT_ERROR with that code in data.id.

What to do. Read data.holder.balance.isFrozen from GET /api/v2/tokens/{assetId}/holder-balances?holderAddress=<address> before you burn from or force a transfer into an address. Lift the freeze first with PUT /api/v2/tokens/{assetId}/address-freezes, sending userAddress and freeze: false. To burn a frozen holder's units and keep the freeze, move them with a forced transfer to an eligible wallet, which a freeze on the sender does not stop, and burn them there.

Fee recipient changes

What changed. On the DALP 3.2 management fee feature, a recipient change through PATCH /api/v2/tokens/{assetId}/features/aum-fee first pays the fees accrued so far to the old recipient, as a rate change already did. It fails, and changes nothing, while the asset refuses that payment: the asset is halted (DALP-1109), the old recipient's address is frozen (DALP-1074), or the old recipient no longer passes the asset's compliance rules (DALP-1110). 3.1 switched the recipient without paying, so the new recipient's first collection included the period the old recipient earned. A management fee moves to the 3.2 feature when its organisation runs System updates.

Who is affected. Clients that change a management fee recipient.

How to detect it. The request still answers 202 Accepted, and its transaction ends FAILED with one of those codes in contractError.id, or, with Prefer: wait, HTTP 422 CONTRACT_ERROR with the code in data.id. A successful change records a fee payment to the old recipient.

What to do. Resume the asset, or unfreeze or re-verify the old recipient, before you change the recipient. Reconcile the payment to the old recipient that the change makes.

Pause and resume codes

What changed. The DALP 3.2 token contract refuses a halt of an asset that is already halted with DALP-1109, and a resume of an active asset with DALP-9059. Every other token contract answers the reverse. PATCH /api/v2/tokens/{assetId}/pause-state compares the request with the indexed pause state and refuses a match with DALP-9488. A contract code reaches a client when the chain holds a state the platform has not indexed yet, such as a second halt sent before the first is indexed. It arrives as contractError.id on the failed transaction, or in data.id of an HTTP 422 CONTRACT_ERROR with Prefer: wait.

Who is affected. Clients that branch on DALP-1109 or DALP-9059 after a halt or a resume.

How to detect it. A halt answers DALP-1109 where it used to answer DALP-9059, or a resume the reverse.

What to do. After a halt or a resume, read DALP-9488, DALP-1109 and DALP-9059 as “the asset already has the state you asked for”, whichever action you sent.

v1 writes under maker-checker

What changed. When an organisation protects a group of changes with maker-checker, a v2 write in that group is held for a second user's approval. The v1 API cannot hold a request, so a v1 write whose v2 route the protected group would hold is refused before anything runs. The refusal is HTTP 403 with the v1 code FORBIDDEN, and data.id is the id that route already answers for a refused caller: DALP-9485 on token routes, DALP-12252 on platform routes and DALP-0003 on identity, compliance and user routes. Four v1 writes had no 403 in 3.1 and gain one in 3.2, answering DALP-12252: POST /api/v1/external-token/register, POST /api/v1/kyc-profile-versions/{versionId}/request-update, POST /api/v1/user/create and POST /api/v1/user/admin/session/revoke. The refusal is not retryable.

Who is affected. v1 clients in an organisation that protects a group, such as Token administration or System administration.

How to detect it. A v1 write answers HTTP 403 whose message says the organisation holds the change for a second user's approval.

What to do. Send the change to the v2 route that data.fix names. It answers 202 and holds the change until a second user approves it.

Prices with too many decimals

What changed. A price write that holds more decimal places than the price feed stores is refused. This covers a token price update, a price feed submission, and the initial price of a token creation. The platform answers HTTP 400 with code FEED_PRICE_NOT_REPRESENTABLE and data.id DALP-12245, and the message names the price and the feed's decimals. 3.1 accepted the value and cut the extra digits off, rounding toward zero.

Who is affected. Clients that send prices with more precision than the feed stores.

How to detect it. The price write answers HTTP 400 with FEED_PRICE_NOT_REPRESENTABLE.

What to do. Round or format the price to the feed's decimals before you send it. The refusal is not retryable unchanged.

Shipped asset class slugs

What changed. Creating a custom asset class, or renaming one, onto the slug of an asset class that DALP ships is refused. The shipped slugs are fixed-income, equity, funds, cash, real-assets, structured, derivatives, yield-instruments and digital-assets. The platform answers HTTP 409 with code SETTINGS_TEMPLATE_NAME_ALREADY_EXISTS, data.id DALP-0463 and the message The slug "<slug>" belongs to a system asset class. Choose a different name.

The upgrade also resolves an organisation's own class that already uses the slug of a shipped class. Each shipped class allows these base types:

Shipped class
Allowed base types
fixed-income
bond
equity
equity
funds
fund
cash
stablecoin, deposit
real-assets
real-estate, precious-metal
structured
bond, fund
derivatives, new in 3.2
equity
yield-instruments, new in 3.2
equity
digital-assets
every base type

If every template in the organisation's own class has a base type the shipped class allows, the shipped class adopts its templates, and the organisation's own class row is deleted, so its identifier no longer exists. Otherwise the class stays the organisation's own and moves to the slug <old slug>-<first eight letters and digits of its identifier, in lower case>, with its name, identifier and templates unchanged. For derivatives and yield-instruments, a template whose own class row was already deleted follows the same split: an equity template joins the shipped class, and any other template moves to <old slug>-custom.

Who is affected. Clients that create or rename asset classes, and clients that look up or store an organisation's own class under the slug of a shipped class.

How to detect it. A create or rename answers HTTP 409 with DALP-0463. After the upgrade, a lookup by the old slug returns the shipped class, or a class identifier stored before the upgrade no longer resolves.

What to do. Choose another slug. Key asset classes by identifier, and read the class list again after the upgrade.

Original asset families

What changed. The seven asset families of the original catalogue, bond, equity, fund, stablecoin, deposit, real-estate and precious-metal, cannot be created or installed unless the deployment turns legacy asset creation on, and it is off by default. The 3.1.19 patch release introduced this refusal, and 3.2 answers it with different codes. A v2 token creation of one of these types answers HTTP 422 with code TOKEN_CREATE_ASSET_FAMILY_DEPRECATED (DALP-12075), where 3.1 answered HTTP 400 with DALP-9103. A v2 factory installation for one of them answers HTTP 422 with code PLATFORM_FACTORY_TYPE_UNUSABLE (DALP-14339), where 3.1 answered with DALP-9104. The v1 token creation answers HTTP 422 with the v1 code INPUT_VALIDATION_FAILED and data.id DALP-9243. None is retryable. The refusal applies to new creations and factory installations, and the platform removes no factory that a system already has. 3.0 had no such refusal.

Who is affected. Clients that still create tokens of these seven types, and clients that branch on the 3.1 status or identifiers.

How to detect it. Read data.features.legacyAssetCreation from GET /api/v2/settings/public-config. When it is false, a create or install of those seven families is refused.

What to do. Create the asset from a current instrument template, or ask the platform operator to turn legacy asset creation on, as Upgrade a deployment to DALP 3.2 describes. Match the 3.2 data.id values above. A client that branches on code replaces the 3.1 codes TOKEN_CREATE_TYPE_DEPRECATED and TOKEN_FACTORY_TYPE_DEPRECATED.

Template publication

What changed. An organisation issues only from instrument templates and structures that are published. The templates an organisation creates itself count as published. Of the templates that DALP ships, Equity, Bond and Stablecoin start published in every organisation, and every other one starts unpublished. A v2 token creation from an unpublished template answers HTTP 409 with code TOKEN_CREATE_TEMPLATE_UNPUBLISHED (DALP-14019). An issuance run answers HTTP 409 with TOKEN_ISSUANCE_RUN_TEMPLATE_UNPUBLISHED (DALP-14020) for an unpublished member template, or TOKEN_ISSUANCE_RUN_STRUCTURE_UNPUBLISHED (DALP-14024) for an unpublished structure. The v1 token creation answers HTTP 422 with the v1 code INPUT_VALIDATION_FAILED and data.id DALP-0316. None is retryable unchanged.

Who is affected. Clients that create tokens or start issuance runs from templates other than Equity, Bond and Stablecoin.

How to detect it. The template's isPublished field is false.

What to do. Publish the template with PUT /api/v2/settings/asset-type-templates/{assetTypeTemplateId}/publication, or ask an organisation owner to publish it in the Console, then send the request again.

Asset documents

What changed. A document confirm whose replaceGroupId names a document group of another document type answers HTTP 404 with DALP-0322, where it used to add a version of the new type to that group. A document delete by a wallet without the governance role answers HTTP 403 with DALP-9485 whether or not the document exists, where it used to answer 404 for a document it could not find. Deleting an instrument template that tokens were issued from answers HTTP 409 with DALP-181131, where it used to answer 500.

Who is affected. Clients that replace asset documents or delete them, and clients that delete instrument templates.

How to detect it. A response that carries one of the data.id values this change names.

What to do. Replace a document only within a group of the same type. Treat a 403 on delete as a missing role, not a missing document. A template that tokens were issued from cannot be deleted.

v1 KYC document uploads and downloads

What changed. The v1 KYC document confirm validates the uploaded file (declared size, a 25 MiB bound and the file's real type) and stores it encrypted, as the v2 upload does. 3.1 stored it unencrypted and unvalidated. A confirm that fails validation answers a refusal and deletes the uploaded object, so request a new upload link and send the file again. The v1 and v2 document download calls always return a relative /api/v2/ path that needs your credential. 3.1 returned an absolute object-storage URL, usable by anyone who held it, for a document that the v1 upload had stored unencrypted. The platform now encrypts those documents in the background. A presigned upload that is never confirmed is deleted 30 minutes after the link was issued.

Who is affected. Clients that upload KYC documents through the v1 presigned upload, and clients that fetch a KYC document from the downloadUrl it returns.

How to detect it. A downloadUrl that starts with /api/v2/ where it used to start with https://, and a confirm that answers a refusal for a file whose type differs from the declared one.

What to do. Send your session or API key when you fetch downloadUrl, and resolve a relative path against your DALP base URL. Declare the file's real size and type on the confirm. Prefer the v2 upload, which sends the file to the platform and never leaves it unencrypted in object storage.

Trading venue reads

What changed. GET /api/v2/users/me/events and GET /api/v2/tokens/{assetId}/events, and the v1 token events, name only the caller's own side of a trade a venue settled. In the Swap row, swap.buyer, swap.seller, and a transfer's from and to can be null, and the decoded buyer, seller, buyOrderHash and sellOrderHash values and involved follow the same rule. A venue operator still reads both sides. An instrument's sessionState, and a calendar occurrence's appliedState, can be trade-at-last, and calendar days, exceptions and venue calendars accept an optional tradeAtLastTime.

Who is affected. Clients that read the counterparty of their own trade from the event reads, and clients that list session states.

How to detect it. A null counterparty on a venue trade, or a session state the client does not know.

What to do. Read the counterparty from the venue's operator reads, with an operator's credentials. Treat an unknown session state as not tradable rather than open.

Webhook registration replays

What changed. POST /api/v2/webhooks treats a retry with the same Idempotency-Key and the same body as a replay. It returns the endpoint the first request registered, with signingSecret set to null, and registers no second endpoint. The operation returns the key it used in the Idempotency-Key response header, including a key the platform generated because the request sent none. A reused key with a different body, a key whose endpoint was deleted, or a key whose registration never completed answers HTTP 409 WEBHOOK_IDEMPOTENCY_KEY_REUSED (DALP-0537), which is not retryable. A retry while the first request is still registering answers HTTP 409 WEBHOOK_CREATE_IN_PROGRESS (DALP-0538), which is retryable.

Every write under /api/v2/webhooks, and POST /api/v2/webhook-receipts, no longer declares a 202 response in the contract: each finishes before it responds and returns 200.

Who is affected. Clients that retry webhook registration, and clients that expect the signing secret on every successful registration response.

How to detect it. A replayed registration returns signingSecret: null.

What to do. Store the signing secret from the first successful response, because a replay does not return it. Keep the Idempotency-Key response header so that a client that lost the response can replay it. On WEBHOOK_CREATE_IN_PROGRESS, send the same request again after a short wait. On WEBHOOK_IDEMPOTENCY_KEY_REUSED, register under a new key. If the secret is lost, rotate it, as Webhooks describes.

Removed v1 operations

What changed. 3.2 removes three v1 operations without the 90-day deprecation notice that Versioning and deprecation describes. The rest of the v1 structure stays, apart from the fields that v1 reads drop wallet and gas fields lists:

3.1 v1 operation
3.2 replacement
GET /api/v1/user/by-wallet/{wallet}
GET /api/v2/users, filtered by wallet. A wallet can match more than one user.
POST /api/v1/user/create-wallet
POST /api/v2/custody/accounts/lifecycle
PATCH /api/v1/user/me
None. The onboarding flag it set no longer exists, and PATCH /api/v2/users/me sets only the language.

Who is affected. v1 clients that call one of the three.

How to detect it. The call answers HTTP 404 with the body {"code":"NOT_FOUND","status":404,"message":"The request could not be completed"} and the content type text/plain. The body has no data object.

What to do. Move the call to the v2 replacement. v1 error bodies change as described in Error bodies drop defined.

v1 reads drop wallet and gas fields

What changed. The v1 user reads no longer return the user's single-wallet fields wallet, executorAddress and signingAddress: GET /api/v1/user/me, GET /api/v1/user/by-id/{userId}, GET /api/v1/user/list, GET /api/v1/user/list/admins, GET /api/v1/user/search, the user results of GET /api/v1/search, and the response of POST /api/v1/user/create. GET /api/v1/user/me also no longer returns effectiveWallet. A user can hold more than one custody account, so one wallet field cannot describe it. GET /api/v1/user/events no longer returns gasPrice or gasUsed, because the platform does not index the gas a transaction used.

Who is affected. v1 clients that read a user's wallet from these responses, or the gas fields of a user event.

How to detect it. A client generated from the 3.2 document fails to compile where it reads the fields. At run time the fields are absent, with no error.

What to do. Read the user's custody accounts from GET /api/v2/participants/{participantId}/custody-accounts, with the participantId that GET /api/v1/user/by-id/{userId} and GET /api/v1/user/list return. Treat gasPrice and gasUsed as absent.

v1 writes under maker-checker

What changed. A v1 client cannot read the 202 answer that holds a request for a second approver. A v1 write whose v2 route an organisation holds under maker-checker, the control in which a second approver must approve a change before it runs, is therefore refused before anything runs. The refusal is the 403 that the v1 route already declares for a caller who lacks access, with a message that names the protection group and a data.fix that names the v2 route to send the change to. A v1 write whose v2 route is never held, such as a mint, runs as before.

Who is affected. v1 clients that change roles, freeze addresses, force transfers or edit settings in an organisation that protects the matching group.

How to detect it. The 403 carries a message that names the protection group.

What to do. Send the change to the v2 route that data.fix names. Maker-checker approval describes the groups and the second approval.

Upgrading from 3.0

A client built against DALP 3.0 needs every change on this page and nothing more. Every v1 and v2 operation that 3.0 published is still in 3.1, so 3.1 removed nothing that a 3.0 client calls. The refusal of the original asset families arrived in the 3.1.19 patch release, so a 3.0 client meets it for the first time on this upgrade.

Removed v2 operations and their successors

Each row is an operation that the DALP 3.1 v2 API published and the 3.2 v2 API does not. Paths are relative to /api/v2. Path parameters keep their position when a successor renames them, such as {tokenAddress} to {assetId}.

3.1 operation
3.2 operation
Note

Next steps