feat: add VASP directory endpoint - #827
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
✱ Stainless preview builds for gridThis PR will update the cli go kotlin openapi php python ruby typescript
|
## Summary Crypto-wallet external accounts were the only account family without a `beneficiary`. This PR adds one, following the fiat pattern — an `INDIVIDUAL`/`BUSINESS` `oneOf` discriminated by `beneficiaryType`. The beneficiary identifies who owns the wallet — the counterparty identity needed for Travel Rule data, independent of custody. This is PR 2 of 3 for VASP counterparty support (Travel Rule): 1. #827 — `/vasps` directory 2. **This PR** — `beneficiary` on crypto-wallet external accounts 3. #829 — `custodyType`/`vaspName` on external accounts ### Field requirements: exactly what is transmitted The individual variant is a new `WalletIndividualBeneficiary` with only **`fullName` + `countryOfResidence`** (both required) — the exact set transmitted as Travel Rule counterparty info. The generic `IndividualBeneficiary` couldn't be reused because it *requires* `birthDate`/`nationality`, which are never transmitted for wallets and would force platforms to collect a third party's date of birth (OpenAPI composition can't relax `required`). Optional PII fields were deliberately omitted: adding optional fields later is non-breaking, while accepting-but-ignoring PII invites needless collection. The `BUSINESS` variant reuses the existing `BusinessBeneficiary` (`legalName` required). ### Semantics (one deliberate divergence from fiat) - Fiat accounts require `beneficiary`. For wallets it is **optional for `FIRST_PARTY`** — when omitted, the customer's verified identity is used, so the dominant own-wallet case sends nothing extra. - **Required for `THIRD_PARTY`** wallets on platforms subject to counterparty requirements (e.g., EU Travel Rule and similar regimes) — enforced at runtime with `400 INVALID_INPUT`, not in the schema, since the requirement is platform-dependent. ### Changes - New `WalletBeneficiaryFields` fragment (the `beneficiary` property) composed into all seven wallet variants: `BASE_WALLET`, `ETHEREUM_WALLET`, `POLYGON_WALLET`, `PLASMA_WALLET`, `SOLANA_WALLET`, `SPARK_WALLET`, `TRON_WALLET` - New `WalletBeneficiaryOneOf` — the named individual/business union (matches the `*OneOf` house convention) - New `WalletIndividualBeneficiary` schema (`fullName` + `countryOfResidence`) - Stainless model entries for all three ### Out of scope - `LIGHTNING` external accounts — Travel Rule counterparty identity for Lightning flows in-band (payment-level), not via a stored account beneficiary. Flagging in case reviewers feel otherwise. ## Testing `make build` bundles cleanly; `redocly lint` and `spectral lint` match the pre-existing baseline on `main` exactly (no new findings). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds url for picker confirmation, drops totalCount (Striga's search returns no total, so it could never be populated). Keeps a single name field: the provider's declare-counterparty call takes only its own id, so the exposed name is a Grid-side key that sparkcore maps back.
Matches the sibling directory endpoints (/discoveries, /uma-providers), which are paginated fetch-and-filter-locally; integrators can cache the directory and search it client-side.
Greptile SummaryAdds an authenticated, cursor-paginated VASP directory to the modular and bundled OpenAPI specifications.
Confidence Score: 5/5The PR appears safe to merge with no actionable defects identified. The modular source, generated bundles, Stainless mapping, response schemas, and Mintlify styling are mutually consistent, while the intentionally omitted total count and deferred counterparty consumer are explicitly covered by the PR scope.
|
| Filename | Overview |
|---|---|
| openapi/paths/vasps/vasps.yaml | Defines the authenticated list operation with bounded cursor-pagination parameters and standard error responses. |
| openapi/components/schemas/vasps/Vasp.yaml | Defines the required VASP name and website fields used by directory entries. |
| openapi/components/schemas/vasps/VaspListResponse.yaml | Defines a cursor-pagination envelope with data, hasMore, and an optional nextCursor. |
| openapi/openapi.yaml | Registers the VASPs tag and modular /vasps path in the source specification. |
| .stainless/stainless.yml | Maps the new VASP models and list operation into generated SDK resources. |
| mintlify/style.css | Adds a sidebar icon rule using an existing globe asset and the established API-tag selector pattern. |
| openapi.yaml | Correctly bundles the new endpoint and schemas from the modular source. |
| mintlify/openapi.yaml | Mirrors the generated root bundle for local Mintlify API-reference rendering. |
Reviews (1): Last reviewed commit: "Fix stale filter wording in the VASP lis..." | Re-trigger Greptile
## Summary Follow-up to #827. `Vasp.url` was marked required, but the upstream directory returns an empty string for it on most entries, so it can't be promised. Drops it from `required` and softens the description to "when known". ## Testing `make build` bundles cleanly; `redocly lint` and `spectral lint` match the pre-existing baseline on `main` exactly (no new findings). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01Ux8rU9Sm2sNFD1FUafY1wV)_
Summary
Adds a VASP directory:
GET /vasps, cursor-paginated. Each entry is{ vaspName, url }—vaspNameis the value a platform passes back when declaring a VASP-hosted counterparty, andurllets a picker UI confirm the right entity.This is PR 1 of 3 for VASP counterparty support (Travel Rule):
/vaspsdirectorybeneficiaryon crypto-wallet external accountscustodyType/vaspNameon external accountsShape decisions
searchparam. Matches the sibling directory endpoints (/discoveries,/uma-providers), which are paginated and filtered client-side. Integrators can cache the directory and search it locally./discoveries, where the returnedbankNameis the value passed back on account creation. The provider's declare-counterparty call takes only its own identifier, so the exposed name is a Grid-side key that the backend maps back — which also keeps the surface portable if the provider set changes.url.totalCount. The upstream search returns no total, so it could never be populated.Changes
GET /vasps(limit,cursor) under a new VASPs tagVaspandVaspListResponseschemasvaspsresource block (listmethod) so the endpoint flows into the documented spec and SDKsTesting
make buildbundles cleanly;redocly lintandspectral lintmatch the pre-existing baseline onmainexactly (no new findings).🤖 Generated with Claude Code