Metadata-Version: 2.4
Name: deps_kyc
Version: 0.0.6
Summary: DEPS Know your client SDK
License: Apache 2.0
License-File: LICENCE
Requires-Python: >=3.12
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Dist: cachetools (>=7.0.5,<8.0.0)
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
Requires-Dist: pydantic-settings (>=2.13.1,<3.0.0)
Requires-Dist: requests (>=2.32.5,<3.0.0)
Description-Content-Type: text/markdown

# deps_kyc

Python SDK for the DEPS Know Your Client (KYC) platform.

## Requirements

- Python 3.12+
- [Poetry](https://python-poetry.org/) for dependency management

## Installation

```bash
poetry add deps_kyc
```

## Quick start

```python
from deps_kyc import DepsKYC, DepsKYCSettings

settings = DepsKYCSettings(
    url="https://your-deps-kyc-instance.example.com",
    aws_client_id="your-client-id",  # Cognito M2M app client id
    aws_client_secret="your-client-secret",  # Cognito M2M app client secret
    aws_region="us-east-1",
    aws_cognito_domain="your-cognito-domain",
)

client = DepsKYC(settings=settings)

# Create a profile type
profile_type = client.profiles.create_profile_type(
    name="Individual KYC",
    description="Standard individual verification",
    subject_kind="Individual",
)

# Create a profile
profile = client.profiles.create_profile(
    name="John Doe",
    profile_type_id=profile_type["id"],
)

# Upload a file
with open("passport.pdf", "rb") as f:
    client.files.upload_file(
        profile_id=profile["id"],
        file_name="passport.pdf",
        file_content=f.read(),
    )

# Submit the profile for verification
client.profiles.submit_profile(profile_id=profile["id"])
```

## Configuration

### Constructor

Pass a `DepsKYCSettings` instance directly:

```python
settings = DepsKYCSettings(
    url="https://your-deps-kyc-instance.example.com",
    aws_client_id="...",
    aws_client_secret="...",
    aws_region="...",
    aws_cognito_domain="...",
)

client = DepsKYC(settings=settings)
```

### Environment variables

`DepsKYCSettings` extends Pydantic's `BaseSettings` and reads from environment variables prefixed with `DEPS_KYC_`:

| Environment variable             | Required | Description                          |
|----------------------------------|----------|--------------------------------------|
| `DEPS_KYC_URL`                   | Yes      | Base URL of the DEPS KYC API         |
| `DEPS_KYC_AWS_CLIENT_ID`        | Yes      | AWS Cognito client ID                |
| `DEPS_KYC_AWS_CLIENT_SECRET`    | Yes      | AWS Cognito client secret            |
| `DEPS_KYC_AWS_REGION`           | Yes      | AWS region for Cognito               |
| `DEPS_KYC_AWS_COGNITO_DOMAIN`   | Yes      | AWS Cognito domain prefix            |
| `DEPS_KYC_AWS_AUTH_SCOPE`       | No       | OAuth2 scope (has a default)         |
| `DEPS_KYC_FILES_API_PREFIX`     | No       | API prefix for files (default: `api/files`)       |
| `DEPS_KYC_PROFILES_API_PREFIX`  | No       | API prefix for profiles (default: `api/profiles`) |
| `DEPS_KYC_DOCUMENTS_API_PREFIX` | No       | API prefix for documents (default: `api/documents`) |
| `DEPS_KYC_CLIENTS_API_PREFIX`   | No       | API prefix for clients (default: `api/clients`)   |

When environment variables are set, instantiate without arguments:

```python
client = DepsKYC()
```

## API reference

The `DepsKYC` client exposes four sub-clients as properties: `profiles`, `documents`, `files`, and `clients`.

### Profiles (`client.profiles`)

**Profile types**

| Method | Description |
|--------|-------------|
| `create_profile_type(name, description, subject_kind)` | Create a new profile type. `subject_kind`: `Individual`, `LegalEntity` |
| `get_profile_type(profile_type_id)` | Get a profile type by ID |
| `update_profile_type(profile_type_id, name, description, subject_kind)` | Update a profile type. `subject_kind`: `Individual`, `LegalEntity` |

**Profile type fields**

| Method | Description |
|--------|-------------|
| `add_profile_type_field(profile_type_id, field_type, field_name, field_description, field_required, is_collection)` | Add a field to a profile type |
| `update_profile_type_field(profile_type_id, field_id, field_name, field_description, field_required, is_collection)` | Update a field |
| `delete_profile_type_field(profile_type_id, field_id)` | Delete a field |

**Document types (on profile types)**

| Method | Description |
|--------|-------------|
| `add_document_type(profile_type_id, document_type_name, document_type_description, document_type_fields)` | Add a document type to a profile type |
| `update_document_type(profile_type_id, document_type_id, document_type_name, document_type_description, document_type_fields)` | Update a document type |
| `delete_document_type(profile_type_id, document_type_id)` | Delete a document type |

**Validation rules (profile-level and document-type-level)**

| Method | Description |
|--------|-------------|
| `add_validation_rule(profile_type_id, validation_rule)` | Add a validation rule to a profile type |
| `update_validation_rule(profile_type_id, validation_rule_id, validation_rule)` | Update a validation rule |
| `delete_validation_rule(profile_type_id, validation_rule_id)` | Delete a validation rule |
| `add_document_type_validation_rule(profile_type_id, document_type_id, validation_rule)` | Add a validation rule to a document type |
| `update_document_type_validation_rule(profile_type_id, document_type_id, validation_rule_id, validation_rule)` | Update a document type validation rule |
| `delete_document_type_validation_rule(profile_type_id, document_type_id, validation_rule_id)` | Delete a document type validation rule |

**Reconciliation rules**

| Method | Description |
|--------|-------------|
| `add_reconciliation_rule(profile_type_id, reconciliation_rule)` | Add a reconciliation rule |
| `update_reconciliation_rule(profile_type_id, reconciliation_rule_id, reconciliation_rule)` | Update a reconciliation rule |
| `delete_reconciliation_rule(profile_type_id, reconciliation_rule_id)` | Delete a reconciliation rule |

**Profiles**

| Method | Description |
|--------|-------------|
| `create_profile(name, profile_type_id)` | Create a new profile |
| `get_profiles(statuses, name, page, per_page)` | List profiles with optional filters. `statuses`: `new`, `file_processing`, `document_processing`, `review`, `processed`, `submitted`, `reconciled`, `not_reconciled`, `escalated`, `approved`, `rejected`. `page` is 0-indexed |
| `get_profile_audit(profile_id, include_queries)` | Get the audit trail of a profile |
| `submit_profile(profile_id)` | Submit a profile. The profile should be in `processed` status |
| `escalate_profile(profile_id, note)` | Escalate a profile. The profile should be in `not_reconciled` status |
| `approve_profile_escalation(profile_id, note)` | Approve an escalation. The profile should be in `escalated` status |
| `reject_profile_escalation(profile_id, note)` | Reject an escalation. The profile should be in `escalated` status |

### Documents (`client.documents`)

| Method | Description |
|--------|-------------|
| `get_documents(profile_id, document_type_kinds, statuses, subject_kinds, subject_name, page, per_page)` | List documents for a profile with optional filters |
| `get_document(document_id)` | Get a single document by ID |
| `add_corrections(document_id, subject_kind, updated_entity)` | Add corrections to a document. The document should be in `extracted`, `not_extracted`, or `corrected` status |
| `retry_document(document_id)` | Retry a document. The document should be in `failed` status and should have retryable error |
| `skip_document(document_id)` | Skip a document. The document should be in `failed` or `not_extracted` status |

### Files (`client.files`)

| Method | Description |
|--------|-------------|
| `upload_file(profile_id, file_name, file_content)` | Upload a file to a profile |
| `get_files(profile_id, statuses, name, page, per_page)` | List files for a profile with optional filters. `statuses`: `new`, `prepared`, `retrieved`, `classified`, `not_classified`, `failed`, `skipped`, `redacted`. `page` is 0-indexed |
| `retry_file(file_id)` | Retry a file. The file should be in `failed` status and should have retryable error |
| `skip_file(file_id)` | Skip a file. The file should be in `failed` or `not_classified` status |
| `assign_document_types(file_id, document_types)` | Assign document types to a file. The file should be in `retrieved`, `classified`, or `not_classified` status |
| `start_pii_redaction(profile_id)` | Start PII redaction for a profile. All files should be in `classified` or `redacted` status, with at least one in `classified` |

### Clients (`client.clients`)

| Method | Description |
|--------|-------------|
| `get_profile(profile_id, include)` | Get a profile with optional related entities (`subjects`, `individuals`, `legal_entities`) |

## Error handling

All API methods raise `DepsKYCException` on non-2xx responses. The exception message includes the HTTP status code and response body:

```python
from deps_kyc import DepsKYC, DepsKYCException

client = DepsKYC()

try:
    profile = client.profiles.get_profile_type("nonexistent-id")
except DepsKYCException as e:
    print(e)  # HTTP 404: {"message": "Not found"}
```

## Built-in resilience

The SDK includes automatic retry and timeout handling:

- **Timeouts** -- 3.05s connection timeout, 60s read timeout
- **Retries** -- up to 8 retries with exponential backoff on status codes 429, 500, 502, 503, 504
- **Token caching** -- Cognito access tokens are cached for 45 minutes to minimize auth requests

## Development

```bash
# Install dependencies
poetry install

# Format code
make format

# Check formatting
make format-check

# Run linter
make lint

# Run type checker
make mypy

# Run all CI checks
make ci
```

## License

Apache 2

