Skip to main content

didit-sdk

CI PyPI version Python versions License: MIT Checked with mypy Ruff

Unofficial, community-maintained Python client for the Didit Identity Verification API.


Highlights

  • Ergonomic Sync & Async: Dual-client architecture built on top of high-performance httpx.
  • Strictly Typed & Validated: 100% type annotations (PEP 561 compliant with py.typed) and robust Pydantic v2 domain models.
  • Didit V3 API Alignment: Full fidelity to upstream V3 schemas (id_verifications[], liveness_checks[], face_matches[], aml_screenings[], reviews[]) with forward compatibility (extra="allow") and backward-compatible property accessors.
  • Complete Session Lifecycle: Covers all 10 documented Didit session statuses (Not Started, In Progress, In Review, Approved, Declined, Expired, Abandoned, Kyc Expired, Resubmitted, Awaiting User) with granular state inspection (is_decided, is_closed, requires_review, requires_user_action).
  • Cryptographic Security: Constant-time HMAC-SHA256 signature verification (X-Signature-V2 & X-Signature), full UTF-8 Unicode canonical JSON support (ensure_ascii=False, allow_nan=False), and authoritative signed body anti-replay verification with strict header matching.
  • FastAPI Integration: Plug-and-play DiditWebhookGuard dependency for securing webhook endpoints with zero boilerplate.
  • Zero-Network Simulation Mode: Built-in SimulatedDidit and SimulatedAsyncDidit to run unit tests and local end-to-end user flows completely offline without live credentials.
  • Resilient Error Hierarchy: Typed exceptions (DiditAuthenticationError, DiditRateLimitError, DiditNotFoundError, DiditServerError) with automated rate-limit retry duration parsing.

Installation

# Core SDK (httpx + pydantic)
pip install didit-sdk

# With FastAPI integration
pip install "didit-sdk[fastapi]"

Quickstart

1. Synchronous Client

from didit import Didit, SessionStatus

# Initialize client (falls back to DIDIT_API_KEY environment variable if omitted)
client = Didit(api_key="your_api_key", webhook_secret="your_webhook_secret")

# Create a verification session
session = client.sessions.create(
    vendor_data="user_12345",
    workflow_id="wf_kyc_standard",
    callback="https://yourapp.com/kyc/complete",
    language="es",
)

print(f"Verification URL: {session.url}")
print(f"Session ID: {session.session_id}")

# Fetch verification decision immediately:
decision = client.sessions.get_decision(session.session_id)

# Or poll until a terminal outcome (Approved or Declined) is reached:
decision = client.sessions.poll_decision(session.session_id, timeout=60.0, interval=2.0)
if decision.status == SessionStatus.APPROVED:
    print(f"User approved! Document: {decision.document.document_number}")

2. Asynchronous Client (asyncio)

import asyncio
from didit import AsyncDidit, SessionStatus


async def main():
    async with AsyncDidit(api_key="your_api_key") as client:
        session = await client.sessions.create(
            vendor_data="user_987",
            workflow_id="wf_kyc_standard",
        )
        print(f"Session URL: {session.url}")

        decision = await client.sessions.get_decision(session.session_id)
        if decision.status.is_terminal:
            print(f"Final outcome: {decision.status}")


asyncio.run(main())

Securing Webhooks with FastAPI

Didit dispatches signed HTTP POST events upon verification completion. didit-sdk provides a dedicated FastAPI dependency to verify signatures in constant time and prevent replay attacks:

from fastapi import FastAPI, Depends
from didit import WebhookPayload, SessionStatus
from didit.integrations.fastapi import DiditWebhookGuard

app = FastAPI()

# Guard reads secret from argument or DIDIT_WEBHOOK_SECRET env var
webhook_guard = DiditWebhookGuard(secret="whsec_...")


@app.post("/api/v1/webhooks/didit")
async def handle_didit_event(
    payload: WebhookPayload = Depends(webhook_guard),
):
    print(f"Received event for session: {payload.session_id}")

    if payload.status == SessionStatus.APPROVED:
        # Mark user verified in your database
        ...
    elif payload.status == SessionStatus.DECLINED:
        # Handle rejection
        ...

    return {"received": True}

Testing & Offline Simulation

Developing without live credentials or mock servers? Use the built-in simulator:

from didit import SimulatedDidit, SessionStatus

# Create in-memory client
sim = SimulatedDidit(webhook_secret="test_secret")

# 1. Create a simulated session
session = sim.sessions.create(vendor_data="dev_user", workflow_id="wf_dev")
assert session.session_id.startswith("sim_")

# 2. Simulate user passing identity check
sim.approve_session(session.session_id)

decision = sim.sessions.get_decision(session.session_id)
assert decision.status == SessionStatus.APPROVED

# 3. Generate signed webhook payloads to test your local receiver
raw_body, headers = sim.generate_webhook_event(session.session_id)
# headers contain valid X-Signature-V2 and fresh X-Timestamp!

Exception Handling

All client errors inherit from DiditError:

from didit import Didit
from didit.errors import (
    DiditAuthenticationError,
    DiditNotFoundError,
    DiditRateLimitError,
    DiditServerError,
)

client = Didit()

try:
    decision = client.sessions.get_decision("non_existent_session")
except DiditNotFoundError:
    print("Session not found")
except DiditRateLimitError as exc:
    print(f"Rate limited. Retry after {exc.retry_after} seconds.")
except DiditAuthenticationError:
    print("Invalid API key")
except DiditServerError as exc:
    print(f"Didit server error ({exc.status_code}): {exc.response_body}")

Environment Variables

Variable Default Description
DIDIT_API_KEY None (Required) Your Didit secret API key
DIDIT_BASE_URL https://verification.didit.me/v3 Didit API base URL
DIDIT_WEBHOOK_SECRET None Shared webhook HMAC secret
DIDIT_TIMEOUT 30.0 HTTP request timeout in seconds
DIDIT_MAX_RETRIES 2 Maximum retry attempts for transient errors

Development & Testing

# Clone the repository
git clone https://github.com/agustin18/didit-sdk.git
cd didit-sdk

# Install dependencies with uv
uv sync --extra dev

# Run unit tests with 100% coverage gate
uv run pytest --cov=didit --cov-report=term-missing

# Lint & Format check
uv run ruff check .
uv run ruff format --check .

# Strict type check
uv run mypy src/didit

License

This project is licensed under the terms of the MIT License.

Metadata

Release files for didit-sdk 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for didit-sdk 0.1.2
File Size Uploaded
didit_sdk-0.1.2.tar.gz 29.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for didit-sdk 0.1.2
File Interpreter ABI Platform
didit_sdk-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 53.7 kB

Release files / didit_sdk-0.1.2.tar.gz

Download URL didit_sdk-0.1.2.tar.gz
Size 29.2 kB
Tags Source
SHA-256 checksum
How to use checksums
202994ccdabf4b7d7896029ee7ec1ef8221fa2252ee660940715887dd18337f0
BLAKE2b-256 checksum
How to use checksums
068f8c4ed664622df169a4267e93619ea31d4f7175af8dda3b95c6f131847ed1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / didit_sdk-0.1.2-py3-none-any.whl

Download URL didit_sdk-0.1.2-py3-none-any.whl
Size 24.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
826fcb4422e887f237bd7fac112c54a901279152900d14d31ee8871cc05d19bc
BLAKE2b-256 checksum
How to use checksums
654ca8ee98ba8e822038d3fc9e41926f0c46b54491e92f4532df92b31bfecb51
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page