Skip to content

feat: add external account ownership verification - #806

Draft
shreyav wants to merge 4 commits into
mainfrom
shreyav/ownership-verification
Draft

feat: add external account ownership verification#806
shreyav wants to merge 4 commits into
mainfrom
shreyav/ownership-verification

Conversation

@shreyav

@shreyav shreyav commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a first-class Ownership Verifications API for verifying that a customer (or the platform) controls a FIRST_PARTY self-custody crypto wallet external account.

Endpoints

  • POST /ownership-verifications — begin a verification for an external account
  • POST /ownership-verifications/{verificationId}/confirm — submit the wallet signature (WALLET_SIGNATURE only)
  • GET /ownership-verifications/{verificationId} — fetch a verification
  • GET /ownership-verifications — list, filterable by externalAccountId and status, with cursor pagination

Resource shape

OwnershipVerification is a oneOf discriminated by method:

  • WALLET_SIGNATURE — returns a messageToSign; completes synchronously via the confirm endpoint
  • LIVENESS — returns a hosted verificationLink plus an embed token; completes asynchronously (webhooks or polling)

Per-attempt statuses: PENDING, PENDING_REVIEW, VERIFIED, FAILED (failed attempts are retried by starting a new verification).

External account changes

  • New PENDING_OWNERSHIP_VERIFICATION external-account status. It only ever appears for FIRST_PARTY self-custody wallet accounts on platforms subject to EU Travel Rule requirements, so existing integrations are unaffected. Accounts in this status can be used below regulatory transfer thresholds; completing ownership verification moves them to ACTIVE.
  • Expanded the ownershipType field description.

Webhooks

  • New OWNERSHIP_VERIFICATION.PENDING_REVIEW, OWNERSHIP_VERIFICATION.VERIFIED, and OWNERSHIP_VERIFICATION.FAILED events carrying the full verification object.
  • New EXTERNAL_ACCOUNT.STATUS_UPDATED event carrying the full external account object (e.g., PENDING_OWNERSHIP_VERIFICATIONACTIVE).

Error codes

  • Kept WALLET_VERIFICATION_REQUIRED; removed OWNERSHIP_TYPE_REQUIRED, THIRD_PARTY_TRANSFER_LIMIT_EXCEEDED, and LIGHTNING_TRANSFER_LIMIT_EXCEEDED; extended the AMOUNT_OUT_OF_RANGE description.

Testing

make build bundles cleanly; redocly lint passes (52 warnings, all pre-existing) and spectral lint reports 0 errors.

🤖 Generated with Claude Code

Add verify-ownership and verify-ownership/confirm endpoints for customer
and platform external accounts, supporting wallet-signature and liveness
verification methods. Adds an ownershipVerificationStatus field to
ExternalAccount, EXTERNAL_ACCOUNT.OWNERSHIP_VERIFICATION_* webhook types,
new error codes, and an expanded ownershipType description.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mintlify

mintlify Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
Grid 🟢 Ready View Preview Aug 6, 2026, 4:56 AM

@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

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

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
grid-flow-builder Ignored Ignored Preview Aug 6, 2026 3:36pm
grid-wallet-demo Ignored Ignored Preview Aug 6, 2026 3:36pm

Request Review

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

✱ Stainless preview builds for grid

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

cli

docs(api): clarify ownership-type parameter usage in external accounts

go

feat(api): add ownership verification webhook event and status field to external accounts

kotlin

feat(api): add ownershipVerificationStatus field and webhook events to external accounts

openapi

feat(api): add ownership verification endpoints/webhook to external accounts, update types

php

feat(api): add ownershipVerificationStatus to ExternalAccount, ExternalAccount webhook

python

feat(api): add ownership verification webhook event and status field to external accounts

ruby

feat(api): add ownership verification status field and webhook events to external accounts

typescript

feat(api): add ExternalAccountWebhookEvent, ownershipVerificationStatus to external accounts

Edit this comment to update them. They will appear in their respective SDK's changelogs.

grid-typescript studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ✅lint ❗test ✅

npm install https://pkg.stainless.com/s/grid-typescript/ee4f661e601f105c4735581248eb7a0cef598ee7/dist.tar.gz
New diagnostics (2 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
grid-openapi studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ⚠️

New diagnostics (2 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
grid-ruby studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ✅ (prev: build ❗) → lint ❗test ✅ (prev: test ❗)

New diagnostics (5 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
💡 Ruby/NameShadowedBuiltin: Renamed Ruby method from "method" to "method_" to avoid shadowing a built-in method.
💡 Ruby/NameShadowedBuiltin: Renamed Ruby method from "method" to "method_" to avoid shadowing a built-in method.
💡 Ruby/NameShadowedBuiltin: Renamed Ruby method from "method" to "method_" to avoid shadowing a built-in method.
grid-go studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

go get github.com/stainless-sdks/grid-go@0869ef5f6cf0cd1d51f5620c8c0c9e1896b8153f
New diagnostics (5 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
⚠️ grid-kotlin studio · code · diff

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ⚠️build ❗lint ❗ (prev: lint ✅) → test ❗

New diagnostics (5 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
grid-python studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ❗lint ❗test ❗

New diagnostics (2 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
grid-php studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅lint ✅ (prev: lint ❗) → test ✅ (prev: test ❗)

New diagnostics (5 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
grid-cli studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ⚠️build ❗lint ❗test ❗

New diagnostics (2 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`

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-08-06 15:48:20 UTC

…consistency

Payments fire webhooks for their creation-time state (OUTGOING_PAYMENT.PENDING),
so ownership verification does the same: NOT_REQUIRED and REQUIRED join the
async transitions, giving webhook consumers the full lifecycle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ions resource

Replace the external-account verb endpoints (verify-ownership +
verify-ownership/confirm under /customers and /platform) with a top-level
Ownership Verifications API mirroring the KYC/KYB Verifications pattern:

- POST /ownership-verifications, GET /ownership-verifications (filter by
  externalAccountId/status with cursor pagination),
  GET /ownership-verifications/{verificationId}, and
  POST /ownership-verifications/{verificationId}/confirm
- OwnershipVerification is a oneOf on method: WalletSignatureOwnershipVerification
  (messageToSign, confirmed synchronously) and LivenessOwnershipVerification
  (verificationLink + token, completes asynchronously)
- Drop ExternalAccount.ownershipVerificationStatus; add the
  PENDING_OWNERSHIP_VERIFICATION external-account status for FIRST_PARTY
  self-custody wallets on platforms subject to EU Travel Rule requirements
- Replace EXTERNAL_ACCOUNT.OWNERSHIP_VERIFICATION_* webhooks with
  OWNERSHIP_VERIFICATION.{PENDING_REVIEW,VERIFIED,FAILED} resource webhooks
  and a new EXTERNAL_ACCOUNT.STATUS_UPDATED webhook
- Trim removed error codes and extend AMOUNT_OUT_OF_RANGE description

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…less resource

- Revert AMOUNT_OUT_OF_RANGE description to its original text
- Rename WALLET_VERIFICATION_REQUIRED to EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED
- Document EXTERNAL_ACCOUNT.STATUS_UPDATED as firing on every status
  transition, not only ownership verification
- Register the ownership_verifications resource in .stainless/stainless.yml so
  the endpoints reach the documented spec and SDKs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant