Skip to content

feat(customers): split BusinessInfo response schema (taxId/incorporatedOn optional)#524

Merged
wuvictor-95 merged 1 commit into
mainfrom
business-info-response-optional
May 28, 2026
Merged

feat(customers): split BusinessInfo response schema (taxId/incorporatedOn optional)#524
wuvictor-95 merged 1 commit into
mainfrom
business-info-response-optional

Conversation

@wuvictor-95
Copy link
Copy Markdown
Contributor

Summary

  • Adds BusinessInfoResponse schema where taxId and incorporatedOn are optional, and points BusinessCustomer (response model) at it.
  • Leaves the existing BusinessInfo schema unchanged for the create request, so KYB creation still enforces taxId + incorporatedOn.
  • Rebundles openapi.yaml / mintlify/openapi.yaml.

Why

Pre-existing business customers in our DB have null tax_id / incorporated_on (data predates the requirement). The current spec marks both required on every read, which causes generated clients to fail when constructing a BusinessCustomer from legacy rows for the grid dashboard. Splitting the read and write models lets us keep the write contract strict without lying about legacy reads.

Test plan

  • make build produces a valid bundle
  • make lint-openapi passes (Redocly validation)
  • Downstream sparkcore PR regenerates the python client off this spec and removes the now-unnecessary none_throws(...) calls

…edOn optional)

Adds BusinessInfoResponse with taxId and incorporatedOn optional so legacy
customers with null values deserialize cleanly. The request-side BusinessInfo
schema keeps both fields required so KYB creation continues to enforce them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
grid-flow-builder Ready Ready Preview, Comment May 28, 2026 4:19pm

Request Review

@github-actions github-actions Bot added the breaking-change Introduces a breaking change to the OpenAPI spec label May 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

⚠️ Breaking OpenAPI changes detected

This PR introduces breaking changes to openapi.yaml:

API Changelog 2025-10-13 vs. 2025-10-13

API Changes

GET /customers

  • ⚠️ the response property data/items/oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/incorporatedOn became optional for the status 200
  • ⚠️ the response property data/items/oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/taxId became optional for the status 200

POST /customers

  • ⚠️ the response property oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/incorporatedOn became optional for the status 201
  • ⚠️ the response property oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/taxId became optional for the status 201

DELETE /customers/{customerId}

  • ⚠️ the response property oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/incorporatedOn became optional for the status 200
  • ⚠️ the response property oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/taxId became optional for the status 200

GET /customers/{customerId}

  • ⚠️ the response property oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/incorporatedOn became optional for the status 200
  • ⚠️ the response property oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/taxId became optional for the status 200

PATCH /customers/{customerId}

  • ⚠️ the response property oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/incorporatedOn became optional for the status 200
  • ⚠️ the response property oneOf[subschema #2: Business Customer]/allOf[subschema #3]/businessInfo/taxId became optional for the status 200

Detected by oasdiff. This PR will need approval from an API reviewer before merge.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

✱ Stainless preview builds for grid

This PR will update the grid SDKs with the following commit messages.

cli

chore(internal): regenerate SDK with no functional changes

csharp

fix(types): use specific BusinessInfo type in CustomerOneOfBusinessIntersectionMember2

go

fix(types): make taxId/incorporatedOn optional in business customer responses

kotlin

feat(api): add BusinessInfo model to customers

openapi

fix(types): make taxId/incorporatedOn optional in BusinessInfoResponse

php

feat(api): add BusinessInfo model and related enums to customers

python

fix(types): make tax_id/incorporated_on optional, inline BusinessInfo in BusinessCustomer

ruby

chore(internal): regenerate SDK with no functional changes

typescript

feat(types): add BusinessInfo interface definition to customers
⚠️ grid-openapi studio · code

Your SDK build had at least one warning diagnostic.
generate ✅

⚠️ grid-ruby studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️build ⏭️lint ✅test ✅

⚠️ grid-typescript studio · code

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅build ✅lint ❗test ✅

npm install https://pkg.stainless.com/s/grid-typescript/8e18059c8b7d20009a784b2e2cb222c11068c99f/dist.tar.gz
⚠️ grid-python studio · code

Your SDK build had at least one warning diagnostic.
generate ✅build ✅lint ✅test ✅

pip install https://pkg.stainless.com/s/grid-python/8a93a3528bce1cf0b267bf01227f3268b27cdc16/grid-0.0.1-py3-none-any.whl
grid-csharp studio · code

generate ⚠️build ❗lint ✅test ⏳

⚠️ grid-go studio · code

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

go get github.com/stainless-sdks/grid-go@2257ee0afba83ed63d4037284e509f6e6dddef70
⚠️ grid-php studio · code

Your SDK build had at least one warning diagnostic.
generate ✅lint ✅test ✅

⚠️ grid-kotlin studio · code

Your SDK build had a failure in the build CI job, which is a regression from the base state.
generate ✅build ❗lint ✅test ❗

⚠️ grid-cli studio · code

Your SDK build had a failure in the test CI job, which is a regression from the base state.
generate ⚠️build ⏭️lint ⏭️test ❗

⏳ These are partial results; builds are still running.


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-05-28 16:41:05 UTC

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 28, 2026

Greptile Summary

   (\\_/)
  ( \u2022\u1d17\u2022)  wuvictor-95 splitting schemas!
  / >\ud83d\udcc4    taxId optional go brrr

This PR introduces a BusinessInfoResponse read-model schema that relaxes taxId and incorporatedOn from required to optional, pointing BusinessCustomer at it while leaving the stricter BusinessInfo write-model intact for KYB creation.

  • New BusinessInfoResponse.yaml: Copy of BusinessInfo with taxId and incorporatedOn removed from required; BusinessCustomer now references this schema.
  • BusinessInfo unchanged: Create requests still enforce both fields, keeping the write contract strict.
  • Bundles regenerated: Both openapi.yaml and mintlify/openapi.yaml are updated via make build.

Confidence Score: 3/5

Safe to merge after verifying whether the server sends null or omits the fields entirely for legacy records — the answer determines whether the nullable annotations are required.

The read/write schema split is the correct approach and BusinessCustomer.yaml is wired up cleanly. The uncertainty is in BusinessInfoResponse.yaml: the DB has null values for taxId/incorporatedOn, and if the API serialises those rows with explicit null values (rather than omitting the keys), OAS 3.1 clients will still reject the response because null is not a valid string. The downstream removal of none_throws() calls suggests the server does return null today.

openapi/components/schemas/customers/BusinessInfoResponse.yaml — specifically the taxId and incorporatedOn field definitions.

Important Files Changed

Filename Overview
openapi/components/schemas/customers/BusinessInfoResponse.yaml New read-model schema making taxId and incorporatedOn optional — needs nullable annotations for both fields since legacy DB rows carry null (not absent) values.
openapi/components/schemas/customers/BusinessCustomer.yaml One-line ref change from BusinessInfo to BusinessInfoResponse — correct and clean.
openapi.yaml Regenerated bundle reflecting source changes; both BusinessInfoResponse and original BusinessInfo schemas are present with correct required fields.
mintlify/openapi.yaml Mintlify bundle regenerated in sync with openapi.yaml — no independent issues.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class BusinessInfo {
        +string legalName [required]
        +string taxId [required]
        +string incorporatedOn [required]
        +string doingBusinessAs
        +string country
    }
    class BusinessInfoResponse {
        +string legalName [required]
        +string taxId [optional]
        +string incorporatedOn [optional]
        +string doingBusinessAs
        +string country
    }
    class BusinessCustomer {
        +BusinessInfoResponse businessInfo
        +BeneficialOwner[] beneficialOwners
    }
    class BusinessCustomerCreateRequest {
        +BusinessInfo businessInfo
    }
    BusinessCustomer --> BusinessInfoResponse : read (response)
    BusinessCustomerCreateRequest --> BusinessInfo : write (request)
Loading

Fix All in Claude Code

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/customers/BusinessInfoResponse.yaml:32-35
**Nullable vs. absent — `taxId` may need `type: 'null'` in OAS 3.1**

The PR description notes that legacy DB rows have `null` values for `tax_id` and `incorporated_on`. Removing these fields from `required` allows them to be *absent*, but in OpenAPI 3.1.0 (which this spec uses) an absent field is not the same as a `null` value — `null` is only valid when the schema explicitly permits it. If the API serialises legacy rows by writing `taxId: null` rather than omitting the key, any strictly-conforming OAS 3.1 client or validator will still reject the response. The existing codebase pattern (see `Card.yaml` `stateReason`) uses `oneOf: [{...}, {type: 'null'}]` for exactly this case.

```suggestion
  taxId:
    oneOf:
      - type: string
        description: Tax identification number
        example: 47-1234567
      - type: 'null'
```

### Issue 2 of 2
openapi/components/schemas/customers/BusinessInfoResponse.yaml:25-29
**`incorporatedOn` also needs nullable annotation**

Same issue as `taxId`: if legacy records serialise this field as `null` rather than omitting it, OAS 3.1 validators and generated clients will reject the response because `null` is not a valid `date`-formatted string.

```suggestion
  incorporatedOn:
    oneOf:
      - type: string
        format: date
        description: Date of incorporation in ISO 8601 format (YYYY-MM-DD)
        example: '2018-03-14'
      - type: 'null'
```

Reviews (1): Last reviewed commit: "feat(customers): split BusinessInfo resp..." | Re-trigger Greptile

Comment on lines +32 to +35
taxId:
type: string
description: Tax identification number
example: 47-1234567
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Nullable vs. absent — taxId may need type: 'null' in OAS 3.1

The PR description notes that legacy DB rows have null values for tax_id and incorporated_on. Removing these fields from required allows them to be absent, but in OpenAPI 3.1.0 (which this spec uses) an absent field is not the same as a null value — null is only valid when the schema explicitly permits it. If the API serialises legacy rows by writing taxId: null rather than omitting the key, any strictly-conforming OAS 3.1 client or validator will still reject the response. The existing codebase pattern (see Card.yaml stateReason) uses oneOf: [{...}, {type: 'null'}] for exactly this case.

Suggested change
taxId:
type: string
description: Tax identification number
example: 47-1234567
taxId:
oneOf:
- type: string
description: Tax identification number
example: 47-1234567
- type: 'null'
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/customers/BusinessInfoResponse.yaml
Line: 32-35

Comment:
**Nullable vs. absent — `taxId` may need `type: 'null'` in OAS 3.1**

The PR description notes that legacy DB rows have `null` values for `tax_id` and `incorporated_on`. Removing these fields from `required` allows them to be *absent*, but in OpenAPI 3.1.0 (which this spec uses) an absent field is not the same as a `null` value — `null` is only valid when the schema explicitly permits it. If the API serialises legacy rows by writing `taxId: null` rather than omitting the key, any strictly-conforming OAS 3.1 client or validator will still reject the response. The existing codebase pattern (see `Card.yaml` `stateReason`) uses `oneOf: [{...}, {type: 'null'}]` for exactly this case.

```suggestion
  taxId:
    oneOf:
      - type: string
        description: Tax identification number
        example: 47-1234567
      - type: 'null'
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

Comment on lines +25 to +29
incorporatedOn:
type: string
format: date
description: Date of incorporation in ISO 8601 format (YYYY-MM-DD)
example: '2018-03-14'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 incorporatedOn also needs nullable annotation

Same issue as taxId: if legacy records serialise this field as null rather than omitting it, OAS 3.1 validators and generated clients will reject the response because null is not a valid date-formatted string.

Suggested change
incorporatedOn:
type: string
format: date
description: Date of incorporation in ISO 8601 format (YYYY-MM-DD)
example: '2018-03-14'
incorporatedOn:
oneOf:
- type: string
format: date
description: Date of incorporation in ISO 8601 format (YYYY-MM-DD)
example: '2018-03-14'
- type: 'null'
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/customers/BusinessInfoResponse.yaml
Line: 25-29

Comment:
**`incorporatedOn` also needs nullable annotation**

Same issue as `taxId`: if legacy records serialise this field as `null` rather than omitting it, OAS 3.1 validators and generated clients will reject the response because `null` is not a valid `date`-formatted string.

```suggestion
  incorporatedOn:
    oneOf:
      - type: string
        format: date
        description: Date of incorporation in ISO 8601 format (YYYY-MM-DD)
        example: '2018-03-14'
      - type: 'null'
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@wuvictor-95 wuvictor-95 enabled auto-merge (squash) May 28, 2026 16:28
@wuvictor-95 wuvictor-95 merged commit 1977252 into main May 28, 2026
9 checks passed
@wuvictor-95 wuvictor-95 deleted the business-info-response-optional branch May 28, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Introduces a breaking change to the OpenAPI spec

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants