npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@snapkyc-ooru/consent-handoff-react

v0.1.5

Published

React SDK: agreement consent flow with QR or intent handoff + txn polling.

Readme

@snapkyc-ooru/consent-handoff-react

KYC consent & identity handoff flow for React — agreement → consent → QR / deeplink → polling → verified.

npm version license: MIT peer: React ≥ 17


What is this?

@snapkyc-ooru/consent-handoff-react is a plug-and-play React + TypeScript SDK that orchestrates the full SnapKYC identity-verification handoff inside your product — without you needing to manage a single API call or UI state machine.

Drop in a single component (or call one function) and your users move through:

Integration agreement  →  Purpose consent  →  QR code / app deeplink
        →  Live status polling  →  Verification summary

Your backend mints a short-lived integrationToken; the SDK handles everything else.


Install

npm install @snapkyc-ooru/consent-handoff-react react react-dom

Peer dependencies: React >= 17, react-dom >= 17


Styles

Import the bundled stylesheet once — typically in your app root or entry file:

import "@snapkyc-ooru/consent-handoff-react/styles.css";

All design tokens are scoped to .snap-consent-handoff. Override them via the theme prop rather than targeting :root in your host app.


The 5-step flow

| Step | Screen | |------|--------| | 1 | Agreement — integration notice card; optional custom title | | 2 | Consent — purpose checkboxes with Cancel / I Agree | | 3 | Handoff — QR scan or Continue with Aadhaar App (Android intent deeplink) | | 4 | Polling — live status until terminal result | | 5 | Success — status strip, confirmed purposes, demographic accordions; Done |

Dismiss controls (×, Cancel, Done) all call onRequestClose and cleanly abort any in-flight work.


Quick start

Option A — Imperative modal (recommended)

Mounts outside your React tree on document.body. Ideal for portals, dashboards, or any flow that should not disrupt your existing layout.

import { useCallback, useRef } from "react";
import {
  openSnapConsentHandoffFlow,
  type CompletionResult,
  type FlowEvent,
} from "@snapkyc-ooru/consent-handoff-react";
import "@snapkyc-ooru/consent-handoff-react/styles.css";

function VerifyButton() {
  const disposeRef = useRef<(() => void) | null>(null);

  const startVerification = useCallback(async () => {
    const integrationToken = await fetchTokenFromYourBackend(); // short-lived token

    disposeRef.current?.();
    disposeRef.current = openSnapConsentHandoffFlow({
      integrationToken,
      rpContext: {
        reference_id: "user-or-order-id",
        session_id: "optional-correlation-id",
      },
      handoffMode: "qr", // or "intent"
      title: "Loan Application KYC Verification",
      pollIntervalMs: 1500,
      pollDeadlineMs: 120_000,
      theme: {
        primary: "#0f766e",
        borderActive: "#0f766e",
        surfaceMuted: "#f0fdfa",
      },
      onFlowEvent(evt: FlowEvent) {
        console.log("phase →", evt);
        // agreement-loading | agreement-ready | txn-created
        // polling | completed | failed | cancelled
      },
      onSuccess(result: CompletionResult) {
        // result.txn_id  — transaction identifier
        // result.status  — terminal status string
        // result.raw     — full API payload (demographics, purposes, evidence)
      },
      onError(err) {
        console.error(err);
      },
    }).dispose;
  }, []);

  return (
    <button type="button" onClick={() => void startVerification()}>
      Verify identity
    </button>
  );
}

Important: store the returned dispose reference and call it on component unmount or navigation. The modal's built-in dismiss buttons do this automatically when using openSnapConsentHandoffFlow.

openSnapConsentQrFlow is a deprecated alias — use openSnapConsentHandoffFlow going forward.


Option B — Declarative component

Embed the flow inline inside a wizard step, settings page, or your own modal shell.

Inline (embedded)

import { useState } from "react";
import {
  SnapConsentHandoffFlow,
  type CompletionResult,
} from "@snapkyc-ooru/consent-handoff-react";
import "@snapkyc-ooru/consent-handoff-react/styles.css";

export function KycStep({ token }: { token: string }) {
  const [hidden, setHidden] = useState(false);
  if (hidden) return null;

  return (
    <SnapConsentHandoffFlow
      integrationToken={token}
      rpContext={{ reference_id: "USER-123", session_id: "sess-abc" }}
      handoffMode="intent"
      displayMode="inline"
      title="Loan Application KYC Verification"
      acceptLanguage="en"
      theme={{ primary: "#0f766e" }}
      onSuccess={(result: CompletionResult) => {
        // result.raw contains demographics, purposes, evidence
      }}
      onRequestClose={() => setHidden(true)}
    />
  );
}

Inside your own modal shell

<SnapConsentHandoffFlow
  integrationToken={token}
  rpContext={{ reference_id: "USER-123" }}
  handoffMode="qr"
  displayMode="modal"
  onRequestClose={() => setShowModal(false)}
/>

| displayMode | Behaviour | |---------------|-----------| | "modal" (default) | SDK renders its own scrim + header × | | "inline" | Borderless sheet; no scrim — sits inside your layout |


Handoff modes

| handoffMode | What happens after I Agree | |---------------|---------------------------| | "qr" | Generates a PNG QR code (POST …/generate-qr). User scans with the authorised app. | | "intent" | Generates an Android app deeplink (POST …/generate-intent?platform=android). User taps Continue with Aadhaar App to open the native app; the SDK polls status until success. |


Callbacks

| Callback | Fires when | |----------|-----------| | onFlowEvent(evt) | On every phase transition (see event names below) | | onSuccess(result) | Terminal success — receives { txn_id?, status, raw } | | onError(err) | Agreement load failure or unrecoverable error | | onRequestClose() | User dismissed via ×, Cancel, or Done |

FlowEvent values

agreement-loading  →  agreement-ready  →  txn-initiating  →  txn-created
  →  handoff-loading  →  handoff-ready  →  polling  →  completed | failed | cancelled

Post-success helpers

import {
  normalizeTxnStatusPayload,
  extractDemographicData,
  demographicAccordionSections,
} from "@snapkyc-ooru/consent-handoff-react";

Theme

All overrides are optional. Unspecified keys fall back to the built-in warm default (orange primary, cream accents). Setting primary alone automatically sets borderActive unless you override it separately.

import {
  DEFAULT_SNAP_CONSENT_THEME,
  resolveSnapConsentTheme,
  type SnapConsentHandoffTheme,
} from "@snapkyc-ooru/consent-handoff-react";

const brand: Partial<SnapConsentHandoffTheme> = {
  primary: "#0f766e",
  surfaceMuted: "#f0fdfa",
};

<SnapConsentHandoffFlow {...props} theme={brand} />;

| Token | Controls | |-------|----------| | primary | Brand accent — I Agree / Done, checkboxes, active borders | | onPrimary | Text on primary buttons | | canvas | Sheet background | | surface / surfaceMuted | Card surfaces (unselected / selected rows, notice) | | text / textMuted | Body and secondary text | | border / borderActive | Default and emphasized borders | | modalOverlay | Modal scrim | | danger | Error text | | success | Success hero and status checkmarks | | successStatusSurface | Status summary card on success | | successPurposesSurface | Confirmed purposes panel | | successAccordionBorder | Demographic accordion borders | | radius / fontFamily / gap / maxWidth / modalZ | Layout and typography |


Props reference

Required props

| Prop | Type | Description | |------|------|-------------| | integrationToken | string | Authorization: Token … sent on every SDK request | | rpContext.reference_id | string | Sent as rp_context.ref_id when initiating a transaction | | handoffMode | "qr" \| "intent" | Handoff delivery mechanism |

Optional props

| Prop | Type | Default | Description | |------|------|---------|-------------| | rpContext.session_id | string | — | Optional correlation metadata for your backend | | acceptLanguage | string | — | Accept-Language header + agreement locale preference | | agreementId | string | — | Reserved; agreement is implied by the integration token | | qrRequestBody | { size?: number } | — | Extra QR params; txn_id is always injected by SDK | | pollIntervalMs | number | 1500 | Polling interval in milliseconds | | pollDeadlineMs | number | 120000 | Polling timeout in milliseconds | | displayMode | "modal" \| "inline" | "modal" | Modal (with scrim) or inline sheet | | title | string | Agreement name | Dialog heading | | theme | Partial<SnapConsentHandoffTheme> | Built-in warm default | Visual overrides | | intentPlatformOverride | "android" \| "ios" | — | Force platform when already known | | userAgent | string | — | Override UA for tests or SSR | | requestGeolocation | boolean | true | Prompt for browser geolocation on load; sends coords in rp_context on initiate when granted | | geolocationOptions | PositionOptions | SDK defaults | Options for getCurrentPosition (timeout, accuracy, etc.) | | onFlowEvent | (evt: FlowEvent) => void | — | Phase transition callback | | onSuccess | (result: CompletionResult) => void | — | Terminal success callback | | onError | (err: unknown) => void | — | Error callback | | onRequestClose | () => void | — | Dismiss callback (required for host-controlled hide) |


Security — credential hygiene

integrationToken is a bearer credential that lives in the browser. Follow these rules:

  • ✅ Mint short-lived tokens on your backend after the user is authenticated
  • ✅ Deliver the token to the client just before calling openSnapConsentHandoffFlow
  • ❌ Never embed long-lived secrets in static bundles or environment variables shipped to the browser

HTTP contract

All route assumptions are documented in docs/HTTP_CONTRACT.md.


License

MIT