Add KYB business-entity fields for Lead — NAICS, structured source of funds, counterparty countries (ENG-10687)#643
Conversation
…funds, counterparty countries (ENG-10687) Adds to BusinessInfo / BusinessInfoUpdate / BusinessInfoResponse: - naicsCode (2-6 digit NAICS) - sourceOfFundsCategories (references the existing SourceOfFunds enum) + sourceOfFundsOtherDescription - purposeOfAccountOtherDescription - expectedCounterpartyCountries Adds MISSING_CONTROL_PERSON and MISSING_GOOD_STANDING_DOCUMENT verification error types. Co-Authored-By: akanter <akanter@users.noreply.github.com>
The legacy free-text sourceOfFunds property coexists on BusinessInfo; a sibling type named SourceOfFunds breaks the generated C# SDK (property / type name collision flagged by the Stainless preview build).
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
This stack of pull requests is managed by Graphite. Learn more about stacking. |
✱ Stainless preview builds for gridThis PR will update the cli go kotlin openapi php python ruby typescript ✅ grid-ruby studio · code
|
|
CI triage on this claimed PR (tip is unchanged from #641, where Lint passed):
|
Greptile SummaryThis PR extends
Confidence Score: 4/5Safe to merge; all changes are additive, optional fields on existing schemas with no breaking changes. The changes are purely additive — new optional fields and two new enum values. The main things to double-check before merging are whether PERSONAL_SAVINGS belongs in a business-only enum and whether the VerificationErrorType description should be updated to cover the new person-level category. Neither would cause a runtime failure, but both affect API consumer clarity. SourceOfFundsCategory.yaml for the PERSONAL_SAVINGS semantic question; VerificationErrorType.yaml for the stale description text.
|
| Filename | Overview |
|---|---|
| openapi/components/schemas/customers/BusinessInfo.yaml | Adds naicsCode (pattern-validated), sourceOfFundsCategories (new SourceOfFundsCategory enum ref), sourceOfFundsOtherDescription, purposeOfAccountOtherDescription, and expectedCounterpartyCountries. Structurally consistent with existing fields. |
| openapi/components/schemas/customers/BusinessInfoUpdate.yaml | Same new fields added as BusinessInfo.yaml; changes are identical in structure and content, consistent with the existing update schema pattern. |
| openapi/components/schemas/customers/BusinessInfoResponse.yaml | Same new fields added as BusinessInfo.yaml; all five new fields are symmetric across create/update/response schemas as expected. |
| openapi/components/schemas/customers/SourceOfFundsCategory.yaml | New enum for structured source-of-funds; includes PERSONAL_SAVINGS which is semantically unusual for a business-entity-only schema and may warrant clarification. |
| openapi/components/schemas/verifications/VerificationErrorType.yaml | Adds MISSING_CONTROL_PERSON and MISSING_GOOD_STANDING_DOCUMENT; the description text wasn't updated to document the new MISSING_CONTROL_PERSON category type. |
Entity Relationship Diagram
%%{init: {'theme': 'neutral'}}%%
erDiagram
BusinessInfo {
string legalName
string taxId
date incorporatedOn
string naicsCode "NEW: 2-6 digit NAICS"
array sourceOfFundsCategories "NEW: SourceOfFundsCategory[]"
string sourceOfFundsOtherDescription "NEW: max 500 chars"
string purposeOfAccountOtherDescription "NEW: max 500 chars"
array expectedCounterpartyCountries "NEW: ISO 3166-1 alpha-2[]"
}
SourceOfFundsCategory {
string OPERATING_REVENUE
string INVESTMENT_INCOME
string LOANS
string VENTURE_CAPITAL
string PERSONAL_SAVINGS
string DONATIONS
string OTHER
}
VerificationErrorType {
string MISSING_CONTROL_PERSON "NEW"
string MISSING_GOOD_STANDING_DOCUMENT "NEW"
string MISSING_BENEFICIAL_OWNER
string MISSING_FIELD
}
BusinessInfo ||--o{ SourceOfFundsCategory : "sourceOfFundsCategories"
%%{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
BusinessInfo {
string legalName
string taxId
date incorporatedOn
string naicsCode "NEW: 2-6 digit NAICS"
array sourceOfFundsCategories "NEW: SourceOfFundsCategory[]"
string sourceOfFundsOtherDescription "NEW: max 500 chars"
string purposeOfAccountOtherDescription "NEW: max 500 chars"
array expectedCounterpartyCountries "NEW: ISO 3166-1 alpha-2[]"
}
SourceOfFundsCategory {
string OPERATING_REVENUE
string INVESTMENT_INCOME
string LOANS
string VENTURE_CAPITAL
string PERSONAL_SAVINGS
string DONATIONS
string OTHER
}
VerificationErrorType {
string MISSING_CONTROL_PERSON "NEW"
string MISSING_GOOD_STANDING_DOCUMENT "NEW"
string MISSING_BENEFICIAL_OWNER
string MISSING_FIELD
}
BusinessInfo ||--o{ SourceOfFundsCategory : "sourceOfFundsCategories"
Comments Outside Diff (1)
-
openapi/components/schemas/verifications/VerificationErrorType.yaml, line 25-31 (link)The
descriptiontext categorises error types into three buckets (MISSING_DOCUMENT, document-quality types, APPLICANT types), butMISSING_CONTROL_PERSONdoesn't fit any of them — it's a missing-person/entity error, not a missing-document or applicant error. Without a description update, API consumers have no guidance on when to expect this new value.MISSING_BENEFICIAL_OWNERhas the same gap, but this PR is the right moment to fix it.Prompt To Fix With AI
This is a comment left during a code review. Path: openapi/components/schemas/verifications/VerificationErrorType.yaml Line: 25-31 Comment: The `description` text categorises error types into three buckets (MISSING_*_DOCUMENT, document-quality types, APPLICANT_* types), but `MISSING_CONTROL_PERSON` doesn't fit any of them — it's a missing-person/entity error, not a missing-document or applicant error. Without a description update, API consumers have no guidance on when to expect this new value. `MISSING_BENEFICIAL_OWNER` has the same gap, but this PR is the right moment to fix it. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 5 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 5
openapi/components/schemas/customers/SourceOfFundsCategory.yaml:7
**`PERSONAL_SAVINGS` in a business-entity enum**
`PERSONAL_SAVINGS` is semantically individual/consumer-oriented and stands out in a schema that applies exclusively to business entities. A client seeing this enum value on a KYB form could incorrectly report the business's source of funds as "personal savings" (e.g., a sole-proprietor confusing business and personal funds), which might create CDD quality issues. Is this intentional (e.g., founder-funded LLC scenarios), or should it be excluded from the business-entity variant in favour of a value like `EQUITY_INVESTMENT` or `OWNER_CAPITAL_CONTRIBUTION`?
### Issue 2 of 5
openapi/components/schemas/verifications/VerificationErrorType.yaml:25-31
The `description` text categorises error types into three buckets (MISSING_*_DOCUMENT, document-quality types, APPLICANT_* types), but `MISSING_CONTROL_PERSON` doesn't fit any of them — it's a missing-person/entity error, not a missing-document or applicant error. Without a description update, API consumers have no guidance on when to expect this new value. `MISSING_BENEFICIAL_OWNER` has the same gap, but this PR is the right moment to fix it.
```suggestion
description: >-
Type of verification error. The category-specific MISSING_*_DOCUMENT types
indicate which document category is needed. Document quality types
(POOR_QUALITY_DOCUMENT, SUSPECTED_FRAUD_DOCUMENT, etc.) indicate specific
issues with uploaded documents. APPLICANT_* types indicate issues with the
applicant themselves (sanctions, fraud, criminal records).
MISSING_BENEFICIAL_OWNER and MISSING_CONTROL_PERSON indicate that a required
person record has not been provided.
example: MISSING_FIELD
```
### Issue 3 of 5
openapi/components/schemas/customers/BusinessInfo.yaml:89-98
Neither `sourceOfFundsOtherDescription` nor `purposeOfAccountOtherDescription` has a `minLength: 1` guard. A client that selects `OTHER` and submits an empty string would pass schema validation while producing a meaningless CDD record. Adding `minLength: 1` to both makes the schema self-documenting about the expected contract and lets validators reject bad payloads before they reach the backend.
```suggestion
sourceOfFundsOtherDescription:
type: string
minLength: 1
maxLength: 500
description: Description of the source of funds when OTHER is selected
example: Proceeds from a legal settlement
purposeOfAccountOtherDescription:
type: string
minLength: 1
maxLength: 500
description: Description of the account purpose when OTHER is selected
example: Escrow for equipment leases
```
### Issue 4 of 5
openapi/components/schemas/customers/BusinessInfoUpdate.yaml:85-94
Same missing `minLength: 1` as in `BusinessInfo.yaml` — applies here in the update schema as well.
```suggestion
sourceOfFundsOtherDescription:
type: string
minLength: 1
maxLength: 500
description: Description of the source of funds when OTHER is selected
example: Proceeds from a legal settlement
purposeOfAccountOtherDescription:
type: string
minLength: 1
maxLength: 500
description: Description of the account purpose when OTHER is selected
example: Escrow for equipment leases
```
### Issue 5 of 5
openapi/components/schemas/customers/BusinessInfoResponse.yaml:90-99
Same missing `minLength: 1` as in `BusinessInfo.yaml` — applies here in the response schema as well.
```suggestion
sourceOfFundsOtherDescription:
type: string
minLength: 1
maxLength: 500
description: Description of the source of funds when OTHER is selected
example: Proceeds from a legal settlement
purposeOfAccountOtherDescription:
type: string
minLength: 1
maxLength: 500
description: Description of the account purpose when OTHER is selected
example: Escrow for equipment leases
```
Reviews (1): Last reviewed commit: "Rename SourceOfFunds enum schema to Sour..." | Re-trigger Greptile
|
📌 Bolt Status 2026-07-07 10:16:12 UTC — ⚡ Agent |
An OTHER selection with an empty description would pass schema validation while producing a meaningless CDD record. Co-Authored-By: akanter <akanter@users.noreply.github.com>
|
LGTM, but adding Victor as the KYB API king. |

Summary
Spec side of webdev ENG-10687 (KYB field gaps for Lead business entities). Companion to the webdev stack rooted at lightsparkdev/webdev#29776.
Adds to
BusinessInfo/BusinessInfoUpdate/BusinessInfoResponse:naicsCode— 2–6 digit NAICS code (Lead takes NAICS viabusiness_details.industry)sourceOfFundsCategories— array referencing the existing (previously unreferenced)SourceOfFundsenum, plussourceOfFundsOtherDescriptionpurposeOfAccountOtherDescription— CDD 'other' description forpurposeOfAccount: OTHERexpectedCounterpartyCountries— geographic-risk CDD field (distinct from send-sideexpectedRecipientJurisdictions)Adds
MISSING_CONTROL_PERSONandMISSING_GOOD_STANDING_DOCUMENTtoVerificationErrorType(control person and Certificate of Good Standing are Lead KYB requirements; the webdev validator will emit these for Lead-partnered platforms).make buildre-bundledopenapi.yaml+mintlify/openapi.yaml.make lint-openapiis baseline-equivalent (same single pre-existing error as main).Test plan
🤖 fierce-nexus-2(#2) | Feedback
Original PR: #641