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: runpip checkafter each install. Raise that pin, or give the SDK its own virtual environment. - A
--no-build-isolationbuild 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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.