Add platform transaction fee config + platformFees fields to the spec#652
Add platform transaction fee config + platformFees fields to the spec#652jacklatourette wants to merge 1 commit into
Conversation
- TransactionFeeConfig schema; transactionFeeConfigs on PlatformConfig + PlatformConfigUpdateRequest (backs webdev#29233) - Quote.platformFeesIncluded + OutgoingTransaction.platformFees (backs webdev#29402) These fields are live in the webdev vendored client; without this spec change the next regen deletes them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
✱ Stainless preview builds for gridThis PR will update the cli csharp go kotlin openapi php python ruby typescript Edit this comment to update them. They will appear in their respective SDK's changelogs. ✅ grid-openapi studio · code · diff
✅ grid-ruby studio · code · diff
✅ grid-go studio · code · diff
✅ grid-kotlin studio · code · diff
|
| 💡 Model/Recommended: We recommend you use a model for `#/components/schemas/TransactionFeeConfig` |
⚠️ grid-python studio · code · diff
Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅→build ✅(prev:build ⏭️) →lint ❗(prev:lint ⏭️) →test ❗pip install https://pkg.stainless.com/s/grid-python/39da9db50f83c91ce0fb398661d2da0d83473c4c/grid-0.0.1-py3-none-any.whlNew diagnostics (1 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/TransactionFeeConfig`
⚠️ grid-csharp studio · code · diff
Your SDK build had a failure in the build CI job, which is a regression from the base state.
generate ⚠️→build ❗(prev:build ⏭️) →lint ✅(prev:lint ⏭️) →test ❗New diagnostics (2 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/TransactionFeeConfig` 💡 Name/Renamed: 267 names were renamed due to language constraints, so fallback names will be used instead.
✅ grid-php studio · code · diff
Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅→lint ✅→test ✅New diagnostics (1 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/TransactionFeeConfig`
✅ grid-cli studio · code · diff
Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ⚠️→build ❗→lint ❗→test ❗New diagnostics (1 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/TransactionFeeConfig`
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-07-06 22:40:38 UTC
Greptile SummaryThis PR formalizes platform transaction fee fields into the Grid API spec, preventing a future SDK regeneration from silently deleting live integrator-facing fields that are already deployed in the vendored webdev client (grid-api 0.0.82).
Confidence Score: 4/5Safe to merge; changes are additive spec additions that match the already-deployed API surface in the vendored webdev client. The schema additions are straightforward and match live API behavior. The two notable rough edges — readOnly fields (id, isActive) surfacing in the shared write schema, and platformFeesIncluded being optional despite the API always returning it — are spec-design concerns that could affect SDK generation quality but do not misrepresent the API contract in a breaking way. TransactionFeeConfig.yaml (shared read/write schema with readOnly fields) and Quote.yaml (platformFeesIncluded missing from required) are worth a second look before the next SDK regeneration.
|
| Filename | Overview |
|---|---|
| openapi/components/schemas/config/TransactionFeeConfig.yaml | New schema for platform transaction fee config; correctly uses readOnly for id/isActive, but the same schema is reused verbatim for both the read response and the write request body, leaving readOnly fields visible to SDK generators in the request context. |
| openapi/components/schemas/config/TransactionFeeEventType.yaml | New enum with two values; description correctly documents the asymmetry between read (full set) and write (CROSS_CURRENCY_TRANSACTION only). |
| openapi/components/schemas/config/PlatformConfig.yaml | Adds transactionFeeConfigs read-side array; placement before createdAt is consistent with existing field ordering. |
| openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml | Adds transactionFeeConfigs write-side array reusing TransactionFeeConfig; readOnly fields (id, isActive) are technically correct per OpenAPI 3.1 semantics but may appear in SDK-generated request types depending on the generator used. |
| openapi/components/schemas/quotes/Quote.yaml | Adds platformFeesIncluded as an optional field; field is documented as always-present (returns 0 when no fee configured) but is not added to the required list, making it technically optional in the spec. |
| openapi/components/schemas/transactions/OutgoingTransaction.yaml | Adds platformFees as an optional field consistent with the existing fees field (also optional); naming mirrors the parent field correctly. |
| openapi.yaml | Generated bundle; diffs correctly reflect all source-file additions plus Redocly relocating Currency/CurrencyAmount definitions earlier. |
| mintlify/openapi.yaml | Generated bundle identical in content to openapi.yaml; changes are a mirror of source schema additions. |
Entity Relationship Diagram
%%{init: {'theme': 'neutral'}}%%
erDiagram
PlatformConfig {
string id
string umaDomain
string webhookEndpoint
datetime createdAt
datetime updatedAt
}
PlatformConfigUpdateRequest {
string umaDomain
string webhookEndpoint
}
TransactionFeeConfig {
string id PK
string feeEventType FK
string sourceCurrency
integer variableFeeBps
boolean isActive
}
TransactionFeeEventType {
string CROSS_CURRENCY_TRANSACTION
string TRANSFER_OUT_TRANSACTION
}
CurrencyAmount {
integer amount
object currency
}
Quote {
string id
integer feesIncluded
integer platformFeesIncluded
}
OutgoingTransaction {
string id
integer fees
integer platformFees
}
PlatformConfig ||--o{ TransactionFeeConfig : "transactionFeeConfigs (read)"
PlatformConfigUpdateRequest ||--o{ TransactionFeeConfig : "transactionFeeConfigs (write/upsert)"
TransactionFeeConfig ||--|| TransactionFeeEventType : "feeEventType"
TransactionFeeConfig ||--|| CurrencyAmount : "fixedFee"
Quote }o--|| Quote : "platformFeesIncluded subset of feesIncluded"
OutgoingTransaction }o--|| OutgoingTransaction : "platformFees subset of fees"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
erDiagram
PlatformConfig {
string id
string umaDomain
string webhookEndpoint
datetime createdAt
datetime updatedAt
}
PlatformConfigUpdateRequest {
string umaDomain
string webhookEndpoint
}
TransactionFeeConfig {
string id PK
string feeEventType FK
string sourceCurrency
integer variableFeeBps
boolean isActive
}
TransactionFeeEventType {
string CROSS_CURRENCY_TRANSACTION
string TRANSFER_OUT_TRANSACTION
}
CurrencyAmount {
integer amount
object currency
}
Quote {
string id
integer feesIncluded
integer platformFeesIncluded
}
OutgoingTransaction {
string id
integer fees
integer platformFees
}
PlatformConfig ||--o{ TransactionFeeConfig : "transactionFeeConfigs (read)"
PlatformConfigUpdateRequest ||--o{ TransactionFeeConfig : "transactionFeeConfigs (write/upsert)"
TransactionFeeConfig ||--|| TransactionFeeEventType : "feeEventType"
TransactionFeeConfig ||--|| CurrencyAmount : "fixedFee"
Quote }o--|| Quote : "platformFeesIncluded subset of feesIncluded"
OutgoingTransaction }o--|| OutgoingTransaction : "platformFees subset of fees"
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
openapi/components/schemas/config/TransactionFeeConfig.yaml:30-46
**`readOnly` fields visible in write context via shared schema**
`TransactionFeeConfig` is referenced by both `PlatformConfig` (response) and `PlatformConfigUpdateRequest` (request body). The `id` and `isActive` fields are marked `readOnly: true`, which OpenAPI 3.1 semantics say should be excluded from requests — but many SDK generators (e.g. OpenAPI Generator Java/TypeScript) do not strip `readOnly` fields from generated request types for shared schemas; they only omit them from serialization, leaving the door open for generated clients to populate them and send them in the PATCH body. A common mitigation is a dedicated write schema (e.g. `TransactionFeeConfigInput`) that omits those two fields entirely, or using `allOf` composition with the read schema to override them out.
### Issue 2 of 2
openapi/components/schemas/quotes/Quote.yaml:90-99
**`platformFeesIncluded` not added to `required` despite always-present semantics**
The description states the field returns `0` when no applicable fee is configured, implying the API always emits it. `feesIncluded` — the parent field this is a subset of — is already in the `required` list. Keeping `platformFeesIncluded` optional means generated clients will model it as nullable/absent, and response-validation code won't catch a server regression that accidentally drops it. If the field is guaranteed in all responses (including those predating this PR), it should be added to `required`; if it may be absent for legacy quotes, that should be noted in the description.
Reviews (1): Last reviewed commit: "Add platform transaction fee config + pl..." | Re-trigger Greptile
| fixedFee: | ||
| $ref: ../common/CurrencyAmount.yaml | ||
| description: >- | ||
| Fixed fee charged per transaction, in the smallest unit of | ||
| `sourceCurrency`. `fixedFee.currency.code` must match `sourceCurrency`. | ||
| isActive: | ||
| type: boolean | ||
| description: >- | ||
| Whether this row is currently active. Reads return only active rows | ||
| unless explicitly filtered otherwise. | ||
| readOnly: true | ||
| example: true | ||
| required: | ||
| - feeEventType | ||
| - sourceCurrency | ||
| - variableFeeBps | ||
| - fixedFee |
There was a problem hiding this comment.
readOnly fields visible in write context via shared schema
TransactionFeeConfig is referenced by both PlatformConfig (response) and PlatformConfigUpdateRequest (request body). The id and isActive fields are marked readOnly: true, which OpenAPI 3.1 semantics say should be excluded from requests — but many SDK generators (e.g. OpenAPI Generator Java/TypeScript) do not strip readOnly fields from generated request types for shared schemas; they only omit them from serialization, leaving the door open for generated clients to populate them and send them in the PATCH body. A common mitigation is a dedicated write schema (e.g. TransactionFeeConfigInput) that omits those two fields entirely, or using allOf composition with the read schema to override them out.
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/config/TransactionFeeConfig.yaml
Line: 30-46
Comment:
**`readOnly` fields visible in write context via shared schema**
`TransactionFeeConfig` is referenced by both `PlatformConfig` (response) and `PlatformConfigUpdateRequest` (request body). The `id` and `isActive` fields are marked `readOnly: true`, which OpenAPI 3.1 semantics say should be excluded from requests — but many SDK generators (e.g. OpenAPI Generator Java/TypeScript) do not strip `readOnly` fields from generated request types for shared schemas; they only omit them from serialization, leaving the door open for generated clients to populate them and send them in the PATCH body. A common mitigation is a dedicated write schema (e.g. `TransactionFeeConfigInput`) that omits those two fields entirely, or using `allOf` composition with the read schema to override them out.
How can I resolve this? If you propose a fix, please make it concise.| platformFeesIncluded: | ||
| type: integer | ||
| format: int64 | ||
| description: >- | ||
| The portion of `feesIncluded` collected by the platform | ||
| (platform-configured transaction fees), in the smallest unit of the | ||
| sending currency. 0 when the platform has no applicable fee configured. | ||
| Already included in `feesIncluded`. | ||
| minimum: 0 | ||
| example: 5 |
There was a problem hiding this comment.
platformFeesIncluded not added to required despite always-present semantics
The description states the field returns 0 when no applicable fee is configured, implying the API always emits it. feesIncluded — the parent field this is a subset of — is already in the required list. Keeping platformFeesIncluded optional means generated clients will model it as nullable/absent, and response-validation code won't catch a server regression that accidentally drops it. If the field is guaranteed in all responses (including those predating this PR), it should be added to required; if it may be absent for legacy quotes, that should be noted in the description.
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/quotes/Quote.yaml
Line: 90-99
Comment:
**`platformFeesIncluded` not added to `required` despite always-present semantics**
The description states the field returns `0` when no applicable fee is configured, implying the API always emits it. `feesIncluded` — the parent field this is a subset of — is already in the `required` list. Keeping `platformFeesIncluded` optional means generated clients will model it as nullable/absent, and response-validation code won't catch a server regression that accidentally drops it. If the field is guaranteed in all responses (including those predating this PR), it should be added to `required`; if it may be absent for legacy quotes, that should be noted in the description.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
The webdev sparkcore stack shipped platform transaction fees against a hand-wired vendored Grid API client (grid-api 0.0.82). The fields are live and testable there, but they do not exist in this spec repo — so the next SDK regen from upstream would silently delete integrator-facing fields. This PR adds them to the spec source (
openapi/components/schemas/) and regenerates the bundles (openapi.yaml,mintlify/openapi.yaml) vianpm run build:openapi.1. New
TransactionFeeConfigschema (backs lightsparkdev/webdev#29233)openapi/components/schemas/config/TransactionFeeConfig.yaml+TransactionFeeEventType.yaml:id(string, readOnly) — system-generated identifierfeeEventType(enum:CROSS_CURRENCY_TRANSACTION,TRANSFER_OUT_TRANSACTION; only the former is accepted byPATCH /configtoday, reads return the full set)sourceCurrency(string) — ISO 4217 / crypto ticker codevariableFeeBps(integer, 0–10000)fixedFee(CurrencyAmount) — must matchsourceCurrencyisActive(boolean, readOnly)Required:
feeEventType,sourceCurrency,variableFeeBps,fixedFee. Rows are keyed by(feeEventType, sourceCurrency); deactivation is done by upserting the same key with zero fees.2.
transactionFeeConfigson platform config (backs lightsparkdev/webdev#29233)PlatformConfig.transactionFeeConfigs— read side; returns rows regardless offeeEventTypeso operator-created rows (e.g. Transfer Out) are visiblePlatformConfigUpdateRequest.transactionFeeConfigs— merge-by-key upsert; onlyCROSS_CURRENCY_TRANSACTION+USDaccepted today, other values return 400; omit to leave configs unchanged3. Platform fee surfacing on quotes and transactions (backs lightsparkdev/webdev#29402)
Quote.platformFeesIncluded(int64, min 0) — the portion offeesIncludedcollected by the platform; already included infeesIncludedOutgoingTransaction.platformFees(int64, min 0) — same, relative tofeesDescriptions match the vendored client/spec in webdev exactly, so a regen from this spec is a no-op against the hand-wired models.
Test plan
npm run lint(Redocly bundle + lint, Spectral--fail-severity=error) — exit 0. Only new finding is one non-failingschema-properties-have-descriptionswarning onTransactionFeeConfig.fixedFee, an artifact of the$ref+ sibling-description pattern already used throughout the spec (e.g.embeddedWalletConfig,sentAmount).Currency/CurrencyAmountearlier (now referenced from the config section).🤖 Generated with Claude Code