Identity Verification (KYC)

Zoneless handles money routing, not identity verification. This guide explains how to integrate an external KYC provider and use the Account object to gate payouts based on verification status.

How it works

Zoneless is intentionally unopinionated about identity verification. Instead of bundling a KYC provider, it gives you the building blocks to plug in any provider you choose — whether that's Stripe Identity, Jumio, Sumsub, or your own in-house solution.

The flow is straightforward: verify the seller through your provider, then update their Zoneless account to reflect the result. Since payouts are always initiated by your platform via the API, you have full control over when and whether a seller gets paid.

Relevant Account fields

Several fields on the Account object are useful for tracking and enforcing verification status.

  • metadata — Store your KYC provider's verification ID, status, or any other structured data as key-value pairs. This is the recommended place to track verification state.
  • payouts_enabled — Read-only boolean indicating whether the account can receive payouts. This becomes true after the account connects an external wallet. Since you control when payouts are created, you can add your own verification checks before calling the Payouts API.
  • requirements — Tracks onboarding requirements like name, email, TOS acceptance, and external wallet. You can use requirements.currently_due to check if an account still has outstanding onboarding steps.
  • individual — Contains the person's name, email, date of birth, and address. This data can be collected during onboarding and passed to your KYC provider.

1. Collect seller information

Create a connected account and send the seller through the onboarding flow. Zoneless collects basic information like name, email, and wallet address. You can also collect additional details by updating the account via the API.

Create account with metadata
import { Zoneless } from '@zoneless/node';
const zoneless = new Zoneless('sk_live_z_YOUR_API_KEY', 'https://api.yourdomain.com');

const account = await zoneless.accounts.create({
  country: 'US',
  email: 'seller@example.com',
  metadata: {
    kyc_status: 'pending',
  },
});

2. Verify with your KYC provider

After the seller completes onboarding, retrieve their account and pass the collected information to your KYC provider. The individual object contains the seller's name, date of birth, and address — the typical inputs for identity verification.

Timing. Listen for the account.updated webhook to know when a seller has completed onboarding (details_submitted becomes true). This is a good trigger to start your KYC check.
Retrieve account for KYC
const account = await zoneless.accounts.retrieve(
  'acct_z_1Nv0FGQ9RKHgCVdK'
);

if (account.details_submitted) {
  // Pass account.individual to your KYC provider
  const kycResult = await yourKycProvider.verify({
    first_name: account.individual?.first_name,
    last_name: account.individual?.last_name,
    email: account.email,
    dob: account.individual?.dob,
    address: account.individual?.address,
  });

  // Store the result on the account
  await zoneless.accounts.update(account.id, {
    metadata: {
      kyc_status: kycResult.status,
      kyc_provider_id: kycResult.id,
    },
  });
}

3. Gate payouts on verification

Since your platform creates all payouts via the API, you can simply check the seller's KYC status before creating a payout. No additional configuration is needed — your application logic is the gate.

You control payouts. Connected accounts cannot initiate their own payouts. Every payout is created by your platform through the API, so verification enforcement is entirely in your hands.
Check KYC before payout
const account = await zoneless.accounts.retrieve(
  'acct_z_1Nv0FGQ9RKHgCVdK'
);

const isVerified =
  account.metadata.kyc_status === 'verified';

if (!isVerified) {
  throw new Error('Account has not passed KYC');
}

const payout = await zoneless.payouts.create({
  amount: 5000,
  currency: 'usdc',
}, {
  zonelessAccount: account.id,
});

Rejecting accounts

If a seller fails KYC, you can reject their account using the Reject endpoint. This sets payouts_enabled and charges_enabled to false and records the reason in requirements.disabled_reason.

Reject a failed account
await zoneless.accounts.update(account.id, {
  metadata: {
    kyc_status: 'failed',
    kyc_provider_id: kycResult.id,
  },
});

await zoneless.accounts.reject(account.id, {
  reason: 'fraud',
});

Example metadata schema

There's no enforced schema for metadata — use whatever keys make sense for your integration. Here's a common pattern:

EXAMPLE METADATA
{
  "metadata": {
    "kyc_status": "verified",
    "kyc_provider": "sumsub",
    "kyc_provider_id": "kyc_5f3a2b1c",
    "kyc_verified_at": "1704067200",
    "kyc_level": "basic"
  }
}