SINAI STANDARD

KYC Providers

Integrate Sumsub, Jumio, or Onfido for automated KYC-to-allowlist management

KYC Providers

The Oracle uses a KycAdapter interface to normalize KYC provider responses. When a provider sends a webhook, the adapter parses the payload and returns a standardized action (add, remove, or none). The Oracle then updates the on-chain allowlist automatically.

KycAdapter Interface

interface KycAdapter {
  name: string;
  checkVerification(walletAddress: string): Promise<KycVerificationResult>;
  handleWebhook(payload: unknown): Promise<KycWebhookResult>;
  listVerifiedWallets(): Promise<string[]>;
}
 
interface KycVerificationResult {
  verified: boolean;
  walletAddress: string;
  provider: string;
  details?: Record<string, unknown>;
}
 
interface KycWebhookResult {
  action: "add" | "remove" | "none";
  walletAddress?: string;
  reason?: string;
}

Selecting a Provider

Set the KYC_PROVIDER environment variable:

KYC_PROVIDER=sumsub    # sumsub | jumio | onfido | mock
KYC_API_KEY=your-key
KYC_API_SECRET=your-secret

The Oracle creates the appropriate adapter at startup via createKycAdapter(provider, { apiKey, apiSecret }).

Webhook Flow

All providers follow the same integration flow:

KYC Provider → POST /v1/kyc/webhook?mint=<mint> → Adapter parses payload
    → action: "add"    → wallet queued for allowlist addition
    → action: "remove" → wallet queued for allowlist removal
    → action: "none"   → no on-chain change

The mint query parameter tells the Oracle which token's allowlist to update. If omitted, falls back to the DEFAULT_MINT environment variable.

Sumsub

Authentication: HMAC-SHA256 request signing with three headers.

KYC_PROVIDER=sumsub
KYC_API_KEY=your-app-token
KYC_API_SECRET=your-secret-key

Request Signing

Every API call to Sumsub includes:

HeaderValue
X-App-TokenYour app token
X-App-Access-TsUnix timestamp (seconds)
X-App-Access-SigHMAC-SHA256(secret, ts + method + path + body)

Verification Check

The adapter looks up applicants by externalUserId (the wallet address). A wallet is verified when:

review.reviewStatus === "completed"
AND review.reviewResult.reviewAnswer === "GREEN"

Webhook Status Mapping

Sumsub StatusActionDescription
GREENaddVerification approved
REDremoveVerification rejected (with rejection labels)
applicantPendingremovePrecautionary removal during review
applicantCreatedremovePrecautionary removal

Configure Sumsub Webhook

In your Sumsub dashboard, set the webhook URL to:

https://your-oracle-host/v1/kyc/webhook?mint=YOUR_MINT_ADDRESS

Jumio

Authentication: Basic Auth.

KYC_PROVIDER=jumio
KYC_API_KEY=your-api-token
KYC_API_SECRET=your-api-secret

Requests use Authorization: Basic base64(apiToken:apiSecret).

Verification Check

Looks up verifications by customerId (the wallet address). A wallet is verified when:

verificationStatus === "APPROVED_VERIFIED"

Webhook Status Mapping

Jumio StatusAction
APPROVED_VERIFIEDadd
DENIED_FRAUDremove
DENIED_UNSUPPORTED_ID_TYPEremove
DENIED_UNSUPPORTED_ID_COUNTRYremove
ERROR_NOT_READABLE_IDremove
NO_ID_UPLOADEDremove

Onfido

Authentication: Token auth.

KYC_PROVIDER=onfido
KYC_API_KEY=your-api-token
# KYC_API_SECRET not required for Onfido

Requests use Authorization: Token token=apiToken.

Verification Check

Fetches applicant checks and verifies when:

check.status === "complete" AND check.result === "clear"

Webhook Handling

Onfido sends check.completed events. The adapter:

  1. Extracts checkId from the webhook payload
  2. Fetches the full check via GET /v3.6/checks/{checkId}
  3. Fetches the applicant via GET /v3.6/applicants/{applicantId}
  4. Maps the result:
Check ResultAction
clearadd
considerremove
unidentifiedremove

Mock Adapter

For development and testing, the mock adapter uses an in-memory set of wallets:

KYC_PROVIDER=mock
// The mock adapter accepts webhook payloads in the format:
// POST /v1/kyc/webhook
// { "action": "verified", "wallet": "walletAddress..." }
// { "action": "revoked", "wallet": "walletAddress..." }

No API keys required. The mock adapter also exposes addVerifiedWallet(wallet) for programmatic testing.

Checking KYC Status

Regardless of provider, you can check any wallet's status:

// GET /v1/kyc/status/<wallet>
const res = await fetch(`${ORACLE_URL}/v1/kyc/status/${wallet}`);
const { verified, provider, walletAddress } = await res.json();
 
// GET /v1/kyc/verified
const all = await fetch(`${ORACLE_URL}/v1/kyc/verified`);
const { wallets, total, provider } = await all.json();

On this page