Skip to content

feat: adding kyb schema#282

Merged
pengying merged 1 commit intomainfrom
03-19-feat_adding_kyb_schema
Mar 20, 2026
Merged

feat: adding kyb schema#282
pengying merged 1 commit intomainfrom
03-19-feat_adding_kyb_schema

Conversation

@pengying
Copy link
Contributor

@pengying pengying commented Mar 19, 2026

TL;DR

Added comprehensive KYC/KYB verification endpoints including beneficial owner management, document upload capabilities, and verification workflow APIs.

What changed?

Added three new API endpoint groups:

Beneficial Owners API:

  • POST /beneficial-owners - Create beneficial owners, directors, and company officers for business customers
  • GET /beneficial-owners - List beneficial owners with filtering by customer ID and role
  • GET /beneficial-owners/{beneficialOwnerId} - Retrieve specific beneficial owner details

Documents API:

  • POST /documents - Upload verification documents (PDF, JPEG, PNG up to 10MB) with multipart/form-data
  • GET /documents - List documents with filtering by holder, type, and status
  • GET /documents/{documentId} - Retrieve document metadata
  • PUT /documents/{documentId} - Replace existing documents
  • DELETE /documents/{documentId} - Delete documents (with conflict handling for submitted docs)

Verifications API:

  • POST /verifications - Submit customers for KYC/KYB verification with detailed error reporting

Enhanced business customer schemas with additional fields including entity type, business type, incorporation details, expected activity volumes, and compliance information.

How to test?

  1. Create a business customer and add beneficial owners using the new beneficial owner endpoints
  2. Upload required documents (passports, proof of address, business licenses) using the document upload API
  3. Submit the customer for verification and check the response for any missing information or validation errors
  4. Test document replacement workflow for rejected documents
  5. Verify pagination works correctly for listing endpoints

Why make this change?

Enables comprehensive KYC/KYB compliance workflows by providing APIs to manage all required verification data including beneficial ownership information, document uploads, and verification status tracking with actionable error messages.

@vercel
Copy link

vercel bot commented Mar 19, 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 Mar 20, 2026 9:48pm

Request Review

Copy link
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 19, 2026

✱ Stainless preview builds

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

kotlin

feat: adding kyb schema

openapi

feat(api): add beneficial_owners/documents/verifications endpoints, update business types

python

feat(api): add beneficial_owners/documents/verifications resources, update business types

typescript

feat(api): add beneficialOwners/documents/verifications resources, update customer types
grid-openapi studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅

⚠️ grid-python 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 ✅

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

Your SDK build had at least one "note" 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/b59117b39c48747c930c489fe60aed81d615889d/dist.tar.gz

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-03-20 21:58:20 UTC

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 19, 2026

Greptile Summary

This PR introduces comprehensive KYC/KYB verification endpoints: beneficial owner management (POST/GET/PATCH /beneficial-owners), document upload and lifecycle (POST/GET/PUT/DELETE /documents), and a verification submission workflow (POST/GET /verifications), along with supporting schemas and sandbox documentation. The feature is well-structured and follows the existing API conventions.

Key issues found:

  • personalIds schema bug (P0): In both BeneficialOwnerPersonalInfo.yaml and BeneficialOwnerPersonalInfoUpdate.yaml, the personalIds field is typed as a direct $ref to PersonalIdentification (a single object), rather than as an array of PersonalIdentification items. The plural name and KYB domain requirements both indicate this should be an array — beneficial owners in many jurisdictions must supply multiple forms of identification. This will cause API consumers to submit/receive a single object where an array is expected.
  • updatedAt missing from required in Verification, BeneficialOwner, and Document schemas: All three response schemas define updatedAt in properties but omit it from required, making it technically optional for strictly-conforming consumers.
  • Webhook examples omit updatedAt from the embedded Verification payload, making examples inconsistent with the schema.
  • Breaking removals in WebhookType.yaml and KycStatus/KybStatus: Several previously published enum values are removed without deprecation; existing webhook subscribers will silently stop receiving events.
  • Document schema missing reviewResult/status field: Clients cannot determine document approval state via the documents API itself.
  • ExpectedActivityVolumes enum values start with digits (10_TO_100, 10K_TO_100K, etc.), which will cause SDK code-generator failures in most target languages.
  • POST /verifications returns 200 rather than 201 Created, inconsistent with POST /beneficial-owners and POST /documents in the same PR.

Confidence Score: 2/5

  • Not safe to merge — the personalIds schema bug will ship a fundamentally broken field type to all API consumers, and several breaking enum removals lack deprecation handling.
  • The personalIds single-object vs. array mismatch is a P0 schema bug that will immediately break any client that tries to submit or read multiple identification documents for a beneficial owner. Combined with the unresolved breaking removals from previous review rounds (webhook types, KYC/KYB status values) and the missing Document.status field, the spec has multiple correctness issues that should be addressed before this ships.
  • openapi/components/schemas/customers/BeneficialOwnerPersonalInfo.yaml and BeneficialOwnerPersonalInfoUpdate.yaml (personalIds type), openapi/components/schemas/webhooks/WebhookType.yaml (breaking removals), openapi/components/schemas/customers/KycStatus.yaml / KybStatus.yaml (breaking removals), openapi/components/schemas/documents/Document.yaml (missing status field).

Important Files Changed

Filename Overview
openapi/components/schemas/customers/BeneficialOwnerPersonalInfo.yaml Defines personal info required for a beneficial owner; personalIds is incorrectly typed as a single $ref to PersonalIdentification (an object) instead of an array — this prevents representing multiple ID documents for one person.
openapi/components/schemas/customers/BeneficialOwnerPersonalInfoUpdate.yaml Partial-update variant of personal info; carries the same personalIds single-object typing bug as BeneficialOwnerPersonalInfo.yaml.
openapi/paths/verifications/verifications.yaml Adds POST (submit verification) and GET (list verifications) endpoints; POST /verifications correctly returns the Verification resource with an id, but uses 200 instead of the conventional 201 Created status code for resource creation.
openapi/components/schemas/verifications/Verification.yaml New Verification schema; has id, customerId, verificationStatus, errors, and createdAt in required, but updatedAt — which is defined in properties — is absent from the required array, making it technically optional to schema-strict consumers.
openapi/components/schemas/documents/Document.yaml New Document response schema; missing a status/reviewResult field, updatedAt is not in required, making document review state invisible to clients querying GET /documents.
openapi/paths/beneficial-owners/beneficial_owners_{beneficialOwnerId}.yaml Defines GET and newly added PATCH operations for a specific beneficial owner; PATCH correctly references BeneficialOwnerUpdateRequest which uses the all-optional BeneficialOwnerPersonalInfoUpdate schema for partial updates — good design.
openapi/components/schemas/customers/BeneficialOwner.yaml Beneficial owner response schema; updatedAt is defined in properties but absent from the required array, making it technically optional for schema-strict consumers even though the field is always populated.
openapi/components/schemas/webhooks/WebhookType.yaml Breaking: removes CUSTOMER.KYC_SUBMITTED, CUSTOMER.KYB_SUBMITTED, CUSTOMER.KYC_MANUALLY_APPROVED/REJECTED, and CUSTOMER.KYB_MANUALLY_APPROVED/REJECTED enum values; existing clients subscribed to those event types will silently stop receiving events.
openapi/components/schemas/customers/ExpectedActivityVolumes.yaml Defines expected transaction count/volume enums; several values start with a digit (10_TO_100, 100_TO_500, 10K_TO_100K) which are invalid identifiers in most code-generation target languages and will cause SDK compilation failures.
mintlify/snippets/sandbox-kyb-verification.mdx New sandbox testing snippet for KYC/KYB; document verification result column shows generic REJECTED label but the Document schema doesn't expose a reviewResult/status field at all, so the table describes state that can't currently be observed via the API.

Sequence Diagram

sequenceDiagram
    participant C as Client
    participant API as Grid API
    participant WH as Client Webhook

    C->>API: POST /customers (business)
    API-->>C: 201 Customer {id, kybStatus: UNVERIFIED}

    C->>API: POST /beneficial-owners
    API-->>C: 201 BeneficialOwner {id, kycStatus: PENDING}

    C->>API: POST /documents (multipart/form-data)
    API-->>C: 201 Document {id, documentType, ...}

    C->>API: POST /verifications {customerId}
    alt Missing information
        API-->>C: 200 Verification {verificationStatus: RESOLVE_ERRORS, errors: [...]}
        C->>API: PATCH /customers/{id} (fix missing fields)
        C->>API: PUT /documents/{documentId} (replace rejected doc)
        C->>API: POST /verifications {customerId} (re-submit)
    end
    API-->>C: 200 Verification {verificationStatus: IN_PROGRESS, errors: []}

    API->>WH: POST webhook {type: VERIFICATION.APPROVED}
    WH-->>API: 200 OK
    Note over C,API: Customer kybStatus → APPROVED
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: openapi/components/schemas/customers/BeneficialOwnerPersonalInfo.yaml
Line: 43-44

Comment:
**`personalIds` typed as single object instead of array**

`personalIds` resolves to the `PersonalIdentification` schema directly — which is `type: object` — meaning the field accepts exactly **one** identification document, not a list. The plural name `personalIds` and the KYB domain context (many jurisdictions require collecting both a government-issued ID *and* a secondary ID such as a tax number) strongly indicate this was intended to be an array.

As it stands, a beneficial owner with two forms of ID (e.g., passport + SSN) cannot be represented. The field should be declared as:

```suggestion
  personalIds:
    type: array
    items:
      $ref: ./PersonalIdentification.yaml
```

The same fix is needed in `BeneficialOwnerPersonalInfoUpdate.yaml` line 38.

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

---

This is a comment left during a code review.
Path: openapi/components/schemas/customers/BeneficialOwnerPersonalInfoUpdate.yaml
Line: 37-38

Comment:
**`personalIds` typed as single object instead of array**

Same issue as in `BeneficialOwnerPersonalInfo.yaml``personalIds` directly `$ref`s the `PersonalIdentification` schema (a single object) rather than defining it as an array of identifications. This should be:

```suggestion
  personalIds:
    type: array
    items:
      $ref: ./PersonalIdentification.yaml
```

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

---

This is a comment left during a code review.
Path: openapi/webhooks/verification-update.yaml
Line: 50-51

Comment:
**Webhook examples missing `updatedAt` field**

The `Verification` schema defines `updatedAt` in its `properties`, and real server responses will include it. Both the `approved` and `resolveErrors` webhook examples omit `updatedAt` from the embedded `Verification` data object. This makes the examples inconsistent with the schema and may confuse integrators who read them to understand the payload shape.

Consider adding `updatedAt` to both examples, e.g.:

```yaml
              createdAt: '2025-08-15T14:00:00Z'
              updatedAt: '2025-08-15T14:32:00Z'
```

The same omission exists in the inline examples inside `openapi/paths/verifications/verifications.yaml` (lines 54 and 62).

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

Last reviewed commit: "feat: adding kyb sch..."

@pengying pengying force-pushed the 03-19-feat_adding_kyb_schema branch from af1544d to 3ed8713 Compare March 19, 2026 22:34
@pengying pengying force-pushed the 03-19-feat_adding_kyb_schema branch from 3ed8713 to 511e2de Compare March 19, 2026 22:41
@pengying pengying force-pushed the 03-19-feat_adding_kyb_schema branch from 511e2de to 6e4f7df Compare March 19, 2026 22:43
@pengying pengying force-pushed the 03-19-feat_adding_kyb_schema branch from 6e4f7df to 01ddcc3 Compare March 19, 2026 22:48
@pengying pengying force-pushed the 03-19-feat_adding_kyb_schema branch from 01ddcc3 to 94498a6 Compare March 19, 2026 22:57
@pengying pengying force-pushed the 03-19-feat_adding_kyb_schema branch from 94498a6 to 127e554 Compare March 19, 2026 23:08
Comment on lines +15 to +16
- BENEFICIAL_OWNER.KYC_MANUALLY_APPROVED
- BENEFICIAL_OWNER.KYC_MANUALLY_REJECTED

Choose a reason for hiding this comment

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

Not sure if we need this level of precision

Choose a reason for hiding this comment

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

we can update later

Comment on lines +15 to +16
- VERIFICATION.IN_PROGRESS
- VERIFICATION.PENDING_MANUAL_REVIEW

Choose a reason for hiding this comment

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

I think just IN_PROGRESS will be fine

Choose a reason for hiding this comment

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

we can update these later

type: string
enum:
- AWAITING_SUBMISSION
- UNVERIFIED

Choose a reason for hiding this comment

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

Is PENDING sufficient? Is unverified meant to be unsubmitted basically?

pattern: '^\+[1-9]\d{1,14}$'
address:
$ref: ../common/Address.yaml
personalIds:

Choose a reason for hiding this comment

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

Can we remove this for now? I think we get this automatically with personal identification document being a hard requirement for all UBOs

Dot-notation path to the field with the issue. Present when type is
MISSING_FIELD or INVALID_FIELD.
example: customer.address.line1
documentType:

Choose a reason for hiding this comment

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

I think documentType might need to return a list of documentTypes for instances where we accept oneOf a set e.g. [DRIVERS, PASSPORT]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought you would return one error for each document issue?

Choose a reason for hiding this comment

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

How do we indicate to users that we are missing oneOf a set of acceptable documents. Let's say for UBOs, we can take either a DRIVERS or a PASSPORT. Would it be confusing if we have two separate MISSING_DOCUMENT objects?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see what you're saying

Comment on lines +43 to +44
personalIds:
$ref: ./PersonalIdentification.yaml
Copy link
Contributor

Choose a reason for hiding this comment

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

P0 personalIds typed as single object instead of array

personalIds resolves to the PersonalIdentification schema directly — which is type: object — meaning the field accepts exactly one identification document, not a list. The plural name personalIds and the KYB domain context (many jurisdictions require collecting both a government-issued ID and a secondary ID such as a tax number) strongly indicate this was intended to be an array.

As it stands, a beneficial owner with two forms of ID (e.g., passport + SSN) cannot be represented. The field should be declared as:

Suggested change
personalIds:
$ref: ./PersonalIdentification.yaml
personalIds:
type: array
items:
$ref: ./PersonalIdentification.yaml

The same fix is needed in BeneficialOwnerPersonalInfoUpdate.yaml line 38.

Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/customers/BeneficialOwnerPersonalInfo.yaml
Line: 43-44

Comment:
**`personalIds` typed as single object instead of array**

`personalIds` resolves to the `PersonalIdentification` schema directly — which is `type: object` — meaning the field accepts exactly **one** identification document, not a list. The plural name `personalIds` and the KYB domain context (many jurisdictions require collecting both a government-issued ID *and* a secondary ID such as a tax number) strongly indicate this was intended to be an array.

As it stands, a beneficial owner with two forms of ID (e.g., passport + SSN) cannot be represented. The field should be declared as:

```suggestion
  personalIds:
    type: array
    items:
      $ref: ./PersonalIdentification.yaml
```

The same fix is needed in `BeneficialOwnerPersonalInfoUpdate.yaml` line 38.

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

Comment on lines +37 to +38
personalIds:
$ref: ./PersonalIdentification.yaml
Copy link
Contributor

Choose a reason for hiding this comment

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

P0 personalIds typed as single object instead of array

Same issue as in BeneficialOwnerPersonalInfo.yamlpersonalIds directly $refs the PersonalIdentification schema (a single object) rather than defining it as an array of identifications. This should be:

Suggested change
personalIds:
$ref: ./PersonalIdentification.yaml
personalIds:
type: array
items:
$ref: ./PersonalIdentification.yaml
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/customers/BeneficialOwnerPersonalInfoUpdate.yaml
Line: 37-38

Comment:
**`personalIds` typed as single object instead of array**

Same issue as in `BeneficialOwnerPersonalInfo.yaml``personalIds` directly `$ref`s the `PersonalIdentification` schema (a single object) rather than defining it as an array of identifications. This should be:

```suggestion
  personalIds:
    type: array
    items:
      $ref: ./PersonalIdentification.yaml
```

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

Comment on lines +50 to +51
errors: []
createdAt: '2025-08-15T14:00:00Z'
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Webhook examples missing updatedAt field

The Verification schema defines updatedAt in its properties, and real server responses will include it. Both the approved and resolveErrors webhook examples omit updatedAt from the embedded Verification data object. This makes the examples inconsistent with the schema and may confuse integrators who read them to understand the payload shape.

Consider adding updatedAt to both examples, e.g.:

              createdAt: '2025-08-15T14:00:00Z'
              updatedAt: '2025-08-15T14:32:00Z'

The same omission exists in the inline examples inside openapi/paths/verifications/verifications.yaml (lines 54 and 62).

Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/webhooks/verification-update.yaml
Line: 50-51

Comment:
**Webhook examples missing `updatedAt` field**

The `Verification` schema defines `updatedAt` in its `properties`, and real server responses will include it. Both the `approved` and `resolveErrors` webhook examples omit `updatedAt` from the embedded `Verification` data object. This makes the examples inconsistent with the schema and may confuse integrators who read them to understand the payload shape.

Consider adding `updatedAt` to both examples, e.g.:

```yaml
              createdAt: '2025-08-15T14:00:00Z'
              updatedAt: '2025-08-15T14:32:00Z'
```

The same omission exists in the inline examples inside `openapi/paths/verifications/verifications.yaml` (lines 54 and 62).

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants