Praxicraft Python SDK
Official Python client for the Praxicraft Assess Public API.
Use it to invite candidates, check invite quota, manage webhooks, enroll hiring pipelines, and fetch results from your ATS, backend, or automation scripts.
pip install praxicraft
Requires Python 3.10+. Full API reference: docs.praxicraft.com
Authentication
Create an organisation API key in Assess:
Assess → Developer → API Keys → create key → copy ct_live_… (shown once).
export PRAXICRAFT_API_KEY="ct_live_xxxxxxxxxxxxxxxx"
Or pass the key when constructing the client:
from praxicraft import Client
client = Client(api_key="ct_live_xxxxxxxxxxxxxxxx")
Optional: override the API host with PRAXICRAFT_API_BASE_URL or Client(base_url=...).
Default host: https://assess.praxicraft.com.
Never commit API keys. Prefer environment variables or a secrets manager.
Scopes and rotation: Authentication
Quickstart
from praxicraft import Client
client = Client() # reads PRAXICRAFT_API_KEY
# List assessments
page = client.assessments.list()
for assessment in page["results"]:
print(assessment["slug"], assessment["status"])
# Invite a candidate (idempotent on email — safe to retry)
invite = client.invites.create(
"senior-backend-screen",
email="candidate@example.com",
name="Jane Doe",
send_email=True,
)
print(invite["invite_token"], invite.get("invite_url"))
# Fetch that candidate's result
result = client.results.retrieve(invite_token=invite["invite_token"])
print(result)
Responses are flat JSON (same shape as the Public API — no { "data": … } wrapper).
What you can do
| Resource | Common methods |
|---|---|
client.org |
retrieve(), stats() |
client.assessments |
list(), retrieve(), create(), update(), activate(), list_cases(), attach_cases(), replace_cases(), remove_case() |
client.invites |
create(), bulk_create(), list(), retrieve(), remind(), cancel() |
client.results |
list(), retrieve(), iter_all() |
client.webhooks |
list(), create(), retrieve(), update(), delete(), test(), deliveries() |
client.pipelines |
list(), retrieve(), enroll(), bulk_enroll(), list_enrollments(), get_enrollment() |
verify_signature |
Verify X-Praxicraft-Signature on webhook payloads |
All paths target /api/v1/public/… on the Assess host.
Check invite quota before bulk sends
org = client.org.retrieve()
if (org.get("invites_remaining") or 0) < len(candidates):
raise SystemExit("Not enough invites remaining this month")
Bulk invites
client.invites.bulk_create(
"senior-backend-screen",
candidates=[
{"email": "a@example.com", "name": "Alex"},
{"email": "b@example.com", "name": "Blair"},
],
send_email=True,
)
Build and activate an assessment via API
assessment = client.assessments.create(title="Backend screen")
client.assessments.attach_cases(
assessment["slug"],
cases=[{"case_id": "<platform-or-org-case-uuid>", "source": "platform"}],
)
client.assessments.activate(assessment["slug"])
Register and test a webhook
hook = client.webhooks.create(
url="https://example.com/hooks/praxicraft",
events=["assessment.completed", "candidate.passed"],
)
# Store hook["secret_key"] (whsec_…) — shown once
client.webhooks.test(hook["id"])
client.webhooks.update(hook["id"], is_active=True)
Enroll into a hiring pipeline
enrollment = client.pipelines.enroll(
"grad-2025",
email="alex@example.com",
name="Alex Lee",
send_email=True,
)
status = client.pipelines.get_enrollment(enrollment["enrollment_id"])
Paginate cohort results
for row in client.results.iter_all("senior-backend-screen", page_size=50):
print(row.get("email"), row.get("score_percentage"), row.get("passed"))
Verify webhook signatures
Assess signs the raw request body with your webhook secret (whsec_…):
from praxicraft import verify_signature
def handle_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
return verify_signature(secret, raw_body, signature_header)
Header format: X-Praxicraft-Signature: sha256=<hex>
Event catalog and payload examples: Webhooks
Errors
Public API errors look like:
{
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This API key does not have the 'candidates:read' scope."
}
}
The SDK raises typed exceptions. Branch on exc.code, not the message text:
from praxicraft import (
AuthenticationError,
InsufficientScopeError,
RateLimitError,
ValidationError,
)
try:
client.invites.create("demo", email="candidate@example.com")
except ValidationError as exc:
# e.g. ASSESSMENT_NOT_ACTIVE, REMINDER_COOLDOWN, VALIDATION_ERROR
print(exc.code, exc.details)
except InsufficientScopeError as exc:
print(exc.code) # INSUFFICIENT_SCOPE, INVITE_QUOTA_EXCEEDED, …
except AuthenticationError as exc:
print(exc.code) # INVALID_API_KEY, EXPIRED_API_KEY
except RateLimitError as exc:
print(exc.retry_after) # seconds from Retry-After, when present
Error codes: Errors
Requirements & support
- Python 3.10, 3.11, or 3.12+
- Dependency:
httpx - Product docs: docs.praxicraft.com
- Issues: GitHub Issues
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file praxicraft-0.1.0.tar.gz.
File metadata
- Download URL: praxicraft-0.1.0.tar.gz
- Upload date:
- Size: 16.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
673758e44c11d79999fd78d92436deaa2f5ff5ab234fecee8346f0e96cfff6ac
|
|
| MD5 |
920954fe5c37a08e4b7d70a5bc2bf1e5
|
|
| BLAKE2b-256 |
56edd3a756c46f3f3aa5cb4f861d4ca7fe26b5a0bfd80c810e6fd60cb9eaa89a
|
Provenance
The following attestation bundles were made for praxicraft-0.1.0.tar.gz:
Publisher:
publish.yml on praxicraft-platform/praxicraft-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
praxicraft-0.1.0.tar.gz -
Subject digest:
673758e44c11d79999fd78d92436deaa2f5ff5ab234fecee8346f0e96cfff6ac - Sigstore transparency entry: 2468262803
- Sigstore integration time:
-
Permalink:
praxicraft-platform/praxicraft-python@90a612cee9c8902b80aa8281b27583f26ba8b2c3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/praxicraft-platform
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@90a612cee9c8902b80aa8281b27583f26ba8b2c3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file praxicraft-0.1.0-py3-none-any.whl.
File metadata
- Download URL: praxicraft-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58944fd304f4f5adfe72a05eb97e3eace0670bc17ba30ccf1048f997301a9248
|
|
| MD5 |
ed0d9ab3023150f4b9498ec54fce5d7e
|
|
| BLAKE2b-256 |
11219bbb83ec88f5728f10354d93e4e38892f60740ad5d6e80070622fdd30585
|
Provenance
The following attestation bundles were made for praxicraft-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on praxicraft-platform/praxicraft-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
praxicraft-0.1.0-py3-none-any.whl -
Subject digest:
58944fd304f4f5adfe72a05eb97e3eace0670bc17ba30ccf1048f997301a9248 - Sigstore transparency entry: 2468262852
- Sigstore integration time:
-
Permalink:
praxicraft-platform/praxicraft-python@90a612cee9c8902b80aa8281b27583f26ba8b2c3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/praxicraft-platform
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@90a612cee9c8902b80aa8281b27583f26ba8b2c3 -
Trigger Event:
push
-
Statement type: