Skip to main content

DCR Platform Python SDK

Python client library for the DCR (Data Clean Room) Platform API. Provides type-safe access to all platform services including data ingestion, privacy-preserving matching, federated query execution, governance, reporting, alerting, and AI-powered analytics.

Requirements

  • Python 3.10+
  • DCR Platform instance (self-hosted or managed)

Compatibility (0.4.3)

0.4.2 raised the lowest versions the SDK accepts, and 0.4.3 keeps them. For each package below but Python, the new lowest version is past every security advisory published for it by 2026-10-27.

Package 0.3.0 0.4.3
Python 3.9 3.10
urllib3 1.26 2.8.0
pydantic 2.0.0 2.4.0
cryptography 41.0.0 50.0.0
certifi none 2026.10.19
idna none 3.15
setuptools (only to build from source) 68.0 84.1.0
  • Python 3.9 could not install 0.3.0 either: its requests>=2.33.0 needs Python 3.10.
  • If one pip command installs the SDK with a pin on an older urllib3, pydantic, cryptography, certifi or idna, or with a package that caps urllib3 below 2.8.0 (such as urllib3<2), pip stops with a resolution error. In separate commands, pip gives the later one what it needs, even a urllib3 below 2.8.0, prints the conflict and exits 0: run pip check after each install. Raise that pin, or give the SDK its own virtual environment.
  • A --no-build-isolation build fails on an installed setuptools below 77.
  • cryptography 50 publishes no wheel for Intel (x86_64) macOS, 32-bit Windows, Windows on ARM64, PyPy 3.10 or Linux with musl 1.1. There, pip builds it from source (a C compiler, Rust and the OpenSSL headers).
  • Python 3.10 reached its upstream end of life on 2026-10-01; a later SDK release may require 3.11.

Certificate authorities

The client checks TLS certificates against the authorities in the certifi package. The SDK needs certifi 2026.10.19 or later, so installing it into an existing environment can upgrade certifi there, for every program in it; a virtual environment of the SDK's own leaves other programs' certifi as it was. Compared with 2026.7.22, certifi 2026.10.19 no longer trusts "emSign ECC Root CA - G3", whose intermediate authorities had keys compromised (GHSA-6q48-6c7w-9gw2), and 2026.10.23 also drops "emSign ECC Root CA - C3" and "emSign Root CA - C1". Where a Linux distribution's certifi reads the system's certificate store, the upgrade replaces it with certifi's own bundle, so an authority added to that store is no longer trusted. Either way, a call that worked can then fail with CERTIFICATE_VERIFY_FAILED. A platform certificate that chains to "emSign ECC Root CA - G3" has to be replaced; do not add that root to a file.

If the platform uses a certificate from a private or corporate authority, or a proxy on your network inspects TLS, set REQUESTS_CA_BUNDLE to a PEM file that holds that authority's root certificate. requests then uses that file instead of certifi's bundle in every program that sees the variable, so the certifi floor above no longer protects those calls. If such a program also calls other hosts, add the public roots from certifi 2026.10.19 or later (python -m certifi prints its file) or from a system store that no longer trusts "emSign ECC Root CA - G3", and build the file again after each certifi upgrade.

Strict certificate checks

On Python 3.13 and later, urllib3 2 checks strictly, whatever file REQUESTS_CA_BUNDLE names, and installing the SDK brings urllib3 2 to every program in its environment. It refuses a certificate the authority issued without an Authority Key Identifier, and an authority certificate, root or intermediate, without a Key Usage extension (a root made by openssl req -x509 with its defaults has none), without a Subject Key Identifier or, with some OpenSSL builds, whose Basic Constraints are not marked critical. The error names the reason (such as Missing Authority Key Identifier or CA cert does not include key usage extension). The certificate at fault has to be issued again: for the last three reasons, that is the authority's own root or intermediate, not the server's.

Installation

Placino support provides the SDK as a package file (a wheel) on request. Install the file you received, with <version> replaced by the version in its name:

pip install ./dcr_client-<version>-py3-none-any.whl

Before you install it, compare the file's SHA-256 with the one Placino support gives you for it on a different channel from the file's: a hash that came with the file shows only that the file was not damaged on the way. Compute it with shasum -a 256 <file> on macOS, sha256sum <file> on Linux or Get-FileHash <file> -Algorithm SHA256 in PowerShell on Windows.

When you install from the file, name the file and that SHA-256 in a requirements file too, in the form pip freeze writes: dcr-client @ file:///path/to/dcr_client-<version>-py3-none-any.whl#sha256=<the SHA-256 you checked>. pip then refuses a file at that path whose SHA-256 differs. Not every installer checks that part (uv 0.10 does not): with another installer, check the file's SHA-256 before every install. dcr-client==<version> there makes pip look for that version on PyPI instead.

pip fetches the SDK's dependencies from PyPI as usual. The distribution is named dcr-client and imports as dcr_client. The client is synchronous: there is no async variant and no async extra. The package declares one extra, dev, which holds the test tools for working on the SDK's own source (see Development); using the SDK does not need it. The installed package reports its version in dcr_client.SDK_VERSION (also dcr_client.__version__).

Client-Side Hashing (Zero PII Exposure)

Hash your data locally before uploading — raw PII never leaves your machine. For the full step-by-step guide (including the standalone dcr-prepare CLI binary that requires no Python), see the "CLI (dcr-prepare)" section of the Integration Guide.

Install the SDK first (see Installation).

from dcr_client.prepare import prepare_csv

# Step 1: Hash your CSV (runs on YOUR machine)
result = prepare_csv(
    input_path="customers.csv",
    output_path="customers_hashed.csv",
    secret_key="hmac-key-from-platform-admin",
    columns={"email": "email", "phone": "phone", "first_name": "name"},
    activation_columns=["email", "phone"],
    activation_platforms=["google", "meta", "tiktok"],
)

# Step 2: Create metadata file (customers_hashed.meta.json)
import json
with open("customers_hashed.meta.json", "w") as f:
    json.dump({
        "activation_key_columns": ["email", "phone"],
        "hash_mode": "pre_hashed",
        "pii_acknowledged_columns": ["first_name"]
    }, f, indent=2)

# Step 3: Upload both files via SFTP to your inbox — the SFTP session's root folder

Quick Start (API Client)

from dcr_client import DCRClient

client = DCRClient(
    base_url="http://localhost:8000",
    username="analyst@acme.com",
    password="secret",
    tenant_id="your-tenant-uuid",
)

# Upload data
upload = client.ingestion.upload("customers.csv")

# Create a clean room and match
room = client.matching.create_room("Campaign Room", participants=["tenant-a", "tenant-b"])
result = client.matching.trigger_match(room.id, "tenant-a", "tenant-b")

# Execute a query
job = client.query.execute_sql(room.id, "SELECT segment, count(*) FROM matched_data GROUP BY segment")
result = client.query.wait_for_result(job.id)
print(result.rows)

Authentication

The SDK supports two authentication methods:

Username/Password (JWT)

client = DCRClient(
    base_url="http://localhost:8000",
    username="user@example.com",
    password="secret",
    tenant_id="tenant-uuid",
    auto_login=True,  # default: logs in automatically on first API call
)

API Key

client = DCRClient(
    base_url="http://localhost:8000",
    api_key="dcrk_live_abc123...",
    tenant_id="tenant-uuid",
)

Token Refresh

Token refresh is handled automatically. The SDK refreshes the JWT before expiry using the stored refresh token. You can also manage tokens manually:

# Manual login
token = client.login()
print(token.access_token, token.expires_in)

# Check auth status
print(client.is_authenticated)

Multi-Tenant Usage

client = DCRClient(
    base_url="http://localhost:8000",
    username="admin@example.com",
    password="secret",
    tenant_id="tenant-1-uuid",
)

# Work with tenant 1
client.ingestion.list_uploads()

# Switch to tenant 2 (same credentials, different tenant context)
client.switch_tenant("tenant-2-uuid")
client.ingestion.list_uploads()  # returns tenant 2's uploads

API Versioning

# Pin to a specific API version
client = DCRClient(
    base_url="http://localhost:8000",
    username="user@example.com",
    password="secret",
    tenant_id="tenant-uuid",
    api_version="2026-03-01",  # date-based API version
)

# Check server version after login
print(client.server_api_version)

API Reference

DCRClient

The main entry point. All module APIs are accessed as properties.

Property Module Description
client.auth AuthAPI Authentication, token management, password reset
client.ingestion IngestionAPI File upload, schema inspection
client.matching MatchingAPI Clean room management, match execution
client.query QueryAPI Query execution, templates, history, export
client.governance GovernanceAPI DUAs, audit trail, DSR, webhooks, retention
client.reports ReportsAPI Report templates, runs, schedules
client.alerts AlertsAPI Alert rules, history, acknowledgement
client.api_keys APIKeysAPI API key CRUD, rotation, scopes
client.ai AIAPI AI query, dictionary, recommendations

AuthAPI — client.auth

Method Description
login(username, password) Authenticate and receive JWT tokens
me() Get current user info (profile, role, tenants)
refresh(refresh_token) Refresh an expired access token
revoke(token) Revoke a token (logout)
request_password_reset(username) Send password reset email
confirm_password_reset(token, new_password) Complete password reset

IngestionAPI — client.ingestion

Method Description
upload(filepath) Upload a CSV/Parquet file
list_uploads(page, page_size) List uploads with pagination
get_upload(upload_id) Get upload details and status
get_schema(upload_id) Get column schema for an upload
preview(filepath) Preview file contents before uploading
upload_encrypted(filepath, wrapped_dek, encrypt_columns, match_columns?) Upload a client-side pre-encrypted file
upload_chunked(filepath, dataset_id, *, match_columns, ...) Chunked upload for large files
get_encryption_public_key() Get the platform RSA public key for client-side encryption

MatchingAPI — client.matching

Method Description
create_room(name, participants) Create a new clean room
list_rooms(page, page_size) List clean rooms
get_room(room_id) Get room details
trigger_match(room_id, requesting_tenant, providing_tenant, identifier_type) Execute a pairwise match
get_results(room_id) Get match results for a room
trigger_multi_match(room_id, participants, identifier_type) Execute multi-party match
get_multi_results(room_id) Get multi-party match results
close_room(room_id) Close a room (no further matches)
add_participant(room_id, tenant_id) Add a participant to a room
remove_participant(room_id, tenant_id) Remove a participant from a room

QueryAPI — client.query

Method Description
execute_sql(room_id, sql, params) Execute a raw SQL query
execute_template(room_id, template_id, params) Execute a query template
get_status(job_id) Check job status
get_result(job_id) Get query result (must be completed)
wait_for_result(job_id, timeout, poll_interval) Poll until result is ready
estimate(room_id, template_id, custom_sql) Estimate query cost/time
export(query_id, format, destination_id) Export results (CSV, Parquet, or to destination)
list_templates() List available query templates
get_template(template_id) Get template details
create_template(name, sql, params) Create a reusable query template
list_history(page, page_size) List query execution history

GovernanceAPI — client.governance

Data Usage Agreements (DUA)

Method Description
create_dua(requesting_org_id, providing_org_id, purpose, duration_days, **kwargs) Create a new DUA
list_duas() List all DUAs
get_dua(dua_id) Get DUA details
approve_dua(dua_id, accepted_min_aggregation_size=None) Approve a DUA waiting for your approval (the party that did not draft the terms approves them). When the data owner's terms raise the minimum group size (get_dua(...).k_raise), name the new size in accepted_min_aggregation_size, or the approval is refused (409 GOV_CONFLICT_048)
revoke_dua(dua_id) Revoke an active DUA

Column Permissions

Method Description
set_permissions(dua_id, permissions) Set column-level permissions on a DUA
get_permissions(dua_id) Get column permissions for a DUA

Audit Trail

Method Description
get_audit_trail(limit, offset, event_type) Query audit trail events
get_audit_stats() Get audit statistics
verify_audit_chain() Verify blockchain-anchored audit chain integrity

JIT Approvals

Method Description
request_jit_approval(dua_id, query_sql, justification) Request just-in-time approval for a query
list_jit_approvals() List JIT approval requests

Data Subject Requests (DSR)

Method Description
create_dsr_request(request_type, user_hash, description) Create a DSR (access/deletion/portability)
list_dsr_requests(status, page, page_size) List DSR requests
process_dsr_request(request_id) Process a pending DSR request

Webhooks

Method Description
create_webhook(url, events, active) Subscribe to platform events
list_webhooks() List webhook subscriptions
update_webhook(webhook_id, **kwargs) Update a webhook
delete_webhook(webhook_id) Delete a webhook
test_webhook(webhook_id) Send a test event to a webhook

Retention Policies

Method Description
list_retention_policies() List data retention policies
update_retention_policy(resource_type, **kwargs) Update retention settings
preview_retention() Preview what would be cleaned up

ReportsAPI — client.reports

Method Description
create_template(name, queries, description, **kwargs) Create a report template
list_templates() List report templates
get_template(template_id) Get template details
update_template(template_id, **kwargs) Update a template
delete_template(template_id) Delete a template
run_report(template_id, params) Run a report
list_runs() List report runs
get_run(run_id) Get run details and output
create_schedule(template_id, cron, **kwargs) Schedule recurring reports
list_schedules() List schedules
update_schedule(schedule_id, **kwargs) Update a schedule
delete_schedule(schedule_id) Delete a schedule

AlertsAPI — client.alerts

Method Description
create_rule(name, metric_type, threshold_value, comparison, **kwargs) Create an alert rule
list_rules() List alert rules
update_rule(rule_id, **kwargs) Update a rule
delete_rule(rule_id) Delete a rule
list_history(status, page, page_size) List alert history
acknowledge(alert_id) Acknowledge a fired alert

APIKeysAPI — client.api_keys

Method Description
list_keys() List API keys
create_key(name, scopes, description, expires_in_days, rate_limit_rpm, rate_limit_rpd) Create an API key (a client signed in as an administrator; a client built with api_key= gets ForbiddenError, 403)
get_key(key_id) Get key details
revoke_key(key_id) Revoke a key
rotate_key(key_id, grace_period_hours) Rotate a key with grace period (a client signed in as an administrator, as for create_key)
list_scopes() List available scopes

AIAPI — client.ai

Natural Language Queries

Method Description
ask(room_id, question, language, auto_fix, max_fix_attempts) High-level: ask a question, get results (auto-fix on error)
query_ai(room_id, message, language) Convert natural language to IR
fix_ir(room_id, original_ir, sql, error, ...) Fix a failed IR using the self-healing loop
compile_and_execute_ir(room_id, ir, ...) Compile IR to SQL and execute

Text-to-SQL & Analysis

Method Description
text_to_sql(query, room_id, schema_context) Convert text to SQL directly
analyze(prompt, type, room_id) Run an AI analysis
get_recommendations(room_id, language, refresh) Get AI-powered query recommendations

Data Dictionary

Method Description
generate_dictionary(upload_id, dataset_name, ...) Generate AI data dictionary
get_dictionary(upload_id) Get dictionary content
get_dictionary_status(upload_id) Check generation status
update_dictionary_column(upload_id, column_name, updates) Update a column's dictionary entry
publish_dictionary(upload_id) Publish dictionary
unpublish_dictionary(upload_id) Unpublish dictionary
archive_dictionary(upload_id) Archive dictionary
wait_for_dictionary(upload_id, timeout, poll_interval) Poll until dictionary generation completes

Status

Method Description
get_status() Check AI service health and model info
get_query_usage(room_id) Get AI query usage stats for a room

Utilities

Client-Side Hashing (HMAC-SHA256)

Hash identifiers locally before uploading to the platform. This ensures raw PII never leaves your infrastructure.

from dcr_client.hashing import hash_identifier, verify_hash_format

# HMAC-SHA256 hash with your org's secret key
hashed = hash_identifier("user@example.com", secret_key="your-hmac-secret")
print(hashed)  # "a1b2c3d4..."

# Verify hash format
assert verify_hash_format(hashed)

# Legacy SHA-256 + salt (for backwards compatibility)
from dcr_client.hashing import hash_identifier_legacy
legacy = hash_identifier_legacy("user@example.com", salt="your-salt")

Normalization

Normalize identifiers before hashing to ensure consistent matching across organizations.

from dcr_client.normalize import normalize_email, normalize_phone, normalize_name

# Email: lowercase, strip dots from local part (Gmail), strip whitespace
email = normalize_email("  John.Doe@Gmail.com  ")
assert email == "johndoe@gmail.com"

# Phone: strip formatting, ensure E.164 prefix
phone = normalize_phone("+1 (555) 123-4567")
assert phone == "+15551234567"

# Name: lowercase, strip whitespace, normalize unicode
name = normalize_name("  José García  ")
assert name == "josé garcía"

# Generic: auto-detect type
from dcr_client.normalize import normalize_identifier
normalized = normalize_identifier("User@Example.COM", "email")

CSV Preparation

Normalize and hash an entire CSV file in one step — ready for upload. This runs entirely on your machine; raw PII never leaves your infrastructure.

from dcr_client.prepare import prepare_csv

result = prepare_csv(
    input_path="raw_customers.csv",
    output_path="hashed_customers.csv",
    secret_key="your-hmac-secret",
    columns={
        "email": "email",       # column_name: identifier_type
        "phone": "phone",
        "full_name": "name",
    },
    keep_original=False,  # remove raw PII columns from output
)

print(f"Processed {result.rows_processed} rows, hashed {result.columns_hashed} columns")
print(f"Output: {result.output_path}")

# Now upload the hashed file
client.ingestion.upload(result.output_path)

CSV Preparation with Activation Keys

Generate per-platform activation keys alongside identity hashes. Different ad platforms normalize phone numbers differently (Meta strips +, Google/TikTok keep it), so each gets its own hash column.

from dcr_client.prepare import prepare_csv

result = prepare_csv(
    input_path="raw_customers.csv",
    output_path="hashed_customers.csv",
    secret_key="your-hmac-secret",
    columns={
        "email": "email",
        "phone": "phone",
        "first_name": "name",
    },
    activation_columns=["email", "phone"],
    activation_platforms=["google", "meta", "tiktok"],
)

print(f"Activation columns: {result.activation_columns_added}")
# ['__ak__email', '__ak__phone_google', '__ak__phone_meta', '__ak__phone_tiktok']

Output CSV columns:

Column Type Description
email HMAC-SHA256 Identity hash (for identity resolution)
phone HMAC-SHA256 Identity hash
first_name HMAC-SHA256 Identity hash
city plain Non-PII, untouched
__ak__email SHA-256 Activation key (one key for every platform, Gmail-normalised)
__ak__phone_google SHA-256 Google Ads: E.164 with + prefix
__ak__phone_meta SHA-256 Meta Ads: digits only, no +
__ak__phone_tiktok SHA-256 TikTok Ads: E.164 with + prefix

Phone normalization per platform:

Platform Input Normalized SHA-256
Google Ads +90 530 123 4567 +905301234567 abc...
Meta Ads +90 530 123 4567 905301234567 def... (different!)
TikTok Ads +90 530 123 4567 +905301234567 abc... (same as Google)

Upload the hashed file via SFTP with a companion .meta.json:

{
  "activation_key_columns": ["email", "phone"],
  "hash_mode": "pre_hashed",
  "pii_acknowledged_columns": ["first_name"]
}

Error Handling

All API errors are mapped to typed exception classes with status code and error code:

from dcr_client.exceptions import (
    DCRError,
    AuthenticationError,
    ForbiddenError,
    NotFoundError,
    ConflictError,
    ValidationError,
    RateLimitError,
    InsufficientCreditsError,
    ServerError,
)

try:
    room = client.matching.get_room("nonexistent-id")
except NotFoundError as e:
    print(f"Not found: {e.message}")
    print(f"Status: {e.status_code}")    # 404
    print(f"Error code: {e.error_code}")  # "ROOM_NOT_FOUND"
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.message}")
except AuthenticationError:
    print("Token expired or invalid")
except DCRError as e:
    print(f"API error {e.status_code}: {e.message}")

Exception Hierarchy

Exception HTTP Status When
AuthenticationError 401 Invalid credentials or expired token
ForbiddenError 403 Insufficient permissions. For a client built with api_key=: error_code AUTH_FORBIDDEN_005 when the key lacks a scope the route asks for (the message names it: (insufficient scope: <scope> required); issue a key with the scope, scopes cannot be added to a key), and AUTH_FORBIDDEN_006 on the organization's users, invitations, sign-in IP allowlist and single sign-on, where no API key may make a change or run a single sign-on connection test or discovery; their reads stay open (sign in as an administrator)
NotFoundError 404 Resource not found
ConflictError 409 Duplicate resource or state conflict
ValidationError 422 Invalid request body
RateLimitError 429 Rate limit exceeded
InsufficientCreditsError 402 Billing/credit issue
ServerError 500+ Internal server error

Examples

The examples/ directory of the SDK's source contains runnable scripts. They are not part of the installed package (the wheel holds only the dcr_client package and its metadata).

File Description
quickstart.py End-to-end workflow: login, upload, room, match, query
multi_tenant.py Multi-tenant operations: switch tenants, cross-org DUA, match
reports.py Report templates, runs, schedules
streaming_events.py SSE event streaming listener
prepare_for_upload.py Client-side hashing with per-platform activation keys
bulk_upload.py Chunked, parallel upload of a large file (ingestion.upload_chunked)

Development

Working on the SDK itself needs access to its source repository; using the SDK does not (see Installation).

# Install from the root of a checkout of the source repository
cd sdk/python
pip install -e ".[dev]"

# Run tests
pytest

# Run specific test module
pytest tests/test_matching.py -v

License

MIT. The text ships as LICENSE, and the package metadata states it as License-Expression: MIT, with no License classifier. Tools that read only the older License field or the classifier do not see it: pip show before pip 25 shows an empty License, and pip-licenses before 5.5 shows UNKNOWN. pip-licenses 5.5 and later reads the expression and shows MIT, not MIT License, so a license allow-list that names only MIT License needs MIT added.

Metadata

Release files for dcr-client 0.4.3

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

Source distribution (sdist)

Source distribution for dcr-client 0.4.3
File Size Uploaded Provenance Actions
dcr_client-0.4.3.tar.gz 58.7 kB Without provenance Details for dcr_client-0.4.3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for dcr-client 0.4.3
File Interpreter ABI Platform Actions
dcr_client-0.4.3-py3-none-any.whl Python 3 none any Details for dcr_client-0.4.3-py3-none-any.whl

Total release size: 115.5 kB

Release files / dcr_client-0.4.3.tar.gz

Download URL dcr_client-0.4.3.tar.gz
Size 58.7 kB
Tags Source
SHA-256 checksum
How to use checksums
9224e4a3536193a7b4b189b81b89fbf92b2127523dccbc375892bba5468d50ca
BLAKE2b-256 checksum
How to use checksums
fd1c143517ded64c8bf9b03853356328b29e3817c202fd0a093ff82e78093bbf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Release files / dcr_client-0.4.3-py3-none-any.whl

Download URL dcr_client-0.4.3-py3-none-any.whl
Size 56.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cda5a1d7deb98705989a28c3176b026cc5ad6a5ebdd16766d6a07ea8621ea157
BLAKE2b-256 checksum
How to use checksums
1d45756cb0e95126b3b406204cab17d274b7c7ee61dc5a079aaf01a0e952c14e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Security for dcr-client 0.4.3

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.4.3 This release

2 release files No provenance

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