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

# Python

> Official Python SDK for the Verilock Identity API.

## Installation

```bash theme={null}
pip install verilock
```

<Info>
  Requires **Python 3.9+**. The SDK uses [httpx](https://www.python-httpx.org/) under the hood for both sync and async support.
</Info>

## Configuration

<CodeGroup>
  ```python Sync theme={null}
  from verilock import Verilock

  client = Verilock(
      api_key="qi_live_...",              # or set VERILOCK_API_KEY env var
      base_url="https://verilock.io/api/v1", # optional
      timeout=30,                          # optional, seconds (default: 30)
      max_retries=2,                       # optional, retries on 429/5xx
  )
  ```

  ```python Async theme={null}
  from verilock import AsyncVerilock

  client = AsyncVerilock(
      api_key="qi_live_...",
      base_url="https://verilock.io/api/v1",
      timeout=30,
      max_retries=2,
  )
  ```
</CodeGroup>

<Tip>
  Use a `qi_test_` key during development. Test keys return simulated results and are free to use.
</Tip>

## Quick Start

<CodeGroup>
  ```python Sync theme={null}
  from verilock import Verilock

  with Verilock(api_key="qi_live_...") as client:
      # Create a KYC session
      session = client.sessions.create({
          "applicant_email": "john@example.com",
          "applicant_name": "John Doe",
          "redirect_url": "https://example.com/callback",
      })

      # Redirect the user to complete verification
      print(session["session_url"])

      # Later, retrieve the result
      result = client.sessions.retrieve(session["id"])
      print(result["status"])  # 'approved' | 'rejected' | 'pending' | ...
  ```

  ```python Async theme={null}
  import asyncio
  from verilock import AsyncVerilock

  async def main():
      async with AsyncVerilock(api_key="qi_live_...") as client:
          session = await client.sessions.create({
              "applicant_email": "john@example.com",
              "applicant_name": "John Doe",
              "redirect_url": "https://example.com/callback",
          })
          print(session["session_url"])

          result = await client.sessions.retrieve(session["id"])
          print(result["status"])

  asyncio.run(main())
  ```
</CodeGroup>

## KYC Sessions

### Create a session

<CodeGroup>
  ```python Sync theme={null}
  session = client.sessions.create({
      "applicant_email": "jane@example.com",
      "applicant_name": "Jane Smith",
      "redirect_url": "https://example.com/done",
  })
  # session["session_url"] -> hosted verification link
  ```

  ```python Async theme={null}
  session = await client.sessions.create({
      "applicant_email": "jane@example.com",
      "applicant_name": "Jane Smith",
      "redirect_url": "https://example.com/done",
  })
  ```
</CodeGroup>

### List sessions

```python theme={null}
response = client.sessions.list(status="approved", page=1, per_page=25)
for item in response["data"]:
    print(f"{item['id']}: {item['status']}")
```

### Retrieve a session

```python theme={null}
session = client.sessions.retrieve("sess_abc123")
```

### Upload a document

```python theme={null}
with open("id_front.jpg", "rb") as f:
    client.sessions.upload_document(
        session_id="sess_abc123",
        file=f.read(),
        side="front",
        document_type="passport",
    )
```

### Upload a selfie

```python theme={null}
with open("selfie.jpg", "rb") as f:
    client.sessions.upload_selfie(session_id="sess_abc123", file=f.read())
```

### Submit for evaluation

```python theme={null}
result = client.sessions.submit("sess_abc123")
print(result["status"])  # 'processing'
```

<Info>
  Call `submit()` only after all required documents and selfie have been uploaded. The session enters asynchronous processing and you will receive a webhook when it completes.
</Info>

## AML Screening

### Screen a person

```python theme={null}
result = client.aml.screen({
    "name": "John Doe",
    "date_of_birth": "1990-01-15",
    "nationality": "US",
})

if result["status"] == "match":
    print(f"Found {result['matches_found']} matches")
```

### Retrieve a screening

```python theme={null}
screening = client.aml.retrieve("aml_abc123")
```

### Submit a decision

```python theme={null}
client.aml.decide("aml_abc123", {
    "decision": "false_positive",
    "reason": "Name similarity only, different date of birth.",
})
```

## Transaction Monitoring

### Screen a transaction

```python theme={null}
result = client.transactions.screen({
    "transaction_ref": "TX-001",
    "sender_name": "Alice Corp",
    "receiver_name": "Bob Ltd",
    "amount": 15000,
    "currency": "USD",
})

if result["risk_level"] == "high":
    print(f"{result['alerts_count']} alerts triggered")
```

### Batch screening

```python theme={null}
batch = client.transactions.screen_batch({
    "transactions": [
        {"transaction_ref": "TX-002", "sender_name": "Alice", "receiver_name": "Bob", "amount": 500, "currency": "EUR"},
        {"transaction_ref": "TX-003", "sender_name": "Carol", "receiver_name": "Dave", "amount": 12000, "currency": "USD"},
    ],
})
```

### List, retrieve, and decide

```python theme={null}
response = client.transactions.list(risk_level="high")

detail = client.transactions.retrieve("txn_abc123")

client.transactions.decide("txn_abc123", {
    "decision": "approve",
    "reason": "Verified sender identity manually.",
})
```

## Premium Features

<Warning>
  Premium features require a paid plan. Calls made without an active subscription raise an `InsufficientBalanceError`.
</Warning>

```python theme={null}
# Biometric face authentication
match = client.biometric.verify(session_id="sess_abc123", selfie=selfie_bytes)

# Government database validation
db_check = client.database.validate(document_number="AB123456", country="US")

# 1:N face duplicate detection
duplicates = client.face_search.search(selfie=selfie_bytes)

# Crypto wallet compliance screening
wallet_result = client.wallet.screen(address="0xabc...", chain="ethereum")

# Reusable KYC credentials
credential = client.credentials.issue(session_id="sess_abc123")

# Age verification from selfie
age = client.age_verify.verify(selfie=selfie_bytes, min_age=18)
```

## Error Handling

All SDK errors extend `VerilockError`. Use specific classes for granular handling.

```python theme={null}
from verilock.errors import (
    VerilockError,
    AuthenticationError,
    InsufficientBalanceError,
    NotFoundError,
    ValidationError,
    RateLimitError,
)

try:
    session = client.sessions.create({
        "applicant_email": "john@example.com",
        "redirect_url": "https://example.com/callback",
    })
except InsufficientBalanceError as e:
    print(f"Balance: {e.balance}, required: {e.required}")
except ValidationError as e:
    print(f"Field errors: {e.errors}")
except RateLimitError as e:
    print(f"Retry after {e.retry_after}s")
except AuthenticationError:
    print("Invalid API key")
except NotFoundError:
    print("Resource not found")
except VerilockError as e:
    print(f"API error [{e.code}]: {e.message}")
```

## Context Managers

Both the sync and async clients support context managers that automatically close the underlying httpx connection pool.

<CodeGroup>
  ```python Sync theme={null}
  from verilock import Verilock

  with Verilock(api_key="qi_live_...") as client:
      session = client.sessions.create({...})
  # Connection pool is closed automatically
  ```

  ```python Async theme={null}
  from verilock import AsyncVerilock

  async with AsyncVerilock(api_key="qi_live_...") as client:
      session = await client.sessions.create({...})
  # Connection pool is closed automatically
  ```
</CodeGroup>

<Tip>
  Using context managers is recommended to avoid resource leaks. Alternatively, call `client.close()` (or `await client.close()` for async) when you are done.
</Tip>
