cmp-consent
Python SDK for the CMP officer-assisted deferred consent flow. Runs in your backend (server-to-server) and holds the application API key — never ship the key to a browser.
Three jobs: raise consent requests, gate PII processing on verify(), and
verify CMP's signed webhooks.
Install
pip install cmp-consent
Authentication
| Credential | Direction | Header |
|---|---|---|
API key cmpk_live_… |
SDK → CMP | X-API-Key (application-scoped) |
Webhook secret whsec_… |
CMP → SDK | HMAC X-CMP-Signature (for verify_webhook) |
from promiz import CMPClient
cmp = CMPClient(
api_base="https://cmp-api.yourdomain",
api_key="cmpk_live_…",
webhook_secret="whsec_…", # only needed for verify_webhook
timeout=10, retries=2,
)
1) Raise a request (officer submitted)
req = cmp.create_consent_request(
email="user@example.com",
notice="kyc-notice", # the notice key for the application
requested_by=officer.id,
idempotency_key=submit_id, # a retried submit won't double-send the email
)
save(applicant_id, consent_id=req.consent_id) # store consent_id at t=0
CMP creates a PENDING request, emails the principal a single-use magic link, and
returns consent_id immediately.
2) Gate every use of the PII
from cmp_consent import ConsentPending
res = cmp.verify(consent_id=req.consent_id, purpose="bureau_pull")
if not res.allowed:
raise ConsentPending(res.reason) # PENDING/DECLINED/EXPIRED → do not process
proceed(proof=res.integrity_hash) # allowed → consent-backed
verify() is fail-closed: any network error or timeout returns allowed=False
(it never raises), so PII is never processed on an ambiguous result. Prefer
require_consent(...) when you want it to raise instead:
cmp.require_consent(consent_id=cid, purpose="bureau_pull") # raises if not granted
3) Receive the callback
# in your webhook route — pass the RAW body bytes
event = cmp.verify_webhook(request.headers, request.body)
if event.type == "CONSENT_GRANTED" and is_new(event.event_id): # dedupe by event_id
unblock(event.consent_id)
Webhooks are at-least-once and unordered: dedupe by event.event_id, order by
event.consent_seq, and treat verify() as the source of truth on any ambiguity.
Manage
cmp.get_request(req.request_id) # poll status (fallback if a webhook is missed)
cmp.resend(req.request_id) # re-mint an expired magic link
cmp.cancel(req.request_id) # cancel-on-save-failure
Async
AsyncCMPClient mirrors the same surface with await:
from cmp_consent import AsyncCMPClient
async with AsyncCMPClient(api_base=…, api_key=…) as cmp:
req = await cmp.create_consent_request(email=e, notice="kyc-notice", requested_by=o)
res = await cmp.verify(consent_id=req.consent_id, purpose="bureau_pull")
Exceptions
ConsentPending · ConsentDeclined · ConsentExpired · SignatureError ·
CMPError (base). Each carries .status (HTTP) and .code (envelope code) when
available.
Metadata
Release files for promiz 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| promiz-0.1.1.tar.gz | 46.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| promiz-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.9 kB
Release files / promiz-0.1.1.tar.gz
| Download URL | promiz-0.1.1.tar.gz |
|---|---|
| Size | 46.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
10e8a18061c49988d8729d06d2a077bdfcfeab5db889260b60c8749b24e8c92d
|
|
BLAKE2b-256 checksum How to use checksums |
4ca2bc1c39b314129dce91c181249033c5cbdbbb251e53181938417019e2d610
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.25
|
Release files / promiz-0.1.1-py3-none-any.whl
| Download URL | promiz-0.1.1-py3-none-any.whl |
|---|---|
| Size | 9.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8c40ebe7c54ce1b940841b0d7a62ea9faa619ed10ce84580c9c137ba19269552
|
|
BLAKE2b-256 checksum How to use checksums |
22db45ed9087613bfe5f74dcfd1d130113163ab575116dee70216abf722cff28
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.25
|