> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.withverifa.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.withverifa.com/_mcp/server.

# API-Only Integration

> Build your own capture UI and submit documents directly via the API.

The API-only approach gives you complete control over the user experience. You
build the capture UI, collect documents and selfies in your own application, and
submit them to Verifa for processing.

## When to use API-only

* You need full control over the user experience and camera flow
* You're building for a non-standard platform (kiosk, desktop, embedded device)
* You're integrating into an existing capture pipeline
* You want to pre-process images before submission

## Flow overview

```
Your App                        Your Server                    Verifa
   │                                │                            │
   │  "Start verification"          │                            │
   │───────────────────────────────▶│                            │
   │                                │  POST /api/v1/sessions     │
   │                                │───────────────────────────▶│
   │                                │  { id, token }             │
   │  { session_id, token }         │◀───────────────────────────│
   │◀───────────────────────────────│                            │
   │                                │                            │
   │  [capture documents + selfie]  │                            │
   │                                │                            │
   │  Upload documents              │                            │
   │───────────────────────────────▶│                            │
   │                                │  POST /verify/{token}/...  │
   │                                │───────────────────────────▶│
   │                                │                            │
   │                                │  POST /verify/{token}/complete
   │                                │───────────────────────────▶│
   │                                │                            │
   │                                │  webhook: session.approved │
   │                                │◀───────────────────────────│
```

## Step 1: Create a session

```bash
curl -X POST https://api.withverifa.com/api/v1/sessions \
  -H "X-API-Key: vk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "external_ref": "user_abc123",
    "country": "US"
  }'
```

Save the `id` and extract the token from the `capture_url` path. The token is
used for the capture endpoints.

## Step 2: Record consent

Before uploading any biometric data, record the user's consent:

```bash
curl -X POST https://api.withverifa.com/verify/{token}/consent \
  -H "Content-Type: application/json" \
  -d '{
    "consent_given": true
  }'
```

This records the timestamp, IP address, and consent version. Consent is required
before documents or selfies can be submitted.

## Step 3: Select document type

```bash
curl -X POST https://api.withverifa.com/verify/{token}/document-type \
  -H "Content-Type: application/json" \
  -d '{
    "document_type": "drivers_license"
  }'
```

Supported document types: `passport`, `drivers_license`, `national_id`,
`residence_permit`.

## Step 4: Upload documents

Upload the front side of the ID document:

```bash
curl -X POST https://api.withverifa.com/verify/{token}/document \
  -F "file=@id_front.jpg" \
  -F "side=front"
```

Upload the back side (required for driver's licenses and national IDs):

```bash
curl -X POST https://api.withverifa.com/verify/{token}/document \
  -F "file=@id_back.jpg" \
  -F "side=back"
```

### Document requirements

| Requirement            | Value                         |
| ---------------------- | ----------------------------- |
| Max file size          | 10 MB                         |
| Supported formats      | JPEG, PNG, WebP, HEIC         |
| Minimum resolution     | 480px on shortest side        |
| Recommended resolution | 1920px+ for best OCR accuracy |

Documents are encrypted with AES-256-GCM immediately on receipt.

## Step 5: Upload selfie

```bash
curl -X POST https://api.withverifa.com/verify/{token}/selfie \
  -F "file=@selfie.jpg"
```

The selfie is compared against the photo on the ID document during face
matching. For best results:

* Well-lit, front-facing photo
* No sunglasses or face coverings
* Neutral expression
* Minimum 480px resolution

## Step 6: Submit additional data (optional)

If your workflow requires additional information:

```bash
curl -X POST https://api.withverifa.com/verify/{token}/user-info \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@example.com",
    "phone": "+15551234567",
    "address_line1": "123 Main St",
    "city": "San Francisco",
    "state": "CA",
    "zip": "94102"
  }'
```

### Upload proof of address

```bash
curl -X POST https://api.withverifa.com/verify/{token}/additional-document \
  -F "file=@utility_bill.pdf" \
  -F "doc_type=proof_of_address" \
  -F "poa_document_type=utility_bill" \
  -F "poa_document_date=2026-07-02" \
  -F "poa_issuing_country=GB"
```

`poa_document_type` is optional but strongly recommended — without it the check
cannot apply the per-type recency window and falls back to the policy default.
`poa_issuing_country` is an ISO 3166-1 alpha-2 code; when omitted it defaults to
the country of the ID document on the session. Both drive the
[proof-of-address policy](/core-concepts/verifications-checks#proof-of-address).

## Step 7: Complete capture

Signal that all data has been submitted and trigger workflow processing:

```bash
curl -X POST https://api.withverifa.com/verify/{token}/complete
```

The session status moves to `processing` and the workflow engine begins
executing checks.

## Step 8: Get the result

### Via webhook

```json
{
  "event": "session.approved",
  "created_at": "2026-02-01T12:05:00Z",
  "idempotency_key": "idk_a1b2c3d4e5f6...",
  "data": {
    "session_id": "session_abc123",
    "external_ref": "user_abc123",
    "status": "approved",
    "is_sandbox": false
  }
}
```

### Via polling

```bash
# Check status
curl https://api.withverifa.com/api/v1/sessions/session_abc123 \
  -H "X-API-Key: vk_live_your_key_here"

# Once completed, get full result
curl https://api.withverifa.com/api/v1/sessions/session_abc123/result \
  -H "X-API-Key: vk_live_your_key_here"
```

## Fraud signals

When using the API-only approach, you won't get automatic client-side fraud
signal collection (behavioral, device fingerprinting, etc.) unless you submit
them manually:

```bash
curl -X POST https://api.withverifa.com/verify/{token}/signals \
  -H "Content-Type: application/json" \
  -d '{
    "completion_time_ms": 45000,
    "device_memory": 8,
    "hardware_concurrency": 4,
    "screen_width": 1920,
    "screen_height": 1080,
    "user_agent": "..."
  }'
```

Server-side signals (IP analysis, document forensics, duplicate detection) run
automatically regardless of integration method.

## Capture endpoint reference

All capture endpoints use the token path (`/verify/\{token\}/...`) and do not
require API key authentication — the token itself serves as the credential.

| Method | Path                                    | Purpose                                                                                   |
| ------ | --------------------------------------- | ----------------------------------------------------------------------------------------- |
| `POST` | `/verify/\{token\}/consent`             | Record biometric consent                                                                  |
| `POST` | `/verify/\{token\}/document-type`       | Select document type                                                                      |
| `POST` | `/verify/\{token\}/document`            | Upload ID front or back                                                                   |
| `POST` | `/verify/\{token\}/selfie`              | Upload selfie photo                                                                       |
| `POST` | `/verify/\{token\}/additional-document` | Upload proof of address (`poa_document_type`, `poa_document_date`, `poa_issuing_country`) |
| `POST` | `/verify/\{token\}/user-info`           | Submit email, phone, address, SSN                                                         |
| `POST` | `/verify/\{token\}/send-otp`            | Send OTP verification code                                                                |
| `POST` | `/verify/\{token\}/verify-otp`          | Validate OTP code                                                                         |
| `GET`  | `/verify/\{token\}/liveness-challenge`  | Get liveness challenge nonce                                                              |
| `POST` | `/verify/\{token\}/signals`             | Submit client-side fraud signals                                                          |
| `POST` | `/verify/\{token\}/complete`            | Finalize capture and trigger processing                                                   |

## Error handling

Capture endpoints return `400 Bad Request` for:

* Expired token (20-minute TTL)
* Session already completed
* Missing consent (must be recorded before document/selfie upload)
* File too large (>10 MB)
* Invalid file format

```json
{
  "error": "bad_request",
  "detail": "Session token has expired. Resume the session to get a new token.",
  "status_code": 400
}
```

## Related

* [Web Capture Flow](/integration-guides/web-capture-flow) — Use the hosted capture UI
  instead
* [Mobile SDK](/integration-guides/mobile-sdk) — Native capture for mobile apps
* [Fraud Signals](/best-practices/fraud-signals) — What signals to submit for best
  detection
* [Sessions](/core-concepts/sessions) — Full session lifecycle