Back to Blog
KYCAPIDeveloper Guide

Building a KYC Verification Flow with CirclesCheck

CirclesCheck Team|

Architecture Overview

A KYC verification flow built on CirclesCheck follows a straightforward request-response cycle with asynchronous webhook delivery. Here is how the pieces fit together:

Client App          CirclesCheck API           Your Server
    |                      |                        |
    |-- Create Session --->|                        |
    |<-- session_id -------|                        |
    |                      |                        |
    |-- Upload Document -->|                        |
    |<-- 202 Accepted -----|                        |
    |                      |                        |
    |                      |--- Verify Document --->|
    |                      |--- Screen Sanctions -->|
    |                      |--- Screen PEPs ------->|
    |                      |                        |
    |                      |-- Webhook POST ------->|
    |                      |                  (update user status)
    |<-- Poll or Push -----------------------------|
    |   (show result)                               |

Your client application creates a verification session and uploads identity documents. CirclesCheck runs document verification, sanctions screening, and PEP checks in parallel. When all checks complete, a webhook fires to your server with the final result. Your frontend can either poll for status or receive a push notification to display the outcome.

This design keeps sensitive document handling on CirclesCheck's infrastructure while giving you full control over the user experience and data storage on your side.

Step 1: Create a Verification Session

Every KYC flow starts by creating a session. The session groups together the document upload and all checks you want to run against the individual.

const session = await fetch('https://api.circlescheck.com/api/v1/kyc/sessions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'cc_live_your_api_key'
  },
  body: JSON.stringify({
    reference_id: 'user_12345',
    checks: ['document', 'sanctions', 'pep']
  })
});

const { session_id } = await session.json();
// Store session_id — you will need it for the document upload and status queries

The reference_id is your internal user identifier. It lets you correlate CirclesCheck verification results back to a user record in your own database. The checks array specifies which verification steps to run. You can request document verification alone or combine it with sanctions and pep screening in a single session.

Step 2: Upload the Identity Document

With a session created, upload the user's identity document. CirclesCheck accepts standard image formats (JPEG, PNG) and PDF files up to 10 MB.

const formData = new FormData();
formData.append('document', file);
formData.append('document_type', 'passport'); // passport, national_id, drivers_license
formData.append('session_id', sessionId);

await fetch('https://api.circlescheck.com/api/v1/kyc/documents', {
  method: 'POST',
  headers: { 'X-API-Key': 'cc_live_your_api_key' },
  body: formData
});

The API returns a 202 Accepted response immediately. Document verification and screening happen asynchronously. Do not block your UI waiting for a synchronous result -- use the webhook described in Step 4 instead.

Step 3: Supported Document Types

CirclesCheck supports the following identity document types:

Document Type document_type Value Notes
Passport passport International travel passport
National ID national_id Government-issued national identity card
Driver's License drivers_license Valid driver's license with photo
Residence Permit residence_permit Temporary or permanent residence permit

Pass the appropriate document_type value when uploading. Accurate document type classification improves extraction accuracy and reduces the chance of a needs_review result.

Step 4: Handle the Verification Webhook

Once all checks in a session complete, CirclesCheck sends a POST request to the webhook URL configured in your dashboard. Your endpoint should process the result and update your user's verification status.

// POST /api/webhooks/kyc (your endpoint)
app.post('/api/webhooks/kyc', (req, res) => {
  const { session_id, status, checks } = req.body;

  // status: 'approved', 'rejected', 'needs_review'
  // checks: { document: 'passed', sanctions: 'clear', pep: 'clear' }

  // Update user status in your database
  await db.users.update({
    where: { verificationSessionId: session_id },
    data: {
      kycStatus: status,
      kycChecks: checks,
      verifiedAt: status === 'approved' ? new Date() : null
    }
  });

  res.sendStatus(200);
});

Return a 200 status code promptly. CirclesCheck will retry webhook delivery with exponential backoff if your endpoint returns an error or times out, so your handler should be idempotent -- processing the same webhook twice should not corrupt your data.

Step 5: Frontend Integration Tips

A smooth frontend experience during KYC verification keeps users engaged and reduces drop-off. Here are practical recommendations:

Upload UI. Use a drag-and-drop zone with a file input fallback. Show a thumbnail preview of the selected document before uploading so users can confirm they picked the right file. Validate file size client-side (reject anything over 10 MB before hitting the API).

Progress states. After the upload call returns 202, show a progress indicator. Use clear labels for each state: "Uploading document...", "Verification in progress...", and "Verification complete." Avoid vague spinners with no context.

Status polling. If you cannot use WebSockets or server-sent events, poll the session status endpoint at reasonable intervals (every 3-5 seconds). Stop polling after receiving a terminal status or after a timeout threshold (e.g., 5 minutes).

Redirect on completion. When verification completes, redirect the user to an appropriate page: a success confirmation for approved, an explanation page for rejected, or a "we'll be in touch" message for needs_review.

Verification Statuses Explained

Every session ends in one of four terminal statuses:

  • approved -- All checks passed. The individual's identity document is valid and they do not appear on any sanctions or PEP lists. You can grant full access.
  • rejected -- One or more checks failed. The document may be invalid, expired, or tampered with, or the individual matched a sanctions list. Do not proceed without manual review.
  • needs_review -- The automated checks could not reach a definitive conclusion. This typically happens with low-quality document images or partial name matches on sanctions/PEP lists. Route these cases to your compliance team.
  • expired -- The session timed out before all required documents were uploaded. Sessions expire after 24 hours. Prompt the user to start a new verification.

Production Checklist

Before going live, walk through this checklist to make sure your integration is production-ready:

  • Use cc_live_ API keys. Test keys (cc_test_) return simulated responses and do not run real verification. Swap to your live key before deploying to production. Never expose API keys in client-side code -- route all API calls through your backend.

  • Verify webhook signatures. Every webhook request includes an X-Signature header. Validate it against your webhook secret to confirm the request originated from CirclesCheck and was not tampered with in transit.

  • Store verification results for audit trail. Compliance regulations require you to retain KYC verification records. Store the full webhook payload, including timestamps, check results, and session metadata. This data is essential during audits.

  • Handle document upload size limits. The maximum file size is 10 MB per document. Enforce this limit on both client and server to provide clear error messages rather than letting the API reject oversized uploads.

  • Implement retry logic for webhook delivery. Although CirclesCheck retries failed webhooks automatically, your system should also handle edge cases. If your database is temporarily unavailable when a webhook arrives, log the raw payload and process it once your database recovers.

  • Use HTTPS for all endpoints. Your webhook receiver must be accessible over HTTPS. CirclesCheck will not deliver webhooks to plain HTTP URLs in production.

Next Steps

This tutorial covers the core KYC verification flow. CirclesCheck also supports batch screening, ongoing monitoring, and adverse media checks. For the full API reference, integration guides, and sandbox environment details, visit the CirclesCheck documentation.

If you run into issues or have questions about your integration, reach out to our developer support team through the dashboard.

Ready to streamline your compliance?

Start screening against 250+ sanctions lists in minutes.

Get Started Free