From e81aa3fba3d5289d41c2ed4f85540a593de0c980 Mon Sep 17 00:00:00 2001 From: Jack Latourette Date: Tue, 23 Jun 2026 11:51:33 -0700 Subject: [PATCH 1/4] feat: add TransactionFeeConfig schema (AT-5613) Adds platform-collected transaction-fee configuration to the Grid API PlatformConfig surface. - New TransactionFeeConfig schema: { id, feeEventType, sourceCurrency, variableFeeBps, fixedFee, isActive }. `id` and `isActive` are read-only. - New TransactionFeeEventType enum: { CROSS_CURRENCY_TRANSACTION, TRANSFER_OUT_TRANSACTION }. - PlatformConfig: optional `transactionFeeConfigs: array`. Reads return all active rows regardless of feeEventType so operator-created Transfer Out rows are visible before the corresponding write path is enabled. - PlatformConfigUpdateRequest: same field, with semantics documented as merge-by-key upsert on (feeEventType, sourceCurrency). To deactivate a fee, send the same key with variableFeeBps=0 and fixedFee.amount=0. Sparkcore's PATCH /config handler (AT-5614) will accept only feeEventType=CROSS_CURRENCY_TRANSACTION + sourceCurrency=USD for v0; the OpenAPI enum is intentionally broader so the Transfer Out fast-follow does not need an SDK regen. --- .stainless/stainless.yml | 2 + mintlify/openapi.yaml | 136 +++++++++++++----- openapi.yaml | 136 +++++++++++++----- .../schemas/config/PlatformConfig.yaml | 9 ++ .../config/PlatformConfigUpdateRequest.yaml | 12 ++ .../schemas/config/TransactionFeeConfig.yaml | 46 ++++++ .../config/TransactionFeeEventType.yaml | 16 +++ 7 files changed, 291 insertions(+), 66 deletions(-) create mode 100644 openapi/components/schemas/config/TransactionFeeConfig.yaml create mode 100644 openapi/components/schemas/config/TransactionFeeEventType.yaml diff --git a/.stainless/stainless.yml b/.stainless/stainless.yml index 85db268ed..e7893d2b6 100644 --- a/.stainless/stainless.yml +++ b/.stainless/stainless.yml @@ -101,6 +101,8 @@ resources: platform_config: '#/components/schemas/PlatformConfig' embedded_wallet_config: "#/components/schemas/EmbeddedWalletConfig" platform_config_update_request: '#/components/schemas/PlatformConfigUpdateRequest' + transaction_fee_config: '#/components/schemas/TransactionFeeConfig' + transaction_fee_event_type: '#/components/schemas/TransactionFeeEventType' methods: retrieve: get /config update: patch /config diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 65956615a..e148cf920 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -8835,6 +8835,88 @@ components: maxLength: 512 description: URL to a PNG logo for the OTP email. Resized to 340x124px. example: https://acme.com/logo.png + TransactionFeeEventType: + type: string + enum: + - CROSS_CURRENCY_TRANSACTION + - TRANSFER_OUT_TRANSACTION + description: |- + The kind of transaction this fee applies to. + - `CROSS_CURRENCY_TRANSACTION` — fee charged on a cross-currency Grid send + (source currency differs from destination currency). + + - `TRANSFER_OUT_TRANSACTION` — fee charged on a Grid Transfer Out + operation. + + Note: only `CROSS_CURRENCY_TRANSACTION` is accepted by `PATCH /config` today. Reads always return the full set, so configurations created by Lightspark operators (e.g. Transfer Out rows) are still visible. + Currency: + type: object + properties: + code: + type: string + description: Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.) + example: USD + name: + type: string + description: Full name of the currency + example: United States Dollar + symbol: + type: string + description: Symbol of the currency + example: $ + decimals: + type: integer + description: Number of decimal places for the currency + minimum: 0 + example: 2 + CurrencyAmount: + type: object + required: + - amount + - currency + properties: + amount: + type: integer + format: int64 + description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + example: 12550 + currency: + $ref: '#/components/schemas/Currency' + TransactionFeeConfig: + type: object + description: 'Platform-collected transaction fee charged on top of corridor fees. Keyed uniquely by `(feeEventType, sourceCurrency)` within a platform. To deactivate a fee, send the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`.' + required: + - feeEventType + - sourceCurrency + - variableFeeBps + - fixedFee + properties: + id: + type: string + description: System-generated unique identifier. + readOnly: true + example: GridPlatformTransactionFeeConfig:019ef5bf-a77e-93be-0000-479e9268bca9 + feeEventType: + $ref: '#/components/schemas/TransactionFeeEventType' + sourceCurrency: + type: string + description: ISO 4217 (fiat) or ticker (crypto) code of the source-side currency this fee applies to. Cross-currency fees are charged in the sending side's currency. + example: USD + variableFeeBps: + type: integer + format: int32 + minimum: 0 + maximum: 10000 + description: Variable fee in basis points (1 bps = 0.01%). Multiplied by the transaction's source-currency amount. + example: 30 + fixedFee: + description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`. + $ref: '#/components/schemas/CurrencyAmount' + isActive: + type: boolean + description: Whether this row is currently active. Reads return only active rows unless explicitly filtered otherwise. + readOnly: true + example: true PlatformConfig: type: object properties: @@ -8876,6 +8958,15 @@ components: Embedded-wallet branding and OTP settings for this platform. Present only when the platform has configured embedded-wallet support; omitted otherwise. + transactionFeeConfigs: + type: array + items: + $ref: '#/components/schemas/TransactionFeeConfig' + description: | + Platform-collected transaction fees, keyed uniquely by + `(feeEventType, sourceCurrency)`. Returned in reads regardless of + `feeEventType` so that operator-created rows (e.g. Transfer Out) are + visible even before the corresponding write path is enabled. createdAt: type: string format: date-time @@ -8976,6 +9067,18 @@ components: Fields omitted from the nested object are left unchanged. Omit this field at the top level to leave the embedded-wallet configuration unchanged entirely. + transactionFeeConfigs: + type: array + items: + $ref: '#/components/schemas/TransactionFeeConfig' + description: | + Merge-by-key upsert of platform transaction fee configs. Each item is + keyed by `(feeEventType, sourceCurrency)`. To deactivate a fee, send + the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`. Only + `feeEventType: CROSS_CURRENCY_TRANSACTION` and `sourceCurrency: USD` + are accepted by this endpoint today; other values return a 400 error. + Omit this field at the top level to leave all transaction fee configs + unchanged. Error400: type: object required: @@ -9106,26 +9209,6 @@ components: type: object description: Additional error details additionalProperties: true - Currency: - type: object - properties: - code: - type: string - description: Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.) - example: USD - name: - type: string - description: Full name of the currency - example: United States Dollar - symbol: - type: string - description: Symbol of the currency - example: $ - decimals: - type: integer - description: Number of decimal places for the currency - minimum: 0 - example: 2 PaymentRail: type: string enum: @@ -10264,19 +10347,6 @@ components: - `CLOSED`: The account cannot send or receive payments. A customer can initiate the closing of an internal account, after which the account transitions to this status. - `FROZEN`: The account cannot send or receive payments. Grid may freeze an account in response to compliance or fraud signals; payments are blocked while the account remains frozen. example: ACTIVE - CurrencyAmount: - type: object - required: - - amount - - currency - properties: - amount: - type: integer - format: int64 - description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) - example: 12550 - currency: - $ref: '#/components/schemas/Currency' PaymentAccountType: type: string enum: diff --git a/openapi.yaml b/openapi.yaml index 65956615a..e148cf920 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -8835,6 +8835,88 @@ components: maxLength: 512 description: URL to a PNG logo for the OTP email. Resized to 340x124px. example: https://acme.com/logo.png + TransactionFeeEventType: + type: string + enum: + - CROSS_CURRENCY_TRANSACTION + - TRANSFER_OUT_TRANSACTION + description: |- + The kind of transaction this fee applies to. + - `CROSS_CURRENCY_TRANSACTION` — fee charged on a cross-currency Grid send + (source currency differs from destination currency). + + - `TRANSFER_OUT_TRANSACTION` — fee charged on a Grid Transfer Out + operation. + + Note: only `CROSS_CURRENCY_TRANSACTION` is accepted by `PATCH /config` today. Reads always return the full set, so configurations created by Lightspark operators (e.g. Transfer Out rows) are still visible. + Currency: + type: object + properties: + code: + type: string + description: Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.) + example: USD + name: + type: string + description: Full name of the currency + example: United States Dollar + symbol: + type: string + description: Symbol of the currency + example: $ + decimals: + type: integer + description: Number of decimal places for the currency + minimum: 0 + example: 2 + CurrencyAmount: + type: object + required: + - amount + - currency + properties: + amount: + type: integer + format: int64 + description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + example: 12550 + currency: + $ref: '#/components/schemas/Currency' + TransactionFeeConfig: + type: object + description: 'Platform-collected transaction fee charged on top of corridor fees. Keyed uniquely by `(feeEventType, sourceCurrency)` within a platform. To deactivate a fee, send the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`.' + required: + - feeEventType + - sourceCurrency + - variableFeeBps + - fixedFee + properties: + id: + type: string + description: System-generated unique identifier. + readOnly: true + example: GridPlatformTransactionFeeConfig:019ef5bf-a77e-93be-0000-479e9268bca9 + feeEventType: + $ref: '#/components/schemas/TransactionFeeEventType' + sourceCurrency: + type: string + description: ISO 4217 (fiat) or ticker (crypto) code of the source-side currency this fee applies to. Cross-currency fees are charged in the sending side's currency. + example: USD + variableFeeBps: + type: integer + format: int32 + minimum: 0 + maximum: 10000 + description: Variable fee in basis points (1 bps = 0.01%). Multiplied by the transaction's source-currency amount. + example: 30 + fixedFee: + description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`. + $ref: '#/components/schemas/CurrencyAmount' + isActive: + type: boolean + description: Whether this row is currently active. Reads return only active rows unless explicitly filtered otherwise. + readOnly: true + example: true PlatformConfig: type: object properties: @@ -8876,6 +8958,15 @@ components: Embedded-wallet branding and OTP settings for this platform. Present only when the platform has configured embedded-wallet support; omitted otherwise. + transactionFeeConfigs: + type: array + items: + $ref: '#/components/schemas/TransactionFeeConfig' + description: | + Platform-collected transaction fees, keyed uniquely by + `(feeEventType, sourceCurrency)`. Returned in reads regardless of + `feeEventType` so that operator-created rows (e.g. Transfer Out) are + visible even before the corresponding write path is enabled. createdAt: type: string format: date-time @@ -8976,6 +9067,18 @@ components: Fields omitted from the nested object are left unchanged. Omit this field at the top level to leave the embedded-wallet configuration unchanged entirely. + transactionFeeConfigs: + type: array + items: + $ref: '#/components/schemas/TransactionFeeConfig' + description: | + Merge-by-key upsert of platform transaction fee configs. Each item is + keyed by `(feeEventType, sourceCurrency)`. To deactivate a fee, send + the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`. Only + `feeEventType: CROSS_CURRENCY_TRANSACTION` and `sourceCurrency: USD` + are accepted by this endpoint today; other values return a 400 error. + Omit this field at the top level to leave all transaction fee configs + unchanged. Error400: type: object required: @@ -9106,26 +9209,6 @@ components: type: object description: Additional error details additionalProperties: true - Currency: - type: object - properties: - code: - type: string - description: Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.) - example: USD - name: - type: string - description: Full name of the currency - example: United States Dollar - symbol: - type: string - description: Symbol of the currency - example: $ - decimals: - type: integer - description: Number of decimal places for the currency - minimum: 0 - example: 2 PaymentRail: type: string enum: @@ -10264,19 +10347,6 @@ components: - `CLOSED`: The account cannot send or receive payments. A customer can initiate the closing of an internal account, after which the account transitions to this status. - `FROZEN`: The account cannot send or receive payments. Grid may freeze an account in response to compliance or fraud signals; payments are blocked while the account remains frozen. example: ACTIVE - CurrencyAmount: - type: object - required: - - amount - - currency - properties: - amount: - type: integer - format: int64 - description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) - example: 12550 - currency: - $ref: '#/components/schemas/Currency' PaymentAccountType: type: string enum: diff --git a/openapi/components/schemas/config/PlatformConfig.yaml b/openapi/components/schemas/config/PlatformConfig.yaml index a1a0cd90f..80766083e 100644 --- a/openapi/components/schemas/config/PlatformConfig.yaml +++ b/openapi/components/schemas/config/PlatformConfig.yaml @@ -38,6 +38,15 @@ properties: Embedded-wallet branding and OTP settings for this platform. Present only when the platform has configured embedded-wallet support; omitted otherwise. + transactionFeeConfigs: + type: array + items: + $ref: ./TransactionFeeConfig.yaml + description: | + Platform-collected transaction fees, keyed uniquely by + `(feeEventType, sourceCurrency)`. Returned in reads regardless of + `feeEventType` so that operator-created rows (e.g. Transfer Out) are + visible even before the corresponding write path is enabled. createdAt: type: string format: date-time diff --git a/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml b/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml index 9222e4c18..ac4e6fd10 100644 --- a/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml +++ b/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml @@ -17,3 +17,15 @@ properties: Fields omitted from the nested object are left unchanged. Omit this field at the top level to leave the embedded-wallet configuration unchanged entirely. + transactionFeeConfigs: + type: array + items: + $ref: ./TransactionFeeConfig.yaml + description: | + Merge-by-key upsert of platform transaction fee configs. Each item is + keyed by `(feeEventType, sourceCurrency)`. To deactivate a fee, send + the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`. Only + `feeEventType: CROSS_CURRENCY_TRANSACTION` and `sourceCurrency: USD` + are accepted by this endpoint today; other values return a 400 error. + Omit this field at the top level to leave all transaction fee configs + unchanged. diff --git a/openapi/components/schemas/config/TransactionFeeConfig.yaml b/openapi/components/schemas/config/TransactionFeeConfig.yaml new file mode 100644 index 000000000..132303b5c --- /dev/null +++ b/openapi/components/schemas/config/TransactionFeeConfig.yaml @@ -0,0 +1,46 @@ +type: object +description: >- + Platform-collected transaction fee charged on top of corridor fees. Keyed + uniquely by `(feeEventType, sourceCurrency)` within a platform. To deactivate + a fee, send the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`. +required: + - feeEventType + - sourceCurrency + - variableFeeBps + - fixedFee +properties: + id: + type: string + description: System-generated unique identifier. + readOnly: true + example: GridPlatformTransactionFeeConfig:019ef5bf-a77e-93be-0000-479e9268bca9 + feeEventType: + $ref: ./TransactionFeeEventType.yaml + sourceCurrency: + type: string + description: >- + ISO 4217 (fiat) or ticker (crypto) code of the source-side currency this + fee applies to. Cross-currency fees are charged in the sending side's + currency. + example: USD + variableFeeBps: + type: integer + format: int32 + minimum: 0 + maximum: 10000 + description: >- + Variable fee in basis points (1 bps = 0.01%). Multiplied by the + transaction's source-currency amount. + example: 30 + fixedFee: + description: >- + Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. + `fixedFee.currency.code` must match `sourceCurrency`. + $ref: ../common/CurrencyAmount.yaml + isActive: + type: boolean + description: >- + Whether this row is currently active. Reads return only active rows + unless explicitly filtered otherwise. + readOnly: true + example: true diff --git a/openapi/components/schemas/config/TransactionFeeEventType.yaml b/openapi/components/schemas/config/TransactionFeeEventType.yaml new file mode 100644 index 000000000..5a22e8e53 --- /dev/null +++ b/openapi/components/schemas/config/TransactionFeeEventType.yaml @@ -0,0 +1,16 @@ +type: string +enum: + - CROSS_CURRENCY_TRANSACTION + - TRANSFER_OUT_TRANSACTION +description: >- + The kind of transaction this fee applies to. + + - `CROSS_CURRENCY_TRANSACTION` — fee charged on a cross-currency Grid send + (source currency differs from destination currency). + + - `TRANSFER_OUT_TRANSACTION` — fee charged on a Grid Transfer Out + operation. + + Note: only `CROSS_CURRENCY_TRANSACTION` is accepted by `PATCH /config` + today. Reads always return the full set, so configurations created by + Lightspark operators (e.g. Transfer Out rows) are still visible. From 7aceb3b332de290e23e092b5a5379eabeee866ab Mon Sep 17 00:00:00 2001 From: Jack Latourette Date: Wed, 24 Jun 2026 16:52:01 -0700 Subject: [PATCH 2/4] docs(config): document fixedFee.amount must be non-negative (AT-5613) The platform transaction fee's fixedFee.amount is enforced non-negative server-side in sparkcore (PATCH /config); document it on the schema so integrators see the constraint. CurrencyAmount stays unconstrained since negative amounts are valid elsewhere in the API. Co-Authored-By: Claude Opus 4.8 (1M context) --- mintlify/openapi.yaml | 2 +- openapi.yaml | 2 +- openapi/components/schemas/config/TransactionFeeConfig.yaml | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index e148cf920..5ddc97fcb 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -8910,7 +8910,7 @@ components: description: Variable fee in basis points (1 bps = 0.01%). Multiplied by the transaction's source-currency amount. example: 30 fixedFee: - description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`. + description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`, and `fixedFee.amount` must be non-negative. $ref: '#/components/schemas/CurrencyAmount' isActive: type: boolean diff --git a/openapi.yaml b/openapi.yaml index e148cf920..5ddc97fcb 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -8910,7 +8910,7 @@ components: description: Variable fee in basis points (1 bps = 0.01%). Multiplied by the transaction's source-currency amount. example: 30 fixedFee: - description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`. + description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`, and `fixedFee.amount` must be non-negative. $ref: '#/components/schemas/CurrencyAmount' isActive: type: boolean diff --git a/openapi/components/schemas/config/TransactionFeeConfig.yaml b/openapi/components/schemas/config/TransactionFeeConfig.yaml index 132303b5c..0bca13213 100644 --- a/openapi/components/schemas/config/TransactionFeeConfig.yaml +++ b/openapi/components/schemas/config/TransactionFeeConfig.yaml @@ -35,7 +35,8 @@ properties: fixedFee: description: >- Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. - `fixedFee.currency.code` must match `sourceCurrency`. + `fixedFee.currency.code` must match `sourceCurrency`, and `fixedFee.amount` + must be non-negative. $ref: ../common/CurrencyAmount.yaml isActive: type: boolean From 9248a32dd1dce94b7d16b8840213f2137352a813 Mon Sep 17 00:00:00 2001 From: Jack Latourette Date: Thu, 25 Jun 2026 14:16:08 -0700 Subject: [PATCH 3/4] fix(config): split TransactionFeeConfig into read/write variants (AT-5613) Add TransactionFeeConfigInput for the PATCH /config write path, omitting the read-only id/isActive fields, so generated SDK request types are unambiguous and do not rely on every toolchain stripping readOnly. This fixes the Stainless SDK build regressions (csharp build, ruby/python lint, kotlin test) caused by the read-only fields appearing in request types. Also clarify the isActive read semantics (no filter param exists). Addresses Greptile review feedback. --- .stainless/stainless.yml | 1 + mintlify/openapi.yaml | 29 +++++++++++++- openapi.yaml | 29 +++++++++++++- .../config/PlatformConfigUpdateRequest.yaml | 2 +- .../schemas/config/TransactionFeeConfig.yaml | 4 +- .../config/TransactionFeeConfigInput.yaml | 38 +++++++++++++++++++ 6 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 openapi/components/schemas/config/TransactionFeeConfigInput.yaml diff --git a/.stainless/stainless.yml b/.stainless/stainless.yml index e7893d2b6..bae0ed10b 100644 --- a/.stainless/stainless.yml +++ b/.stainless/stainless.yml @@ -102,6 +102,7 @@ resources: embedded_wallet_config: "#/components/schemas/EmbeddedWalletConfig" platform_config_update_request: '#/components/schemas/PlatformConfigUpdateRequest' transaction_fee_config: '#/components/schemas/TransactionFeeConfig' + transaction_fee_config_input: '#/components/schemas/TransactionFeeConfigInput' transaction_fee_event_type: '#/components/schemas/TransactionFeeEventType' methods: retrieve: get /config diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 5ddc97fcb..424d7e853 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -8914,7 +8914,7 @@ components: $ref: '#/components/schemas/CurrencyAmount' isActive: type: boolean - description: Whether this row is currently active. Reads return only active rows unless explicitly filtered otherwise. + description: Whether this row is currently active. Reads return only active rows; inactive rows are never returned by this endpoint. readOnly: true example: true PlatformConfig: @@ -9047,6 +9047,31 @@ components: type: object description: Additional error details additionalProperties: true + TransactionFeeConfigInput: + type: object + description: 'Write-side variant of a platform transaction fee config, used in the merge-by-key upsert on `PATCH /config`. Keyed uniquely by `(feeEventType, sourceCurrency)` within a platform. To deactivate a fee, send the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`. The system-generated `id` and the derived `isActive` flag are read-only and are therefore omitted here.' + required: + - feeEventType + - sourceCurrency + - variableFeeBps + - fixedFee + properties: + feeEventType: + $ref: '#/components/schemas/TransactionFeeEventType' + sourceCurrency: + type: string + description: ISO 4217 (fiat) or ticker (crypto) code of the source-side currency this fee applies to. Cross-currency fees are charged in the sending side's currency. + example: USD + variableFeeBps: + type: integer + format: int32 + minimum: 0 + maximum: 10000 + description: Variable fee in basis points (1 bps = 0.01%). Multiplied by the transaction's source-currency amount. + example: 30 + fixedFee: + description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`, and `fixedFee.amount` must be non-negative. + $ref: '#/components/schemas/CurrencyAmount' PlatformConfigUpdateRequest: type: object properties: @@ -9070,7 +9095,7 @@ components: transactionFeeConfigs: type: array items: - $ref: '#/components/schemas/TransactionFeeConfig' + $ref: '#/components/schemas/TransactionFeeConfigInput' description: | Merge-by-key upsert of platform transaction fee configs. Each item is keyed by `(feeEventType, sourceCurrency)`. To deactivate a fee, send diff --git a/openapi.yaml b/openapi.yaml index 5ddc97fcb..424d7e853 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -8914,7 +8914,7 @@ components: $ref: '#/components/schemas/CurrencyAmount' isActive: type: boolean - description: Whether this row is currently active. Reads return only active rows unless explicitly filtered otherwise. + description: Whether this row is currently active. Reads return only active rows; inactive rows are never returned by this endpoint. readOnly: true example: true PlatformConfig: @@ -9047,6 +9047,31 @@ components: type: object description: Additional error details additionalProperties: true + TransactionFeeConfigInput: + type: object + description: 'Write-side variant of a platform transaction fee config, used in the merge-by-key upsert on `PATCH /config`. Keyed uniquely by `(feeEventType, sourceCurrency)` within a platform. To deactivate a fee, send the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`. The system-generated `id` and the derived `isActive` flag are read-only and are therefore omitted here.' + required: + - feeEventType + - sourceCurrency + - variableFeeBps + - fixedFee + properties: + feeEventType: + $ref: '#/components/schemas/TransactionFeeEventType' + sourceCurrency: + type: string + description: ISO 4217 (fiat) or ticker (crypto) code of the source-side currency this fee applies to. Cross-currency fees are charged in the sending side's currency. + example: USD + variableFeeBps: + type: integer + format: int32 + minimum: 0 + maximum: 10000 + description: Variable fee in basis points (1 bps = 0.01%). Multiplied by the transaction's source-currency amount. + example: 30 + fixedFee: + description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`, and `fixedFee.amount` must be non-negative. + $ref: '#/components/schemas/CurrencyAmount' PlatformConfigUpdateRequest: type: object properties: @@ -9070,7 +9095,7 @@ components: transactionFeeConfigs: type: array items: - $ref: '#/components/schemas/TransactionFeeConfig' + $ref: '#/components/schemas/TransactionFeeConfigInput' description: | Merge-by-key upsert of platform transaction fee configs. Each item is keyed by `(feeEventType, sourceCurrency)`. To deactivate a fee, send diff --git a/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml b/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml index ac4e6fd10..096dbf8a1 100644 --- a/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml +++ b/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml @@ -20,7 +20,7 @@ properties: transactionFeeConfigs: type: array items: - $ref: ./TransactionFeeConfig.yaml + $ref: ./TransactionFeeConfigInput.yaml description: | Merge-by-key upsert of platform transaction fee configs. Each item is keyed by `(feeEventType, sourceCurrency)`. To deactivate a fee, send diff --git a/openapi/components/schemas/config/TransactionFeeConfig.yaml b/openapi/components/schemas/config/TransactionFeeConfig.yaml index 0bca13213..2fae0247d 100644 --- a/openapi/components/schemas/config/TransactionFeeConfig.yaml +++ b/openapi/components/schemas/config/TransactionFeeConfig.yaml @@ -41,7 +41,7 @@ properties: isActive: type: boolean description: >- - Whether this row is currently active. Reads return only active rows - unless explicitly filtered otherwise. + Whether this row is currently active. Reads return only active rows; + inactive rows are never returned by this endpoint. readOnly: true example: true diff --git a/openapi/components/schemas/config/TransactionFeeConfigInput.yaml b/openapi/components/schemas/config/TransactionFeeConfigInput.yaml new file mode 100644 index 000000000..818d17334 --- /dev/null +++ b/openapi/components/schemas/config/TransactionFeeConfigInput.yaml @@ -0,0 +1,38 @@ +type: object +description: >- + Write-side variant of a platform transaction fee config, used in the + merge-by-key upsert on `PATCH /config`. Keyed uniquely by + `(feeEventType, sourceCurrency)` within a platform. To deactivate a fee, send + the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`. The + system-generated `id` and the derived `isActive` flag are read-only and are + therefore omitted here. +required: + - feeEventType + - sourceCurrency + - variableFeeBps + - fixedFee +properties: + feeEventType: + $ref: ./TransactionFeeEventType.yaml + sourceCurrency: + type: string + description: >- + ISO 4217 (fiat) or ticker (crypto) code of the source-side currency this + fee applies to. Cross-currency fees are charged in the sending side's + currency. + example: USD + variableFeeBps: + type: integer + format: int32 + minimum: 0 + maximum: 10000 + description: >- + Variable fee in basis points (1 bps = 0.01%). Multiplied by the + transaction's source-currency amount. + example: 30 + fixedFee: + description: >- + Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. + `fixedFee.currency.code` must match `sourceCurrency`, and `fixedFee.amount` + must be non-negative. + $ref: ../common/CurrencyAmount.yaml From bc836492eaa75cbc1626f57afeb0cd19bd0885b6 Mon Sep 17 00:00:00 2001 From: Jack Latourette Date: Thu, 25 Jun 2026 15:39:36 -0700 Subject: [PATCH 4/4] fix(config): enforce fixedFee.amount >= 0 and mark read-side id/isActive required (AT-5613) Addresses Greptile review on the rebased PR: - TransactionFeeConfigInput.fixedFee now constrains amount to minimum: 0 (via allOf over CurrencyAmount), so negative fixed fees are rejected at schema validation instead of an undocumented server 400. - TransactionFeeConfig marks the always-present readOnly id and isActive as required, so generated SDK response types are non-optional. --- mintlify/openapi.yaml | 9 ++++++++- openapi.yaml | 9 ++++++++- .../components/schemas/config/TransactionFeeConfig.yaml | 2 ++ .../schemas/config/TransactionFeeConfigInput.yaml | 7 ++++++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 424d7e853..fe87a2e0d 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -8886,10 +8886,12 @@ components: type: object description: 'Platform-collected transaction fee charged on top of corridor fees. Keyed uniquely by `(feeEventType, sourceCurrency)` within a platform. To deactivate a fee, send the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`.' required: + - id - feeEventType - sourceCurrency - variableFeeBps - fixedFee + - isActive properties: id: type: string @@ -9071,7 +9073,12 @@ components: example: 30 fixedFee: description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`, and `fixedFee.amount` must be non-negative. - $ref: '#/components/schemas/CurrencyAmount' + allOf: + - $ref: '#/components/schemas/CurrencyAmount' + - type: object + properties: + amount: + minimum: 0 PlatformConfigUpdateRequest: type: object properties: diff --git a/openapi.yaml b/openapi.yaml index 424d7e853..fe87a2e0d 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -8886,10 +8886,12 @@ components: type: object description: 'Platform-collected transaction fee charged on top of corridor fees. Keyed uniquely by `(feeEventType, sourceCurrency)` within a platform. To deactivate a fee, send the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`.' required: + - id - feeEventType - sourceCurrency - variableFeeBps - fixedFee + - isActive properties: id: type: string @@ -9071,7 +9073,12 @@ components: example: 30 fixedFee: description: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`, and `fixedFee.amount` must be non-negative. - $ref: '#/components/schemas/CurrencyAmount' + allOf: + - $ref: '#/components/schemas/CurrencyAmount' + - type: object + properties: + amount: + minimum: 0 PlatformConfigUpdateRequest: type: object properties: diff --git a/openapi/components/schemas/config/TransactionFeeConfig.yaml b/openapi/components/schemas/config/TransactionFeeConfig.yaml index 2fae0247d..abb30a082 100644 --- a/openapi/components/schemas/config/TransactionFeeConfig.yaml +++ b/openapi/components/schemas/config/TransactionFeeConfig.yaml @@ -4,10 +4,12 @@ description: >- uniquely by `(feeEventType, sourceCurrency)` within a platform. To deactivate a fee, send the same key with `variableFeeBps: 0` and `fixedFee.amount: 0`. required: + - id - feeEventType - sourceCurrency - variableFeeBps - fixedFee + - isActive properties: id: type: string diff --git a/openapi/components/schemas/config/TransactionFeeConfigInput.yaml b/openapi/components/schemas/config/TransactionFeeConfigInput.yaml index 818d17334..f8ae28df3 100644 --- a/openapi/components/schemas/config/TransactionFeeConfigInput.yaml +++ b/openapi/components/schemas/config/TransactionFeeConfigInput.yaml @@ -35,4 +35,9 @@ properties: Fixed fee charged per transaction, in the smallest unit of `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`, and `fixedFee.amount` must be non-negative. - $ref: ../common/CurrencyAmount.yaml + allOf: + - $ref: ../common/CurrencyAmount.yaml + - type: object + properties: + amount: + minimum: 0