> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pods.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# KYC (Backend)

> Server-to-server KYC: submit a Sumsub share token or external evidence, then poll status

This example demonstrates the Pods KYC flow from your **backend**. KYC is a pure server-to-server
flow — no wallet signing. You onboard a user with one of the KYC flows, then poll
`GET /api/v1/kyc/status` until the profile reaches a terminal state.

<Warning>
  Keep `PODS_API_KEY` on your **backend**. Never expose it in a browser or mobile client.
</Warning>

## Overview

This example covers:

* **Sumsub reusable KYC** (Brazil Pix) — `POST /api/v1/kyc/sumsub-share-token`
* **External evidence** — `POST /api/v1/kyc/sessions` then `POST /api/v1/kyc/sessions/{kycUserId}/submit`
* **Status polling** — `GET /api/v1/kyc/status`

## Prerequisites

* Node.js 18+
* Pods API key
* For Sumsub: an approved Sumsub applicant + a freshly generated share token
* For external evidence: liveness + document images you captured yourself

## Installation

```bash theme={null}
npm install axios
```

## Environment Variables

```bash theme={null}
PODS_API_KEY=your-api-key
WALLET_ADDRESS=0x...
# Sumsub flow
SUMSUB_SHARE_TOKEN=...
SUMSUB_APPLICANT_ID=...
# External-evidence flow (base64 image bytes)
LIVENESS_IMAGE_BASE64=...
DOC_FRONT_BASE64=...
DOC_BACK_BASE64=...
```

## Step-by-Step Walkthrough

### 1. Initialize API Client + status poller

```javascript theme={null}
import axios from 'axios'

const pods = axios.create({
  baseURL: 'https://api.pods.finance',
  headers: {
    'x-api-key': process.env.PODS_API_KEY,
    'Content-Type': 'application/json'
  }
})

const TERMINAL = ['approved', 'rejected', 'rejected_retryable', 'blocked']

async function pollKycStatus (kycUserId) {
  while (true) {
    const { data: status } = await pods.get('/api/v1/kyc/status', { params: { kycUserId } })
    console.log('status:', status.status, 'brlaEnabled:', status.brlaEnabled)
    if (TERMINAL.includes(status.status)) return status
    await new Promise(resolve => setTimeout(resolve, 5000))
  }
}
```

### 2. Sumsub Reusable KYC (Brazil Pix)

Submit the fresh Sumsub share token your backend generated for the approved applicant. Pods returns
a `kycUserId` — store it for status checks.

```javascript theme={null}
const { data } = await pods.post('/api/v1/kyc/sumsub-share-token', {
  cpf: '52998224725',
  shareToken: process.env.SUMSUB_SHARE_TOKEN,
  sumsubApplicantId: process.env.SUMSUB_APPLICANT_ID,
  email: 'user@example.com',
  walletAddress: process.env.WALLET_ADDRESS
})

console.log('kycUserId:', data.kycUserId, 'status:', data.status)
await pollKycStatus(data.kycUserId)
```

### 3. External Evidence

Create a session, then submit the identity evidence you captured. Each image is either inline
`base64` or an HTTPS `url` Pods fetches — pick one per image.

```javascript theme={null}
const { data: session } = await pods.post('/api/v1/kyc/sessions', {
  cpf: '12345678909',
  email: 'maria@example.com',
  walletAddress: process.env.WALLET_ADDRESS
})

const { data: submit } = await pods.post(`/api/v1/kyc/sessions/${session.kycUserId}/submit`, {
  applicant: { fullName: 'Maria Silva', dateOfBirth: '1990-05-14', phone: '+5511999999999' },
  address: {
    country: 'BRA', state: 'SP', city: 'São Paulo', zipCode: '01310100',
    streetAddress: 'Av. Paulista', number: '1000', complement: 'apto 51'
  },
  liveness: {
    image: { base64: process.env.LIVENESS_IMAGE_BASE64, mimeType: 'image/jpeg' },
    attestation: {
      provider: 'aws_rekognition_face_liveness',
      status: 'SUCCEEDED',
      confidence: 95.78,
      sessionId: '47fa2a26-bb7a-4ca0-85f1-129ad6a6eb0b',
      challenge: { type: 'FaceMovementAndLightChallenge', version: '2.0.0' },
      completedAt: '2026-08-13T14:58:10Z'
    }
  },
  documents: {
    documentType: 'RG',
    front: { base64: process.env.DOC_FRONT_BASE64, mimeType: 'image/jpeg' },
    back: { base64: process.env.DOC_BACK_BASE64, mimeType: 'image/jpeg' },
    attestation: {
      provider: 'bigdatacorp_documentoscopy',
      onboardingId: 'b7d2...',
      extracted: { fullName: 'MARIA SILVA', birthDate: '1990-05-14', cpf: '12345678909', documentType: 'RG' }
    }
  }
})

console.log('submit status:', submit.status)
await pollKycStatus(session.kycUserId)
```

<Warning>
  `/submit` does real work (creates the subaccount + uploads images) and can take \~20s. A timeout or
  non-JSON body does **not** mean failure — always poll `/status` before concluding. See
  [External Evidence KYC](/guides/kyc/external-evidence).
</Warning>

## Status values

`created` → `provider_pending` → `approved` / `rejected` / `rejected_retryable` / `blocked`. Money
movement is enabled once `status` is `approved` and the capability flag you need
(`brlaEnabled` for Brazil, `usdEnabled` for foreign USD) is `true`.

## Running the Example

```bash theme={null}
node kyc-backend.js sumsub            # or: external-evidence
```

See the full runnable script: [`kyc-backend.js`](/examples/javascript/kyc-backend.js).

## Next Steps

<CardGroup cols={2}>
  <Card title="Pods Ramp" icon="money-bill-transfer" href="/examples/javascript/ramp">
    Move money once the wallet is approved
  </Card>

  <Card title="KYC Overview" icon="id-card" href="/guides/kyc/overview">
    All KYC flows and endpoints
  </Card>

  <Card title="External Evidence Guide" icon="camera" href="/guides/kyc/external-evidence">
    Full evidence payload and rejection handling
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    View complete API documentation
  </Card>
</CardGroup>
