Skip to content

fix(api): let a rejected Avenia KYC account become approved on a successful retry#1273

Merged
ebma merged 2 commits into
stagingfrom
fix/avenia-kyc-retry-approval
Jul 17, 2026
Merged

fix(api): let a rejected Avenia KYC account become approved on a successful retry#1273
ebma merged 2 commits into
stagingfrom
fix/avenia-kyc-retry-approval

Conversation

@ebma

@ebma ebma commented Jul 17, 2026

Copy link
Copy Markdown
Member

Problem

Seen in Sentry: a new user completed KYC successfully, but ramp registration failed with "No completed Avenia profile found for this API key user."

Root cause: updateAveniaKycOutcome only flipped accounts whose status was in_review (deliberately mirroring the legacy UPDATE ... WHERE internal_status = 'Requested' guard), and the in-flight poll branch in fetchSubaccountKycStatus explicitly excluded rejected accounts. So for a user whose first KYC attempt was rejected and whose retry succeeded:

  1. First attempt: account goes in_reviewrejected.
  2. Retry via the KYC machine reuses the existing subaccount (no createSubaccount call), so nothing resets the status.
  3. The approved retry polls updateAveniaKycOutcome, which silently no-ops (rejected ≠ in_review). The user still sees "KYC approved" because the poll response is built from Avenia's live attempt status.
  4. resolveAveniaAccountForUser finds no approved account → registration fails.

Fix

  • updateAveniaKycOutcome: approved is now the only terminal state. A stale attempt read never downgrades an approved account, but rejected/pending accounts follow the latest provider attempt result (Avenia is the source of truth). Repeated polls of an unchanged outcome no-op, preserving the write-once behavior.
  • fetchSubaccountKycStatus: a rejected account with a fresh processing attempt moves back to in_review (previously excluded).
  • Security spec (docs/security-spec/05-integrations/brla.md) updated — it documented the old guard.

Tests

Three regression tests in brla.controller.test.ts:

  • rejected → approved on a successful retried attempt (fails without the fix)
  • rejected → in_review while a retried attempt is processing
  • approved is never downgraded by a stale rejected attempt read

bun test (25/25 in file), bun lint, bun typecheck all pass.

Note for production

Production (pre-cutover tax_ids model) has the same flaw until this ships via staging → main. The affected user's row is presumably stuck at internal_status = 'Rejected' and needs a one-off flip to Accepted after confirming their identity is CONFIRMED on Avenia's side.

…essful retry

updateAveniaKycOutcome only flipped accounts that were in_review (mirroring
the legacy WHERE internal_status = 'Requested' guard), and the in-flight
poll branch excluded rejected accounts. A user whose first KYC attempt was
rejected and whose retry succeeded therefore stayed 'rejected' forever, and
ramp registration failed with 'No completed Avenia profile found for this
API key user' despite a successful KYC.

Approval is now the only terminal state: a stale attempt read never
downgrades an approved account, but rejected/pending accounts follow the
latest provider attempt, and a processing retry moves a rejected account
back to in_review. Repeated polls of an unchanged outcome no-op.
@netlify

netlify Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploy Preview for vortexfi ready!

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

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

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

@netlify

netlify Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit ce55835
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6a5a63be38e73f000855b1f3
😎 Deploy Preview https://deploy-preview-1273--vortex-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

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

@netlify

netlify Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploy Preview for vrtx-dashboard ready!

Name Link
🔨 Latest commit ce55835
🔍 Latest deploy log https://app.netlify.com/projects/vrtx-dashboard/deploys/6a5a63be303207000874ac85
😎 Deploy Preview https://deploy-preview-1273--vrtx-dashboard.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

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

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

Fixes a BRLA/Avenia KYC state-machine edge case in the API where a subaccount that was previously rejected could not transition to approved after a successful retry, causing downstream ramp registration to fail despite the provider showing an approved outcome.

Changes:

  • Adjusts updateAveniaKycOutcome so approved is the only terminal state, allowing rejectedapproved on successful retried attempts while preventing downgrades from approved.
  • Updates fetchSubaccountKycStatus to allow a rejected account to move back to in_review while a new attempt is processing.
  • Adds regression tests covering rejected→approved retry, rejected→in_review while processing, and “approved never downgraded” behavior; updates the BRLA security spec to match.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
docs/security-spec/05-integrations/brla.md Updates integration/security documentation to reflect the new “approved is terminal” transition rules.
apps/api/src/api/services/avenia/avenia-customer.service.ts Updates the poll-driven outcome transition logic to allow rejected→approved retries and preserve idempotency/terminal semantics.
apps/api/src/api/controllers/brla.controller.ts Expands in-flight polling logic to include retried attempts for previously rejected accounts.
apps/api/src/api/controllers/brla.controller.test.ts Adds regression tests that reproduce and prevent the stuck-rejected scenario and confirm approved is never downgraded.

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

Pre-existing useSortedClasses violation on staging that fails 'bun verify' in CI.
@ebma
ebma merged commit 42f8945 into staging Jul 17, 2026
7 of 8 checks passed
@ebma
ebma deleted the fix/avenia-kyc-retry-approval branch July 17, 2026 17:18
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.

2 participants