Add COP fields needed for Thunes standard-bank payouts#468
Conversation
Thunes' Colombia bank corridor requires four fields beyond what the current CopAccountInfo and CopBeneficiary schemas expose: - bankName: needed at the account level for bank rail - bankAccountType: account_type CPI for std banks (CHECKING/SAVINGS) - documentType / documentNumber: id_type / id_number that nearly every Colombian payer needs to KYC-match the beneficiary Add these as optional properties so existing COP_ACCOUNT consumers aren't broken. Required-field enforcement moves to the sparkcore runtime where it can be rail-conditional (BANK_TRANSFER wants bankName + accountNumber + bankAccountType; MOBILE_MONEY only wants phoneNumber). This is the foundation PR — sparkcore wiring (receiver fields, account.py response shape, CURRENCY_PAYEE_FIELD_MAPPINGS update, union-shape extraction) follows once Stainless regenerates the SDK.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✱ Stainless preview builds for gridThis PR will update the kotlin openapi python typescript
|
Greptile SummaryThis PR extends the COP (Colombian Peso) payment schema with four new optional fields needed for Thunes standard-bank payouts:
Confidence Score: 4/5Safe to merge as a schema-only change; all new fields are optional and existing consumers are unaffected. The account-side fields (bankName, bankAccountType) are well-specified with enums and length guards. The beneficiary-side fields (documentType, documentNumber) are both unconstrained plain strings — documentType in particular maps to Thunes id_type with a known finite set of accepted values (CC/CE/NIT/PP), and the absence of an enum means a caller sending an unsupported value will only discover the error at Thunes, not at schema validation time. The example object also lost its MOBILE_MONEY shape, which may confuse consumers. openapi/components/schemas/common/CopBeneficiary.yaml — documentType and documentNumber have no validation constraints.
|
| Filename | Overview |
|---|---|
| openapi/components/schemas/common/CopAccountInfoBase.yaml | Adds bankName (string, 1-255) and bankAccountType (enum: CHECKING/SAVINGS) as optional BANK_TRANSFER fields; updates rail description and example (example now only covers BANK_TRANSFER, MOBILE_MONEY shape is no longer shown) |
| openapi/components/schemas/common/CopBeneficiary.yaml | Adds documentType and documentNumber as optional fields; both lack enum/length constraints that would push validation to the API boundary rather than deferring failures to Thunes |
| openapi.yaml | Generated bundle reflecting CopAccountInfoBase and CopBeneficiary source changes; no direct edits needed here per CLAUDE.md |
| mintlify/openapi.yaml | Mintlify copy of the generated bundle; identical changes to root openapi.yaml, correct per build process |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[COP Payout Request] --> B{paymentRails}
B -->|BANK_TRANSFER| C[CopAccountInfoBase]
B -->|MOBILE_MONEY| D[CopAccountInfoBase]
C --> C1[accountType: COP_ACCOUNT]
C --> C2[bankName ✨ new]
C --> C3[accountNumber]
C --> C4[bankAccountType ✨ new]
D --> D1[accountType: COP_ACCOUNT]
D --> D2[phoneNumber]
A --> E[CopBeneficiary]
E --> E1[beneficiaryType / fullName]
E --> E2[documentType ✨ new]
E --> E3[documentNumber ✨ new]
E --> E4[existing fields...]
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/common/CopBeneficiary.yaml:31-38
**`documentType` is unconstrained string; `documentNumber` lacks length guards**
The PR description explicitly names the accepted values for `documentType` (CC, CE, NIT, PP) and maps them to Thunes `id_type`. Without an `enum` constraint, any arbitrary string passes schema validation and the error will only surface at the Thunes payer-match step — after the payout has been accepted. Every other typed field in this PR adds stronger validation (`bankAccountType` gets an enum; `bankName` and `accountNumber` get `minLength`/`maxLength`). `documentNumber` similarly lacks `minLength: 1`, so an empty string is schema-valid even though Thunes would reject it.
If the set of valid `documentType` values is expected to grow over time (beyond CC/CE/NIT/PP), a comment explaining that is worth adding; otherwise an `enum` keeps the contract explicit and moves validation to the API boundary.
### Issue 2 of 2
openapi/components/schemas/common/CopAccountInfoBase.yaml:38-42
The example was updated to show only BANK_TRANSFER fields and `phoneNumber` was removed entirely. Because the schema is intentionally a union of both rails (per the PR description), the example now gives no indication of how a MOBILE_MONEY payload looks. Consider splitting into named `examples` so consumers can see both rail shapes.
```suggestion
examples:
bankTransfer:
summary: BANK_TRANSFER rail
value:
accountType: COP_ACCOUNT
bankName: Bancolombia
accountNumber: '1234567890'
bankAccountType: CHECKING
mobileMoney:
summary: MOBILE_MONEY rail (Nequi / Daviplata)
value:
accountType: COP_ACCOUNT
phoneNumber: '+573001234567'
```
Reviews (1): Last reviewed commit: "Add COP fields needed for Thunes standar..." | Re-trigger Greptile
| documentType: | ||
| type: string | ||
| description: Identity document type — required by most Colombian banks | ||
| (e.g. CC for Cédula de Ciudadanía, CE for Cédula de Extranjería, NIT, | ||
| PP for passport) | ||
| documentNumber: | ||
| type: string | ||
| description: Identity document number — required by most Colombian banks |
There was a problem hiding this comment.
documentType is unconstrained string; documentNumber lacks length guards
The PR description explicitly names the accepted values for documentType (CC, CE, NIT, PP) and maps them to Thunes id_type. Without an enum constraint, any arbitrary string passes schema validation and the error will only surface at the Thunes payer-match step — after the payout has been accepted. Every other typed field in this PR adds stronger validation (bankAccountType gets an enum; bankName and accountNumber get minLength/maxLength). documentNumber similarly lacks minLength: 1, so an empty string is schema-valid even though Thunes would reject it.
If the set of valid documentType values is expected to grow over time (beyond CC/CE/NIT/PP), a comment explaining that is worth adding; otherwise an enum keeps the contract explicit and moves validation to the API boundary.
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/common/CopBeneficiary.yaml
Line: 31-38
Comment:
**`documentType` is unconstrained string; `documentNumber` lacks length guards**
The PR description explicitly names the accepted values for `documentType` (CC, CE, NIT, PP) and maps them to Thunes `id_type`. Without an `enum` constraint, any arbitrary string passes schema validation and the error will only surface at the Thunes payer-match step — after the payout has been accepted. Every other typed field in this PR adds stronger validation (`bankAccountType` gets an enum; `bankName` and `accountNumber` get `minLength`/`maxLength`). `documentNumber` similarly lacks `minLength: 1`, so an empty string is schema-valid even though Thunes would reject it.
If the set of valid `documentType` values is expected to grow over time (beyond CC/CE/NIT/PP), a comment explaining that is worth adding; otherwise an `enum` keeps the contract explicit and moves validation to the API boundary.
How can I resolve this? If you propose a fix, please make it concise.| example: | ||
| accountType: COP_ACCOUNT | ||
| bankName: Bancolombia | ||
| accountNumber: '1234567890' | ||
| phoneNumber: '+1234567890' | ||
| bankAccountType: CHECKING |
There was a problem hiding this comment.
The example was updated to show only BANK_TRANSFER fields and
phoneNumber was removed entirely. Because the schema is intentionally a union of both rails (per the PR description), the example now gives no indication of how a MOBILE_MONEY payload looks. Consider splitting into named examples so consumers can see both rail shapes.
| example: | |
| accountType: COP_ACCOUNT | |
| bankName: Bancolombia | |
| accountNumber: '1234567890' | |
| phoneNumber: '+1234567890' | |
| bankAccountType: CHECKING | |
| examples: | |
| bankTransfer: | |
| summary: BANK_TRANSFER rail | |
| value: | |
| accountType: COP_ACCOUNT | |
| bankName: Bancolombia | |
| accountNumber: '1234567890' | |
| bankAccountType: CHECKING | |
| mobileMoney: | |
| summary: MOBILE_MONEY rail (Nequi / Daviplata) | |
| value: | |
| accountType: COP_ACCOUNT | |
| phoneNumber: '+573001234567' |
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/common/CopAccountInfoBase.yaml
Line: 38-42
Comment:
The example was updated to show only BANK_TRANSFER fields and `phoneNumber` was removed entirely. Because the schema is intentionally a union of both rails (per the PR description), the example now gives no indication of how a MOBILE_MONEY payload looks. Consider splitting into named `examples` so consumers can see both rail shapes.
```suggestion
examples:
bankTransfer:
summary: BANK_TRANSFER rail
value:
accountType: COP_ACCOUNT
bankName: Bancolombia
accountNumber: '1234567890'
bankAccountType: CHECKING
mobileMoney:
summary: MOBILE_MONEY rail (Nequi / Daviplata)
value:
accountType: COP_ACCOUNT
phoneNumber: '+573001234567'
```
How can I resolve this? If you propose a fix, please make it concise.|
@greptile-apps re: the two pieces of feedback:
Split |
|
Both look good:
Both items resolved. LGTM on this PR pending the sparkcore follow-up. Tip: You can customize Greptile's behavior for this repo with |
|
@greptile anything else? |
|
Looking at the diff more carefully, I owe a correction on my first comment — The only remaining open point is the example comment (BANK_TRANSFER fields only, no MOBILE_MONEY shape shown). One minor thing I didn't flag before: That's it overall — the schema changes are clean and well-constrained. |
Summary
Thunes' Colombia bank corridor requires four fields beyond what the current `CopAccountInfo` / `CopBeneficiary` schemas expose. Without these, standard-bank COP payouts fail at the Thunes payer-match step:
All four added as optional so existing consumers aren't broken. The runtime decides which subset is required based on the chosen `paymentRails`:
That rail-conditional enforcement lands in sparkcore alongside the receiver-field updates.
Why optional, not required
We've been bitten by the union-validation pattern before (PKR's `iban required` regression, SLV's bank-vs-mobile rails). The grid-api schema describes the union of all possible fields; the runtime enforces which subset is needed for the selected rail. This keeps the schema permissive and the validation in one place.
Test plan
Out of scope (followup)
🤖 Generated with Claude Code