An integration guide for DTCC Digital Assets APIs organized around four core implementation journeys: account management, wallet management, conversion order processing, and corporate action handling. Each section presents the workflow sequence, key endpoints, request and response patterns, business rules, and operational considerations required to implement and operate integrations with the platform.
This reference organizes DTCC Digital Assets APIs by implementation journey rather than by internal service boundary. The guide covers four core journeys: account management, wallet management, conversion order processing, and corporate action handling. Each tab presents a compact workflow summary, a searchable endpoint reference, representative payloads, business rules, status handling, and retry guidance.
| Environment | Identity (Authentication) | Services |
|---|---|---|
| PSE (Pre-Production) | https://api.pse.lds.dtcc-da.com/identity | https://api.pse.lds.dtcc-da.com/services |
| Production | https://api.ledgerscan.dtcc-da.com/identity/ | https://api.ledgerscan.dtcc-da.com/services/ |
/connect/token with grant_type=urn:dtcc:params:oauth:grant-type:service-account-credentials, type=conveyance, entity context, tenant context via acr_values=tenant:{tenantId}, and service-account credentials. Downstream API calls use Authorization: Bearer {access_token}.Each tab starts with a compact flow showing call ordering, parallel-load opportunities, branch behavior, and service dependencies before endpoint-level detail.
Workflows begin with a service account token request using the conveyance model and tenant/entity context. The resulting access token is passed as a bearer token to each API.
Each journey includes business rules, retry guidance, permission considerations, idempotency, async state tracking, and graceful degradation for reference-data failures.
| Journey | Primary APIs | Purpose |
|---|---|---|
| Account Management | /connect/token, /v1/accounts/{accountId}, /v1/accounts/{accountId}/summary, /v1/accounts/{accountId}/activities, /v1/conversion/accounts/{accountId}/balances | Retrieve account detail, hierarchy, access control information, on-chain activity, and security-level balances. |
| Wallet Management | /connect/token, /v1/conversion/wallets, /v1/conversion/clients | Load wallet inventory and supporting account reference data for administration and status tracking. |
| Conversion Orders | /connect/token, /v1/conversion/securities, /v1/conversion/securities/issue-types, /v1/conversion/securities/issue-subtypes, /v1/conversion/securities, /v1/conversion/wallets, /v1/conversion/orders, /v1/conversion/async-operations/query, /v1/conversion/orders/{orderId} | Discover eligible securities, retrieve order inputs, submit conversion orders, resolve the order identifier from the async operation, and retrieve order detail. |
| Corporate Actions | /connect/token, /v2/corporate-actions, /v2/corporate-actions/event-types, /v1/conversion/securities, /v2/corporate-actions/{id} | Discover, filter, and inspect corporate action events and lifecycle context. |
This framework defines the Workflow Certification Program for Tokenization Service integrations. The intent of these certifications is not to validate individual API endpoints. Endpoint behavior, request formats, and response structures are already covered by API specifications and implementation guides. Instead, this framework validates that a customer or integration partner can correctly execute the complete business workflows required for production operation.
Complete the ordered sequence of steps that compose each production workflow.
Apply the platform business rules that govern eligibility, direction, and validation.
Handle invalid credentials, expired credentials, access denials, and service failures.
Demonstrate production supportability across the certified workflows.
Sustain and monitor operations after go-live without introducing risk.
Produce request logs, response payloads, and execution output as audit evidence.
Each workflow document follows the same seven-step structure. The objective is to verify that customers can correctly implement the platform's intended workflow patterns rather than simply execute API calls successfully.
Establish a valid session.
Load inputs for the workflow.
Enforce platform constraints.
Perform the operation.
Confirm expected results.
Manage error conditions.
Capture supporting audit artifacts.
The following workflow certifications are currently included within the Tokenization Service certification framework.
All workflow certifications require demonstration of the following.
| Requirement | Detail |
|---|---|
| Authentication | Valid service account authentication. |
| Error Handling | Invalid credentials, expired credentials, access denied, invalid requests, not found conditions, and service failures. |
| Workflow Completion | All required steps executed successfully. |
| Auditability | Request logs, response payloads, execution output, workflow results, and negative-test results. |
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request, invalid parameter, or invalid credentials | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. |
Conversion order submission adds two codes: 409 for a duplicate X-Idempotency-Key, and 422 for a business rule violation such as invalid conversion direction, quantity of zero, or submission outside operational hours.
These workflow certifications serve as the formal production-readiness gate for Tokenization Service integrations.
Participant onboarding establishes the DTCC entity, account hierarchy, wallet structure, administrative access, and service-account readiness required before a participant can begin using Tokenization Services.
Capture participant name, DTC account information, technical contact, entity administrator, and go-live inputs.
The Integration team uses the Operations Portal to initiate participant onboarding, validate onboarding inputs, and launch the provisioning workflow.
Create the participant entity, DTCC standard account structure, provision wallets, assign controller permissions, create the participant administrator, send onboarding notifications, and activate the participant.
Account Management provides service-account based workflows for retrieving account detail, viewing sub-accounts and linked wallets, interpreting access control metadata, and querying on-chain account activities. Accounts give participants the ability to organize their wallets, and each account is made up of a group of subaccounts or wallets. Two accounts are configured by default: Internal, for wallets a participant uses for its own proprietary purposes, and Clients, for wallets registered on behalf of a participant's clients. Balance, on-chain activity, and the list of wallets mapped to an account are available to view for each account. Account Entity and Activity data are served by the DTCC APIs.
/connect/token with grant_type=urn:dtcc:params:oauth:grant-type:service-account-credentials, type=conveyance, entity context, tenant context via acr_values=tenant:{tenantId}, and service-account credentials. Downstream API calls use Authorization: Bearer {access_token}.A condensed view of the API call sequence, dependencies, and branch rules.
POST /connect/token
Obtain a service-account bearer token.
GET /v1/accounts/{accountId}
Retrieve account and optional access control metadata.
GET /v1/accounts/{accountId}/summary
Retrieve child accounts and linked wallets.
GET /v1/accounts/{accountId}/activities
Retrieve filtered on-chain activity from the DTCC APIs.
Show partial account data if downstream hierarchy or activity services are unavailable.
Retrieves account data and optional page-level access control permissions. This call confirms that the account exists and that the caller has access.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| accountId | path | string | Yes | The account identifier |
| includeObac | query | boolean | Optional | When true, includes access-control information |
const accountResponse = await fetch(
`${baseUrl}/v1/accounts/${accountId}?includeObac=true`,
{ headers: { 'Authorization': `Bearer ${access_token}` } }
);
if (!accountResponse.ok) throw new Error(`Account fetch failed: ${accountResponse.status}`);
const account = await accountResponse.json();{
"accountId": "account-id",
"name": "Example Account",
"externalId": null,
"itemId": null,
"accessControl": { "isController": true, "isShared": false, "hasShareRequest": false }
}interface AccountWithAccessControlResponse {
accountId: string;
name: string;
externalId: string | null;
itemId: string | null;
accessControl: AccessControlInfo | null;
}
interface AccessControlInfo {
isController: boolean;
isShared: boolean;
hasShareRequest: boolean;
}Handle 401 by requesting a new JWT access credential and retrying once; handle 403 as account lockout or access denied; handle 404 as account not found; validate bad requests client-side before submission.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request or invalid query parameter | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. |
Read-only GET; safe to retry on 5xx or network timeout. Do not retry credential requests on 4xx.
Returns child accounts and linked wallets below a parent account for display and access-control interpretation.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| accountId | path | string | Yes | The parent account identifier |
| includeObac | query | boolean | Optional | When true, the response includes access control metadata per item |
| count | query | integer | Optional | Pagination page size |
| offset | query | integer | Optional | Pagination offset |
| sortOrder | query | string | Optional | Sort order: Ascending or Descending |
| orderBy | query | string | Optional | Account order field: Name |
| type | query | string | Optional | Filter: Account or Wallet |
| searchFor | query | string | Optional | Search for filter |
GET /v1/accounts/{accountId}/summary
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
| Input (query) | orderBy | AccountHierarchyFields | Name |
| Input (query) | type | AccountHierarchyEntityType | Wallet, Account |
| Output (response) | entityType | AccountHierarchyEntityType | Wallet, Account |
| Output (response) | trackingStatus | TrackingStatus | None, Enabled, Disabled, Enabling, Disabling |
| Output (response) | linkageStatus | LinkageStatus | Linking, Unlinking, Linked |
const summaryResponse = await fetch(
`${baseUrl}/v1/accounts/${accountId}/summary?includeObac=true`,
{ headers: { 'Authorization': `Bearer ${access_token}` } }
);
const summaryItems = await summaryResponse.json();
const accountIds = summaryItems.filter(i => i.type === 'Account').map(i => i.id);
const walletIds = summaryItems.filter(i => i.type === 'Wallet').map(i => i.id);[
{ "id": "account-1", "name": "Sub Account", "type": "Account", "lockedOperations": ["Updating"] },
{ "id": "wallet-1", "name": "Wallet A", "type": "Wallet", "walletDetails": { "address": "0x..." } }
]interface AccountSummary {
id: string;
name: string;
type: 'Account' | 'Wallet';
externalId?: string;
lockedOperations?: string[];
walletDetails?: { address: string };
accessControl?: { isShared: boolean };
}If the summary fetch fails, account data may still be shown while hierarchy detail is unavailable; present a partial state and a retry option.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request or invalid query parameter | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. |
Read-only GET; safe to retry on 5xx or network timeout.
Retrieves paginated on-chain activities with date range, activity type, network, source wallet, destination wallet, pagination, and sorting filters.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| accountId | path | string | Yes | Parent or sub-account identifier |
| startDate | query | ISO 8601 string | Optional | Filter activities from this date; defaults to the current week if omitted |
| endDate | query | ISO 8601 string | Optional | Filter activities until this date; clamp future values to now |
| activityType | query | string | Optional | Filter by activity type |
| network | query | string | Optional | Ledger network filter |
| fromWalletId | query | string | Optional | Source wallet filter |
| toWalletId | query | string | Optional | Destination wallet filter |
| limit / offset | query | integer | Optional | Pagination controls |
| sortBy / sortOrder | query | string | Optional | Sorting controls |
GET /v1/accounts/{accountId}/activities
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
| Input (query) | activityType | ActivityType | Payment, PaymentReceived, Fee, Mint, Burn, Pause, Unpause, Freeze, Unfreeze, Revoked, Clawedback, Clawback |
const params = new URLSearchParams();
params.set('startDate', startDate.toISOString());
params.set('endDate', endDate.toISOString());
if (activityType) params.set('activityType', activityType);
if (network) params.set('network', network);
if (fromWalletId) params.set('fromWalletId', fromWalletId);
if (toWalletId) params.set('toWalletId', toWalletId);
const activitiesResponse = await fetch(
`${baseUrl}/v1/accounts/${accountId}/activities?${params.toString()}`,
{ headers: { 'Authorization': `Bearer ${access_token}` } }
);
const activities = await activitiesResponse.json();{
"items": [{
"ledger": "Ethereum",
"wallet": { "id": "wallet-1", "name": "Source Wallet", "walletId": "0x..." },
"primaryCounterWallet": { "id": "wallet-2", "name": "Destination Wallet", "walletId": "0x..." },
"createdAt": "2026-01-01T00:00:00Z",
"totalValue": 500,
"pricedInItem": "TOKEN",
"status": "Completed",
"operations": [{ "amount": 500, "token": { "symbol": "TOK" } }]
}],
"metadata": { "asOfDate": "2026-01-01T00:00:00Z" }
}interface AccountActivitiesWithMetaDataApiModel {
items: AccountActivitiesApiModel[];
metadata: { asOfDate: string };
}These are the definitions for the activityType input filter and output values.
| Value | Description |
|---|---|
Payment | A token or native asset transfer sent from a tracked wallet to another address. |
PaymentReceived | A token or native asset transfer received by a tracked wallet from another address. |
Fee | A gas/transaction fee deducted from a tracked wallet, with the fee amount debited from the sender and credited to the miner/burn address. |
Mint | A token issuance where new tokens are created and credited to a wallet, with no originating holder. |
Burn | A token destruction where tokens are permanently removed from circulation, with no receiving holder. |
Pause | A token contract administrative event indicating the token was paused (transfers disabled) by a contract administrator. |
Unpause | A token contract administrative event indicating the token was unpaused (transfers re-enabled) by a contract administrator. |
Freeze | An administrative event indicating a specific wallet address was frozen on a token contract, preventing it from transacting. |
Unfreeze | An administrative event indicating a specific wallet address was unfrozen on a token contract, restoring its ability to transact. |
Revoked | Applied to the source wallet in a clawback operation — tokens were forcibly removed from this wallet by a contract administrator. |
Clawedback | Applied to the destination wallet in a clawback operation — tokens were forcibly transferred into this wallet as the recipient of a clawback. |
Clawback | Applied to the token perspective in a clawback operation — represents the overall forced transfer event at the token contract level, combining both the revocation and receipt. |
The response includes metadata with asOfDate, indicating the point-in-time represented by the activity data.
Handle invalid filters as 400; expired credentials as 401; access restrictions as 403; service failures as error states with retry. Activity retrieval fails independently of account retrieval, so account detail remains displayable when activity retrieval fails.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request or invalid query parameter | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. Continue to display account detail without activities. |
Read-only GET; safe to retry on 5xx or network timeout. If the activity service is unavailable, show account details without activities rather than failing the whole view.
Retrieves balance records for an account.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| accountId | path | string | Yes | Identifier of the account whose balances are being queried; the same identifier used by /v1/accounts/{accountId}, and it must be accessible under the calling service account's permissions. |
GET /v1/conversion/accounts/{accountId}/balances
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Output (response) | status | FinancialSecurityStatus | Onboarding, Active, Pause, Failed |
| Output (response) | type | WalletTypes | Internal, Client |
const balancesResponse = await fetch(
`${baseUrl}/v1/conversion/accounts/${accountId}/balances`,
{ headers: { 'Authorization': `Bearer ${access_token}` } }
);
if (!balancesResponse.ok) throw new Error(`Balance retrieval failed: ${balancesResponse.status}`);
const balances = await balancesResponse.json();{
"items": [
{
"cusip": "037833100",
"security": "Apple Inc. Common Stock",
"issueSubType": {
"id": "1",
"code": "CS",
"description": "Common Stock"
},
"totalQuantity": 131,
"walletsCount": 2,
"ledgersCount": 2,
"holders": [
{
"quantity": 101,
"wallet": {
"id": "06572a05-8033-4088-8b0a-01faa6ed74df",
"name": "First Bank EVM Wallet",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
},
"ledger": {
"name": "Ethereum",
"network": "Ethereum_Mainnet"
}
},
{
"quantity": 30,
"wallet": {
"id": "1b6e961b-6c6a-4726-8f4c-f2330a5782fa",
"name": "American Bank Besu Wallet",
"walletAddress": "0x4bDb16A35fc5fdc4A8701Bc5B688D254E99027c0"
},
"ledger": {
"name": "Besu private",
"network": "Besu"
}
}
]
}
],
"metadata": {
"asOfDate": "2026-05-18T00:00:00Z"
}
}interface AccountBalancesResponse {
items: AccountSecurityBalancesResponse[];
metadata: AccountBalancesMetadataResponse;
}
interface AccountSecurityBalancesResponse {
cusip: string;
security: string;
issueSubType: { id: string; code: string; description: string };
totalQuantity: number;
walletsCount: number;
ledgersCount: number;
holders: SecurityBalanceHolder[];
}
interface SecurityBalanceHolder {
quantity: number;
wallet: { id: string; name: string; walletAddress: string };
ledger: { name: string; network: string };
}
interface AccountBalancesMetadataResponse {
asOfDate: string;
}The response includes metadata with asOfDate, indicating the point-in-time represented by the balance data.
Handle 401 by requesting a new JWT access credential and retrying once; handle 403 as either an insufficient permission scope or an account the service account is not authorized for; handle 404 as an unknown or inaccessible account identifier and correct the value rather than retrying.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request or invalid query parameter | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. |
Read-only GET; safe to retry on 5xx or network timeout using exponential backoff with a bounded retry limit. Because this call is expected to run frequently as part of pre-conversion checks and scheduled reconciliation, refresh the JWT access credential proactively ahead of its expiry rather than re-authenticating reactively after a 401.
This endpoint uses the Service Account Credentials grant, consistent with the rest of this guide. Request the credential from /connect/token with grant_type=urn:dtcc:params:oauth:grant-type:service-account-credentials, type=conveyance, entity context, tenant context via acr_values=tenant:{tenantId}, and service-account credentials before calling this endpoint.
Demonstrate that a customer integration can authenticate using a service account, retrieve an account, retrieve account hierarchy information, retrieve account activities, retrieve and reconcile account balances, and correctly handle expected errors and service failures. The purpose of this test is to certify that the integration correctly implements the Account Management workflow before production access is granted.
Five sequential stages. Stages two through five depend on the JWT access credential issued in stage one using the Service Account Credentials grant.
Obtain a JWT access credential using SA-AccountBot.
Confirm the account identifier matches the request.
Retrieve child accounts and linked wallets.
Retrieve paginated activities with asOfDate.
Retrieve, reconcile, and interpret security-level balances.
Provisioned service account, service account password, tenant ID, and a valid account ID. Service accounts and permissions are provisioned by DTCC. The service account must be granted the permission required to access the balances endpoint used in step five.
const tenantId = "<tenant-id>";
const accountId = "<account-id>";
const baseUrl = "<base-url>";
const username = "SA-AccountBot";
const password = process.env.SERVICE_ACCOUNT_PASSWORD!;This step validates that the integration can obtain a JWT access credential from the Identity Server using service account credentials. The JWT access credential proves the caller's identity and must be supplied as a bearer credential on every subsequent API call in this workflow.
async function authenticate() {
const response = await fetch(`${baseUrl}/connect/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "urn:dtcc:params:oauth:grant-type:service-account-credentials",
type: "conveyance",
entity: ENTITY_ID_OF_WHICH_SA_IS_MEMBER_OF,
acr_values: `tenant:${tenantId}`,
username,
password
})
});
if (!response.ok) {
throw new Error(`Authentication failed: ${response.status}`);
}
const data = await response.json();
return data.access_token;
}HTTP 200; JWT access credential returned; credential accepted by downstream APIs.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid credentials; invalid_client | Surface authentication rejected. Do not retry with the same credentials. |
| 500, 503 | Identity Server failure | Retry with exponential backoff, maximum three attempts. |
Verify the integration can retrieve account information.
async function getAccount(accessToken: string, accountId: string) {
const response = await fetch(
`${baseUrl}/v1/accounts/${accountId}?includeObac=true`,
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
if (!response.ok) {
throw new Error(`Account retrieval failed: ${response.status}`);
}
return response.json();
}{
accountId: string,
name: string
}HTTP 200; account returned; account identifier matches request; response successfully parsed. The workflow requires account resolution before subsequent operations.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 401 | Expired or invalid JWT access credential | Re-authenticate, then retry once. |
| 403 | Insufficient permissions | Surface access denied. Do not retry. |
| 404 | Account not found | Surface account not found. Do not retry. |
Verify the integration can retrieve child accounts and linked wallets.
async function getHierarchy(accessToken: string, accountId: string) {
const response = await fetch(
`${baseUrl}/v1/accounts/${accountId}/summary?includeObac=true`,
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
if (!response.ok) {
throw new Error(`Summary retrieval failed: ${response.status}`);
}
return response.json();
}[
{
id: string,
name: string,
type: "Account" | "Wallet"
}
]The summary endpoint returns a combined hierarchy view of child accounts and linked wallets.
HTTP 200; hierarchy returned; child entities visible; wallets visible; no parsing errors.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 401 | Expired or invalid JWT access credential | Re-authenticate, then retry once. |
| 403 | Insufficient permissions | Surface access denied. Do not retry. |
| 500, 503 | Service failure | Display account detail without hierarchy and offer retry. |
Verify the integration can retrieve activities.
async function getActivities(accessToken: string, accountId: string) {
const response = await fetch(
`${baseUrl}/v1/accounts/${accountId}/activities`,
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
if (!response.ok) {
throw new Error(`Activity retrieval failed: ${response.status}`);
}
return response.json();
}{
items: [],
metadata: {
asOfDate: string
}
}The activities workflow retrieves paginated activity data and includes metadata such as asOfDate.
HTTP 200; activities returned; metadata returned; data successfully parsed.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid filter or query parameter | Correct the filter values. Do not retry as-is. |
| 401 | Expired or invalid JWT access credential | Re-authenticate, then retry once. |
| 500, 503 | Activity service failure | Account data remains available; fail activity retrieval gracefully with a clear error message. |
Verify the integration can retrieve security-level account balances, interpret the balance payload, reconcile aggregate totals against holder allocations, and process the response metadata.
This endpoint uses the same Service Account Credentials grant as the rest of the workflow, so it reuses the JWT access credential issued in Step 1 rather than obtaining a separate credential.
typescriptasync function getBalances(accessToken: string, accountId: string) {
const response = await fetch(
`${baseUrl}/v1/conversion/accounts/${accountId}/balances`,
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
if (!response.ok) throw new Error(`Balance request failed: ${response.status}`);
return response.json();
}
const balances = await getBalances(accessToken, accountId);Confirm the balance response returns an items collection and a metadata object.
{
cusip: string,
security: string,
issueSubType: { id: string, code: string, description: string },
totalQuantity: number,
walletsCount: number,
ledgersCount: number,
holders: []
}Every balance record must carry a CUSIP, security name, issue sub-type, total quantity, wallet count, ledger count, and holder records. Total quantity, wallet count, and ledger count must each be zero or greater, and holder records must be present wherever balances exist.
typescriptfor (const item of balances.items) {
const holderTotal = item.holders.reduce(
(sum, holder) => sum + holder.quantity,
0
);
if (holderTotal !== item.totalQuantity) {
throw new Error("Balance reconciliation failed");
}
}Aggregate balances must reconcile with holder-level allocations, so the sum of holder quantities equals totalQuantity. Using the sample response, holders of 101 and 30 reconcile to a total of 131.
HTTP 200; balance data retrieved; payload interpreted; aggregate totals reconcile with holder allocations; balance snapshot date available.
This endpoint returns distinct error envelope shapes depending on which layer rejects the request.
| HTTP | Condition | Response body | Expected integration behavior |
|---|---|---|---|
| 400 | Malformed authentication request | errorCode, message, requestId, tracingUrl | Correct the request encoding and verify all required fields are present. Do not retry as-is. |
| 401 | Invalid service account credentials | errorCode, message, requestId, tracingUrl | Authentication rejected. Verify credentials before retrying. |
| 401 | Expired or missing access token; GATEWAY__UNAUTHENTICATED | ErrorCode, ErrorMessage | Re-authenticate, then retry once. |
| 403 | Service account lacks the required permission for this account | errorCode, message, requestId, tracingUrl | Confirm the service account was granted the balances permission. Do not retry. |
| 403 | Token does not grant access to this account; GATEWAY__ACCESSDENIED | ErrorCode, ErrorMessage | Surface access denied. Do not retry. |
| 403 | User locked out; AUTHORIZATION_ENGINE__USER_LOCKED_OUT_EXCEPTION | ErrorCode, ErrorMessage | Surface the lockout. Do not retry until the lockout is cleared. |
| 404 | Account identifier does not exist or is not accessible | errorCode, message, requestId, tracingUrl | Validate the account identifier before calling. Do not retry without correcting it. |
| 500, 503 | Service failure | errorCode, errorMessage | Retry with exponential backoff and a bounded retry count. Surface the failure once retries are exhausted and terminate the workflow safely. |
Access tokens are time-limited. Cache the credential, refresh it proactively ahead of expiry rather than reacting to a 401, and never reuse an expired token.
Execute the complete workflow in a single transaction.
async function runWorkflow() {
const token = await authenticate();
const account = await getAccount(token, accountId);
const hierarchy = await getHierarchy(token, accountId);
const activities = await getActivities(token, accountId);
const balances = await getBalances(token, accountId);
return { account, hierarchy, activities, balances };
}Authenticate PASS
Retrieve Account PASS
Retrieve Hierarchy PASS
Query Activity PASS
Query Balances PASS
Workflow Complete PASSEach constituent call returns its expected success code.
| ID | Scenario | HTTP | Expected result |
|---|---|---|---|
| NT-1 | Invalid Credentials | 400 | Authentication rejected with invalid_client. Invalid service account credentials are an expected authentication failure scenario. |
| NT-2 | Expired Credential | 401 | Re-authentication required. |
| NT-3 | Invalid Account | 404 | Account Not Found. |
| NT-4 | Access Denied | 403 | Access Denied for insufficient permissions. |
| NT-5 | Activity Service Failure | 500, 503 | Account data remains available, activity retrieval fails gracefully, and the user receives a clear error message. |
| NT-6 | Invalid Service Account Credentials (Step 5) | 401 | Authentication rejected. Verify credentials before retrying. |
| NT-7 | Insufficient Permission (Step 5) | 403 | Access denied. Confirm the service account was granted the balances permission. |
| NT-8 | Expired Balance Token (Step 5) | 401 | Re-authentication required; GATEWAY__UNAUTHENTICATED. |
| NT-9 | Unauthorized Account Access (Step 5) | 403 | Access denied; GATEWAY__ACCESSDENIED. |
| NT-10 | Locked Out User (Step 5) | 403 | Lockout detected; AUTHORIZATION_ENGINE__USER_LOCKED_OUT_EXCEPTION. |
| NT-11 | Malformed Authentication Request (Step 5) | 400 | Bad request. Correct the request encoding and confirm all required fields are present. |
| NT-12 | Balance Service Failure (Step 5) | 500, 503 | Retry logic executed, failure reported, and the workflow terminated safely. |
A customer is certified only on successful authentication, account retrieval, hierarchy retrieval, activity retrieval, and balance retrieval with payload validation, reconciliation, and metadata processing, plus token lifecycle management and a bounded retry strategy, plus proper error handling for invalid credentials, expired credentials, invalid account, insufficient permission, access denied, user lockout, malformed authentication requests, and service failures, plus evidence collection showing each stage completed successfully.
Wallet Management covers wallet inventory and ledger/account reference data used to support wallet administration and status tracking. This guide version focuses on wallet discovery and reference-data retrieval APIs.
/connect/token with grant_type=urn:dtcc:params:oauth:grant-type:service-account-credentials, type=conveyance, entity context, tenant context via acr_values=tenant:{tenantId}, and service-account credentials. Downstream API calls use Authorization: Bearer {access_token}.A condensed view of the API call sequence, dependencies, and branch rules.
POST /connect/token
Obtain service-account token.
GET /v1/conversion/wallets
Display current wallet inventory.
GET /v1/conversion/clients
Load account reference data.
Use wallet inventory views to monitor wallet state and status changes.
Loads existing wallets for inventory display and status review.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| Count | query | integer | Optional | Page size |
| Offset | query | integer | Optional | Pagination offset |
| search | query | string | Optional | Search by wallet name |
| networks | query | string[] | Optional | Filter by network name |
| type | query | string | Optional | internal or client |
| sortBy / sortOrder | query | string | Optional | Sorting controls |
GET /v1/conversion/wallets
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | orderBy | WalletFields | Name, Network, CreatedAt |
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
| Output (response) | type | WalletTypes | Internal, Client |
| Output (response) | state | ProcessingStates | Idle, Registering, FailedCompliance, Failed |
const walletsResponse = await fetch(`${baseUrl}/v1/conversion/wallets`, {
headers: { 'Authorization': `Bearer ${access_token}` }
});
const wallets = await walletsResponse.json();[
{ "id": "wallet-1", "name": "Example Wallet", "type": "CLIENT", "network": "Ethereum", "walletAddress": "0x...", "state": "Idle" }
]interface WalletSummary {
id: string;
name: string;
type: string;
network: string;
walletAddress: string;
account: AccountDetails;
state: string;
participant?: Participant;
}Handle 401 by requesting a new JWT access credential and retrying once; handle 5xx with retry and a visible error state. Wallet registration is not available through this API, so no create or update failure paths apply.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request or invalid query parameter | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. Terminate the inventory view safely if the failure persists. |
Read-only GET; safe to retry on 5xx or network timeout.
Fetches existing accounts from the DTCC APIs.
GET /v1/conversion/clients
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
const accountsResponse = await fetch(`${baseUrl}/v1/conversion/clients`, {
headers: { 'Authorization': `Bearer ${access_token}` }
});
const accounts = await accountsResponse.json();[
{ "name": "Example Account", "externalId": "external-id", "id": "account-id" }
]interface AccountDetails { name: string; externalId: string; id?: string; entityId?: string; }If client reference data fails, wallet context may be incomplete; present a retry path and keep the wallet inventory visible. The reference-data call fails independently of the wallet inventory call.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request or invalid query parameter | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. Keep wallet inventory available and present a meaningful error rather than terminating the workflow. |
Reference data GETs are read-only and safe to retry. The call must resolve before inventory review begins.
Demonstrate that a customer integration can authenticate with a service account, load wallet inventory, load account and client reference data, review wallet inventory, and track wallet status. This certification verifies that the customer can correctly consume wallet-related APIs and construct an operational view of wallet inventory and status. The workflow is read-only and intended to support wallet administration and operational monitoring.
Authenticate once, load wallet inventory, then retrieve client reference data before reviewing inventory and tracking status.
Obtain a JWT access credential using SA-WalletReader.
GET /v1/conversion/wallets
/v1/conversion/clients
Correlate wallets with client and account data.
Extract and review wallet status values.
Tenant ID, service account, service account password, base URL, and known wallet data. Required access: /connect/token, /v1/conversion/wallets, /v1/conversion/clients.
const tenantId = "<tenant-id>";
const baseUrl = "<base-url>";
const username = "SA-WalletReader";
const password = process.env.SERVICE_ACCOUNT_PASSWORD!;async function authenticate() {
const response = await fetch(`${baseUrl}/connect/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "urn:dtcc:params:oauth:grant-type:service-account-credentials",
type: "conveyance",
entity: ENTITY_ID_OF_WHICH_SA_IS_MEMBER_OF,
acr_values: `tenant:${tenantId}`,
username,
password
})
});
if (!response.ok) throw new Error(`Authentication failed: ${response.status}`);
const data = await response.json();
return data.access_token;
}
async function getWallets(accessToken: string) {
const response = await fetch(`${baseUrl}/v1/conversion/wallets`, {
headers: { Authorization: `Bearer ${accessToken}` }
});
if (!response.ok) throw new Error(`Wallet retrieval failed: ${response.status}`);
return response.json();
}
async function getClients(accessToken: string) {
const response = await fetch(`${baseUrl}/v1/conversion/clients`, {
headers: { Authorization: `Bearer ${accessToken}` }
});
if (!response.ok) throw new Error(`Client retrieval failed: ${response.status}`);
return response.json();
}This step validates that the integration can obtain a JWT access credential from the Identity Server using service account credentials. The JWT access credential proves the caller's identity and must be supplied as a bearer credential on every subsequent API call in this workflow.
const accessToken = await authenticate();HTTP 200; JWT access credential returned; credential accepted by downstream APIs. Evidence: authentication successful, credential received.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid credentials; invalid client | Surface authentication rejected. Do not retry with the same credentials. |
| 500, 503 | Identity Server failure | Retry with exponential backoff, maximum three attempts. |
Verify wallet inventory can be retrieved.
const wallets = await getWallets(accessToken);Wallet data returned; response is non-empty; wallet identifiers present.
HTTP 200; wallet inventory successfully loaded. Evidence: wallet inventory response, wallet count.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid query parameters such as an invalid sort field or malformed filter | Correct the request. Do not retry as-is. |
| 401 | Expired or invalid JWT access credential | Re-authenticate, then retry once. |
| 403 | Missing permission | Surface access denied. Do not retry. |
| 500, 503 | Wallet endpoint failure | Inventory unavailable; surface the failure and terminate the workflow safely. |
Verify client reference data can be loaded.
const clients = await getClients(accessToken);Available client records returned.
HTTP 200; reference data available and usable. Evidence: client response.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 401 | Expired or invalid JWT access credential | Re-authenticate, then retry once. |
| 403 | Missing permission | Surface access denied. Do not retry. |
| 500, 503 | Reference data failure | Detect the failure, present a meaningful error, and keep the workflow running with wallet inventory still available. |
Verify wallet inventory can be correlated with reference data.
const inventoryView = wallets.map(wallet => ({
wallet,
client: clients.find(c => c.id === wallet.clientId)
}));Wallets visible; wallets associated with client and account data; inventory records complete.
Inventory review completed. Evidence: sample wallet records, reference-data mapping.
This step performs no additional API calls. If reference data was unavailable in Step 3, the integration must present the inventory without client correlation rather than failing the review.
Verify wallet status data is visible and reviewable.
const walletStatuses = wallets.map(wallet => ({
walletId: wallet.id,
status: wallet.status
}));Status field available; status values populated; statuses can be reported.
Wallet state visible; wallet status review completed. Evidence: status report, sample wallet statuses.
This step performs no additional API calls. If the status field is absent from the wallet inventory response, treat the step as failed rather than defaulting to an assumed status.
Execute the complete wallet management workflow in a single run to confirm every stage succeeds in sequence.
async function executeWalletWorkflow() {
const token = await authenticate();
const wallets = await getWallets(token);
const clients = await getClients(token);
return { wallets, clients };
}Authenticate PASS
Load Wallets PASS
Load Clients PASS
Review Inventory PASS
Track Status PASS
Workflow Complete PASSEach constituent call returns its expected success code.
| ID | Scenario | HTTP | Expected result |
|---|---|---|---|
| NT-1 | Invalid Credentials | 400 | Authentication rejected; invalid client. |
| NT-2 | Expired Credential | 401 | Re-authentication required. |
| NT-3 | Missing Permission | 403 | Access denied. |
| NT-4 | Invalid Query Parameters | 400 | Validation error, for example an invalid sort field or malformed filter. |
| NT-5 | Reference Data Failure | 500, 503 | Failure detected; meaningful error presented; workflow does not crash. |
| NT-6 | Wallet Endpoint Failure | 500, 503 | Inventory unavailable; failure surfaced; workflow terminated safely. |
A customer is certified only on successful authentication, wallet inventory retrieval, client retrieval, inventory review, and wallet-status review, plus proper handling of all negative tests.
Conversion Operations covers discovery of registered securities, filter reference data, active securities for order entry, eligible idle wallets, and conversion order submission. Conversions are the mechanism for clients to convert securities between electronic book entry and digital tokenized forms. When submitting a conversion order, the participant selects the source wallet and the destination wallet. The process for converting securities from electronic to digital and back to electronic is identical; the only difference is what is assigned as the source and destination of the securities. Submission is asynchronous and returns no response body, so the integration polls the async operation with its idempotency key to obtain the order identifier before retrieving the order. Submitted orders are processed straight through, moving from Processing to Completed.
/connect/token with grant_type=urn:dtcc:params:oauth:grant-type:service-account-credentials, type=conveyance, entity context, tenant context via acr_values=tenant:{tenantId}, and service-account credentials. Downstream API calls use Authorization: Bearer {access_token}.A condensed view of the API call sequence, dependencies, and branch rules.
POST /connect/token
Obtain service-account token.
GET /v1/conversion/securitiesGET /v1/conversion/securities/issue-typesGET /v1/conversion/securities/issue-subtypes in parallel.
GET /v1/conversion/securities?status=ActiveGET /v1/conversion/wallets in parallel.
Use Active securities and wallets that are Idle and conversion eligible.
POST /v1/conversion/orders
Returns 202 Accepted with no body. Retain the idempotency key.
POST /v1/conversion/async-operations/query
Poll with the idempotency key until resourceId is populated.
GET /v1/conversion/orders/{orderId}
Use resourceId as the order identifier.
Orders are processed straight through from Processing to Completed. Failed is terminal.
resourceId.Fetches a paginated securities list alongside issue type and issue sub-type reference data.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| Offset / Count | query | integer | Optional | Controls which slice of the securities list is returned, where offset sets the starting position and limit sets the number of records per page. |
| search | query | string | Optional | Free-text term matched against issuer name, security name, or CUSIP to narrow the securities list. |
| network | query | string | Optional | Restricts results to securities tokenized on a single ledger network, specified by network name. |
| issueTypeId | query | string | Optional | Restricts results to a high-level security classification such as equity or debt, referenced by an identifier from the issue types endpoint. |
| issueSubTypeId | query | string | Optional | Restricts results to a finer classification nested beneath the high-level type, such as common stock, referenced by an identifier from the issue sub-types endpoint. |
| status | query | string | Optional | Restricts results to securities in a single lifecycle status. Accepts one FinancialSecurityStatus value: Onboarding, Active, Pause, or Failed. |
| sortBy / sortOrder | query | string | Optional | Determines which field the securities list is ordered on and whether that order runs ascending or descending. |
GET /v1/conversion/securities
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | orderBy | FinancialSecurityFields | CUSIP, Description, Issuer |
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
| Input (query) | status | FinancialSecurityStatus | Onboarding, Active, Pause, Failed |
| Output (response) | status | FinancialSecurityStatus | Onboarding, Active, Pause, Failed |
| Output (response) | processingState | ProcessingStates | Idle, Registering, FailedCompliance, Failed |
GET /v1/conversion/securities/issue-types
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
GET /v1/conversion/securities/issue-subtypes
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
const [securitiesResponse, issueTypesResponse, issueSubTypesResponse] = await Promise.all([
fetch(`${baseUrl}/v1/conversion/securities?offset=0&limit=25`, { headers: { 'Authorization': `Bearer ${access_token}` } }),
fetch(`${baseUrl}/v1/conversion/securities/issue-types`, { headers: { 'Authorization': `Bearer ${access_token}` } }),
fetch(`${baseUrl}/v1/conversion/securities/issue-subtypes`, { headers: { 'Authorization': `Bearer ${access_token}` } })
]);
const securities = await securitiesResponse.json();[
{
"id": "security-id",
"cusip": "123456789",
"issuerName": "Example Issuer",
"security": "Example Security",
"securitySymbol": "EX",
"status": "Active",
"tokens": [{ "ledgerId": "ledger-id", "name": "Token", "ledger": { "name": "Ethereum", "network": "Ethereum" } }],
"restrictions": {
"dOChill": { "enabled": false, "updatedAt": null },
"complianceLock": { "enabled": false, "updatedAt": null },
"globalLock": { "enabled": false, "updatedAt": null }
}
}
]interface EligibleSecurityApiModel {
id: string;
cusip: string;
issuerName: string;
security: string;
securitySymbol: string;
issueType: IssueType;
issueSubType: IssueType;
tokens: EligibleSecurityToken[];
status: string;
trackingTrancheId: string;
restrictions: SecurityRestrictions;
}
interface IssueType { id: string; code: string; description: string; }
interface SecurityRestrictions {
dOChill: RestrictionState;
globalLock: RestrictionState;
complianceLock: RestrictionState;
}
interface RestrictionState { enabled: boolean; updatedAt: string | null; }The securities response includes embedded token and ledger network associations, reducing the need for per-security lookup calls. Issue type, issue sub-type, and network are independent filter dimensions.
All eligible securities carry an Active status, but a security can be Active and still have a chill or lock in place. A chill or lock is a transfer restriction: while it is in effect, transfers of the security's tokens are blocked across the entire security (all of its tokens), not just a single account or wallet. When the restriction is lifted, transfers are allowed again. Confirming that status is Active is not enough to know a security is usable right now; you must also read the restrictions object on the same response.
The restrictions object is always present and always reports all three restriction types. Each type is an object with an enabled flag (true when the restriction is currently in effect) and an updatedAt timestamp (UTC) recording when it last changed, or null if it has never applied. Because every type is always present, always test the enabled flag rather than checking whether a field exists.
| Field | Restriction | What it means |
|---|---|---|
dOChill | Delivery Order (DO) Chill | A temporary restriction on transfers, typically while a settlement or verification is pending. |
globalLock | Global Lock | A block on all transfers of the token, typically during a corporate action or a regulatory investigation. |
complianceLock | Compliance Lock | A block on transfers imposed for regulatory or compliance reasons, for example a sanctions concern. |
restrictions object and check each type for "enabled": true. If any of the three is enabled, the security's token transfers are currently restricted even when status is Active. Use the updatedAt value on the enabled restriction to see when that chill or lock was last applied. Restriction state can change at any time as upstream events are processed, so poll the endpoint and re-read restrictions when you need a current view."status": "Active",
"restrictions": {
"dOChill": { "enabled": false, "updatedAt": null },
"complianceLock": { "enabled": false, "updatedAt": null },
"globalLock": { "enabled": true, "updatedAt": "2026-09-25T14:03:00Z" }
}typescript · checking for a restrictionfunction isRestricted(security) {
const r = security.restrictions;
return r.dOChill.enabled || r.globalLock.enabled || r.complianceLock.enabled;
}
// Active status alone is not sufficient; also confirm no restriction is enabled.
const usable = security.status === 'Active' && !isRestricted(security);If securities fail to load, show an error state. If issue type, issue sub-type, or ledger reference data fails, disable the corresponding filter while still rendering the securities list.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request or invalid query parameter | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. Disable only the affected filter rather than the whole view. |
All four endpoints are read-only GETs and safe to retry on 5xx or network timeout.
Loads order-entry inputs in parallel and filters wallets to Idle state.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| status | query | string | Yes | Active |
| Offset / Count | query | integer | Optional | Pagination controls |
| participantId | query | string | Optional | The 8-digit DTC participant number that identifies the member firm whose wallets are being retrieved, for example 00002667. This is the same identifier used across all DTC and DTCC services to uniquely identify a participant. |
GET /v1/conversion/securities
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | orderBy | FinancialSecurityFields | CUSIP, Description, Issuer |
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
| Output (response) | status | FinancialSecurityStatus | Onboarding, Active, Pause, Failed |
| Output (response) | processingState | ProcessingStates | Idle, Registering, FailedCompliance, Failed |
GET /v1/conversion/wallets
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | orderBy | WalletFields | Name, Network, CreatedAt |
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
| Output (response) | type | WalletTypes | Internal, Client |
| Output (response) | state | ProcessingStates | Idle, Registering, FailedCompliance, Failed |
const [securitiesResponse, walletsResponse] = await Promise.all([
fetch(`${baseUrl}/v1/conversion/securities?status=Active`, { headers: { 'Authorization': `Bearer ${access_token}` } }),
fetch(`${baseUrl}/v1/conversion/wallets`, { headers: { 'Authorization': `Bearer ${access_token}` } })
]);
const securities = await securitiesResponse.json();
const wallets = await walletsResponse.json();
const eligibleWallets = wallets.filter(w => w.state === 'Idle');{
"securities": [
{ "id": "security-id", "cusip": "123456789", "issuerName": "Example Issuer", "security": "Example Security", "status": "Active" }
],
"wallets": [
{ "id": "wallet-id", "name": "Example Wallet", "type": "INTERNAL", "network": "DTC_Classic", "walletAddress": "0x...", "state": "Idle" }
]
}interface Security {
id: string;
cusip: string;
issuerName: string;
security: string;
securitySymbol?: string;
issueType: IssueType;
issueSubType: IssueType;
status: string;
trackingTrancheId: string;
}
interface WalletSummary {
id: string;
name: string;
type: string;
network: string;
walletAddress: string;
account: AccountDetails;
state: string;
participant?: Participant;
}Only Active securities are eligible for orders. A wallet is eligible as a source or destination candidate only when its processing state is Idle and conversionEligible is true. Both conditions must hold; an Idle wallet that is not conversion eligible must be excluded.
An Active status is necessary but not sufficient. A security can be Active and still carry a chill or lock, which blocks transfers of its tokens while the restriction is in effect. Before treating an Active security as usable, read its restrictions object and confirm that dOChill, globalLock, and complianceLock all report "enabled": false. See the List Registered Securities and Filter Data card for the full restriction model.
Handle 401 by requesting a new JWT access credential and retrying once; handle 5xx with retry and an error state. If no Active security or no Idle wallet is returned, block order submission rather than submitting an ineligible order.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request or invalid query parameter | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. Block order submission until inputs load successfully. |
Both calls are read-only GETs and safe to retry on 5xx or network timeout.
Prior to submitting a conversion order, we recommend to retrieve and check the wallet and securities from the appropriate endpoints.
Submission is asynchronous. POST /v1/conversion/orders returns 202 Accepted with no response body, so the order identifier is not available on the submission response. Retain the X-Idempotency-Key supplied at submission, poll POST /v1/conversion/async-operations/query with that key until resourceId is populated, then retrieve the order from GET /v1/conversion/orders/{orderId}. Orders are processed straight through, entering Processing and settling at Completed.
GET /v1/conversion/orders
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| searchFor | query | string | Optional | Free-text search key filter |
| fromWallet | query | string | Optional | Source wallet identifier filter |
| toWallet | query | string | Optional | Destination wallet identifier filter |
| updateFrom / updateTo | query | string | Optional | Lower and upper bounds of the update date range |
| ids | query | array | Optional | Filter by specific order identifiers |
| financialSecurityIds | query | array | Optional | Filter by security identifiers |
| status | query | array | Optional | Filter by order status |
| origin | query | array | Optional | Filter by how the order was originated |
| includeObac | query | boolean | Optional | Include access control metadata when true |
| orderBy / sortOrder | query | string | Optional | Sorting controls |
| Offset / Count | query | integer | Optional | Pagination controls |
POST /v1/conversion/orders
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| X-Idempotency-Key | header | string | Yes | Max 255 characters; prevents duplicate submissions. Retain this value for the async operation query. |
| financialSecurityId | body | string | Yes | Security being converted |
| fromWalletId | body | string | Yes | The source account or wallet from which the position will be debited. For Classic accounts, the format is {participantId}-10 (Free NA) or {participantId}-11 (Free MA), for example 00002667-10. These are auto-generated at onboarding and retrievable via GET /v1/conversion/wallets. For digital wallets, this is the registered blockchain wallet address. |
| toWalletId | body | string | Yes | The destination account or wallet to which the converted position will be credited. For Classic accounts, the format is {participantId}-10 (Free NA) or {participantId}-11 (Free MA), for example 00002667-10. These are auto-generated at onboarding and retrievable via GET /v1/conversion/wallets. For digital wallets, this is the registered blockchain wallet address. |
| activityType | body | string | Yes | MemoSeg reduction reason code indicating how the conversion affects the participant's segregated position. Code 040 reduces (debits) the MemoSeg quantity; code 098 leaves the MemoSeg position unchanged. |
| quantity | body | string | Yes | Amount to convert; must be greater than zero |
| participantId | body | string | Optional | The 8-digit DTC participant number that identifies the member firm on whose behalf the conversion order is submitted, for example 00002667. This is the same identifier used across all DTC and DTCC services to uniquely identify a participant. |
| note | body | string | Optional | Optional order note |
POST /v1/conversion/async-operations/query
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| idempotencyKeys | body | array | Yes | One or more idempotency keys retained from prior submissions. The field accepts an array and the server processes all keys supplied in a single query. As a best practice, submit no more than 100 keys per request. See the Batch Query Behavior section below. |
| Offset / Count | body | integer | Optional | Pagination controls |
GET /v1/conversion/orders/{orderId}
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| orderId | path | string | Yes | Order identifier, taken from resourceId on the async operation response |
The idempotencyKeys field on POST /v1/conversion/async-operations/query accepts an array, and the server processes all keys supplied in a single query. As a best practice, submit no more than 100 idempotency keys per request; submitting very large arrays in a single request may impact performance.
| Behavior | Detail |
|---|---|
| Unmatched keys | Submitting keys for operations that do not exist does not return an error; the response contains only the records that matched. For example, submitting 10 keys where only 5 have corresponding operations returns a list of 5 results, with no indication of which keys were unmatched. |
| Pagination | The response is paginated. Even for a large number of submitted keys, the number of records returned per page is controlled by the Count and Offset parameters. When the number of matching results exceeds the Count value, page through the results to retrieve them all. |
| Recommended batch size | Submit no more than 100 keys per request to avoid the performance impact of very large arrays. |
GET /v1/conversion/orders
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | orderBy | OrderFields | CreatedAt, UpdatedAt |
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
| Input (query) | status | OrderStatus | Processing, Completed, Failed |
| Input (query) | origin | OrderOrigin | Participant, Admin, ForcedReconversion |
| Output (response) | status | OrderStatus | Processing, Completed, Failed |
| Output (response) | origin | OrderOrigin | Participant, Admin, ForcedReconversion |
POST /v1/conversion/async-operations/query
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Output (response) | objectKind | ObjectKind | Undefined, Order, Wallet, FinancialSecurity |
| Output (response) | status | AsyncOperationStatus | New, Processing, Completed, Failed |
| Output (response) | operationName | OperationName | Undefined, Register, Submit, Import |
GET /v1/conversion/orders/{orderId}
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Output (response) | status | OrderStatus | Processing, Completed, Failed |
| Output (response) | origin | OrderOrigin | Participant, Admin, ForcedReconversion |
The submission endpoint POST /v1/conversion/orders returns no response body, so it publishes no response enumerations. The tables above cover the three calls that read order state.
GET /v1/conversion/orders
const query = new URLSearchParams({
fromWallet: sourceWallet.id,
toWallet: destinationWallet.id,
financialSecurityIds: selectedSecurity.id,
status: 'Processing',
offset: '0',
count: '20'
});
const ordersResponse = await fetch(`${baseUrl}/v1/conversion/orders?${query}`, {
headers: { 'Authorization': `Bearer ${access_token}` }
});
const existingOrders: OrderResponse[] = await ordersResponse.json();POST /v1/conversion/orders
const idempotencyKey = generateIdempotencyKey();
const orderResponse = await fetch(`${baseUrl}/v1/conversion/orders`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json',
'X-Idempotency-Key': idempotencyKey
},
body: JSON.stringify({
financialSecurityId: selectedSecurity.id,
fromWalletId: sourceWallet.id,
toWalletId: destinationWallet.id,
activityType: '040',
quantity: '500'
})
});
// 202 Accepted with no response body. Retain idempotencyKey for the async query.
if (orderResponse.status !== 202) {
throw new Error('Conversion order was not accepted');
}POST /v1/conversion/async-operations/query
const asyncResponse = await fetch(`${baseUrl}/v1/conversion/async-operations/query`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
idempotencyKeys: [idempotencyKey],
offset: 0,
count: 10
})
});
const operations: AsyncOperationResponse[] = await asyncResponse.json();
const orderId = operations[0]?.resourceId;GET /v1/conversion/orders/{orderId}
const detailResponse = await fetch(`${baseUrl}/v1/conversion/orders/${orderId}`, {
headers: { 'Authorization': `Bearer ${access_token}` }
});
const orderDetail: OrderDetailResponse = await detailResponse.json();
// orderDetail.status === 'Processing'GET /v1/conversion/orders
[
{
"id": "order-id",
"symbol": "EXMPL",
"cusip": "037833100",
"fromWallet": { "id": "source-wallet-id", "name": "Participant Classic Account", "walletAddress": "00002667-10" },
"toWallet": { "id": "destination-wallet-id", "name": "First Bank EVM Wallet", "walletAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" },
"quantity": 500,
"status": "Processing",
"errorMessage": null,
"createdAt": "2026-01-01T00:00:00Z",
"updatedAt": "2026-01-01T00:00:00Z",
"activityType": "040",
"origin": "Participant"
}
]POST /v1/conversion/orders returns 202 Accepted with an empty body.
POST /v1/conversion/async-operations/query
[
{
"id": "async-operation-id",
"idempotencyKey": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"objectKind": "Order",
"status": "Completed",
"operationName": "Submit",
"resourceId": "order-id",
"resourceUrl": "/v1/conversion/orders/order-id",
"errorCode": null,
"errorMessage": null,
"asOfDate": "2026-01-01T00:00:00Z"
}
]GET /v1/conversion/orders/{orderId}
{
"orderId": "order-id",
"participant": { "id": "participant-id", "participantNumber": "00002667" },
"financialSecurityInfo": { "cusip": "037833100", "security": "Example Security" },
"fromWallet": { "id": "source-wallet-id", "name": "Participant Classic Account", "walletAddress": "00002667-10" },
"toWallet": { "id": "destination-wallet-id", "name": "First Bank EVM Wallet", "walletAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" },
"quantity": 500,
"submittedBy": "SA-ConversionBot",
"status": "Completed",
"errorMessage": null,
"createdAt": "2026-01-01T00:00:00Z",
"updatedAt": "2026-01-01T00:05:00Z",
"activityType": "040",
"fromTransactionId": "0x639dc5a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d",
"toTransactionId": "12200cc6f1e2d3c4b5a69788796a5b4c3d2e1f00998877665544332211aabbccdd",
"check": { "status": "Passed" },
"origin": "Participant"
}While an order is in Processing, both fromTransactionId and toTransactionId are null. They are populated with the on-chain ledger transaction hashes once the order reaches Completed and the underlying ledger events settle. See the Transaction Identifiers section below for field-level semantics.
The order detail response (GET /v1/conversion/orders/{orderId}) is the only endpoint that exposes the on-chain transaction identifiers for a conversion. The orders list endpoint (GET /v1/conversion/orders) does not return these fields.
| Field | Contains |
|---|---|
fromTransactionId | The on-chain ledger transaction hash for the source (debit) leg of the conversion — for example, a Burn on Besu such as 0x639dc5.... |
toTransactionId | The on-chain ledger transaction hash for the destination (credit) leg of the conversion — for example, a Mint on Canton such as 12200cc6.... |
Both fields are null until the order settles, then carry the ledger-native transaction hash for their respective leg. The hash format follows the convention of the underlying network (for example, a 0x-prefixed 32-byte hex string on Besu/EVM networks). Because a conversion moves between a DTC Classic account and a digital wallet, only the digital-wallet leg corresponds to a blockchain transaction; the DTC Classic leg (identifiers in the {participantId}-10 / {participantId}-11 format) is not an on-chain event, so the transaction identifier that carries an on-chain hash depends on the conversion direction.
fromTransactionId or toTransactionId returned here, because both systems independently report the same underlying chain data. Providers that expose an internal reference rather than the raw on-chain hash will not match directly; compare against the on-chain hash surfaced by the provider.interface OrderResponse {
id: string;
symbol?: string;
cusip?: string;
fromWallet: WalletRefResponse;
toWallet: WalletRefResponse;
quantity: number;
status: string;
errorMessage?: string;
createdAt: string;
obac?: AccessControlInfoResponse;
activityType?: string;
updatedAt?: string;
origin?: string;
}
interface AsyncOperationResponse {
id: string;
idempotencyKey: string;
objectKind: string;
status: string;
operationName: string;
resourceId?: string;
resourceUrl?: string;
errorCode?: string;
errorMessage?: string;
asOfDate: string;
}
interface OrderDetailResponse {
orderId: string;
participant: ParticipantResponse;
financialSecurityInfo?: OrderFinancialSecurityInfo;
fromWallet: WalletRefResponse;
toWallet: WalletRefResponse;
quantity: number;
submittedBy?: string;
submitterEmail?: string;
submitterComment?: string;
status: string;
errorMessage?: string;
createdAt: string;
updatedAt?: string;
activityType?: string;
fromTransactionId?: string;
toTransactionId?: string;
check: CheckInfoResponse;
origin?: string;
}Conversion direction must move between DTC_Classic and non-Classic wallets. Orders can be submitted Monday-Friday, 02:00-18:15 ET, with operational-hours enforcement server-side. Query the orders list before submitting to detect whether a prior submission with the same intent already exists, filtering on fromWallet, toWallet, financialSecurityIds, or status.
Handle 400 validation errors, 403 permission or lockout failures, 409 duplicate idempotency key, 422 business rule violations, and 5xx server errors. Submission does not execute the conversion synchronously; a 202 Accepted response confirms only that the order was queued for processing. Treat an async operation status of Failed as a submission failure and surface errorCode and errorMessage.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request, missing X-Idempotency-Key, quantity of zero, or wallet identifier not found see Error Code Reference below | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Security or order identifier not found see Error Code Reference below | Surface not found. Do not create a replacement order. |
| 409 | Duplicate X-Idempotency-Key, or a conflicting wallet already exists see Error Code Reference below | Treat the order as already submitted. Query the async operation with the same key to resolve the existing order identifier. |
| 422 | Business rule violation such as invalid conversion direction, ineligible security, ineligible wallet, or submission outside operational hours | Surface the specific rule violation. Do not retry until the input is corrected. |
| 500 | Unhandled or infrastructure failure see Error Code Reference below | Not retryable without platform investigation. Surface the failure and escalate. |
| 503 | Service failure | Retry with the same X-Idempotency-Key, maximum three attempts. |
Some error responses include a machine-readable errorCode that pinpoints the specific cause beyond the HTTP status. The tables below enumerate the codes returned for HTTP 400, 404, 409, and 500. Not every error response carries a specific code — responses without one are fully described by the summary table above.
Each entry lists the returned errorCode, the condition that triggers it, and the recommended correction.
| Error Code | Cause | Correction |
|---|---|---|
ConversionOperationsEngine__InvalidQuantity | quantity ≤ 0. | Submit with a positive quantity. |
ConversionOperationsEngine__SameOrderWallets | fromWalletId and toWalletId are identical. | Use two distinct wallet IDs. |
ConversionOperationsEngine__SameWalletLedgers | Both wallets are on the same network. | Use wallets on different networks. |
ConversionOperationsEngine__WalletIsNotConversionEligible | The wallet has conversionEligible = false. | Use a wallet that has completed registration and is marked eligible. |
ConversionOperationsEngine__OperationCannotBePerformed | The wallet's processingState is not Idle. | Wait for wallet registration to complete before submitting. |
ConversionOperationsEngine__OrderFinancialInstrumentRequired | Neither assetId nor financialSecurityId was provided. | Include exactly one of assetId or financialSecurityId. |
ConversionOperationsEngine__MultipleOrderFinancialInstrumentsProvided | Both assetId and financialSecurityId were provided. | Remove one of the two fields. |
ConversionOperationsEngine__InactiveFinancialSecurityOrderCreatingException | The referenced security is not in Active status. | Use an active security. |
ConversionOperationsEngine__OutsideAllowedTimeWindow | Request submitted outside operational hours or on a holiday. | Resubmit during the operational window. |
ConversionOperationsEngine__InvalidOrderActivityType | activityType is not a supported value. | Use a supported activity type code. |
ConversionOperationsEngine__InvalidWalletAddress | The wallet address is not valid for the specified network. | Correct the address format. |
ConversionOperationsEngine__WalletNotFound | The wallet ID does not exist or is not accessible to your tenant. | Verify the wallet ID. |
| Error Code | Cause | Correction |
|---|---|---|
ConversionOperationsEngine__OrderNotFound | The order ID does not exist or is not accessible. | Verify the order ID. |
ConversionOperationsEngine__AssetNotFound | The assetId does not exist. | Verify the asset ID. |
ConversionOperationsEngine__FinancialSecurityNotFound | The financialSecurityId does not exist. | Verify the financial security ID. |
| Error Code | Cause | Correction |
|---|---|---|
AsyncOperation__AsyncOperationAlreadyExists | The idempotency key was already used and the original request succeeded. | Query async operations with the key to retrieve the existing order ID. Do not resubmit. |
ConversionOperationsEngine__WalletAlreadyExists | A wallet with the same address and network already exists in your tenant. | Use the existing wallet or register with a different address. |
ConversionOperationsEngine__WalletWithSameNameAlreadyExist | A wallet with the same name already exists in your tenant. | Choose a different wallet name. |
Unhandled or infrastructure failures. The errorCode value is "500". Not retryable without platform investigation.
The submission POST is protected by X-Idempotency-Key. Retrying with the same key is safe and will not create duplicate orders. Do not generate a new key on retry, because the key is also the lookup value for the async operation query. The async operation query and the order detail retrieval are read-only and safe to retry.
Demonstrate that a customer integration can execute the complete conversion order lifecycle: authenticate with the Service Account Credentials grant, load order, security, and wallet reference data, validate security and wallet eligibility, submit a conversion order, enforce idempotency controls, track asynchronous processing, correlate the async operation to the resulting order, retrieve final order detail, monitor lifecycle progression, and reconstruct the transaction audit trail. Unlike Account Management and Wallet Management, this certification validates both retrieval operations and transactional business processes.
Authenticate, load reference data in parallel, validate eligibility, submit, then track the order asynchronously to a terminal state.
Service Account Credentials grant against /connect/token.
Orders, securities, and wallets in parallel, plus issue type, issue sub-type, and ledger filters.
Security status equals Active.
Wallet state equals Idle and conversionEligible is true.
POST /v1/conversion/orders returns 202 Accepted with no body.
Query by X-Idempotency-Key until a terminal status.
resourceId resolves the order identifier.
GET /v1/conversion/orders/{orderId}.
Processing to Completed, then reconstruct the audit trail.
| Rule | Requirement |
|---|---|
| Security Eligibility | Known security states are Onboarding, Active, Pause, and Failed. Only Active securities are eligible for conversion-order creation. |
| Wallet Eligibility | Known wallet states are Idle, Registering, FailedCompliance, and Failed. Only wallets that are Idle and conversion eligible may be used. |
| Conversion Direction | Conversion must occur between DTC Classic and digital wallets. |
| Quantity Validation | Quantity must be greater than zero. |
| Idempotency | Every submission must include X-Idempotency-Key. The same key must remain traceable throughout asynchronous processing. |
| Asynchronous Processing | Order creation returns HTTP 202 Accepted. Completion is determined through async operation tracking, not from the submission response. |
| Operational Hours | Monday to Friday, 02:00 ET to 18:15 ET. Enforcement is server-side. |
const tenantId = "<tenant-id>";
const baseUrl = "<base-url>";
const username = "SA-ConversionBot";
const password = process.env.SERVICE_ACCOUNT_PASSWORD!;async function authenticate() {
const response = await fetch(`${baseUrl}/connect/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "urn:dtcc:params:oauth:grant-type:service-account-credentials",
type: "conveyance",
entity: ENTITY_ID_OF_WHICH_SA_IS_MEMBER_OF,
acr_values: `tenant:${tenantId}`,
username,
password
})
});
if (!response.ok) {
throw new Error(`Authentication failed: ${response.status}`);
}
const data = await response.json();
return data.access_token;
}
async function loadReferenceData(accessToken: string) {
const [ordersResponse, securitiesResponse, walletsResponse] = await Promise.all([
fetch(`${baseUrl}/v1/conversion/orders`, { headers: { Authorization: `Bearer ${accessToken}` } }),
fetch(`${baseUrl}/v1/conversion/securities`, { headers: { Authorization: `Bearer ${accessToken}` } }),
fetch(`${baseUrl}/v1/conversion/wallets`, { headers: { Authorization: `Bearer ${accessToken}` } })
]);
return {
orders: await ordersResponse.json(),
securities: await securitiesResponse.json(),
wallets: await walletsResponse.json()
};
}
async function loadFilterData(accessToken: string) {
const [issueTypesResponse, issueSubTypesResponse] = await Promise.all([
fetch(`${baseUrl}/v1/conversion/securities/issue-types`, { headers: { Authorization: `Bearer ${accessToken}` } }),
fetch(`${baseUrl}/v1/conversion/securities/issue-subtypes`, { headers: { Authorization: `Bearer ${accessToken}` } })
]);
return {
issueTypes: await issueTypesResponse.json(),
issueSubTypes: await issueSubTypesResponse.json()
};
}
function generateIdempotencyKey() {
return crypto.randomUUID();
}
async function queryOperation(accessToken: string, idempotencyKey: string) {
const response = await fetch(`${baseUrl}/v1/conversion/async-operations/query`, {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ idempotencyKeys: [idempotencyKey], offset: 0, count: 10 })
});
const [operation] = await response.json();
return operation;
}
async function getOrder(accessToken: string, orderId: string) {
const response = await fetch(`${baseUrl}/v1/conversion/orders/${orderId}`, {
headers: { Authorization: `Bearer ${accessToken}` }
});
if (!response.ok) {
throw new Error(`Order retrieval failed with status ${response.status}`);
}
return response.json();
}Verify that the integration can obtain an access credential using the Service Account Credentials grant. The credential proves the caller's identity and must be supplied as a bearer credential on every subsequent call in this workflow.
const accessToken = await authenticate();The token response returns access_token, token_type, and expires_in; the helper returns the access_token value, which is usable on a subsequent API call.
HTTP 200; credential issued and usable.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid service account credentials or malformed grant request | Correct the credentials. Do not retry with the same values. |
| 403 | Service account lacks the required permission | Surface the permission failure. Do not retry. |
| 500, 503 | Identity service failure | Retry with exponential backoff, maximum three attempts. |
Verify that all prerequisite data loads successfully. Order, security, and wallet inventories supply the order inputs; issue type, issue sub-type, and ledger reference data supply the filter context.
const { orders, securities, wallets } = await loadReferenceData(accessToken);
const { issueTypes, issueSubTypes } = await loadFilterData(accessToken);Order inventory returned. Security inventory returned. Wallet inventory returned. Issue type reference data returned. Issue sub-type reference data returned. Ledger network data returned.
HTTP 200 on each call; all six data sets available for downstream steps.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 401 | Expired or invalid access credential | Re-authenticate, then retry once. |
| 403 | Service account lacks the required permission | Surface access denied. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff. Each call fails independently, so present a partial state rather than blocking the whole load. |
Verify that a valid conversion security can be identified from the security inventory loaded in Step 2.
const security = securities.find(security => security.status === "Active");The security exists and carries CUSIP, issuer, issue type, issue sub-type, restrictions, and token network information. Status equals Active. Securities in Onboarding, Pause, or Failed status are not eligible.
Eligible security identified.
This step performs no API calls. If no Active security is found, the integration must block order submission rather than proceeding with an ineligible security.
Verify that suitable source and destination wallets can be selected from the wallet inventory loaded in Step 2.
const eligibleWallets = wallets.filter(
wallet => wallet.state === "Idle" && wallet.conversionEligible === true
);The wallet exists, state equals Idle, conversionEligible equals true, network information is present, and no failure state is set. A wallet that satisfies only one of the two eligibility conditions is not a valid candidate. Wallets in Registering, FailedCompliance, or Failed state are not eligible.
Eligible wallets identified.
This step performs no API calls. If no wallet is both Idle and conversion eligible, the integration must block order submission rather than proceeding with an ineligible wallet.
Verify that a conversion order can be submitted with a valid idempotency key and accepted for asynchronous processing.
const idempotencyKey = generateIdempotencyKey();
const response = await fetch(`${baseUrl}/v1/conversion/orders`, {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
"X-Idempotency-Key": idempotencyKey
},
body: JSON.stringify({
financialSecurityId: security.id,
fromWalletId: sourceWallet.id,
toWalletId: destinationWallet.id,
quantity: "100",
activityType: "040"
})
});HTTP 202 returned. The request is accepted and no validation error is returned. No response body is expected, so the order identifier is not available at this point. Retain idempotencyKey for the async operation query.
HTTP 202 Accepted with no response body.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request, missing X-Idempotency-Key, or quantity of zero | Correct the request. Do not retry as-is. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 409 | Duplicate X-Idempotency-Key | Treat the order as already submitted. Query the async operation with the same key to resolve the existing order. |
| 422 | Business rule violation such as invalid direction, ineligible security, ineligible wallet, or submission outside operational hours | Surface the specific rule violation. Do not retry until the input is corrected. |
| 500, 503 | Service failure | Retry with the same X-Idempotency-Key, maximum three attempts. |
Verify that resubmitting an identical request with the same idempotency key does not create a duplicate order.
Submit the same order request twice using the same X-Idempotency-Key.
A duplicate order is not created, idempotency is maintained, and the operation remains traceable under the original key.
Duplicate processing prevented.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 409 | Duplicate X-Idempotency-Key | Treat as already submitted. Reconcile against the existing order rather than resubmitting. |
| 400 | X-Idempotency-Key omitted entirely | Submission rejected. Add the header before retrying. |
Verify that asynchronous operations can be monitored using the idempotency key retained at submission.
const operation = await queryOperation(accessToken, idempotencyKey);The operation returns id, idempotencyKey echoing the submitted key, objectKind, status, operationName, and asOfDate. Known status values are New, Processing, Completed, and Failed. resourceId populates once processing begins.
HTTP 200; async operation successfully tracked.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 401 | Expired or invalid access credential | Re-authenticate, then retry once. |
| 500, 503 | Service failure | Retry with exponential backoff. The query is read-only and safe to repeat. |
Verify that an async operation can be correlated to the order it produced.
const operation = await queryOperation(accessToken, idempotencyKey);
const order = await getOrder(accessToken, operation.resourceId);resourceId is populated, resourceUrl is populated, the referenced order exists, and the order is retrievable.
HTTP 200; async operation successfully correlated to an order.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 404 | resourceId references an order that cannot be retrieved | Surface not found. Do not create a replacement order. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. |
Verify that final order detail can be accessed once the order identifier is known.
const orderDetail = await getOrder(accessToken, operation.resourceId);The response carries order identifier, status, participant information, transaction information, and audit timestamps.
HTTP 200; order detail successfully retrieved.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 401 | Expired or invalid access credential | Re-authenticate, then retry once. |
| 404 | Order identifier not found | Surface not found. Do not create a replacement order. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. |
Verify that lifecycle transitions can be monitored through to a terminal state.
Valid states are Processing, Completed, and Failed. Status progression is visible, a terminal state is reached, and the final state is recorded. Failed is terminal and must be surfaced with errorMessage rather than resubmitted.
HTTP 200; current order status returned and reportable.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 401 | Expired or invalid access credential | Re-authenticate, then retry once. |
| 404 | Order identifier not found | Surface not found. Do not create a replacement order. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. |
Verify that the complete transaction history can be reconstructed from the artifacts produced across the workflow.
The order identifier is traceable, the idempotency key is traceable, the async operation is traceable, audit timestamps are available, and transaction identifiers are available.
End-to-end audit trace established.
This step performs no additional API calls. It relies on the artifacts captured in Steps 5 through 10. If any link in the chain cannot be reconstructed, the audit trace is incomplete and the workflow must be treated as unverified.
Execute the complete workflow from authentication through audit validation in a single uninterrupted run.
Authentication PASS
Reference Data PASS
Security Validation PASS
Wallet Validation PASS
Order Submission PASS
Idempotency PASS
Async Tracking PASS
Correlation PASS
Order Retrieval PASS
Lifecycle PASS
Audit PASSEach constituent call returns its expected success code.
Complete workflow executed and every stage verified.
| ID | Scenario | HTTP | Expected result |
|---|---|---|---|
| NT-1 | Invalid service account credentials | 400 | Authentication rejected. |
| NT-2 | Invalid security | 422 | Order rejected. |
| NT-3 | Ineligible wallet | 422 | Order rejected. |
| NT-4 | Missing idempotency key | 400 | Request rejected. |
| NT-5 | Expired access credential | 401 | Authorization failure; re-authentication required. |
| NT-6 | Async processing failure | — | Async operation returns Failed status with errorCode and errorMessage present. Not an HTTP error condition. |
| NT-7 | Invalid order retrieval | 404 | Order not found; error handled without creating a replacement order. |
| NT-8 | Service unavailable | 503 | Retry attempted, failure reported, workflow stops safely. |
The customer must demonstrate authentication, reference-data retrieval, active-security validation, wallet eligibility validation covering both idle state and the conversion-eligible flag, conversion-order submission, idempotency compliance, async-operation monitoring, async-to-order correlation, final-order retrieval, lifecycle monitoring, audit validation, required negative-test handling, and end-to-end workflow execution.
Corporate Actions workflows support discovery, filtering, and detail inspection of corporate action events. The initial load retrieves events, event type taxonomy, and securities filters in parallel. Event detail is retrieved from a single endpoint and includes classification, security, date, DTC mandatory, and support context.
The information provided by corporate action notification API endpoints leverages DTCC's ISO CANO messages. For a data dictionary on these data points, please refer to DTCC's ISO 20022 Messaging Specifications, located here.
/connect/token with grant_type=urn:dtcc:params:oauth:grant-type:service-account-credentials, type=conveyance, entity context, tenant context via acr_values=tenant:{tenantId}, and service-account credentials. Downstream API calls use Authorization: Bearer {access_token}.A condensed view of the API call sequence, dependencies, and branch rules.
POST /connect/token
Obtain service-account token.
POST /v2/corporate-actions
Retrieve paginated events with a JSON body; offset and count are required, filters are optional.
GET /v2/corporate-actions/event-typesGET /v1/conversion/securities
Load in parallel.
GET /v2/corporate-actions/{id}
Retrieve full event detail.
Interpret status, category, DTC mandatory classification, support flag, and relevant core dates.
GET /v2/corporate-actions/{id}, including security data, event classification, core dates, DTC mandatory classification, support status, and payout-related metadata.Fetches the events list alongside event taxonomy and securities filter options. The events list is retrieved with POST /v2/corporate-actions, which carries the pagination and filter fields in a JSON body; offset and count are required and the filter fields are optional. The event taxonomy and securities calls remain GET requests.
GET /v1/corporate-actions (query-string parameters) to POST /v2/corporate-actions (JSON request body). GET /v1/corporate-actions is deprecated. The filter fields that were previously query parameters are now supplied in the request body, which must include the required offset and count fields.POST /v2/corporate-actions — all filter and pagination fields are supplied in the JSON request body (EventFilterRequest). The body is required and must include offset and count; the remaining filter fields are optional. A request with no body, or one that omits offset or count, is rejected with HTTP 400.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| offset | body | integer (int32) | Yes | Zero-based index of the first record to return. Minimum 0. |
| count | body | integer (int32) | Yes | Maximum number of records to return per page. Minimum 1. |
| ids | body | string[] | Optional | Filter by internal event identifiers (GUID format). Up to 50 values. |
| eventIds | body | string[] | Optional | Filter by external (DTC) event identifiers. Up to 50 values. |
| eventTypes | body | EventTypeFilterItem[] | Optional | Filter by event types and subtypes. Each item is an object with a required type (EventTypeEnumeration) and an optional subType (SubEventTypeEnumeration). Up to 50 values. |
| securityIds | body | string[] | Optional | Filter by event security identifier. Up to 50 values. |
| securityCusips | body | string[] | Optional | Filter by security CUSIP. Up to 50 values. |
| status | body | EventStatusEnumeration[] | Optional | Filter by event status. Up to 5 values. |
| searchFor | body | string | Optional | Free-text search key filter. Maximum length 50 characters. |
| orderBy | body | EventFields enum | Optional | Field the results are sorted on. |
| sortOrder | body | SortOrder enum | Optional | Sort direction: Ascending or Descending. |
POST /v2/corporate-actions
| Direction | Parameter / Field | Enum Name | Reference |
|---|---|---|---|
| Input (body) | eventTypes[].type | EventTypeEnumeration | Event Type ▾ |
| Input (body) | eventTypes[].subType | SubEventTypeEnumeration | Sub-Event Type ▾ |
| Input (body) | status | EventStatusEnumeration | Event Status ▾ |
| Input (body) | orderBy | EventFields | Event Fields ▾ |
| Input (body) | sortOrder | SortOrder | Sort Order ▾ |
| Output (response) | subType | SubEventTypeEnumeration | Sub-Event Type ▾ |
| Output (response) | status | EventStatusEnumeration | Event Status ▾ |
The POST /v2/corporate-actions response (CorporateActionEventResponse) returns type, subType, and status; dtcMandatory and category are returned only by the event detail endpoint GET /v2/corporate-actions/{id}. Enumeration values are defined once in the Corporate Action Enumerations reference at the end of this section.
GET /v2/corporate-actions/event-types
| Direction | Parameter / Field | Enum Name | Reference |
|---|---|---|---|
| Output (response) | eventType | EventTypeEnumeration | Event Type ▾ |
| Output (response) | subEventType | SubEventTypeEnumeration | Sub-Event Type ▾ |
This endpoint returns the authoritative list of valid event type / sub-event type pairs, including the numeric codes used by the service. See the Event Type and Sub-Event Type references.
GET /v1/conversion/securities
| Direction | Parameter / Field | Enum Name | Values |
|---|---|---|---|
| Input (query) | orderBy | FinancialSecurityFields | CUSIP, Description, Issuer |
| Input (query) | sortOrder | SortOrder | Ascending, Descending |
| Output (response) | status | FinancialSecurityStatus | Onboarding, Active, Pause, Failed |
| Output (response) | processingState | ProcessingStates | Idle, Registering, FailedCompliance, Failed |
const [eventsResponse, eventTypesResponse, securitiesResponse] = await Promise.all([
fetch(`${baseUrl}/v2/corporate-actions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ orderBy: 'UpdatedAt', sortOrder: 'Descending', count: 25, offset: 0 })
}),
fetch(`${baseUrl}/v2/corporate-actions/event-types`, {
headers: { 'Authorization': `Bearer ${access_token}` }
}),
fetch(`${baseUrl}/v1/conversion/securities`, {
headers: { 'Authorization': `Bearer ${access_token}` }
})
]);
const events = await eventsResponse.json();[
{
"id": "9149E44A-CAB4-4CB4-8656-A01BDA2B18EC",
"eventId": "0000006",
"typeDescription": "Cash Dividend",
"type": "CashDividend",
"subTypeDescription": "DRIP (DTC Only)",
"subType": "DRIPDTCOnly",
"status": "Approved",
"positionCaptureDate": "2025-03-12T00:00:00Z",
"declaredPayableDate": "2025-03-12T00:00:00",
"earliestDTCAnticipatedPaymentDate": null,
"earliestDTCInstructionExpirationDate": null,
"digitalAllocationDate": "2025-05-05T19:00:00Z",
"updatedAt": "2025-05-05T19:00:00Z",
"security": { "id": "E6789C8C-2CA4-446C-AE93-0A9AFFCC457A", "externalId": "E6789C8C-2CA4-446C-AE93-0A9AFFCC457A", "cusip": "46090E103", "name": "Invesco QQQ Trust", "onboardedDate": "2024-01-15T00:00:00Z" },
"dtcProcessingIndicator": true
}
]interface CorporateActionEventResponse {
id: string;
eventId: string;
typeDescription: string;
type: string;
subTypeDescription: string;
subType: string;
status: string;
positionCaptureDate: string | null;
declaredPayableDate: string | null;
earliestDTCAnticipatedPaymentDate: string | null;
earliestDTCInstructionExpirationDate: string | null;
digitalAllocationDate: string | null;
updatedAt: string | null;
security: SecurityListResponse;
dtcProcessingIndicator: boolean;
}
interface SecurityListResponse {
id: string;
externalId: string;
cusip: string;
name: string;
onboardedDate: string;
}
interface EventTypePairResponse {
eventType: number;
eventTypeCode: string;
eventTypeDescription: string;
subEventType: number;
subEventTypeCode: string;
subEventTypeDescription: string;
}Approved, ConditionallyApproved, Incomplete, Cancelled, and Deleted.
Handle 401 by requesting a new JWT access credential and retrying once, 403 as lockout or missing permission, 400 as invalid filters, and 5xx with retry. If the event-type or securities call fails, disable the affected filter while keeping the events list usable.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Malformed request or invalid filter parameter | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Resource not found | Surface not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. Keep the workflow stable and disable only the affected filter. |
All three calls are read-only queries and safe to retry on 5xx or network timeout. The events list is a POST /v2/corporate-actions that performs a read with no side effects, so retrying it is safe; the event-types and securities calls are GET requests.
Retrieves full detail for a specific corporate action event.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | Yes | Unique key of a single corporate action event, taken from the results of the event list call. |
GET /v2/corporate-actions/{id}
| Direction | Parameter / Field | Enum Name | Reference |
|---|---|---|---|
| Output (response) | type | EventTypeEnumeration | Event Type ▾ |
| Output (response) | subType | SubEventTypeEnumeration | Sub-Event Type ▾ |
| Output (response) | status | EventStatusEnumeration | Event Status ▾ |
| Output (response) | dtcMandatory | DtcMandatory | DTC Mandatory ▾ |
| Output (response) | category | EventCategory | Event Category ▾ |
Enumeration values are defined once in the Corporate Action Enumerations reference at the end of this section.
const eventDetailResponse = await fetch(
`${baseUrl}/v2/corporate-actions/${eventId}`,
{ headers: { 'Authorization': `Bearer ${access_token}` } }
);
if (!eventDetailResponse.ok) {
const error = await eventDetailResponse.json();
throw new Error(`Event detail request failed: ${error.ErrorCode} - ${error.ErrorMessage}`);
}
const eventDetail = await eventDetailResponse.json();{
"id": "event-record-id",
"eventId": "123456",
"eventGroup": "Distribution",
"eventType": "CashDividend",
"eventTypeDescription": "Cash Dividend",
"subEventType": "CDeemedDividend",
"subEventTypeDescription": "305C - Deemed Dividend",
"status": "ConditionallyApproved",
"category": "MandatoryCashDistribution",
"isSupported": false,
"dtcMandatory": "Mandatory",
"security": { "id": "security-id", "cusip": "46625H202", "name": "Example Security", "issuerDescription": "Example Issuer", "assetClass": "Equity", "assetType": "Common Stock", "ticker": "EX" },
"coreDates": { "exDate": "2026-06-24T00:00:00Z", "recordDate": "2026-06-24T00:00:00Z", "declaredPayableDate": "2026-06-24T00:00:00Z", "allocationDateTime": null }
}interface CorporateActionEventResponse {
id: string;
security: { id: string; cusip: string; name: string; issuerDescription: string; assetClass: string; assetType: string; ticker: string };
eventId: string;
eventGroup: string;
eventType: string;
eventTypeDescription: string;
subEventType: string;
subEventTypeDescription: string;
status: string;
category: string;
isSupported: boolean;
dtcMandatory: string;
reconversionDate: string | null;
coreDates: Record<string, string | null>;
}The event detail response includes security information, event classification, status, category, DTC mandatory classification, support indicator, reconversion date, and coreDates. No separate entitlements endpoint is needed for the workflow described in the source material.
Handle 401 as an expired credential, 403 as lockout or missing detail permission, 404 as an invalid or deleted event, 400 as an invalid event ID format, and 500 with retry. The response body carries ErrorCode and ErrorMessage, which should be surfaced rather than discarded.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid event identifier format | Correct the request. Do not retry as-is. |
| 401 | JWT access credential missing, expired, or invalid | Request a new JWT access credential, then retry once. |
| 403 | Service account lacks the required permission, or the account is locked | Surface access denied. Do not retry. |
| 404 | Invalid or deleted event | Surface event not found. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. |
Read-only GET; safe to retry on 5xx or network timeout. Use exponential backoff with a maximum of three retries.
These enumerations are shared across the Corporate Actions endpoints. Each value is defined once here; the endpoint tables above link into this reference. Values are case-sensitive and must be sent exactly as shown.
High-level corporate action classification. Used as the type field inside each eventTypes[] filter item, and returned as type on event responses. Descriptions are sourced from the SR2026 Corporate Action Announcements Data Dictionary.
| Value | Event Name | Description |
|---|---|---|
AutomaticDividendReinvestment | Automatic Dividend Reinvestment | A mandatory event for which the dividend payment is paid only in additional shares of the same security. This event type will predominantly be used for US securities and differs from the Dividend Reinvestment option (a voluntary portion of the event that applies to non-US securities). "DRIP" also appears as an option under relevant events (e.g., Cash Dividend). |
CapitalGainsDistribution | Capital Gains Distribution | A distribution of cash that the issuer has determined will be declared as income financed from capital gains and not ordinary income. |
CashDividend | Cash Dividend | A distribution of cash to shareholders, paid by the issuer, usually based upon current earnings and/or accumulated profits as declared by the board of directors. There are separate events for Dividends with Options and Stock Dividends. |
CDEarlyRedemption | CD Early Redemption | A feature of a security that allows an issuer to make a payment to the security holder. This event will be used for securities subject to redemptions other than those categorized as full and partial calls (e.g., early CD redemptions). Deferred until Phase 4 implementation. |
Change | Change | An event where the issuer is announcing a change in company or security details. |
Consent | Consent | Solicitation to security holders for their agreement (consent) to proposed changes in the terms of the security, usually without a formal general meeting. A fee may be paid to the security holder. |
Conversion | Conversion | Conversion of securities (generally convertible bonds or preferred shares) into another form of securities (usually common shares) at a pre-stated price or rate. |
Default | Default | A notice of failure by the issuer to honour commitments made within the terms of the issued security. It usually relates to making timely payments of interest and principal as they come due. A payment may be made in lieu of reinstituting the original payments. |
Distribution | Distribution | A distribution by the issuer that is not classified as another specific event. |
DividendWithOption | Dividend with Option | A distribution of a dividend to shareholders with the choice of payment method. The shareholder has the option to choose the form of payment (e.g., securities, cash, or both). There are separate events for Cash Dividends, Stock Dividends, and Dividends with Options. |
DutchAuction | Dutch Auction | Identifies a tender event in which the corporation offers to purchase up to a certain amount of securities within a price range. Holders must submit a specific price within that range that they would be willing to accept. The actual tender price is not determined until the end of the offer. |
ExchangeOffer | Exchange Offer | An offer to surrender securities in exchange for other securities or a combination of securities and cash. The exchange may also include a consent solicitation. |
FinalPaydown | Final Paydown | The final distribution of principal due on a security, typically CMOs. |
FullCall | Full Call | The security is redeemed for cash in its entirety on a date that is prior to the maturity date, and for which the holders receive the principal amount of the security. |
FullPrerefunding | Full Prerefunding | The exercise of a privilege by the issuer to repay, in full, any debt security prior to maturity when the issuer deposits assets in trust. This irrevocably restricts their use to satisfaction of the debt. |
GeneralInformation | General Information | General information provided by the issuer that should not result in material changes to the security. |
Interest | Interest | The payment of an obligation that the issuer agrees to make to holders of an interest-bearing security. Usually, the payment is made in cash and on a scheduled basis. |
Liquidation | Liquidation | A company reports its intentions to dismantle its business, paying off debts in order of priority and distributing the remaining assets in cash and/or securities to the owners of the securities. The payment of proceeds may require the presentation of securities. |
MandatoryExchange | Mandatory Exchange | A corporate action requiring the surrender of certificates to be exchanged for a new security or cash. |
MandatoryPut | Mandatory Put | The mandatory exchange of all outstanding bonds (with a putable feature) for cash or a new security, where the event security is remarketed. The issuer may offer holders the right to retain their securities instead of exchanging them. |
Maturity | Maturity | The final repayment, usually in cash, by an issuer for the entire issue, or remaining outstanding securities of a specific security on a specified date. |
Meeting | Meeting | A meeting of a company's share or bond holders to address resolutions put forth by the issuer. |
Merger | Merger | The exchange of one company's security for another company's security, cash, or a combination of cash and securities. |
NameChange | Name Change | The issuer changes its name or the name of its security/securities. This may involve surrendering physical securities. A new security identifier may be assigned to the new name, especially if new securities are issued. |
OddLotOffer | Odd Lot Offer | Identifies a tender offer event made by the corporation or its agent to purchase shares from odd-lot shareholders. An odd-lot shareholder typically refers to a holder of less than 100 shares of stock. |
PartialCall | Partial Call | Securities are redeemed by the issuer for cash, in part, before their scheduled maturity date. The outstanding amount of securities will be proportionally reduced based on a specific percentage of holding. A lottery may be run where pooled securities are held. |
PartialDefeasance | Partial Defeasance | Issuer sets aside cash in escrow to pay off a portion of the issue before the maturity date. New securities are issued for the portion defeased. |
PartialMandatoryPut | Partial Mandatory Put | The mandatory exchange of a portion of bonds where the exchanged securities are usually remarketed. The issuer may offer holders the right to retain instead of exchanging their securities. |
PartialPrerefunding | Partial Prerefunding | Similar to a Full Prerefunding, a partial prerefunding is the exercise of a privilege by the issuer to repay, in part, any debt security prior to maturity when the issuer deposits assets in trust. This irrevocably restricts their use to satisfaction of the debt. New securities are issued for the portion prerefunded. |
PayInKind | Pay in Kind | Income on interest-bearing securities where the payment is made in additional securities rather than cash. |
PlanOfReorganization | Plan of Reorganization | Reorganisation plan devised by the company or trustees that is usually associated with a bankruptcy filing. Identifies an event where a vote/consent is sought for the plan of reorganisation. |
Principal | Principal | A cash payment that represents a reduction of the principal in the security. |
Put | Put | A feature of a bond that entitles the holder to elect to surrender the bond for cash during a predetermined time period, with a predetermined payable date. |
RedemptionOfRights | Redemption of Rights | An event where the issuer pays proceeds to shareholders prior to acquiring or merging with an existing company. |
RedemptionOfWarrants | Redemption of Warrants | An event where the issuer pays proceeds to holders at or after the expiration date of the warrant rather than expire the warrant for no cash (worthless). |
Reorganization | Reorganization | A reorganization event announced by the issuer that cannot be classified as another event. |
ReturnOfCapital | Return of Capital | A distribution of cash resulting from the sale of a capital asset or securities, or any other transaction unrelated to retained earnings. Security holders may have to adjust the "cost basis" of their holding to account for the payment. |
ReverseStockSplit | Reverse Stock Split | The exchange of a company's security for the same company's new security at a preset rate. This corporate action reduces the number of shares outstanding. |
RightsDistribution | Rights Distribution | Securities distributed to common stock holders of a company that grant the option to purchase new or additional securities of the same company during a predetermined time period at a predetermined price. A Rights Distribution is accompanied by a corresponding Rights Subscription, which provides the details related to exercising the rights. In some cases, DTC will allocate rights that are not issued by the company to process a subscription offer. |
RightsSubscription | Rights Subscription | A privilege granted to holders of rights to purchase new or additional securities. Rights are often tradable in a secondary market. |
SaleOfRights | Sale Of Rights | A feature of a security that allows an issuer to make a payment to the security holder. This event will be used for securities subject to redemptions other than full and partial calls (e.g., early CD redemptions). |
SecuritySeparation | Security Separation | No corresponding entry in the SR2026 data dictionary; description not published. |
SpecialDividend | Special Dividend | A cash payment to shareholders that represents an extra or non-regular payment. There are separate events for Cash Dividends, Dividends with Options, and Stock Dividends. |
SpinOff | Spin-Off | A distribution of subsidiary securities to the shareholders of the parent company without a surrender of securities or payment. A spin-off represents a form of divestiture resulting in an independent company. |
StockDividend | Stock Dividend | A dividend paid to shareholders in the form of shares of stock in either the issuing company or in another company. There are separate events for Cash Dividends, Dividends with Options, and Special Dividends. |
StockSplit | Stock Split | The increase in a company's number of outstanding shares of stock without any change in the shareholder's equity or the aggregate market value at the time of the split. The share price is normally reduced. Forward split events are included here. |
TaxEvent | Tax Event | Tax Event announcements are information only announcements regarding taxable events that may give rise to information and/or withholding obligations which occur even in the absence of an actual distribution of dividend and interest payments ("Tax Events"). |
TaxRefund | Tax Refund | An event that enables DTC to make a withholding tax refund, usually on non-U.S. dividends. |
TenderOffer | Tender Offer | An offer made to security holders, normally by a third party, requesting them to sell (tender) their securities for a specified price (usually at a premium over prevailing market prices). Generally, the objective of a tender offer is to take control of the target company. |
Termination | Termination | A security, usually a form of a derivative (e.g., ADR or UIT), for which the agent or issuer has decided to terminate the derivative based on a change to the underlying security(ies) or a change in strategy. |
WarrantsExercise | Warrants Exercise | A feature of a security that permits the holder to exercise an option to exchange the security into another form (usually, warrants into shares). The exercise will commonly require a payment based upon a pre-determined value and time. |
Worthless | Worthless | DTCC advising its participants that their positions in that security will be taken down. The announcement is created when DTC receives a formal letter advising that securities held are worthless. |
Bankruptcy | Bankruptcy | Bankruptcy (Vote) is a legal process for relieving debt that the borrower cannot repay. It's a measure of last resort that typically requires liquidating assets or entering a repayment plan. (Added 7/15/2026.) |
Finer classification nested beneath an event type. Valid type / subType pairings are enumerated by GET /v2/corporate-actions/event-types; do not assume every subtype is valid for every type. Descriptions are sourced from the SR2026 Corporate Action Announcements Data Dictionary; some codes apply across multiple event types and are described generically.
| Value | Sub-Event Name | Description |
|---|---|---|
None | (Non-specific) | Non-specific event with no sub-event classification. |
DRIPDTCOnly | DRIP (DTC only) | Identifies an event where the Issue is eligible for a Dividend Reinvestment program. |
OptOutDTCOnly | Opt Out (DTC only) | Identifies an event (e.g., Cash Dividend) where DTC offers a DRIP option as a default option (holder must opt out of the DRIP Option). |
Domicile | Domicile | Identifies an event where the Issuer has changed the domicile of the Corporation / a change in the place of incorporation of the legal entity of the issuing company. |
DomicileNewCUSIP | Domicile New CUSIP | A domicile change accompanied by the assignment of a new CUSIP. |
DomicilePresentationRequired | Domicile Presentation Required | A domicile change for which presentation of securities is required. |
DomicileNewCUSIPPresentationRequired | Domicile New CUSIP Presentation Required | A domicile change with a new CUSIP for which presentation of securities is required. |
WithPayout | With Payout | Identifies an event where a fee is paid to the registered holder. DTC will not be processing as information only event. |
WithoutPayout | Without Payout | Identifies an event where no fee is paid. DTC will not be processing as information only event. |
FinalPayment | Final Payment | Identifies an event that include notification of a final payment in lieu of the original commitment. |
InterimPayment | Interim Payment | Identifies an event that include notification of an interim payment in lieu of the original commitment. |
TaxCredit | Tax Credit | Tax credit notification for informational purpose only. A tax credit will be generated in addition to a redemption and interest allocations. This tax credit can be a part of the debt instrument or can be stripped and traded separately. Commonly known in U.S. as Build America Bonds. |
Consent | Consent | Identifies an event that includes a consent fee. |
A144 | 144a | Identifies an event where the security is a 144a private placement security. |
CashAndSecurities | Cash and Securities | Identifies an event with a combination of Cash and Securities as payout. |
RegS | Reg S | Identifies an event where the security is a Regulation S type. |
Unwind | Unwind | Identifies an event where "unwinding" of the basket of securities occurs. Example: in order to participate in the tender offer of one of the underlying securities, holder must unwind the basket. DTC creates the event for the holder to respond - "unwind". Tender Offer itself would be a separate event. OR notification that UIT can be "unwinded" in IVORs (separate instruction system) with expiration dates supplied. |
Conversion | Ongoing | Identifies an event with an expiration date stipulated in the security, or a security with an open-ended expiration date up to/near the maturity date for holders to convert the security. This expiration is usually a date far into the future. |
ImportantNotice | Important Notice | Identifies an information only event of DTC Money Market Instrument (MMI) Important Notice announcements. Announcements are based on changes to the MMI security that are entered by the agent. Specific to MMI Important notices only. |
DayExemptionQualifiedNotice | Day Exemption Qualified Notice | Identifies a qualified notice issued by a publicly traded partnership stating applicability of the 10 percent exception under IRS regulation 1.1446(f)-4(b)(3). |
BasedOnRecordDateHoldings | Based on Record Date Holdings | Identifies an event processed as dividend event. |
PresentationRequired | Presentation Required | Identifies an event processed as Reorg event and may include options. |
Retain | Retain | Identifies an event that include an option to retain the event securities rather than exchange them. |
Securities | Securities | Identifies an event where the payment will be made in the form of securities. |
Annual | Annual | Identifies an annual meeting event. |
Extraordinary | Extraordinary | Identifies an extraordinary meeting event. |
General | General Meeting | Identifies a meeting event called by the company on behalf of security holders at which the company can present corporate resolutions that may require a vote by the holders. |
Special | Special | Identifies a special meeting event. |
Cash | Cash | Identifies an event with a payout of cash only. |
CUSIPChangePresentationRequired | CUSIP Change Presentation Required | Identifies a CUSIP change for which presentation of securities is required. |
NewCUSIP | New CUSIP | Identifies an event accompanied by the assignment of a new CUSIP. |
Vote | Vote | Identifies an event where a vote is sought. |
MortgageBacked | Mortgage-Backed | Indicates an event where the Issue has an early redemption feature that allows the holder to elect to sell bonds back to the issuer on a monthly basis, according to specified conditions. |
SurvivorOptions | Survivor Options | Indicates an event where the Issue has an early redemption feature. This feature allows the holder to elect to sell bonds back to the issuer on a predetermined basis (excluding monthly) according to specific priorities. |
SPAC | SPAC | Identifies a Special Purpose Acquisition Company to raise money through an initial public offering (IPO) to acquire or merge with an existing company. |
SaleOfAssets | Sale of Assets | Identifies an event where the distribution is from the proceeds of the sale of assets. |
PhysicalRightsNotIssued | Physical Rights not Issued | Identifies an event where the company is not issuing a security with the right to subscribe for additional shares. In these instances, a User CUSIP is created by DTC (as opposed to a company-issued CUSIP) in order to identify these issues. |
ADR | ADR | Identifies when the event security is an ADR, or a sale of rights where domicile restrictions require the ADR agent to sell rather than issue rights. |
PoisonPill | Poison Pill | Identifies sale of rights event where issuers redeem poison pill rights. |
CDeemedDividend | 305C - Deemed Dividend | Deemed distribution under Section 305(c) of the Internal Revenue Code. |
DividendEquivalentPayment | 871M | Dividend Equivalent Payment under Section 871(m) of the Internal Revenue Code. |
ExcessOfCumulativeNetIncome | ECNI | Identifies when a publicly traded partnership identifies the amount realised on such portion of the distribution as an amount in excess of cumulative net income under IRS regulation 1.1446(f)-4(c)(2)(iii). |
SClassifications1042 | RCLA (1042S Classifications) | Identifies distributions that have multiple components for tax withholding and 1042-S reporting purposes. |
BidTenderSealedTender | Bid Tender/Sealed Tender | Identifies a tender offer event in which the holder can choose the price at which they are willing to tender their securities. This price may or may not be accepted by the offeror. |
CashInLieu | Cash in Lieu | Identifies DTC specific sub-event where holders can elect to "sell" whole shares to satisfy fractional entitlements (usually as a result of a merger) at the beneficial owner level. |
ConvertAndTender | Convert and Tender | Identifies a tender offer event in which the holder must convert securities in order to take part in the event. |
MiniTender | Mini Tender | Identifies a tender offer event presented by a third party to shareholders of the target company, in which the conditions of the offer (e.g., quantity sought) are less than the requirements established by market regulators. As such, regulatory approval is not required. |
OfferToPurchase | Offer to Purchase | Identifies a tender offer event made by another company or the issuing company (buy back) to purchase a portion or all of the outstanding shares. |
SelfTender | Self Tender | Identifies a tender offer event made by the issuing company (buy back) to purchase a portion or all of the outstanding shares. |
GDR | GDR | Identifies when the event security is a GDR. |
MeetingType | Meeting Type | Identifies the type classification for a meeting event. |
| Value | Meaning |
|---|---|
Approved | Event is approved and final. |
ConditionallyApproved | Approved subject to conditions. |
Incomplete | Event data is not yet complete. |
Cancelled | Event was cancelled. |
Deleted | Event was deleted. |
| Value | Meaning |
|---|---|
Mandatory | Participation is automatic; no election required. |
MandatoryWithOptions | Mandatory, but participants may choose from options. |
Voluntary | Participation requires explicit election by the holder. |
| Value |
|---|
InterestOnTreasury |
MaturityOnTreasury |
MandatoryCashDistribution |
MandatorySecurityDistribution |
MandatoryWithChoiceDistribution |
MandatoryReorganization |
VoluntaryReorganization |
Meetings |
| Value |
|---|
Id |
EventId |
EventType |
Status |
SecurityName |
SecurityCusip |
PositionCaptureDate |
DeclaredPayableDate |
EarliestDTCAnticipatedPaymentDate |
EarliestDTCInstructionExpirationDate |
UpdatedAt |
DigitalAllocationDate |
| Value |
|---|
Ascending |
Descending |
| Category | Relevant Core Dates |
|---|---|
InterestOnTreasury | declaredPayableDate, allocationDateTime |
MaturityOnTreasury | declaredPayableDate, allocationDateTime |
MandatoryCashDistribution | exDate, recordDate, dtcPositionCaptureDate or captureDate, declaredPayableDate, anticipateDate, allocationDateTime |
MandatorySecurityDistribution | exDate, recordDate, positionCaptureDate, allocationDateTime |
MandatoryWithChoiceDistribution | exDate, recordDate, positionCaptureDate, instructionExpiration, allocationDateTime |
MandatoryReorganization | effectiveDateCompany, positionCaptureDate, allocationDateTime, reconversionDate |
VoluntaryReorganization | dtcInstructionExpirationDateTime, positionCaptureDate, allocationDateTime |
Meetings | meetingDate |
| Value | Meaning |
|---|---|
Mandatory | Participation is automatic; no election required. |
MandatoryWithOption | Mandatory but participants may choose from options. |
Voluntary | Participation requires explicit election by the holder. |
POST /v2/corporate-actions query, while the event type, securities, and event-detail calls are read-only GET requests. All are side-effect free and can be retried on network or service failures. The DTCC APIs provides event listing, taxonomy, securities reference data, and event details.This certification verifies both API connectivity and the customer's ability to correctly interpret corporate action event data.
Authenticate, load events, load event types and securities in parallel, inspect the event, then apply context.
Obtain a JWT access credential using SA-CorporateActionsBot.
POST /v2/corporate-actions
event-types plus securities in parallel.
GET /v2/corporate-actions/{id}
Status, category, DTC classification, and core dates.
GET /v2/corporate-actions/{id}. No additional entitlement endpoint is required.| Rule | Requirement |
|---|---|
| Parallel Loading | Retrieve events, event types, and securities concurrently. |
| Supported Statuses | Approved, ConditionallyApproved, Incomplete, Cancelled, Deleted. |
| Detail Retrieval | All detail information is retrieved from GET /v2/corporate-actions/{id}. |
| Context Interpretation | Customers must correctly apply status, category, DTC mandatory classification, support indicator, and core dates. |
Tenant ID, service account, service account password, base URL, a known corporate action event, and securities access. Required endpoints: /connect/token, POST /v2/corporate-actions, /v2/corporate-actions/event-types, /v1/conversion/securities, /v2/corporate-actions/{id}.
const tenantId = "<tenant-id>";
const baseUrl = "<base-url>";
const username = "SA-CorporateActionsBot";
const password = process.env.SERVICE_ACCOUNT_PASSWORD!;async function loadCorporateActionsData(accessToken: string) {
const [eventsResponse, eventTypesResponse, securitiesResponse] = await Promise.all([
fetch(`${baseUrl}/v2/corporate-actions`, {
method: "POST",
headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
body: JSON.stringify({ offset: 0, count: 25 })
}),
fetch(`${baseUrl}/v2/corporate-actions/event-types`, { headers: { Authorization: `Bearer ${accessToken}` } }),
fetch(`${baseUrl}/v1/conversion/securities`, { headers: { Authorization: `Bearer ${accessToken}` } })
]);
return {
events: await eventsResponse.json(),
eventTypes: await eventTypesResponse.json(),
securities: await securitiesResponse.json()
};
}
async function getEventDetail(accessToken: string, eventId: string) {
const response = await fetch(`${baseUrl}/v2/corporate-actions/${eventId}`, {
headers: { Authorization: `Bearer ${accessToken}` }
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Event detail failed: ${error.ErrorCode} - ${error.ErrorMessage}`);
}
return response.json();
}This step validates that the integration can obtain a JWT access credential from the Identity Server using service account credentials. The JWT access credential proves the caller's identity and must be supplied as a bearer credential on every subsequent API call in this workflow.
HTTP 200; JWT access credential returned; credential accepted by downstream APIs.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid credentials | Surface authentication rejected. Do not retry with the same credentials. |
| 500, 503 | Identity Server failure | Retry with exponential backoff, maximum three attempts. |
Verify the integration can retrieve a paginated corporate action event inventory with statuses and event identifiers populated.
const data = await loadCorporateActionsData(accessToken);Events returned; pagination supported; statuses populated; event identifiers returned.
HTTP 200; corporate action inventory loaded successfully.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid filter parameters | Correct the request. Do not retry as-is. |
| 401 | Expired or invalid JWT access credential | Re-authenticate, then retry once. |
| 403 | Missing permission | Surface access denied. Do not retry. |
| 500, 503 | Service failure | Retry with exponential backoff, maximum three attempts. |
Verify the integration can retrieve the event type and sub-event type taxonomy used for hierarchical event filtering.
Event types returned; sub-event types returned; hierarchy available.
HTTP 200; event taxonomy successfully loaded.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 401 | Expired or invalid JWT access credential | Re-authenticate, then retry once. |
| 500, 503 | Event-type service unavailable | Filter data unavailable; display a meaningful error and keep the workflow stable. |
Verify the integration can retrieve securities reference data, including CUSIPs and support flags, for filtering corporate action events.
Security identifiers returned; CUSIPs returned; support flags returned.
HTTP 200; security filtering possible.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 401 | Expired or invalid JWT access credential | Re-authenticate, then retry once. |
| 500, 503 | Securities service unavailable | Security filter unavailable; display a meaningful error and keep the workflow stable. |
Verify the integration can narrow the event inventory to a relevant subset using status, event type, sub-event type, security, and search text.
const approvedEvents = events.filter(
event => event.status === "Approved"
);Successful filtering by status, event type, sub-event type, security, and search text.
Relevant events identified.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid filter parameters | Validation error. Correct the filter values before retrying. |
Verify the integration can retrieve full event detail from the single detail endpoint, including security, category, status, classification, and core dates.
const detail = await getEventDetail(accessToken, eventId);Security returned; category returned; status returned; DTC classification returned; core dates returned.
HTTP 200; event detail successfully retrieved.
| HTTP | Condition | Expected integration behavior |
|---|---|---|
| 400 | Invalid event identifier format | Correct the request. Do not retry as-is. |
| 404 | Invalid event ID; event not found | Surface event not found. Do not retry. |
| 500, 503 | Event detail service failure | Invoke retry logic with exponential backoff and handle the failure gracefully. |
Verify the integration derives correct business context from the returned category, status, support indicator, and DTC classification.
if (detail.dtcMandatory === "Voluntary") {
console.log("Election Required");
}Interpret category, status, support flag, and DTC classification.
Business context successfully derived.
This step performs no API calls. If a required context field is absent from the event detail response, treat the step as failed rather than applying a default classification.
Verify the integration identifies the correct set of lifecycle dates for each corporate action category it processes.
| Category | Required Dates |
|---|---|
InterestOnTreasury | declaredPayableDate, allocationDateTime |
MaturityOnTreasury | declaredPayableDate, allocationDateTime |
MandatoryCashDistribution | exDate, recordDate, captureDate, declaredPayableDate, anticipateDate, allocationDateTime |
Meetings | meetingDate |
Correctly identify the relevant dates for at least one event available in each category.
The correct set of lifecycle dates is identified for each category present in the test dataset.
This step performs no API calls. If a required date is null for a category that mandates it, surface the gap rather than substituting a placeholder date.
Verify the integration correctly classifies each event as Mandatory, MandatoryWithOption, or Voluntary and acts on whether an election is required.
| Value | Meaning |
|---|---|
Mandatory | Participation automatic. |
MandatoryWithOption | Options available to holder. |
Voluntary | Election required. |
The customer correctly classifies events using the returned values.
Each event is classified as Mandatory, MandatoryWithOption, or Voluntary, and election requirements are applied accordingly.
This step performs no API calls. An unrecognized dtcMandatory value must be surfaced as an unhandled classification rather than defaulted to Mandatory.
Execute the complete corporate actions workflow in a single run to confirm every stage succeeds in sequence.
async function executeWorkflow() {
const token = await authenticate();
const { events, eventTypes, securities } = await loadCorporateActionsData(token);
const selectedEvent = events[0];
const detail = await getEventDetail(token, selectedEvent.id);
return { events, eventTypes, securities, detail };
}Authentication PASS
Load Events PASS
Load Event Types PASS
Load Securities PASS
Filter Events PASS
Retrieve Detail PASS
Apply Context PASS
Workflow Complete PASSEach constituent call returns its expected success code.
| ID | Scenario | HTTP | Expected result |
|---|---|---|---|
| NT-1 | Invalid Credentials | 400 | Authentication rejected. |
| NT-2 | Expired Credential | 401 | Re-authentication required. |
| NT-3 | Invalid Event ID | 404 | Event not found. |
| NT-4 | Missing Permission | 403 | Access denied. |
| NT-5 | Invalid Filter Parameters | 400 | Validation error. |
| NT-6 | Event-Type Service Unavailable | 500, 503 | Filter data unavailable; meaningful error displayed; workflow remains stable. |
| NT-7 | Securities Service Unavailable | 500, 503 | Security filter unavailable; meaningful error displayed; workflow remains stable. |
| NT-8 | Event Detail Service Failure | 500, 503 | Retry logic invoked; graceful failure handling. |
The customer must demonstrate authentication, event retrieval, event-type retrieval, securities retrieval, event filtering, event-detail retrieval, context interpretation, category-date interpretation, DTC classification interpretation, negative-test handling, and end-to-end workflow execution.
This tab records every change applied to this reference guide. Each version card carries the date the change was applied, and lists the changes grouped by the tab or section of the site affected.
grant_type=urn:dtcc:params:oauth:grant-type:service-account-credentials, type=conveyance, entity context, and acr_values=tenant:{tenantId}.username / password to state that service-account credentials are always used.authenticate() call against /connect/token using the Service Account Credentials grant, consistent with the certification helpers.clientId / clientSecret / scope test data, rewrote Step 5 to reuse the Step 1 token via getBalances(accessToken, accountId), reworded the Step 5 error envelopes and negative tests (NT-1, NT-6, NT-7) away from client-credential terminology, and changed the production criteria from "invalid scope" to "insufficient permission".clientId / clientSecret / CONVERSION_SCOPE test data with tenantId / username / password; rewrote the authenticate() helper to use the service-account grant; and reworded Step 1, Step 2, Step 5, and negative test NT-1 away from client-credential terminology.status input query filter to the List Registered Securities and Filter Data card. Documented it in the Request Parameters table as an optional single-value filter accepting one FinancialSecurityStatus value (Onboarding, Active, Pause, Failed), and added a matching Input (query) row to the GET /v1/conversion/securities Enumerations table.authenticate() helper with the Account and Wallet certification helpers so it returns data.access_token directly rather than the full token object, and simplified the Step 1 call site to const accessToken = await authenticate();.EventTypeEnumeration and SubEventTypeEnumeration as value / description tables, collapsed behind a show-all disclosure. Numeric codes are not published in the reference because the OpenAPI enums are declared as plain string enums; the authoritative code mapping is whatever GET /v2/corporate-actions/event-types returns at runtime.EventStatusEnumeration, DtcMandatory, EventCategory, EventFields, SortOrder) once in the same reference and linked to them from the endpoint tables.type / subType pairings are enumerated by GET /v2/corporate-actions/event-types.GET /v1/corporate-actions to POST /v2/corporate-actions. The filter and pagination fields are now supplied in a JSON request body (EventFilterRequest) rather than query-string parameters.EventFilterRequest schema: offset and count are required; ids filters by internal (GUID) event identifiers and eventIds by external DTC event identifiers; eventTypes is an array of EventTypeFilterItem objects (each with a type and optional subType); added securityIds and securityCusips; retained status, searchFor, orderBy, and sortOrder as body fields.POST, updated the path header, added an endpoint-migration callout, moved all filter fields to the request body, relabeled the Enumerations table to POST /v2/corporate-actions with Input (body) directions (including the EventFields values for orderBy and the nested eventTypes[].type / eventTypes[].subType filter), and rewrote the sample request to issue a POST with a JSON body.CorporateActionEventResponse schema, using type, typeDescription, subType, subTypeDescription, the DTC date fields, dtcProcessingIndicator, and the SecurityListResponse security shape.POST query while the event-types and securities calls remain GET requests.loadCorporateActionsData helper, and Step 2 now call POST /v2/corporate-actions with a JSON body./v2/corporate-actions in place of /v1/corporate-actions.POST /v2/corporate-actions in place of the deprecated GET /v1/corporate-actions.POST /v2/corporate-actions, noting that Offset and Count are sent in the request body rather than the query string.idempotencyKeys array on POST /v1/conversion/async-operations/query. Clarified that the server processes all keys supplied in a single query, and established a best practice of submitting no more than 100 keys per request to avoid the performance impact of very large arrays.Count and Offset, and the recommended batch size.fromTransactionId and toTransactionId fields on the Submit and Track Conversion Orders card. Added a Transaction Identifiers section defining each field as the on-chain ledger transaction hash for the source (debit) and destination (credit) legs, noting they are null while an order is Processing and populated once the order reaches Completed.GET /v1/conversion/orders/{orderId} and not on the orders list endpoint.Completed order with populated fromTransactionId and toTransactionId hashes.activityType input filter and the output values.activityType row (ActivityType enumeration) to the Get Account Activities Enumerations table, covering Payment, PaymentReceived, Fee, Mint, Burn, Pause, Unpause, Freeze, Unfreeze, Revoked, Clawedback, and Clawback.ConversionOperationsEngine__WalletNotFound error code from the HTTP 404 table to the HTTP 400 table in the Error Code Reference./v1/tracking/networks endpoint from the Primary API Journeys table for the Wallet Management and Conversion Orders rows./v1/tracking/networks ledger-networks endpoint. Renamed the combined "Load Ledger and Account Reference Data" endpoint to "Load Account Reference Data", now covering only /v1/conversion/clients, and updated the workflow summary and certification test (flow, prerequisites, helper functions, steps, and end-to-end execution) to load account reference data only./v1/tracking/networks from the List Registered Securities and Filter Data endpoint, its workflow summary, enumerations, sample request, and certification filter-data helper. The network query filter on the securities list is retained./v1/tracking/networks from the API Families modal and the Endpoint Catalog.errorCode values, causes, and corrections for HTTP 400, 404, 409, and 500.Active status but may still have a chill or lock in effect, that a chill or lock is a transfer restriction applied to the entire security, and how to read the restrictions object to detect it.dOChill, globalLock, and complianceLock, each reported with an enabled flag and an updatedAt timestamp, and noted that the enabled flag should always be tested rather than checking whether a field exists.restrictions block to the securities response template and the SecurityRestrictions and RestrictionState types to the TypeScript interface.Active status is necessary but not sufficient, and that an Active security must be confirmed unrestricted before it is treated as usable.string UUID type with string on the account hierarchy summary and account activities parameter tables.string UUID type with string on the Submit and Track Conversion Orders parameter table./v1/networks/ledgers to /v1/tracking/networks to match the API specification.name, displayName, networkStatus, nodeFormats, and listenerEnabled, and fixed the sample request filter accordingly.status to networkStatus and removed the nodeStatus / NetworkNodeStatus row, which the specification does not define.Declined from the lifecycle states, leaving Processing, Completed, and Failed.queryOperation and getOrder helper functions, which the source document referenced without defining, and changed the correlation step to query by idempotency key rather than by an operation identifier the workflow never produces.loadFilterData and extended Step 2 to call it, so the issue type, issue sub-type, and ledger endpoints remain within the certification.Offset and Count on the orders list, the securities list, the wallets list, and the async operation query.type on the account balances card.limit and offset unchanged on the account hierarchy summary and account activities cards.type and state, matching the API integration guide, and removed the note that recorded the earlier naming discrepancy.Offset and Count.offset and count unchanged.Deleting from the ProcessingStates values in all five places it appeared, covering both wallet and security rows so the enumeration keeps a single definition.X-Idempotency-Key throughout.COE_SCOPE environment variable reference with API_SCOPE.Offset and Count query parameters, the X-Paging response headers, endpoint support, and iteration guidance.Offset and Count in the request body rather than the query string, an exception to the model's general rule.POST /v1/conversion/orders returns 202 Accepted with no response body, so the order identifier is resolved by polling the async operation with the idempotency key retained at submission.GET /v1/conversion/orders, POST /v1/conversion/async-operations/query, and GET /v1/conversion/orders/{orderId}.asyncOperationStatus, objectKind, and operationName to the async operation query, and orderBy to the orders list.OrderOrigin enumeration with values Participant, Admin, and ForcedReconversion.conversionEligible true in addition to an Idle processing state, applied across the eligibility rules, the workflow summary, the key business rules, and the production certification criteria.resourceId before retrieving the order. Remaining certification steps are being regenerated.Failed to the FinancialSecurityStatus values published on the account balances card.FailedCompliance and Failed to the ProcessingStates values.type and state, while the guide follows the enumeration reference document naming.FinancialSecurityStatus and ProcessingStates value additions into the securities reference enumerations on the events card./v1/tracking/networks and /v1/conversion/clients.