From e433f00defdd3fb0e50e589ce2bae1e9151ed502 Mon Sep 17 00:00:00 2001 From: shreyav Date: Thu, 13 Aug 2026 21:14:44 +0000 Subject: [PATCH 1/4] feat: add custody type and VASP link to external accounts --- .stainless/stainless.yml | 1 + mintlify/openapi.yaml | 21 +++++++++++++++++++ openapi.yaml | 21 +++++++++++++++++++ .../components/schemas/errors/Error400.yaml | 2 ++ .../external_accounts/CustodyType.yaml | 12 +++++++++++ .../external_accounts/ExternalAccount.yaml | 8 +++++++ .../ExternalAccountCreateRequest.yaml | 9 ++++++++ 7 files changed, 74 insertions(+) create mode 100644 openapi/components/schemas/external_accounts/CustodyType.yaml diff --git a/.stainless/stainless.yml b/.stainless/stainless.yml index 49100e04c..d5e8591e3 100644 --- a/.stainless/stainless.yml +++ b/.stainless/stainless.yml @@ -209,6 +209,7 @@ resources: external_account: '#/components/schemas/ExternalAccount' external_account_create: '#/components/schemas/ExternalAccountCreateRequest' external_account_info_one_of: "#/components/schemas/ExternalAccountInfoOneOf" + custody_type: "#/components/schemas/CustodyType" business_beneficiary: "#/components/schemas/BusinessBeneficiary" address: "#/components/schemas/Address" beneficiary_verified_data: "#/components/schemas/BeneficiaryVerifiedData" diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 6e4523f11..80a9de2c3 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -11835,6 +11835,7 @@ components: | STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED | Multiple active provider account links exist; pass `stablecoinProviderAccountId` to select one | | CARDHOLDER_KYC_NOT_APPROVED | The cardholder's KYC status is not `APPROVED`, so a card cannot be issued | | TRANSACTION_SIZE_LIMIT_EXCEEDED | The requested amount exceeds the configured maximum single-transaction amount for this trade corridor or withdrawal currency | + | COUNTERPARTY_USER_INFO_REQUIRED | The destination account needs beneficiary information before this transfer can proceed; update the external account with a `beneficiary` and retry | enum: - INVALID_INPUT - END_USER_TERMS_VERSION_NOT_FOUND @@ -11878,6 +11879,7 @@ components: - STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED - CARDHOLDER_KYC_NOT_APPROVED - TRANSACTION_SIZE_LIMIT_EXCEEDED + - COUNTERPARTY_USER_INFO_REQUIRED message: type: string description: Error message @@ -16858,6 +16860,13 @@ components: - THIRD_PARTY description: Whether the external account belongs to the customer themselves (first party) or to someone else (third party) example: FIRST_PARTY + CustodyType: + type: string + enum: + - SELF_CUSTODY + - VASP_HOSTED + description: 'How a crypto wallet external account is custodied: `SELF_CUSTODY` for a wallet whose keys the owner controls directly, or `VASP_HOSTED` for an account held at a Virtual Asset Service Provider (an exchange or other custodial platform). Only relevant for crypto wallet accounts; defaults to `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` wallets — `VASP_HOSTED` accounts never require it.' + example: SELF_CUSTODY BeneficiaryVerificationStatus: type: string enum: @@ -19230,6 +19239,12 @@ components: example: acc_123456789 ownershipType: $ref: '#/components/schemas/OwnershipType' + custodyType: + $ref: '#/components/schemas/CustodyType' + vaspId: + type: string + description: The VASP that custodies this account, from the VASP directory (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. + example: Vasp:019542f5-b3e7-1d02-0000-000000000001 currency: type: string description: The ISO 4217 currency code @@ -20144,6 +20159,12 @@ components: example: ext_acc_123456 ownershipType: $ref: '#/components/schemas/OwnershipType' + custodyType: + $ref: '#/components/schemas/CustodyType' + vaspId: + type: string + description: The VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. + example: Vasp:019542f5-b3e7-1d02-0000-000000000001 defaultUmaDepositAccount: type: boolean description: Whether to set the external account as the default UMA deposit account. When set to true, incoming payments to this customer's UMA address will be automatically deposited into this external account. False if not provided. Note that only one external account can be set as the default UMA deposit account for a customer, so if there is already a default UMA deposit account, this will override the existing default UMA deposit account. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. diff --git a/openapi.yaml b/openapi.yaml index 6e4523f11..80a9de2c3 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -11835,6 +11835,7 @@ components: | STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED | Multiple active provider account links exist; pass `stablecoinProviderAccountId` to select one | | CARDHOLDER_KYC_NOT_APPROVED | The cardholder's KYC status is not `APPROVED`, so a card cannot be issued | | TRANSACTION_SIZE_LIMIT_EXCEEDED | The requested amount exceeds the configured maximum single-transaction amount for this trade corridor or withdrawal currency | + | COUNTERPARTY_USER_INFO_REQUIRED | The destination account needs beneficiary information before this transfer can proceed; update the external account with a `beneficiary` and retry | enum: - INVALID_INPUT - END_USER_TERMS_VERSION_NOT_FOUND @@ -11878,6 +11879,7 @@ components: - STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED - CARDHOLDER_KYC_NOT_APPROVED - TRANSACTION_SIZE_LIMIT_EXCEEDED + - COUNTERPARTY_USER_INFO_REQUIRED message: type: string description: Error message @@ -16858,6 +16860,13 @@ components: - THIRD_PARTY description: Whether the external account belongs to the customer themselves (first party) or to someone else (third party) example: FIRST_PARTY + CustodyType: + type: string + enum: + - SELF_CUSTODY + - VASP_HOSTED + description: 'How a crypto wallet external account is custodied: `SELF_CUSTODY` for a wallet whose keys the owner controls directly, or `VASP_HOSTED` for an account held at a Virtual Asset Service Provider (an exchange or other custodial platform). Only relevant for crypto wallet accounts; defaults to `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` wallets — `VASP_HOSTED` accounts never require it.' + example: SELF_CUSTODY BeneficiaryVerificationStatus: type: string enum: @@ -19230,6 +19239,12 @@ components: example: acc_123456789 ownershipType: $ref: '#/components/schemas/OwnershipType' + custodyType: + $ref: '#/components/schemas/CustodyType' + vaspId: + type: string + description: The VASP that custodies this account, from the VASP directory (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. + example: Vasp:019542f5-b3e7-1d02-0000-000000000001 currency: type: string description: The ISO 4217 currency code @@ -20144,6 +20159,12 @@ components: example: ext_acc_123456 ownershipType: $ref: '#/components/schemas/OwnershipType' + custodyType: + $ref: '#/components/schemas/CustodyType' + vaspId: + type: string + description: The VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. + example: Vasp:019542f5-b3e7-1d02-0000-000000000001 defaultUmaDepositAccount: type: boolean description: Whether to set the external account as the default UMA deposit account. When set to true, incoming payments to this customer's UMA address will be automatically deposited into this external account. False if not provided. Note that only one external account can be set as the default UMA deposit account for a customer, so if there is already a default UMA deposit account, this will override the existing default UMA deposit account. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. diff --git a/openapi/components/schemas/errors/Error400.yaml b/openapi/components/schemas/errors/Error400.yaml index bbb12f63f..28edc6ec5 100644 --- a/openapi/components/schemas/errors/Error400.yaml +++ b/openapi/components/schemas/errors/Error400.yaml @@ -56,6 +56,7 @@ properties: | STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED | Multiple active provider account links exist; pass `stablecoinProviderAccountId` to select one | | CARDHOLDER_KYC_NOT_APPROVED | The cardholder's KYC status is not `APPROVED`, so a card cannot be issued | | TRANSACTION_SIZE_LIMIT_EXCEEDED | The requested amount exceeds the configured maximum single-transaction amount for this trade corridor or withdrawal currency | + | COUNTERPARTY_USER_INFO_REQUIRED | The destination account needs beneficiary information before this transfer can proceed; update the external account with a `beneficiary` and retry | enum: - INVALID_INPUT - END_USER_TERMS_VERSION_NOT_FOUND @@ -99,6 +100,7 @@ properties: - STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED - CARDHOLDER_KYC_NOT_APPROVED - TRANSACTION_SIZE_LIMIT_EXCEEDED + - COUNTERPARTY_USER_INFO_REQUIRED message: type: string description: Error message diff --git a/openapi/components/schemas/external_accounts/CustodyType.yaml b/openapi/components/schemas/external_accounts/CustodyType.yaml new file mode 100644 index 000000000..8197cd502 --- /dev/null +++ b/openapi/components/schemas/external_accounts/CustodyType.yaml @@ -0,0 +1,12 @@ +type: string +enum: + - SELF_CUSTODY + - VASP_HOSTED +description: >- + How a crypto wallet external account is custodied: `SELF_CUSTODY` for a + wallet whose keys the owner controls directly, or `VASP_HOSTED` for an + account held at a Virtual Asset Service Provider (an exchange or other + custodial platform). Only relevant for crypto wallet accounts; defaults to + `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` + wallets — `VASP_HOSTED` accounts never require it. +example: SELF_CUSTODY diff --git a/openapi/components/schemas/external_accounts/ExternalAccount.yaml b/openapi/components/schemas/external_accounts/ExternalAccount.yaml index 08449b833..e9568235f 100644 --- a/openapi/components/schemas/external_accounts/ExternalAccount.yaml +++ b/openapi/components/schemas/external_accounts/ExternalAccount.yaml @@ -24,6 +24,14 @@ allOf: example: acc_123456789 ownershipType: $ref: ./OwnershipType.yaml + custodyType: + $ref: ./CustodyType.yaml + vaspId: + type: string + description: >- + The VASP that custodies this account, from the VASP directory + (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. + example: Vasp:019542f5-b3e7-1d02-0000-000000000001 currency: type: string description: The ISO 4217 currency code diff --git a/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml b/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml index b97ace94c..e13540852 100644 --- a/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml +++ b/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml @@ -20,6 +20,15 @@ allOf: example: ext_acc_123456 ownershipType: $ref: ./OwnershipType.yaml + custodyType: + $ref: ./CustodyType.yaml + vaspId: + type: string + description: >- + The VASP that custodies this account, from the VASP directory + (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; + must be omitted otherwise. + example: Vasp:019542f5-b3e7-1d02-0000-000000000001 defaultUmaDepositAccount: type: boolean description: >- From ccff0defc054948d5b5137c9b574e3300a9cead9 Mon Sep 17 00:00:00 2001 From: shreyav Date: Thu, 13 Aug 2026 21:37:27 +0000 Subject: [PATCH 2/4] Identify custodying VASP by canonical name, matching the discovery pattern --- mintlify/openapi.yaml | 12 ++++++------ openapi.yaml | 12 ++++++------ .../schemas/external_accounts/ExternalAccount.yaml | 9 +++++---- .../ExternalAccountCreateRequest.yaml | 10 +++++----- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 80a9de2c3..e0daf069a 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -19241,10 +19241,10 @@ components: $ref: '#/components/schemas/OwnershipType' custodyType: $ref: '#/components/schemas/CustodyType' - vaspId: + vaspName: type: string - description: The VASP that custodies this account, from the VASP directory (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. - example: Vasp:019542f5-b3e7-1d02-0000-000000000001 + description: Canonical name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. + example: Kraken currency: type: string description: The ISO 4217 currency code @@ -20161,10 +20161,10 @@ components: $ref: '#/components/schemas/OwnershipType' custodyType: $ref: '#/components/schemas/CustodyType' - vaspId: + vaspName: type: string - description: The VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. - example: Vasp:019542f5-b3e7-1d02-0000-000000000001 + description: Canonical name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. + example: Kraken defaultUmaDepositAccount: type: boolean description: Whether to set the external account as the default UMA deposit account. When set to true, incoming payments to this customer's UMA address will be automatically deposited into this external account. False if not provided. Note that only one external account can be set as the default UMA deposit account for a customer, so if there is already a default UMA deposit account, this will override the existing default UMA deposit account. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. diff --git a/openapi.yaml b/openapi.yaml index 80a9de2c3..e0daf069a 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -19241,10 +19241,10 @@ components: $ref: '#/components/schemas/OwnershipType' custodyType: $ref: '#/components/schemas/CustodyType' - vaspId: + vaspName: type: string - description: The VASP that custodies this account, from the VASP directory (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. - example: Vasp:019542f5-b3e7-1d02-0000-000000000001 + description: Canonical name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. + example: Kraken currency: type: string description: The ISO 4217 currency code @@ -20161,10 +20161,10 @@ components: $ref: '#/components/schemas/OwnershipType' custodyType: $ref: '#/components/schemas/CustodyType' - vaspId: + vaspName: type: string - description: The VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. - example: Vasp:019542f5-b3e7-1d02-0000-000000000001 + description: Canonical name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. + example: Kraken defaultUmaDepositAccount: type: boolean description: Whether to set the external account as the default UMA deposit account. When set to true, incoming payments to this customer's UMA address will be automatically deposited into this external account. False if not provided. Note that only one external account can be set as the default UMA deposit account for a customer, so if there is already a default UMA deposit account, this will override the existing default UMA deposit account. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. diff --git a/openapi/components/schemas/external_accounts/ExternalAccount.yaml b/openapi/components/schemas/external_accounts/ExternalAccount.yaml index e9568235f..1410d9290 100644 --- a/openapi/components/schemas/external_accounts/ExternalAccount.yaml +++ b/openapi/components/schemas/external_accounts/ExternalAccount.yaml @@ -26,12 +26,13 @@ allOf: $ref: ./OwnershipType.yaml custodyType: $ref: ./CustodyType.yaml - vaspId: + vaspName: type: string description: >- - The VASP that custodies this account, from the VASP directory - (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. - example: Vasp:019542f5-b3e7-1d02-0000-000000000001 + Canonical name of the VASP that custodies this account, from the + VASP directory (`GET /vasps`). Only present when `custodyType` is + `VASP_HOSTED`. + example: Kraken currency: type: string description: The ISO 4217 currency code diff --git a/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml b/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml index e13540852..407921758 100644 --- a/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml +++ b/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml @@ -22,13 +22,13 @@ allOf: $ref: ./OwnershipType.yaml custodyType: $ref: ./CustodyType.yaml - vaspId: + vaspName: type: string description: >- - The VASP that custodies this account, from the VASP directory - (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; - must be omitted otherwise. - example: Vasp:019542f5-b3e7-1d02-0000-000000000001 + Canonical name of the VASP that custodies this account, from the + VASP directory (`GET /vasps`). Required when `custodyType` is + `VASP_HOSTED`; must be omitted otherwise. + example: Kraken defaultUmaDepositAccount: type: boolean description: >- From eec9000b3019ebc502ab1b3d75f85e6cdf3bae4d Mon Sep 17 00:00:00 2001 From: shreyav Date: Thu, 20 Aug 2026 00:18:33 +0000 Subject: [PATCH 3/4] Scope custodyType and vaspName to crypto wallet accounts They were on the shared account schemas, where they applied to 38 fiat variants and Lightning. Moves them into a WalletCustodyFields fragment composed into the seven wallet variants, matching how the wallet beneficiary is scoped. --- .stainless/stainless.yml | 1 + mintlify/openapi.yaml | 42 ++++++++++--------- openapi.yaml | 42 ++++++++++--------- .../BaseWalletExternalAccountInfo.yaml | 3 +- .../EthereumWalletExternalAccountInfo.yaml | 1 + .../external_accounts/ExternalAccount.yaml | 9 ---- .../ExternalAccountCreateRequest.yaml | 9 ---- .../PlasmaWalletExternalAccountInfo.yaml | 1 + .../PolygonWalletExternalAccountInfo.yaml | 3 +- .../SolanaWalletExternalAccountInfo.yaml | 3 +- .../SparkWalletExternalAccountInfo.yaml | 3 +- .../TronWalletExternalAccountInfo.yaml | 3 +- .../WalletCustodyFields.yaml | 11 +++++ 13 files changed, 70 insertions(+), 61 deletions(-) create mode 100644 openapi/components/schemas/external_accounts/WalletCustodyFields.yaml diff --git a/.stainless/stainless.yml b/.stainless/stainless.yml index ac980ca97..d40bbe16a 100644 --- a/.stainless/stainless.yml +++ b/.stainless/stainless.yml @@ -210,6 +210,7 @@ resources: external_account_create: '#/components/schemas/ExternalAccountCreateRequest' external_account_info_one_of: "#/components/schemas/ExternalAccountInfoOneOf" custody_type: "#/components/schemas/CustodyType" + wallet_custody_fields: "#/components/schemas/WalletCustodyFields" business_beneficiary: "#/components/schemas/BusinessBeneficiary" # Ownership verification (challenge/verify) ownership_verification_method: "#/components/schemas/OwnershipVerificationMethod" diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index e2107ba81..8b31c3c45 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -17250,13 +17250,6 @@ components: - THIRD_PARTY description: Whether the external account belongs to the customer themselves (`FIRST_PARTY`) or to someone else (`THIRD_PARTY`). Required when creating self-custody crypto wallet external accounts on platforms subject to counterparty requirements — for example, under the EU Travel Rule or similar requirements in other regions; recommended for all other accounts, where providing it can unlock additional capabilities and smoother compliance handling. example: FIRST_PARTY - CustodyType: - type: string - enum: - - SELF_CUSTODY - - VASP_HOSTED - description: 'How a crypto wallet external account is custodied: `SELF_CUSTODY` for a wallet whose keys the owner controls directly, or `VASP_HOSTED` for an account held at a Virtual Asset Service Provider (an exchange or other custodial platform). Only relevant for crypto wallet accounts; defaults to `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` wallets — `VASP_HOSTED` accounts never require it.' - example: SELF_CUSTODY BeneficiaryVerificationStatus: type: string enum: @@ -19390,16 +19383,34 @@ components: mapping: INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + CustodyType: + type: string + enum: + - SELF_CUSTODY + - VASP_HOSTED + description: 'How a crypto wallet external account is custodied: `SELF_CUSTODY` for a wallet whose keys the owner controls directly, or `VASP_HOSTED` for an account held at a Virtual Asset Service Provider (an exchange or other custodial platform). Only relevant for crypto wallet accounts; defaults to `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` wallets — `VASP_HOSTED` accounts never require it.' + example: SELF_CUSTODY + WalletCustodyFields: + type: object + properties: + custodyType: + $ref: '#/components/schemas/CustodyType' + vaspName: + type: string + description: Name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. + example: Kraken BaseWalletExternalAccountInfo: title: Base Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/BaseWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' EthereumWalletExternalAccountInfo: title: Ethereum L1 Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/EthereumWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' LightningInfo: type: object description: | @@ -19433,26 +19444,31 @@ components: allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/PolygonWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' PlasmaWalletExternalAccountInfo: title: Plasma Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/PlasmaWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' SolanaWalletExternalAccountInfo: title: Solana Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' SparkWalletExternalAccountInfo: title: Spark Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/SparkWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' TronWalletExternalAccountInfo: title: Tron Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/TronWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' CnyBeneficiary: title: Individual Beneficiary type: object @@ -19629,12 +19645,6 @@ components: example: acc_123456789 ownershipType: $ref: '#/components/schemas/OwnershipType' - custodyType: - $ref: '#/components/schemas/CustodyType' - vaspName: - type: string - description: Canonical name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. - example: Kraken currency: type: string description: The ISO 4217 currency code @@ -20549,12 +20559,6 @@ components: example: ext_acc_123456 ownershipType: $ref: '#/components/schemas/OwnershipType' - custodyType: - $ref: '#/components/schemas/CustodyType' - vaspName: - type: string - description: Canonical name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. - example: Kraken defaultUmaDepositAccount: type: boolean description: Whether to set the external account as the default UMA deposit account. When set to true, incoming payments to this customer's UMA address will be automatically deposited into this external account. False if not provided. Note that only one external account can be set as the default UMA deposit account for a customer, so if there is already a default UMA deposit account, this will override the existing default UMA deposit account. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. diff --git a/openapi.yaml b/openapi.yaml index e2107ba81..8b31c3c45 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -17250,13 +17250,6 @@ components: - THIRD_PARTY description: Whether the external account belongs to the customer themselves (`FIRST_PARTY`) or to someone else (`THIRD_PARTY`). Required when creating self-custody crypto wallet external accounts on platforms subject to counterparty requirements — for example, under the EU Travel Rule or similar requirements in other regions; recommended for all other accounts, where providing it can unlock additional capabilities and smoother compliance handling. example: FIRST_PARTY - CustodyType: - type: string - enum: - - SELF_CUSTODY - - VASP_HOSTED - description: 'How a crypto wallet external account is custodied: `SELF_CUSTODY` for a wallet whose keys the owner controls directly, or `VASP_HOSTED` for an account held at a Virtual Asset Service Provider (an exchange or other custodial platform). Only relevant for crypto wallet accounts; defaults to `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` wallets — `VASP_HOSTED` accounts never require it.' - example: SELF_CUSTODY BeneficiaryVerificationStatus: type: string enum: @@ -19390,16 +19383,34 @@ components: mapping: INDIVIDUAL: '#/components/schemas/SwiftBeneficiary' BUSINESS: '#/components/schemas/BusinessBeneficiary' + CustodyType: + type: string + enum: + - SELF_CUSTODY + - VASP_HOSTED + description: 'How a crypto wallet external account is custodied: `SELF_CUSTODY` for a wallet whose keys the owner controls directly, or `VASP_HOSTED` for an account held at a Virtual Asset Service Provider (an exchange or other custodial platform). Only relevant for crypto wallet accounts; defaults to `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` wallets — `VASP_HOSTED` accounts never require it.' + example: SELF_CUSTODY + WalletCustodyFields: + type: object + properties: + custodyType: + $ref: '#/components/schemas/CustodyType' + vaspName: + type: string + description: Name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. + example: Kraken BaseWalletExternalAccountInfo: title: Base Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/BaseWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' EthereumWalletExternalAccountInfo: title: Ethereum L1 Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/EthereumWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' LightningInfo: type: object description: | @@ -19433,26 +19444,31 @@ components: allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/PolygonWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' PlasmaWalletExternalAccountInfo: title: Plasma Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/PlasmaWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' SolanaWalletExternalAccountInfo: title: Solana Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/SolanaWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' SparkWalletExternalAccountInfo: title: Spark Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/SparkWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' TronWalletExternalAccountInfo: title: Tron Wallet allOf: - $ref: '#/components/schemas/BaseExternalAccountInfo' - $ref: '#/components/schemas/TronWalletInfo' + - $ref: '#/components/schemas/WalletCustodyFields' CnyBeneficiary: title: Individual Beneficiary type: object @@ -19629,12 +19645,6 @@ components: example: acc_123456789 ownershipType: $ref: '#/components/schemas/OwnershipType' - custodyType: - $ref: '#/components/schemas/CustodyType' - vaspName: - type: string - description: Canonical name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Only present when `custodyType` is `VASP_HOSTED`. - example: Kraken currency: type: string description: The ISO 4217 currency code @@ -20549,12 +20559,6 @@ components: example: ext_acc_123456 ownershipType: $ref: '#/components/schemas/OwnershipType' - custodyType: - $ref: '#/components/schemas/CustodyType' - vaspName: - type: string - description: Canonical name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. - example: Kraken defaultUmaDepositAccount: type: boolean description: Whether to set the external account as the default UMA deposit account. When set to true, incoming payments to this customer's UMA address will be automatically deposited into this external account. False if not provided. Note that only one external account can be set as the default UMA deposit account for a customer, so if there is already a default UMA deposit account, this will override the existing default UMA deposit account. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. diff --git a/openapi/components/schemas/external_accounts/BaseWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/BaseWalletExternalAccountInfo.yaml index 7bf6935f4..4e3941a2e 100644 --- a/openapi/components/schemas/external_accounts/BaseWalletExternalAccountInfo.yaml +++ b/openapi/components/schemas/external_accounts/BaseWalletExternalAccountInfo.yaml @@ -2,4 +2,5 @@ title: Base Wallet allOf: - $ref: ./BaseExternalAccountInfo.yaml - $ref: ../common/BaseWalletInfo.yaml - \ No newline at end of file + + - $ref: ./WalletCustodyFields.yaml diff --git a/openapi/components/schemas/external_accounts/EthereumWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/EthereumWalletExternalAccountInfo.yaml index 147cc3806..d2d181e44 100644 --- a/openapi/components/schemas/external_accounts/EthereumWalletExternalAccountInfo.yaml +++ b/openapi/components/schemas/external_accounts/EthereumWalletExternalAccountInfo.yaml @@ -2,3 +2,4 @@ title: Ethereum L1 Wallet allOf: - $ref: ./BaseExternalAccountInfo.yaml - $ref: ../common/EthereumWalletInfo.yaml + - $ref: ./WalletCustodyFields.yaml diff --git a/openapi/components/schemas/external_accounts/ExternalAccount.yaml b/openapi/components/schemas/external_accounts/ExternalAccount.yaml index 1410d9290..08449b833 100644 --- a/openapi/components/schemas/external_accounts/ExternalAccount.yaml +++ b/openapi/components/schemas/external_accounts/ExternalAccount.yaml @@ -24,15 +24,6 @@ allOf: example: acc_123456789 ownershipType: $ref: ./OwnershipType.yaml - custodyType: - $ref: ./CustodyType.yaml - vaspName: - type: string - description: >- - Canonical name of the VASP that custodies this account, from the - VASP directory (`GET /vasps`). Only present when `custodyType` is - `VASP_HOSTED`. - example: Kraken currency: type: string description: The ISO 4217 currency code diff --git a/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml b/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml index 407921758..b97ace94c 100644 --- a/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml +++ b/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml @@ -20,15 +20,6 @@ allOf: example: ext_acc_123456 ownershipType: $ref: ./OwnershipType.yaml - custodyType: - $ref: ./CustodyType.yaml - vaspName: - type: string - description: >- - Canonical name of the VASP that custodies this account, from the - VASP directory (`GET /vasps`). Required when `custodyType` is - `VASP_HOSTED`; must be omitted otherwise. - example: Kraken defaultUmaDepositAccount: type: boolean description: >- diff --git a/openapi/components/schemas/external_accounts/PlasmaWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/PlasmaWalletExternalAccountInfo.yaml index bcddbab97..95bc270ad 100644 --- a/openapi/components/schemas/external_accounts/PlasmaWalletExternalAccountInfo.yaml +++ b/openapi/components/schemas/external_accounts/PlasmaWalletExternalAccountInfo.yaml @@ -2,3 +2,4 @@ title: Plasma Wallet allOf: - $ref: ./BaseExternalAccountInfo.yaml - $ref: ../common/PlasmaWalletInfo.yaml + - $ref: ./WalletCustodyFields.yaml diff --git a/openapi/components/schemas/external_accounts/PolygonWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/PolygonWalletExternalAccountInfo.yaml index 8e6594f5e..177821b06 100644 --- a/openapi/components/schemas/external_accounts/PolygonWalletExternalAccountInfo.yaml +++ b/openapi/components/schemas/external_accounts/PolygonWalletExternalAccountInfo.yaml @@ -2,4 +2,5 @@ title: Polygon Wallet allOf: - $ref: ./BaseExternalAccountInfo.yaml - $ref: ../common/PolygonWalletInfo.yaml - \ No newline at end of file + + - $ref: ./WalletCustodyFields.yaml diff --git a/openapi/components/schemas/external_accounts/SolanaWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/SolanaWalletExternalAccountInfo.yaml index ff2134035..8396349d4 100644 --- a/openapi/components/schemas/external_accounts/SolanaWalletExternalAccountInfo.yaml +++ b/openapi/components/schemas/external_accounts/SolanaWalletExternalAccountInfo.yaml @@ -2,4 +2,5 @@ title: Solana Wallet allOf: - $ref: ./BaseExternalAccountInfo.yaml - $ref: ../common/SolanaWalletInfo.yaml - \ No newline at end of file + + - $ref: ./WalletCustodyFields.yaml diff --git a/openapi/components/schemas/external_accounts/SparkWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/SparkWalletExternalAccountInfo.yaml index 267a5ddf6..28e460a70 100644 --- a/openapi/components/schemas/external_accounts/SparkWalletExternalAccountInfo.yaml +++ b/openapi/components/schemas/external_accounts/SparkWalletExternalAccountInfo.yaml @@ -2,4 +2,5 @@ title: Spark Wallet allOf: - $ref: ./BaseExternalAccountInfo.yaml - $ref: ../common/SparkWalletInfo.yaml - \ No newline at end of file + + - $ref: ./WalletCustodyFields.yaml diff --git a/openapi/components/schemas/external_accounts/TronWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/TronWalletExternalAccountInfo.yaml index f3dedf87b..758b87d8a 100644 --- a/openapi/components/schemas/external_accounts/TronWalletExternalAccountInfo.yaml +++ b/openapi/components/schemas/external_accounts/TronWalletExternalAccountInfo.yaml @@ -2,4 +2,5 @@ title: Tron Wallet allOf: - $ref: ./BaseExternalAccountInfo.yaml - $ref: ../common/TronWalletInfo.yaml - \ No newline at end of file + + - $ref: ./WalletCustodyFields.yaml diff --git a/openapi/components/schemas/external_accounts/WalletCustodyFields.yaml b/openapi/components/schemas/external_accounts/WalletCustodyFields.yaml new file mode 100644 index 000000000..dd16f60a6 --- /dev/null +++ b/openapi/components/schemas/external_accounts/WalletCustodyFields.yaml @@ -0,0 +1,11 @@ +type: object +properties: + custodyType: + $ref: ./CustodyType.yaml + vaspName: + type: string + description: >- + Name of the VASP that custodies this account, from the VASP directory + (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be + omitted otherwise. + example: Kraken From 1b83fc7c3bd6f279481438110f6f46b69d0d1f4c Mon Sep 17 00:00:00 2001 From: shreyav Date: Thu, 20 Aug 2026 00:33:43 +0000 Subject: [PATCH 4/4] Infer custody from vaspName and drop the transfer error code custodyType was redundant with vaspName and made two contradictory states expressible (VASP_HOSTED without a name, SELF_CUSTODY with one). Presence of vaspName now means the wallet is VASP-held. Defers the transfer-time beneficiary error code. --- .stainless/stainless.yml | 1 - mintlify/openapi.yaml | 13 +------------ openapi.yaml | 13 +------------ openapi/components/schemas/errors/Error400.yaml | 2 -- .../schemas/external_accounts/CustodyType.yaml | 12 ------------ .../external_accounts/WalletCustodyFields.yaml | 7 +++---- 6 files changed, 5 insertions(+), 43 deletions(-) delete mode 100644 openapi/components/schemas/external_accounts/CustodyType.yaml diff --git a/.stainless/stainless.yml b/.stainless/stainless.yml index 45edb702a..a4e97538e 100644 --- a/.stainless/stainless.yml +++ b/.stainless/stainless.yml @@ -209,7 +209,6 @@ resources: external_account: '#/components/schemas/ExternalAccount' external_account_create: '#/components/schemas/ExternalAccountCreateRequest' external_account_info_one_of: "#/components/schemas/ExternalAccountInfoOneOf" - custody_type: "#/components/schemas/CustodyType" wallet_custody_fields: "#/components/schemas/WalletCustodyFields" business_beneficiary: "#/components/schemas/BusinessBeneficiary" wallet_beneficiary_fields: "#/components/schemas/WalletBeneficiaryFields" diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 6bc57ca4c..b76d3997d 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -12317,7 +12317,6 @@ components: | CARDHOLDER_KYC_NOT_APPROVED | The cardholder's KYC status is not `APPROVED`, so a card cannot be issued | | TRANSACTION_SIZE_LIMIT_EXCEEDED | The requested amount exceeds the configured maximum single-transaction amount for this trade corridor or withdrawal currency | | EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED | The destination account's ownership must be verified before this transfer can proceed | - | COUNTERPARTY_USER_INFO_REQUIRED | The destination account needs beneficiary information before this transfer can proceed; update the external account with a `beneficiary` and retry | enum: - INVALID_INPUT - END_USER_TERMS_VERSION_NOT_FOUND @@ -12362,7 +12361,6 @@ components: - CARDHOLDER_KYC_NOT_APPROVED - TRANSACTION_SIZE_LIMIT_EXCEEDED - EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED - - COUNTERPARTY_USER_INFO_REQUIRED message: type: string description: Error message @@ -19535,21 +19533,12 @@ components: beneficiary: description: The wallet's owner. Optional for `FIRST_PARTY` accounts — the customer's verified identity is used. Required for `THIRD_PARTY` wallets on platforms with counterparty requirements (e.g. EU Travel Rule); if missing there, creation fails with `400 INVALID_INPUT`. $ref: '#/components/schemas/WalletBeneficiaryOneOf' - CustodyType: - type: string - enum: - - SELF_CUSTODY - - VASP_HOSTED - description: 'How a crypto wallet external account is custodied: `SELF_CUSTODY` for a wallet whose keys the owner controls directly, or `VASP_HOSTED` for an account held at a Virtual Asset Service Provider (an exchange or other custodial platform). Only relevant for crypto wallet accounts; defaults to `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` wallets — `VASP_HOSTED` accounts never require it.' - example: SELF_CUSTODY WalletCustodyFields: type: object properties: - custodyType: - $ref: '#/components/schemas/CustodyType' vaspName: type: string - description: Name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. + description: Name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Present when the wallet is held at an exchange or other custodial platform; omit it for self-custody wallets. Ownership verification applies only to self-custody wallets. example: Kraken BaseWalletExternalAccountInfo: title: Base Wallet diff --git a/openapi.yaml b/openapi.yaml index 6bc57ca4c..b76d3997d 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -12317,7 +12317,6 @@ components: | CARDHOLDER_KYC_NOT_APPROVED | The cardholder's KYC status is not `APPROVED`, so a card cannot be issued | | TRANSACTION_SIZE_LIMIT_EXCEEDED | The requested amount exceeds the configured maximum single-transaction amount for this trade corridor or withdrawal currency | | EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED | The destination account's ownership must be verified before this transfer can proceed | - | COUNTERPARTY_USER_INFO_REQUIRED | The destination account needs beneficiary information before this transfer can proceed; update the external account with a `beneficiary` and retry | enum: - INVALID_INPUT - END_USER_TERMS_VERSION_NOT_FOUND @@ -12362,7 +12361,6 @@ components: - CARDHOLDER_KYC_NOT_APPROVED - TRANSACTION_SIZE_LIMIT_EXCEEDED - EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED - - COUNTERPARTY_USER_INFO_REQUIRED message: type: string description: Error message @@ -19535,21 +19533,12 @@ components: beneficiary: description: The wallet's owner. Optional for `FIRST_PARTY` accounts — the customer's verified identity is used. Required for `THIRD_PARTY` wallets on platforms with counterparty requirements (e.g. EU Travel Rule); if missing there, creation fails with `400 INVALID_INPUT`. $ref: '#/components/schemas/WalletBeneficiaryOneOf' - CustodyType: - type: string - enum: - - SELF_CUSTODY - - VASP_HOSTED - description: 'How a crypto wallet external account is custodied: `SELF_CUSTODY` for a wallet whose keys the owner controls directly, or `VASP_HOSTED` for an account held at a Virtual Asset Service Provider (an exchange or other custodial platform). Only relevant for crypto wallet accounts; defaults to `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` wallets — `VASP_HOSTED` accounts never require it.' - example: SELF_CUSTODY WalletCustodyFields: type: object properties: - custodyType: - $ref: '#/components/schemas/CustodyType' vaspName: type: string - description: Name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be omitted otherwise. + description: Name of the VASP that custodies this account, from the VASP directory (`GET /vasps`). Present when the wallet is held at an exchange or other custodial platform; omit it for self-custody wallets. Ownership verification applies only to self-custody wallets. example: Kraken BaseWalletExternalAccountInfo: title: Base Wallet diff --git a/openapi/components/schemas/errors/Error400.yaml b/openapi/components/schemas/errors/Error400.yaml index 044f412a5..8af97d754 100644 --- a/openapi/components/schemas/errors/Error400.yaml +++ b/openapi/components/schemas/errors/Error400.yaml @@ -57,7 +57,6 @@ properties: | CARDHOLDER_KYC_NOT_APPROVED | The cardholder's KYC status is not `APPROVED`, so a card cannot be issued | | TRANSACTION_SIZE_LIMIT_EXCEEDED | The requested amount exceeds the configured maximum single-transaction amount for this trade corridor or withdrawal currency | | EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED | The destination account's ownership must be verified before this transfer can proceed | - | COUNTERPARTY_USER_INFO_REQUIRED | The destination account needs beneficiary information before this transfer can proceed; update the external account with a `beneficiary` and retry | enum: - INVALID_INPUT - END_USER_TERMS_VERSION_NOT_FOUND @@ -102,7 +101,6 @@ properties: - CARDHOLDER_KYC_NOT_APPROVED - TRANSACTION_SIZE_LIMIT_EXCEEDED - EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED - - COUNTERPARTY_USER_INFO_REQUIRED message: type: string description: Error message diff --git a/openapi/components/schemas/external_accounts/CustodyType.yaml b/openapi/components/schemas/external_accounts/CustodyType.yaml deleted file mode 100644 index 8197cd502..000000000 --- a/openapi/components/schemas/external_accounts/CustodyType.yaml +++ /dev/null @@ -1,12 +0,0 @@ -type: string -enum: - - SELF_CUSTODY - - VASP_HOSTED -description: >- - How a crypto wallet external account is custodied: `SELF_CUSTODY` for a - wallet whose keys the owner controls directly, or `VASP_HOSTED` for an - account held at a Virtual Asset Service Provider (an exchange or other - custodial platform). Only relevant for crypto wallet accounts; defaults to - `SELF_CUSTODY`. Ownership verification applies only to `SELF_CUSTODY` - wallets — `VASP_HOSTED` accounts never require it. -example: SELF_CUSTODY diff --git a/openapi/components/schemas/external_accounts/WalletCustodyFields.yaml b/openapi/components/schemas/external_accounts/WalletCustodyFields.yaml index dd16f60a6..f971b8054 100644 --- a/openapi/components/schemas/external_accounts/WalletCustodyFields.yaml +++ b/openapi/components/schemas/external_accounts/WalletCustodyFields.yaml @@ -1,11 +1,10 @@ type: object properties: - custodyType: - $ref: ./CustodyType.yaml vaspName: type: string description: >- Name of the VASP that custodies this account, from the VASP directory - (`GET /vasps`). Required when `custodyType` is `VASP_HOSTED`; must be - omitted otherwise. + (`GET /vasps`). Present when the wallet is held at an exchange or other + custodial platform; omit it for self-custody wallets. Ownership + verification applies only to self-custody wallets. example: Kraken