Skip to main content

humanauthn-python-sdk

Official Python SDK for the online (HTTP API) version of HumanAuthn by Verifik.

HumanAuthn is an authentication + encryption primitive that turns a live biometric sample plus stored entropy into a verifiable credential — a HumanID, represented on the wire as a zelfProof token. This SDK wraps the current online HumanAuthn endpoints (/v2/human-id/encrypt, /encrypt-qr-code, /decrypt, /preview) in a small, typed client with a single runtime dependency (httpx). (The legacy /v2/zelf-proof/* routes are deprecated and not used.)

How it works

  • Enrollment (encrypt): capture a live face, bind it to your public_data and private metadata, and receive a zelf_proof HumanID token. Store that token (for example on the user record). HumanAuthn keeps no biometric template.
  • Authentication (decrypt): send a fresh face plus the stored zelf_proof. Only the enrolled face reconstructs the key, so a successful decrypt is the authentication, and it returns the private metadata.
  • Preview (preview): read the public, non-sensitive data of a HumanID without any biometric input.

See the HumanAuthn overview for the underlying primitive.

Installation

pip install humanauthn
uv add humanauthn

Requires Python 3.9+. The development environment targets Python 3.14.

Authentication (Verifik JWT)

Every request authenticates with a Verifik client JWT (a bearer token). You pass it as api_key; the SDK sends it as Authorization: Bearer <token>. Keep it in an environment variable and server-side only — never ship it to a browser.

# .env
VERIFIK_CLIENT_JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC...

Without a valid token, real API calls fail with 401 (HumanAuthnApiError with .is_auth_error is True).

Getting a token

  • Dashboard (recommended): sign in to the Verifik web app at ai.verifik.co and copy your client access token.

  • API (email OTP): request a code, then confirm it — see API key access via email.

    # 1) Request an OTP by email
    curl -X POST "https://api.verifik.co/v2/projects/email-login?email=you@example.com" \
      -H "Accept: application/json"
    
    # 2) Confirm it -> { data: { accessToken, tokenType: "bearer" } }
    curl -X POST "https://api.verifik.co/v2/projects/email-login/confirm" \
      -H "Content-Type: application/json" \
      -d '{ "email": "you@example.com", "otp": "123456" }'
    

    (The OTP can be delivered by email/SMS/WhatsApp depending on your account, so this flow is interactive — the SDK does not automate it. Generate the token once and paste it into VERIFIK_CLIENT_JWT.)

Lifetime, renewal, and expiry

  • A token is valid for about 30 days.

  • Renew a still-valid token (no re-login) via /v2/auth/session. expiresIn is measured in months (1 = one month):

    curl "https://api.verifik.co/v2/auth/session?origin=refresh&expiresIn=1" \
      -H "Authorization: Bearer $VERIFIK_CLIENT_JWT"
    # -> { "accessToken": "<new-jwt>", "tokenType": "bearer" }
    
  • Once a token has expired it can no longer be renewed — generate a new one (dashboard or email OTP) and update VERIFIK_CLIENT_JWT.

  • Treat the token like a password: if it leaks, re-issue it and replace the env var.

Quick start

import os
from humanauthn import HumanAuthnClient

client = HumanAuthnClient(api_key=os.environ["VERIFIK_CLIENT_JWT"])

# Enrollment: bind metadata to a live biometric sample, get a HumanID token.
enrolled = client.encrypt(
    face_base64=face_base64,          # base64 (or data: URI) facial image
    identifier="user42",              # alphanumeric
    public_data={"org": "Zelf"},      # string key-value pairs
    metadata={"userId": "42"},        # encrypted, owner-only
    require_liveness=True,
)

# Authentication: only the enrolled face reconstructs the key and decrypts.
result = client.decrypt(zelf_proof=enrolled.zelf_proof, face_base64=live_face)
print("Welcome back", result.identifier, result.metadata)

API

The client exposes one method per HumanAuthn endpoint. Arguments are keyword-only snake_case and are mapped to the API's camelCase JSON.

Method Endpoint Purpose
encrypt(...) POST /v2/human-id/encrypt Create a HumanID from a live sample + metadata
encrypt_qr_code(...) POST /v2/human-id/encrypt-qr-code Same as encrypt, plus a QR-code rendering
decrypt(...) POST /v2/human-id/decrypt Verify a live sample against a HumanID and reveal metadata
preview(...) POST /v2/human-id/preview Read public data without biometrics

encrypt(...)

Required: face_base64, identifier (alphanumeric), public_data (string map), metadata (string map). Optional: os (DESKTOP | ANDROID | IOS), require_liveness, liveness_detection_prior_creation, tolerance, password, reference_face_base64, verifier_key. Returns EncryptResult(zelf_proof, ipfs?, public_data?, credits?).

decrypt(...)

Required: zelf_proof, face_base64. Optional: os, password, verifier_key. Successful decryption is authentication; it returns DecryptResult(identifier, metadata, public_data, face_crop_base64?, difficulty?, ...). A face that doesn't match cannot reconstruct the key and surfaces as a HumanAuthnApiError.

Client options

HumanAuthnClient(
    api_key,                              # required: Verifik client JWT
    base_url="https://api.verifik.co",    # optional
    default_os="DESKTOP",                 # optional, applied when a call omits `os`
    timeout=30.0,                         # optional, per-request timeout in seconds
    max_retries=2,                        # optional, retries transient 5xx/network errors
    http_client=None,                     # optional httpx.Client (for tests / a future async client)
)

Images may be passed either as a raw base64 string or as a data:image/...;base64,... data URI; the SDK normalizes both.

Errors

All errors extend HumanAuthnError:

  • HumanAuthnConfigError — invalid input or client configuration.
  • HumanAuthnApiError — non-2xx response (.status, .code, .is_auth_error, .is_retryable).
  • HumanAuthnTimeoutError — request exceeded timeout.

Costs and credits

Verifik bills through a shared credit system; you buy and monitor credits in the dashboard. Approximate HumanAuthn usage:

Operation Typical cost
encrypt / encrypt_qr_code (create a HumanID) ~0.84 credits per HumanID
decrypt (authenticate) billed monthly per active user, not per call
preview small per-call charge

Each successful encrypt returns a credits object describing the charge. Exact pricing and inclusions depend on your plan, so check your dashboard and the credits docs. Credits can expire, so purchase them close to when you plan to use them. (Costs above are indicative and may change — treat the dashboard as the source of truth.)

Integrations

Reference examples live in examples/integrations/. They import the published package and keep the Verifik JWT server-side:

  • Flask — enroll + authenticate routes.
  • FastAPI — enroll + authenticate routes.
  • Browser capture — grab faceBase64 from a webcam and POST it to your backend.

Typical flow: the browser captures a face → your backend calls encrypt (enroll) or decrypt (authenticate) with your JWT → you store the returned zelf_proof on the user and issue your own session.

Security and privacy

  • HumanAuthn stores no biometric templates — authentication reconstructs an ephemeral key from the live face plus stored entropy.
  • Keep the Verifik JWT server-side; never expose it to the browser or commit it.
  • Do not log face_base64, metadata, or the JWT, and send everything over HTTPS.
  • Treat face images as sensitive biometric data; don't persist them unless you have a lawful basis and user consent.

Development

uv sync --all-extras --dev
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run pytest
uv run python examples/quickstart.py      # enroll -> preview -> authenticate (local mock)
uv run python examples/verify_real_api.py # smoke-test auth (needs VERIFIK_CLIENT_JWT)
uv run python examples/real_roundtrip.py  # enroll + authenticate a real face (see below)

Testing with a real face

examples/real_roundtrip.py runs a full enroll → preview → authenticate cycle. By default it uses the bundled AI-generated synthetic face at examples/faces/generated-test-face.jpg (not a real person; licensed for testing).

# Default synthetic face, against a local mock (no credentials):
uv run python examples/real_roundtrip.py

# Against the real Verifik API (charges credits):
VERIFIK_CLIENT_JWT=<token> uv run python examples/real_roundtrip.py

To use a different face, supply it locally — it is never committed (face images are biometric data, and fixtures/ plus image files are git-ignored). Use your own face or a licensed/synthetic one; do not commit other people's faces.

HUMANAUTHN_FACE_IMAGE=./fixtures/me.jpg uv run python examples/real_roundtrip.py
# or a pre-encoded image:
HUMANAUTHN_FACE_BASE64=<base64> uv run python examples/real_roundtrip.py

The test suite injects a mock transport, so it runs fully offline. The examples/quickstart.py demo runs the real SDK against a local in-process mock of the HumanAuthn API (examples/mock_server.py) so it works with no credentials. To target the real API, set your Verifik client JWT:

VERIFIK_CLIENT_JWT=<token> uv run python examples/quickstart.py

Resources

License

MIT

Release files for humanauthn 0.1.0

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

Source distribution (sdist)

Source distribution for humanauthn 0.1.0
File Size Uploaded
humanauthn-0.1.0.tar.gz 226.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for humanauthn 0.1.0
File Interpreter ABI Platform
humanauthn-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 239.1 kB

Release files / humanauthn-0.1.0.tar.gz

Download URL humanauthn-0.1.0.tar.gz
Size 226.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b2161bf7453ea98a40e13a8022b2640635498f4b8a9c41791709166a8654e255
BLAKE2b-256 checksum
How to use checksums
eb39c4490674e4af37c9a4b1083f009a87a3a2c2a61375672afbb1fd65338e55
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 26, 2026.

Transparency log

Release files / humanauthn-0.1.0-py3-none-any.whl

Download URL humanauthn-0.1.0-py3-none-any.whl
Size 12.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7962a34df3a8f3c541c20e39086e50085240b5d7f1b564823e4f44d172765b38
BLAKE2b-256 checksum
How to use checksums
6def5843168cfdeecb82455c9fa1af986910f4b27a604cc432169fc489fda0d9
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 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

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