Skip to content

[READY] Feat/kyb deep link#1215

Merged
ebma merged 16 commits into
stagingfrom
feat/kyb-deep-link
Jun 16, 2026
Merged

[READY] Feat/kyb deep link#1215
ebma merged 16 commits into
stagingfrom
feat/kyb-deep-link

Conversation

@Sharqiewicz

@Sharqiewicz Sharqiewicz commented Jun 10, 2026

Copy link
Copy Markdown
Member

Quote-less KYB deep link with region selector

Adds a partner-facing deep link that takes business users straight into KYB verification - no quote required. Partners can send their users to the widget with a single URL and have them complete business verification for any supported region.

Flow

?kyb / ?kybLocked → email/OTP auth → region selector → provider KYB → success screen
  • Brazil → Avenia KYB. The company name and CNPJ are collected together on a single company form (no separate CNPJ card). The CNPJ field is editable in the deep-link flow and validated as CNPJ-only (KYB is business-only); in the normal quoted flow it stays read-only and pre-filled from the quote.
  • Mexico / Colombia / USA → Alfredpay business KYB (the business customer type is preselected). MX/CO business deep links are routed to the company KYB form, not the individual KYC form.
  • Europe / Mykobo is intentionally excluded (individual KYC only, requires a connected wallet).
  • After success, the user lands on a "KYB Completed" screen; Continue resets to the standard quote form with the session still authenticated and the deep-link params stripped from the URL.

URL parameters

URL Behavior
?kyb KYB mode, region selector shown
?kyb=BR | MX | CO | US Selector shown with the region preselected (user can change it)
?kybLocked=BR | MX | CO | US Selector skipped, region pinned, back navigation disabled
?kybLocked=BR (specifically) Additionally defaults the widget locale to pt-BR (an explicit locale in the path still wins, e.g. /en/widget?kybLocked=BR)
unknown / empty region code (e.g. ?kybLocked=ZZ, bare ?kybLocked) Degrades gracefully to the open selector - the region is not treated as locked

Query keys are case-sensitive per the W3C/RFC URL spec: the parameter is kybLocked (lowercase k). A re-cased KybLocked is a different key and is ignored.

externalSessionId, partnerId, and apiKey are forwarded in KYB mode, so partner/session attribution works the same as in the quoted flow.

Implementation notes

  • kybLink context object - all KYB deep-link state (fiatToken, regionLocked) lives in a single optional context object whose presence enables the mode. It is registered in initialRampContext, so RESET_RAMP fully exits KYB mode.
  • New machine states - SelectRegion (region picker, auto-skipped when locked and resolved to a fiat token), KybLinkComplete (terminal success screen), and PostAuthRouting (a transient state that routes a successful login - token check or OTP - to the destination recorded in postAuthTarget, replacing the duplicated transition branches in CheckAuth/VerifyingOTP). A chosen region routes straight to the shared KYC node; Brazil collects its CNPJ on the Avenia company form rather than on a dedicated step.
  • Graceful region-lock degradation - ?kybLocked= only pins the region when the code resolves to a known region. An unknown or empty code yields regionLocked: false, so the selector is shown with working back navigation instead of a dead-end.
  • Locked-region back behavior - with ?kybLocked=, the parent KYC GO_BACK is an explicit guarded no-op (it would otherwise restart the child KYC machine). The back button is hidden on the locked KYB entry screen and on the region selector (the selector is the root of the flow). Deeper KYB steps that own their own back navigation keep the button.
  • CNPJ-only validation - useKYBForm gains a requireCnpj flag; when the tax ID is user-typed (the quote-less deep link), it must be a CNPJ, so a "business" entry can't silently resolve to an individual account downstream.
  • Region table as single source of truth - KYB_REGIONS maps each region code to its fiat token (provider routing), label key, and optional defaultLocale. Adding Spanish for MX/CO later is a data-only change.
  • Shared package & backend - BrlaCreateSubaccountRequest.quoteId is now optional; the backend stores it as a nullable initialQuoteId (provenance only, never an authorization input). The subaccount actor only requires a quote in the normal flow and skips the quote-bound KYC-status lookup in deep-link mode.
  • Misc - the dropdown trigger press scale now actually animates (transition-property previously covered only colors), and the panel no longer animates on initial mount.

Docs

  • OpenAPI (docs/api/openapi/vortex.openapi.json) - createSubaccount quoteId is now documented as optional, with the quote-less KYB deep-link case described.
  • API guide - new partner-facing page docs/api/pages/13-kyb-deep-link.md (registered in the Apidog page manifest) covering the flow, URL parameters, attribution, and embedding.
  • Security spec (docs/security-spec/05-integrations/brla.md) - documents quote-less subaccount creation and that the nullable initialQuoteId does not weaken any access check.

Testing

  • bun typecheck clean, Biome clean on changed files, frontend test suite passing (23/23).

@netlify

netlify Bot commented Jun 10, 2026

Copy link
Copy Markdown

Deploy Preview for vortexfi ready!

Name Link
🔨 Latest commit b9077ea
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/6a317f90606a34000892a323
😎 Deploy Preview https://deploy-preview-1215--vortexfi.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jun 10, 2026

Copy link
Copy Markdown

Deploy Preview for vortex-sandbox failed. Why did it fail? →

Name Link
🔨 Latest commit b9077ea
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6a317f906c46d200080b1651

@Sharqiewicz Sharqiewicz changed the title [WIP] Feat/kyb deep link [READY] Feat/kyb deep link Jun 11, 2026
@ebma ebma requested a review from Copilot June 12, 2026 15:09

Copilot AI left a comment

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.

Pull request overview

Adds a partner-facing, quote-less KYB deep-link flow to the widget that routes business users through auth → region selection (optionally locked) → provider KYB/KYC → completion, while keeping attribution parameters (partner/session/apiKey) consistent with the quoted flow.

Changes:

  • Introduces KYB deep-link search params (kyb, kybLocked) and a region table (KYB_REGIONS) to drive routing + default locale behavior.
  • Extends the ramp/KYC XState machines with new KYB-specific states/events (region selection, BR tax-id step, post-auth routing, terminal completion screen).
  • Updates BRLA subaccount creation to allow quote-less KYB (optional quoteId), and adds UI steps + translations for region selection and CNPJ entry.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/shared/src/endpoints/brla.endpoints.ts Makes quoteId optional for BRLA subaccount creation requests to support quote-less KYB.
apps/frontend/src/types/searchParams.ts Adds kyb / kybLocked URL parameters to the ramp search schema.
apps/frontend/src/translations/pt.json Adds PT translations for region selector + CNPJ (KYB tax id) step.
apps/frontend/src/translations/en.json Adds EN translations for region selector + CNPJ (KYB tax id) step.
apps/frontend/src/routes/{-$locale}.tsx Applies default locale when a region-pinned KYB link is used (e.g. BR → pt-BR).
apps/frontend/src/pages/widget/index.tsx Renders new KYB steps and a KYB completion screen; adjusts Avenia KYB/KYC routing for deep links.
apps/frontend/src/machines/types.ts Adds kybLink context and new KYB-related machine events/targets.
apps/frontend/src/machines/ramp.machine.ts Adds KYB entry event + new states (SelectRegion/KybRouting/EnterKybTaxId/KybLinkComplete/PostAuthRouting).
apps/frontend/src/machines/ramp.context.ts Ensures kybLink is included/reset via initial/reset context.
apps/frontend/src/machines/kyc.states.ts Routes KYC child selection via quote fiat token or KYB-selected fiat token; adjusts back behavior and completion target for KYB deep links.
apps/frontend/src/machines/actors/brla/createSubaccount.actor.ts Skips quote-bound KYC status lookup and relaxes quoteId requirement when in KYB deep-link mode.
apps/frontend/src/hooks/useStepBackNavigation.ts Hides back navigation on the region selector step.
apps/frontend/src/hooks/useRampUrlParams.ts Detects KYB deep-link params and boots the machine directly into KYB mode.
apps/frontend/src/constants/kybRegions.ts Adds the region-to-fiat/label/defaultLocale mapping and lookup helper.
apps/frontend/src/components/widget-steps/RegionSelectStep/index.tsx Implements the KYB region selector UI step.
apps/frontend/src/components/widget-steps/KybTaxIdStep/index.tsx Implements the BR-only CNPJ entry step for KYB deep links.
apps/frontend/src/components/ui/DropdownSelector.tsx Tweaks dropdown trigger transition properties and disables initial mount animation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/frontend/src/machines/ramp.machine.ts Outdated
Comment thread apps/frontend/src/constants/kybRegions.ts Outdated

@ebma ebma left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For the Brazil flow, instead of having one card where the user only enters the CNPJ, only to then get to another card where they enter the CNPJ and the name, I suggest we skip the first one and only show the second one if possible. ie. let's only ask the user about company name and CNPJ together, not in separate cards of the flow. Or is there a reason for this split?

Image Image

When I go to http://localhost:5173/en/widget?kybLocked=MX, I immediately see the form for KYC but not KYB. Are we not supporting KYB in Mexico? I also don't see the option to switch between KYB and KYC for Mexico in the card.

Image

Besides that, I think the rest is good. Let's address copilots code review comments and please also ask your agent to update the relevant files of the security-spec and adjust the openapi.json and also create a new markdown page that we can put into the API docs.

Drop the separate CNPJ-only step and enter the CNPJ together with the company name on the Avenia KYB form (editable only in the quote-less deep link; read-only when supplied by a quote). Validate it as
  CNPJ-only via a new useKYBForm requireCnpj flag so a business deep link can't resolve to an individual account. Only honor ?kybLocked= when the region code is valid, otherwise fall back to the open selector, and
  fix the region-code JSDoc.
In Alfredpay CheckingStatus, send business customers on API-based countries (MX/CO) to FillingKybForm instead of the individual KYC form, mirroring the existing CreatingCustomer branch.
With ?kybLocked= the region selector is skipped, so the first KYB screen has nothing to go back to and its GO_BACK is a guarded no-op. Hide the button there while keeping it on deeper KYB steps that own
  their back navigation.
Make createSubaccount quoteId optional in the OpenAPI spec, add a KYB Deep Link guide page (manifest entry included), and note quote-less subaccount creation in the BRLA security spec.

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.

Comment on lines 34 to 37
defaultValues: {
[ExtendedAveniaFieldOptions.TAX_ID]: initialData?.taxId || taxIdFromStore || "",
[ExtendedAveniaFieldOptions.FULL_NAME]: initialData?.fullName || ""
},
Comment thread docs/api/pages/13-kyb-deep-link.md Outdated
Comment on lines +27 to +31
| `?kybLocked=BR` \| `MX` \| `CO` \| `US` | Selector skipped, region pinned, back navigation into the selector disabled. |
| `?kybLocked=BR` (specifically) | Additionally defaults the widget locale to `pt-BR`. An explicit locale in the path still wins (e.g. `/en/widget?kybLocked=BR` stays English). |
| unknown region code (e.g. `?kybLocked=ZZ`) | Degrades gracefully to the open selector; the region is **not** treated as locked. |

Region codes are case-insensitive.
@ebma ebma merged commit c935ab5 into staging Jun 16, 2026
3 of 7 checks passed
@ebma ebma deleted the feat/kyb-deep-link branch June 16, 2026 16:54
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