feat: Platform fee rules — configurable per-transaction-type markup#204
feat: Platform fee rules — configurable per-transaction-type markup#204
Conversation
✱ Stainless preview buildsThis PR will update the kotlin openapi python typescript Edit this comment to update them. They will appear in their respective SDK's changelogs.
|
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/BrlExternalAccountInfo` (not found in spec). |
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/DkkExternalAccountInfo` (not found in spec). |
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/GbpExternalAccountInfo` (not found in spec). |
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/HkdExternalAccountInfo` (not found in spec). |
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/IdrExternalAccountInfo` (not found in spec). |
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/InrExternalAccountInfo` (not found in spec). |
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/MxnExternalAccountInfo` (not found in spec). |
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/MyrExternalAccountInfo` (not found in spec). |
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/PhpExternalAccountInfo` (not found in spec). |
| ❗ Reference/NotFound: Ignored reference `#/components/schemas/SgdExternalAccountInfo` (not found in spec). |
⚠️ grid-typescript studio · code · diff
There was a regression in your SDK.
generate ❗(prev:generate ✅) →build ✅→lint ✅→test ✅npm install https://pkg.stainless.com/s/grid-typescript/d8c7da3843f81278f396db6142b450d662857824/dist.tar.gzNew diagnostics (26 error, 1 warning, 13 note)
❗ Reference/NotFound: Ignored reference `#/components/schemas/BrlExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/DkkExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/GbpExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/HkdExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/IdrExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/InrExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/MxnExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/MyrExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/PhpExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/SgdExternalAccountInfo` (not found in spec).
⚠️ grid-python studio · code · diff
There was a regression in your SDK.
generate ❗(prev:generate ✅) →build ⏳→lint ⏳→test ⏳New diagnostics (26 error, 1 warning, 13 note)
❗ Reference/NotFound: Ignored reference `#/components/schemas/BrlExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/DkkExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/GbpExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/HkdExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/IdrExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/InrExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/MxnExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/MyrExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/PhpExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/SgdExternalAccountInfo` (not found in spec).
⚠️ grid-kotlin studio · code · diff
There was a regression in your SDK.
generate ❗(prev:generate ✅) →build ✅→lint ✅→test ✅New diagnostics (26 error, 1 warning, 3 note)
❗ Reference/NotFound: Ignored reference `#/components/schemas/BrlExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/DkkExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/GbpExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/HkdExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/IdrExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/InrExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/MxnExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/MyrExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/PhpExternalAccountInfo` (not found in spec). ❗ Reference/NotFound: Ignored reference `#/components/schemas/SgdExternalAccountInfo` (not found in spec).
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-02-19 19:52:40 UTC
Greptile SummaryAdds complete CRUD API for platform fee rules, enabling Grid platforms to configure per-transaction-type markup layered on top of Lightspark and counterparty fees. The implementation extends existing rate details schemas to surface platform fees in quotes and transactions, adds five new endpoints under Key additions:
Consistency with existing patterns:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| openapi/components/schemas/fee_rules/FeeRule.yaml | Core fee rule schema definition with proper types and constraints; missing maximum constraint on fixedFee (present in request schemas but not response) |
| openapi/paths/fee-rules/fee_rules.yaml | Well-documented POST and GET endpoints with clear examples and comprehensive error responses |
| openapi/paths/fee-rules/fee_rules_{feeRuleId}.yaml | Complete CRUD operations for individual fee rules with appropriate HTTP methods and response codes |
| openapi/components/schemas/transactions/IncomingRateDetails.yaml | Extends existing schema with three new optional platform fee fields, maintaining consistency with existing grid API fee fields |
| openapi/components/schemas/transactions/OutgoingRateDetails.yaml | Extends existing schema with three new optional platform fee fields, maintaining consistency with existing grid API fee fields |
| mintlify/snippets/platform-fee-rules.mdx | Comprehensive documentation with clear examples, proper MDX structure, and accurate example data matching the OpenAPI spec |
| openapi/openapi.yaml | Main spec file with new fee-rules paths added; literal path /fee-rules/report appears after parameterized /{feeRuleId} which may cause routing issues |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Platform creates FeeRule] --> B{POST /fee-rules}
B --> C[Validate transactionType & feeType]
C --> D{Check duplicate}
D -->|Exists| E[409 FEE_RULE_EXISTS]
D -->|New| F{Validate fee amounts}
F -->|Invalid| G[400 INVALID_FEE_RULE]
F -->|Valid| H[Create FeeRule entity]
H --> I[Return 201 with FeeRule]
J[Customer requests quote] --> K[Evaluate active FeeRules]
K --> L{Matching rule for txn type?}
L -->|Yes| M[Calculate platform fees]
L -->|No| N[No platform fees]
M --> O[Include in rateDetails]
N --> O
O --> P[Return quote with platformFixedFee, platformVariableFeeRate, platformVariableFeeAmount]
Q[Transaction settles] --> R[Collect platform fees]
R --> S[Credit platform subledger]
T[Platform requests report] --> U{GET /fee-rules/report?month=YYYY-MM}
U --> V{Valid month format?}
V -->|No| W[400 INVALID_MONTH_FORMAT]
V -->|Yes| X[Aggregate fees by transaction type]
X --> Y[Return FeeReport with lines]
Last reviewed commit: 9a66dbe
| { | ||
| "month": "2026-02", | ||
| "totalPlatformFeesCollected": 128450, | ||
| "currency": "USD", | ||
| "lines": [ | ||
| { | ||
| "transactionType": "CROSS_BORDER_PAYOUT", | ||
| "transactionCount": 97, | ||
| "platformFeesCollected": 51300, | ||
| "currency": "USD" | ||
| }, | ||
| { | ||
| "transactionType": "TRANSFER_OUT", | ||
| "transactionCount": 245, | ||
| "platformFeesCollected": 62500, | ||
| "currency": "USD" | ||
| } | ||
| ] | ||
| } |
There was a problem hiding this comment.
Fee report example total doesn't match line items
The totalPlatformFeesCollected is 128,450 but the two line items sum to only 113,800 (51,300 + 62,500). The OpenAPI example in fee_rules_report.yaml includes a third RAMP_OFF line (14,650) that makes the total correct — but the documentation snippet is missing that line.
Either add the missing RAMP_OFF line to match the total, or adjust totalPlatformFeesCollected to 113,800.
| { | |
| "month": "2026-02", | |
| "totalPlatformFeesCollected": 128450, | |
| "currency": "USD", | |
| "lines": [ | |
| { | |
| "transactionType": "CROSS_BORDER_PAYOUT", | |
| "transactionCount": 97, | |
| "platformFeesCollected": 51300, | |
| "currency": "USD" | |
| }, | |
| { | |
| "transactionType": "TRANSFER_OUT", | |
| "transactionCount": 245, | |
| "platformFeesCollected": 62500, | |
| "currency": "USD" | |
| } | |
| ] | |
| } | |
| ```json 200 OK | |
| { | |
| "month": "2026-02", | |
| "totalPlatformFeesCollected": 128450, | |
| "currency": "USD", | |
| "lines": [ | |
| { | |
| "transactionType": "CROSS_BORDER_PAYOUT", | |
| "transactionCount": 97, | |
| "platformFeesCollected": 51300, | |
| "currency": "USD" | |
| }, | |
| { | |
| "transactionType": "TRANSFER_OUT", | |
| "transactionCount": 245, | |
| "platformFeesCollected": 62500, | |
| "currency": "USD" | |
| }, | |
| { | |
| "transactionType": "RAMP_OFF", | |
| "transactionCount": 58, | |
| "platformFeesCollected": 14650, | |
| "currency": "USD" | |
| } | |
| ] | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: mintlify/snippets/platform-fee-rules.mdx
Line: 131-149
Comment:
**Fee report example total doesn't match line items**
The `totalPlatformFeesCollected` is 128,450 but the two line items sum to only 113,800 (51,300 + 62,500). The OpenAPI example in `fee_rules_report.yaml` includes a third `RAMP_OFF` line (14,650) that makes the total correct — but the documentation snippet is missing that line.
Either add the missing `RAMP_OFF` line to match the total, or adjust `totalPlatformFeesCollected` to 113,800.
```suggestion
```json 200 OK
{
"month": "2026-02",
"totalPlatformFeesCollected": 128450,
"currency": "USD",
"lines": [
{
"transactionType": "CROSS_BORDER_PAYOUT",
"transactionCount": 97,
"platformFeesCollected": 51300,
"currency": "USD"
},
{
"transactionType": "TRANSFER_OUT",
"transactionCount": 245,
"platformFeesCollected": 62500,
"currency": "USD"
},
{
"transactionType": "RAMP_OFF",
"transactionCount": 58,
"platformFeesCollected": 14650,
"currency": "USD"
}
]
}
```
How can I resolve this? If you propose a fix, please make it concise.| @@ -0,0 +1,164 @@ | |||
| import { gridBaseUrl } from '/snippets/variables.mdx'; | |||
There was a problem hiding this comment.
Unused import: gridBaseUrl
gridBaseUrl is imported but never used in this file — the cURL examples hardcode https://api.lightspark.com/grid/2025-10-13 directly. This is consistent with other snippet files (none of them actually use gridBaseUrl in code blocks), so consider removing the unused import.
| import { gridBaseUrl } from '/snippets/variables.mdx'; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: mintlify/snippets/platform-fee-rules.mdx
Line: 1
Comment:
**Unused import: `gridBaseUrl`**
`gridBaseUrl` is imported but never used in this file — the cURL examples hardcode `https://api.lightspark.com/grid/2025-10-13` directly. This is consistent with other snippet files (none of them actually use `gridBaseUrl` in code blocks), so consider removing the unused import.
```suggestion
```
How can I resolve this? If you propose a fix, please make it concise.| /fee-rules: | ||
| $ref: paths/fee-rules/fee_rules.yaml | ||
| /fee-rules/{feeRuleId}: | ||
| $ref: paths/fee-rules/fee_rules_{feeRuleId}.yaml | ||
| /fee-rules/report: | ||
| $ref: paths/fee-rules/fee_rules_report.yaml |
There was a problem hiding this comment.
Literal path /fee-rules/report ordered after parameterized /fee-rules/{feeRuleId}
/fee-rules/{feeRuleId} appears before /fee-rules/report. While OpenAPI 3.x says path ordering doesn't determine routing, many code generators and API gateways use first-match-wins semantics — which means /fee-rules/report could be matched by the {feeRuleId} parameter, with "report" captured as the ID.
This is the same pattern already present with /customers/{customerId} before /customers/kyc-link, so it may be intentional for this repo. But if any consumer relies on YAML ordering, swapping the literal path above the parameterized one would be safer:
| /fee-rules: | |
| $ref: paths/fee-rules/fee_rules.yaml | |
| /fee-rules/{feeRuleId}: | |
| $ref: paths/fee-rules/fee_rules_{feeRuleId}.yaml | |
| /fee-rules/report: | |
| $ref: paths/fee-rules/fee_rules_report.yaml | |
| /fee-rules: | |
| $ref: paths/fee-rules/fee_rules.yaml | |
| /fee-rules/report: | |
| $ref: paths/fee-rules/fee_rules_report.yaml | |
| /fee-rules/{feeRuleId}: | |
| $ref: paths/fee-rules/fee_rules_{feeRuleId}.yaml |
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/openapi.yaml
Line: 94-99
Comment:
**Literal path `/fee-rules/report` ordered after parameterized `/fee-rules/{feeRuleId}`**
`/fee-rules/{feeRuleId}` appears before `/fee-rules/report`. While OpenAPI 3.x says path ordering doesn't determine routing, many code generators and API gateways use first-match-wins semantics — which means `/fee-rules/report` could be matched by the `{feeRuleId}` parameter, with `"report"` captured as the ID.
This is the same pattern already present with `/customers/{customerId}` before `/customers/kyc-link`, so it may be intentional for this repo. But if any consumer relies on YAML ordering, swapping the literal path above the parameterized one would be safer:
```suggestion
/fee-rules:
$ref: paths/fee-rules/fee_rules.yaml
/fee-rules/report:
$ref: paths/fee-rules/fee_rules_report.yaml
/fee-rules/{feeRuleId}:
$ref: paths/fee-rules/fee_rules_{feeRuleId}.yaml
```
How can I resolve this? If you propose a fix, please make it concise.| description: >- | ||
| Variable fee as a decimal rate of the transaction amount | ||
| (e.g., 0.005 = 0.5%). Applied to the sending amount at quote creation. | ||
| Required when feeType is PERCENTAGE or HYBRID. |
There was a problem hiding this comment.
variableFeeRate description is misleading for incoming transactions
The description says "Applied to the sending amount at quote creation," but this is only accurate for outgoing transactions. For incoming transactions (TRANSFER_IN, RAMP_ON), the IncomingRateDetails.platformVariableFeeAmount description says it's applied to the receiving amount — matching how the existing gridApiVariableFeeRate works in that context.
Consider using direction-neutral language like "Applied to the transaction amount at quote creation" to avoid confusing consumers who read both this schema and the rate details.
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!
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/fee_rules/FeeRule.yaml
Line: 35-38
Comment:
**`variableFeeRate` description is misleading for incoming transactions**
The description says "Applied to the sending amount at quote creation," but this is only accurate for outgoing transactions. For incoming transactions (`TRANSFER_IN`, `RAMP_ON`), the `IncomingRateDetails.platformVariableFeeAmount` description says it's applied to the *receiving* amount — matching how the existing `gridApiVariableFeeRate` works in that context.
Consider using direction-neutral language like "Applied to the transaction amount at quote creation" to avoid confusing consumers who read both this schema and the rate details.
<sub>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!</sub>
How can I resolve this? If you propose a fix, please make it concise.| fixedFee: | ||
| type: integer | ||
| format: int64 | ||
| description: >- | ||
| Fixed fee in the smallest unit of USD (cents). Applied per transaction. | ||
| Required when feeType is FIXED or HYBRID. | ||
| minimum: 0 |
There was a problem hiding this comment.
fixedFee missing maximum constraint in response schema
The FeeRuleCreateRequest and FeeRulePatchRequest schemas both specify maximum: 10000 for fixedFee, but this response schema omits it. Meanwhile, variableFeeRate (line 40) does have maximum: 0.20 here. Code generators that derive validation from the response schema (e.g., for round-trip testing or SDK type constraints) would not enforce the $100 cap on this field.
| fixedFee: | |
| type: integer | |
| format: int64 | |
| description: >- | |
| Fixed fee in the smallest unit of USD (cents). Applied per transaction. | |
| Required when feeType is FIXED or HYBRID. | |
| minimum: 0 | |
| fixedFee: | |
| type: integer | |
| format: int64 | |
| description: >- | |
| Fixed fee in the smallest unit of USD (cents). Applied per transaction. | |
| Required when feeType is FIXED or HYBRID. | |
| minimum: 0 | |
| maximum: 10000 | |
| example: 150 |
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/fee_rules/FeeRule.yaml
Line: 24-30
Comment:
**`fixedFee` missing `maximum` constraint in response schema**
The `FeeRuleCreateRequest` and `FeeRulePatchRequest` schemas both specify `maximum: 10000` for `fixedFee`, but this response schema omits it. Meanwhile, `variableFeeRate` (line 40) does have `maximum: 0.20` here. Code generators that derive validation from the response schema (e.g., for round-trip testing or SDK type constraints) would not enforce the $100 cap on this field.
```suggestion
fixedFee:
type: integer
format: int64
description: >-
Fixed fee in the smallest unit of USD (cents). Applied per transaction.
Required when feeType is FIXED or HYBRID.
minimum: 0
maximum: 10000
example: 150
```
How can I resolve this? If you propose a fix, please make it concise.Introduce a complete CRUD API for platform-configurable fee markup,
enabling platforms to define per-transaction-type fees that layer on
top of Lightspark and counterparty fees.
New endpoints:
- POST /fee-rules — create a fee rule (one per transaction type)
- GET /fee-rules — list all fee rules with optional filters
- GET /fee-rules/{feeRuleId} — retrieve a single rule
- PATCH /fee-rules/{feeRuleId} — partial update (amounts, enabled)
- DELETE /fee-rules/{feeRuleId} — remove a rule
- GET /fee-rules/report — monthly aggregated fee revenue
New schemas:
- FeeRule, FeeTransactionType, FeeRuleType
- FeeRuleCreateRequest, FeeRulePatchRequest
- FeeReport, FeeReportLine
Extended existing schemas:
- OutgoingRateDetails and IncomingRateDetails gain platform fee
fields (platformFixedFee, platformVariableFeeRate,
platformVariableFeeAmount) so quotes and transactions surface the
platform markup alongside Lightspark fees.
New error codes:
- INVALID_FEE_RULE (400), INVALID_MONTH_FORMAT (400)
- FEE_RULE_NOT_FOUND (404), FEE_RULE_EXISTS (409)
Documentation:
- Shared snippet with guide, cURL examples, and fee layering table
- Wrapper pages in all four use-case tabs (Payouts, Ramps, Rewards,
Global P2P) under Platform Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
- Rename operationId from listFeeRules to getFeeRules to avoid
Stainless auto-detecting the endpoint as paginated (matches the
getExchangeRates convention for non-paginated collections)
- Reorder paths: /fee-rules/report before /fee-rules/{feeRuleId}
so the literal path precedes the parameterized one
- Add missing RAMP_OFF line item to the doc snippet fee report
example so the total (128,450) matches the sum of line items
- Remove unused gridBaseUrl import from the snippet
Co-authored-by: Cursor <cursoragent@cursor.com>
228a292 to
9a66dbe
Compare
| fixedFee: | ||
| type: integer | ||
| format: int64 | ||
| description: >- | ||
| Fixed fee in the smallest unit of USD (cents). Applied per transaction. | ||
| Required when feeType is FIXED or HYBRID. | ||
| minimum: 0 |
There was a problem hiding this comment.
maximum: 10000 constraint missing in response schema
The FeeRuleCreateRequest and FeeRulePatchRequest schemas both enforce maximum: 10000 for fixedFee, but this response schema omits it. Meanwhile, variableFeeRate on line 40 does include maximum: 0.20. Code generators deriving validation from response schemas won't enforce the $100 cap.
| fixedFee: | |
| type: integer | |
| format: int64 | |
| description: >- | |
| Fixed fee in the smallest unit of USD (cents). Applied per transaction. | |
| Required when feeType is FIXED or HYBRID. | |
| minimum: 0 | |
| fixedFee: | |
| type: integer | |
| format: int64 | |
| description: >- | |
| Fixed fee in the smallest unit of USD (cents). Applied per transaction. | |
| Required when feeType is FIXED or HYBRID. | |
| minimum: 0 | |
| maximum: 10000 | |
| example: 150 |
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/fee_rules/FeeRule.yaml
Line: 24-30
Comment:
`maximum: 10000` constraint missing in response schema
The `FeeRuleCreateRequest` and `FeeRulePatchRequest` schemas both enforce `maximum: 10000` for `fixedFee`, but this response schema omits it. Meanwhile, `variableFeeRate` on line 40 does include `maximum: 0.20`. Code generators deriving validation from response schemas won't enforce the $100 cap.
```suggestion
fixedFee:
type: integer
format: int64
description: >-
Fixed fee in the smallest unit of USD (cents). Applied per transaction.
Required when feeType is FIXED or HYBRID.
minimum: 0
maximum: 10000
example: 150
```
How can I resolve this? If you propose a fix, please make it concise.| description: >- | ||
| Variable fee as a decimal rate of the transaction amount | ||
| (e.g., 0.005 = 0.5%). Applied to the sending amount at quote creation. |
There was a problem hiding this comment.
Consider using direction-neutral language for variableFeeRate description. Currently says "Applied to the sending amount" but for incoming transactions it's applied to the receiving amount.
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/fee_rules/FeeRule.yaml
Line: 35-37
Comment:
Consider using direction-neutral language for `variableFeeRate` description. Currently says "Applied to the sending amount" but for incoming transactions it's applied to the receiving amount.
How can I resolve this? If you propose a fix, please make it concise.| /fee-rules: | ||
| $ref: paths/fee-rules/fee_rules.yaml | ||
| /fee-rules/report: | ||
| $ref: paths/fee-rules/fee_rules_report.yaml | ||
| /fee-rules/{feeRuleId}: | ||
| $ref: paths/fee-rules/fee_rules_{feeRuleId}.yaml |
There was a problem hiding this comment.
Consider if literal path /fee-rules/report should appear before parameterized /{feeRuleId} to avoid potential routing conflicts in code generators. This pattern matches existing /customers paths so may be intentional.
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!
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/openapi.yaml
Line: 94-99
Comment:
Consider if literal path `/fee-rules/report` should appear before parameterized `/{feeRuleId}` to avoid potential routing conflicts in code generators. This pattern matches existing `/customers` paths so may be intentional.
<sub>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!</sub>
How can I resolve this? If you propose a fix, please make it concise.
Summary
Adds a complete CRUD API for platform-configurable fee rules, enabling Grid platforms to define per-transaction-type markup that layers on top of Lightspark and counterparty fees.
This closes the last major gap in the Grid pricing model: the API already surfaces fee fields on quotes and transactions (
gridApiFixedFee,gridApiVariableFeeRate,gridApiVariableFeeAmount), but platforms had no way to configure their own markup. This PR adds that capability.Fee architecture (three layers)
New endpoints
POST/fee-rulesGET/fee-rulesGET/fee-rules/{feeRuleId}PATCH/fee-rules/{feeRuleId}DELETE/fee-rules/{feeRuleId}GET/fee-rules/reportNew schemas
transactionType,feeType(FIXED / PERCENTAGE / HYBRID),fixedFee,variableFeeRate,enabledTRANSFER_IN,TRANSFER_OUT,RAMP_ON,RAMP_OFF,CROSS_BORDER_PAYOUTFIXED,PERCENTAGE,HYBRIDExtended existing schemas
OutgoingRateDetailsandIncomingRateDetailsgain three new optional fields so quotes and transactions surface platform markup alongside Lightspark fees:platformFixedFee(int64, cents)platformVariableFeeRate(double, e.g. 0.005 = 0.5%)platformVariableFeeAmount(int64, cents, calculated)New error codes
INVALID_FEE_RULE(400) — missing fields, amounts out of range, or wrong feeType/field combinationINVALID_MONTH_FORMAT(400) — month parameter not in YYYY-MM formatFEE_RULE_NOT_FOUND(404)FEE_RULE_EXISTS(409) — duplicate transaction typeDocumentation
snippets/platform-fee-rules.mdx) with layering explanation, cURL examples, and constraintsDesign decisions
variableFeeRateas adouble— matches existinggridApiVariableFeeRateconvention (not basis points)fixedFeein cents (int64) — matches existinggridApiFixedFeeconventionfeeTypeimmutable on PATCH — avoids ambiguity about which fields become required/optional mid-lifecycle; delete and recreate instead/fee-rules/report— colocated with rules since it's platform fee revenue, not general transaction analyticscurrencyfield for future multi-currency supportTest plan
npm run lintpasses (Redocly validation confirmed clean)npm run build:openapibundles successfullyrateDetailsextensions don't break existing quote/transaction examplesMade with Cursor