Developer API Reference
The Praxicraft Assess API lets your team automate candidate invitations, retrieve structured results, and integrate with your ATS — all via a simple REST interface authenticated with API keys.
https://praxicraft.comJSONBearer TokenAuthentication
All Public API requests are authenticated with an organization-level API key passed as a Bearer token. Generate keys in your dashboard under Developer → API Keys. Keys are scoped to your organization and always start with `ct_live_`.
https://praxicraft.com/api/v1/public/assessments/API Key Format
Include your API key in the `Authorization` header on every request. Only owners and admins can generate API keys from the dashboard.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | required | Bearer ct_live_xxxxxxxxxxxxxxxx |
| Content-Type | string | required | application/json (required on POST / PATCH) |
Response Fields
| Field | Type | Description |
|---|---|---|
| (varies) | object|array | The response payload — public API returns the resource directly, not wrapped in a status envelope |
Example Response
// Successful response — resource returned directly (no wrapper)
{
"id": "d290f1ee-...",
"title": "Senior Backend Engineer Screen",
"slug": "senior-backend-screen",
...
}
// Error response — always an error object
{
"error": {
"message": "Invalid API key.",
"code": "INVALID_API_KEY"
}
}Rate Limits
Public API endpoints are rate-limited per organization to protect reliability. Exceeding the limit returns HTTP 429.
https://praxicraft.com/api/v1/public/...Rate Limit Rules
Read endpoints allow up to 500 requests per hour. Write endpoints (invite creation, reminders) allow 100 requests per hour. Limits reset on a rolling 1-hour window.
Response Fields
| Field | Type | Description |
|---|---|---|
| X-RateLimit-Limit | integer | Max requests allowed in the window |
| X-RateLimit-Remaining | integer | Requests remaining in current window |
| Retry-After | integer | Seconds to wait before retrying (429 responses only) |
Example Response
HTTP/1.1 429 Too Many Requests
Retry-After: 14
{
"error": {
"message": "Request was throttled. Expected available in 14 seconds.",
"code": "RATE_LIMITED"
}
}Plan Limits
Invite quotas are enforced per organisation per calendar month and depend on your subscription plan. Exceeding your quota returns HTTP 403 with code INVITE_QUOTA_EXCEEDED. Enterprise plans have no hard limits.
https://praxicraft.com/api/v1/public/assessments/Invite Quotas by Plan
Each plan has a monthly invite quota tracked across all assessments. Only `active` assessments can receive invitations — attempting to invite to a draft or archived assessment returns 400 ASSESSMENT_NOT_ACTIVE.
Response Fields
| Field | Type | Description |
|---|---|---|
| Free | plan | 20 invitations / month · 3 active assessments · 2 team seats |
| Starter | plan | 100 invitations / month · 15 active assessments · 5 team seats |
| Growth | plan | 500 invitations / month · 50 active assessments · 15 team seats |
| Enterprise | plan | Unlimited invitations · Unlimited assessments · Unlimited seats |
Example Response
// 403 response when quota is exceeded
{
"error": {
"message": "Monthly invite quota exceeded. Upgrade your plan to invite more candidates.",
"code": "INVITE_QUOTA_EXCEEDED"
}
}
// 400 response when assessment is not active
{
"error": {
"message": "This assessment is not active and cannot accept new invitations.",
"code": "ASSESSMENT_NOT_ACTIVE"
}
}Assessments
Retrieve your assessments to build custom invitation flows or ATS integrations. You can filter by status and search by title.
https://praxicraft.com/api/v1/public/assessments/List Assessments
Returns assessments for your organization. Defaults to `active` only. Use the `status` param to list drafts or archived assessments.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| status | string | optional | Filter by status: active | draft | archived (default: active) |
| search | string | optional | Case-insensitive substring match on assessment title |
| page | integer | optional | Page number (default: 1) |
| page_size | integer | optional | Results per page, max 100 (default: 20) |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Assessment UUID |
| title | string | Assessment title |
| slug | string | URL-safe identifier used in all invite endpoints |
| status | string | active | draft | archived |
| case_count | integer | Number of technical cases in this assessment |
| time_limit_minutes | integer|null | Per-session time cap, null = no limit |
| passing_score | integer | Minimum percentage required to pass |
| invitation_count | integer | Total candidates invited |
| enforce_fullscreen | boolean | Whether fullscreen mode is enforced during the assessment |
| copy_paste_disabled | boolean | Whether copy/paste is blocked during the assessment |
| meta | object | Plan quota info: { plan, invite_limit, invites_used, invites_remaining } |
GET /api/v1/public/assessments/?status=active&search=backend Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"title": "Senior Backend Engineer Screen",
"slug": "senior-backend-screen",
"status": "active",
"case_count": 3,
"time_limit_minutes": 90,
"passing_score": 70,
"invitation_count": 12,
"enforce_fullscreen": true,
"copy_paste_disabled": false
}
],
"meta": {
"plan": "starter",
"invite_limit": 100,
"invites_used": 12,
"invites_remaining": 88
}
}https://praxicraft.com/api/v1/public/assessments/:slug/Get Assessment Detail
Retrieves full configuration for a single assessment. This endpoint returns the assessment settings and passing threshold, but excludes the list of cases which are available via the sub-resource endpoint.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| slug | string | required | Assessment slug from the list endpoint |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Assessment UUID |
| slug | string | URL-safe identifier |
| title | string | Assessment title |
| description | string | Candidate-visible description |
| status | string | active | draft | archived |
| time_limit_minutes | integer|null | Session time cap in minutes |
| passing_score | integer | Minimum score percentage to pass |
| case_count | integer | Number of technical cases |
| enforce_fullscreen | boolean | Whether fullscreen mode is enforced |
| copy_paste_disabled | boolean | Whether copy/paste is blocked |
| created_at | ISO 8601 | Creation timestamp |
GET /api/v1/public/assessments/senior-backend-screen/ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"slug": "senior-backend-screen",
"title": "Senior Backend Engineer Screen",
"description": "A 3-case technical screen covering SQL, Python, and system design.",
"status": "active",
"time_limit_minutes": 90,
"passing_score": 70,
"case_count": 3,
"enforce_fullscreen": true,
"copy_paste_disabled": false,
"created_at": "2024-03-22T12:00:00Z"
}https://praxicraft.com/api/v1/public/assessments/:slug/cases/List Assessment Cases
Returns the list of platform or custom technical cases attached to a specific assessment.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| slug | string | required | Assessment slug |
| page | integer | optional | Page number (default: 1) |
| page_size | integer | optional | Results per page, max 100 (default: 20) |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Case UUID |
| title | string | Case title |
| type | string | platform | org (source of the case) |
| case_type | string | coding | sql | text | mcq |
| difficulty | string | easy | medium | hard |
| order | integer | Display sequence in the assessment |
| points | integer | Score weight of this case |
Example Response
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": "c1...",
"title": "SQL Join Master",
"type": "platform",
"case_type": "sql",
"difficulty": "medium",
"order": 0,
"points": 100
}
]
}Invitations
Programmatically invite candidates from your ATS or internal hiring tool. All invite endpoints are idempotent — re-inviting the same email returns the existing invitation rather than creating a duplicate.
https://praxicraft.com/api/v1/public/assessments/:slug/invites/Create Invitation
Creates a unique assessment link for one candidate. Idempotent: if the email was already invited to this assessment, returns the existing invitation with HTTP 200 (not 201).
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| string | required | Candidate email address | |
| name | string | optional | Candidate full name (displayed in the email and on the landing page) |
| role | string | optional | Job role or position the candidate is being screened for (e.g. Senior Backend Engineer). Shown in the invitation email. |
| send_email | boolean | optional | Set true to immediately dispatch the invitation email (default: false) |
Response Fields
| Field | Type | Description |
|---|---|---|
| message | string | Confirmation message |
| invite_token | uuid | Unique token for this invitation — use this to fetch results later |
| invite_url | url | Direct link the candidate opens to start the assessment |
| expires_at | ISO 8601 | Invitation expiry (7 days from creation) |
| status | string | pending | started | completed | expired |
POST /api/v1/public/assessments/senior-backend-screen/invites/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
{
"email": "jane.doe@example.com",
"name": "Jane Doe",
"role": "Senior Backend Engineer",
"send_email": true
}Example Response
{
"message": "Invitation created successfully.",
"invite_token": "c7b3e2a1-1234-5678-abcd-ef0123456789",
"invite_url": "https://praxicraft.com/take/c7b3e2a1-1234-5678-abcd-ef0123456789",
"expires_at": "2025-03-22T10:00:00Z",
"status": "pending"
}https://praxicraft.com/api/v1/public/assessments/:slug/invites/bulk/Bulk Create Invitations
Creates invitations for multiple candidates in one request. Emails that were already invited are silently skipped and returned in the `skipped` array. Useful for ATS batch imports.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| candidates | array | required | Array of candidate objects, each with `email` and optional `name` and `role` |
| send_email | boolean | optional | Send invitation email to all newly created invitations (default: false) |
Response Fields
| Field | Type | Description |
|---|---|---|
| invited | array | Candidates who received a new invitation — includes email, invite_token, invite_url |
| skipped | array | Candidates skipped with a reason (already_invited | missing_email | error) |
POST /api/v1/public/assessments/senior-backend-screen/invites/bulk/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
{
"candidates": [
{ "email": "alice@example.com", "name": "Alice Chen", "role": "Senior Backend Engineer" },
{ "email": "bob@example.com", "name": "Bob Smith", "role": "Senior Backend Engineer" },
{ "email": "jane@example.com" }
],
"send_email": true
}Example Response
{
"invited": [
{
"email": "alice@example.com",
"invite_token": "a1b2c3d4-...",
"invite_url": "https://praxicraft.com/take/a1b2c3d4-..."
},
{
"email": "bob@example.com",
"invite_token": "e5f6a7b8-...",
"invite_url": "https://praxicraft.com/take/e5f6a7b8-..."
}
],
"skipped": [
{ "email": "jane@example.com", "reason": "already invited" }
]
}https://praxicraft.com/api/v1/public/invites/:token/Get Invitation Status
Returns the current status and metadata for a single invitation. Use this to poll pending invitations from your ATS without pulling the full scored result.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| token | uuid | required | The invite_token from the create invitation response |
Response Fields
| Field | Type | Description |
|---|---|---|
| invite_token | uuid | Unique token for this invitation |
| string | Candidate email address | |
| name | string|null | Candidate name if provided |
| role | string|null | Job role the candidate was invited for |
| status | string | pending | started | completed | expired |
| assessment | object | { slug, title } — the assessment this invitation belongs to |
| created_at | ISO 8601 | When the invitation was created |
| expires_at | ISO 8601 | Invitation expiry timestamp |
| started_at | ISO 8601|null | When the candidate started the session |
| completed_at | ISO 8601|null | When the candidate completed the session |
| reminded_at | ISO 8601|null | Timestamp of the last reminder email sent |
GET /api/v1/public/invites/c7b3e2a1-.../ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"invite_token": "c7b3e2a1-1234-5678-abcd-ef0123456789",
"email": "jane.doe@example.com",
"name": "Jane Doe",
"role": "Senior Backend Engineer",
"status": "started",
"assessment": {
"slug": "senior-backend-screen",
"title": "Senior Backend Engineer Screen"
},
"created_at": "2025-03-15T09:00:00Z",
"expires_at": "2025-03-22T09:00:00Z",
"started_at": "2025-03-15T14:00:00Z",
"completed_at": null,
"reminded_at": null
}https://praxicraft.com/api/v1/public/invites/:token/remind/Send Reminder
Re-sends the invitation email to the candidate. Only valid for invitations in `pending` or `started` status. Returns 400 if the invitation is expired or already completed.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| token | uuid | required | The invite_token from the create invitation response |
Response Fields
| Field | Type | Description |
|---|---|---|
| message | string | Confirmation that the reminder was dispatched |
POST /api/v1/public/invites/c7b3e2a1-.../remind/ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"message": "Reminder sent."
}https://praxicraft.com/api/v1/public/invites/:token/Cancel Invitation
Permanently deletes a pending invitation. Returns 400 if the candidate has already started or completed the assessment.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| token | uuid | required | The invite_token to cancel |
DELETE /api/v1/public/invites/c7b3e2a1-.../ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
HTTP/1.1 204 No Content
https://praxicraft.com/api/v1/public/invites/List All Invitations
Returns a global list of all candidate invitations across all assessments in your organisation. Paginated.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| page | integer | optional | Page number (default: 1) |
| page_size | integer | optional | Results per page, max 100 (default: 20) |
Response Fields
| Field | Type | Description |
|---|---|---|
| invite_token | uuid | Unique token for this invitation |
| assessment_title | string | Title of the assessment |
| assessment_slug | string | Slug of the assessment |
| string | Candidate email | |
| status | string | pending | started | completed | expired |
| sent_at | ISO 8601 | When the invite was sent |
GET /api/v1/public/invites/ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"count": 125,
"next": "http.../public/invites/?page=2",
"previous": null,
"results": [
{
"invite_token": "c7b3e2a1-...",
"assessment_title": "Senior Backend Engineer Screen",
"assessment_slug": "senior-backend-screen",
"email": "jane@example.com",
"status": "completed",
"sent_at": "2025-03-15T14:00:00Z"
}
]
}Results
Retrieve candidate scores and structured reports once an assessment is completed. Results are available immediately after completion.
https://praxicraft.com/api/v1/public/assessments/:slug/results/List Assessment Results
Returns all completed candidate sessions for an assessment, ordered by most recent. Paginated.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| slug | string | required | Assessment slug |
| page | integer | optional | Page number (default: 1) |
| page_size | integer | optional | Results per page, max 100 (default: 20) |
Response Fields
| Field | Type | Description |
|---|---|---|
| candidate_email | string | Candidate email |
| candidate_name | string | Candidate name (if provided) |
| status | string | pending | started | completed | expired |
| score_percentage | float | Aggregated score as a percentage (0–100) |
| passed | boolean | Whether the candidate met the passing threshold |
| started_at | ISO 8601 | Session start timestamp |
| completed_at | ISO 8601|null | Completion timestamp |
| invite_token | uuid | Use this to fetch the detailed result |
GET /api/v1/public/assessments/senior-backend-screen/results/ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"count": 2,
"results": [
{
"candidate_email": "jane@example.com",
"candidate_name": "Jane Doe",
"status": "completed",
"score_percentage": 85.0,
"passed": true,
"started_at": "2025-03-15T14:00:00Z",
"completed_at": "2025-03-15T15:20:00Z",
"invite_token": "c7b3e2a1-..."
}
]
}https://praxicraft.com/api/v1/public/invites/:token/result/Get Detailed Result
Returns the full result for a specific candidate, including per-case scores, violation counts, and a link to the detailed report.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| token | uuid | required | The invite_token from the create invitation response |
Response Fields
| Field | Type | Description |
|---|---|---|
| candidate_email | string | Candidate email |
| candidate_name | string | Candidate name |
| assessment | string | Assessment title |
| status | string | Invitation status |
| score | integer | Raw score achieved |
| max_score | integer | Maximum possible score |
| score_percentage | float | Percentage score (0–100) |
| passed | boolean | Whether the candidate met the passing threshold |
| passing_score | integer | Passing threshold configured for this assessment |
| started_at | ISO 8601 | When the session started |
| completed_at | ISO 8601|null | When the session completed |
| violation_counts | object | { fullscreen_violations, copy_paste_violations } |
| detailed_report_url | url | API URL for the full session report (requires session auth) |
| cases | array | Per-case breakdown: case_order, case_title, score, max_score, status, time_spent_seconds |
GET /api/v1/public/invites/c7b3e2a1-.../result/ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"candidate_email": "jane@example.com",
"candidate_name": "Jane Doe",
"assessment": "Senior Backend Engineer Screen",
"status": "completed",
"score": 255,
"max_score": 300,
"score_percentage": 85.0,
"passed": true,
"passing_score": 70,
"started_at": "2025-03-15T14:00:00Z",
"completed_at": "2025-03-15T15:20:00Z",
"violation_counts": {
"fullscreen_violations": 0,
"copy_paste_violations": 2
},
"detailed_report_url": "/api/v1/assess/assessments/senior-backend-screen/results/...",
"cases": [
{
"case_order": 1,
"case_title": "Query Optimization",
"score": 100,
"max_score": 100,
"status": "correct",
"time_spent_seconds": 820
},
{
"case_order": 2,
"case_title": "ETL Pipeline",
"score": 100,
"max_score": 100,
"status": "correct",
"time_spent_seconds": 1540
},
{
"case_order": 3,
"case_title": "Data Modeling",
"score": 55,
"max_score": 100,
"status": "incorrect",
"time_spent_seconds": 2100
}
]
}Custom Cases
Manage your organization's private question library programmatically. You can create, list, update, and delete custom cases using these endpoints.
https://praxicraft.com/api/v1/public/cases/List Custom Cases
Returns all custom cases created by your organisation. Paginated.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| page | integer | optional | Page number |
| page_size | integer | optional | Results per page |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Case UUID |
| title | string | Case title |
| case_type | string | coding | sql | multiple_choice |
| difficulty | string | easy | medium | hard |
Example Response
{
"count": 42,
"results": [
{
"id": "e21a...",
"title": "Custom Algorithm 1",
"case_type": "coding",
"difficulty": "hard"
}
]
}https://praxicraft.com/api/v1/public/cases/create/Create Custom Case
Programmatically add a new question to your library.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| title | string | required | Case title |
| description | string | required | Internal description |
| question | string | required | The actual question text (Markdown supported) |
| case_type | string | required | coding | sql | multiple_choice |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | New case UUID |
| title | string | Echoes the title |
Example Response
{
"id": "new-uuid",
"title": "My New Case"
}https://praxicraft.com/api/v1/public/cases/:id/Retrieve Custom Case
Retrieves the full configuration of a specific custom case, including the question text.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | required | Case UUID |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Case UUID |
| title | string | Case title |
| question | string | Question text (Markdown) |
| case_type | string | coding | sql | multiple_choice |
Example Response
{
"id": "e21a...",
"title": "Custom Algorithm 1",
"question": "### Implement Quicksort...",
"case_type": "coding"
}https://praxicraft.com/api/v1/public/cases/:id/Update Custom Case
Updates specific fields of an existing custom case.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | required | Case UUID |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| title | string | optional | New title |
| question | string | optional | New question text |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Case UUID |
| title | string | Updated title |
Example Response
{
"id": "e21a...",
"title": "Updated Title"
}https://praxicraft.com/api/v1/public/cases/:id/Delete Custom Case
Permanently removes a custom case from your organization's library.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | required | Case UUID |
Example Response
HTTP/1.1 204 No Content
Pipelines
Automate multi-stage hiring flows. Pipelines group multiple assessments into ordered stages.
https://praxicraft.com/api/v1/public/pipelines/List Pipelines
Returns all hiring pipelines for your organisation.
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Pipeline UUID |
| title | string | Pipeline title |
| slug | string | Pipeline slug |
| stage_count | integer | Number of stages |
Example Response
{
"count": 2,
"results": [
{
"id": "p1...",
"title": "Grad Program 2025",
"slug": "grad-2025",
"stage_count": 3
}
]
}https://praxicraft.com/api/v1/public/pipelines/:slug/Get Pipeline Detail
Retrieves the full configuration of a hiring pipeline, including the ordered list of assessment stages.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| slug | string | required | Pipeline slug |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Pipeline UUID |
| title | string | Pipeline title |
| description | string | Internal description |
| stages | array | List of stages: { order, assessment_id, assessment_title, assessment_slug } |
Example Response
{
"id": "p1...",
"title": "Grad Program 2025",
"stages": [
{ "order": 0, "assessment_title": "Initial Screen", "assessment_slug": "initial-screen" },
{ "order": 1, "assessment_title": "Technical Deep Dive", "assessment_slug": "tech-deep-dive" }
]
}https://praxicraft.com/api/v1/public/pipelines/:slug/enroll/Enroll Candidate
Enrolls a candidate into a pipeline and triggers the first stage invitation.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| string | required | Candidate email | |
| name | string | optional | Candidate name |
Response Fields
| Field | Type | Description |
|---|---|---|
| message | string | Confirmation message |
| enrollment_id | uuid | New enrollment UUID |
| status | string | Hiring status |
| current_stage | integer | Zero-indexed stage |
Example Response
{
"message": "Candidate enrolled successfully.",
"enrollment_id": "e1...",
"status": "active",
"current_stage": 0
}https://praxicraft.com/api/v1/public/pipelines/enrollments/:id/Get Enrollment Status
Retrieves the current progress and history of a candidate within a specific pipeline enrollment.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | required | Enrollment ID from the enrollment response |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Enrollment UUID |
| status | string | active | completed | rejected |
| current_stage | integer | Index of the candidate's current stage |
| history | array | Chronological log of stage transitions and assessment results |
Example Response
{
"id": "e1...",
"status": "active",
"current_stage": 1,
"history": [
{ "event": "enrolled", "timestamp": "..." },
{ "event": "stage_completed", "stage": 0, "score": 95 }
]
}Webhooks
Receive real-time push notifications for candidate lifecycle events. Configure webhook endpoints in your dashboard under Developer → Webhooks or via the API. All payloads are signed with HMAC-SHA256.
https://praxicraft.com/api/v1/public/webhooks/List Webhooks
Returns all configured webhook endpoints for your organisation.
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | Webhook ID |
| url | string | Target endpoint URL |
| is_active | boolean | Whether the webhook is enabled |
| event_types | array | List of subscribed events |
Example Response
{
"count": 1,
"results": [
{
"id": "550e8400-...",
"url": "https://api.acme.com/webhooks",
"is_active": true,
"event_types": ["assessment.completed", "candidate.passed"]
}
]
}https://praxicraft.com/api/v1/public/webhooks/Create Webhook
Register a new endpoint to receive event notifications.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| url | string | required | Your HTTPS endpoint URL |
| event_types | array | required | List of events to subscribe to |
| description | string | optional | Internal label |
Response Fields
| Field | Type | Description |
|---|---|---|
| id | uuid | New webhook ID |
| signing_secret | string | Use this to verify X-Praxicraft-Signature headers |
Example Response
{
"id": "550e8400-...",
"signing_secret": "whsec_..."
}https://praxicraft.com/api/v1/public/webhooks/:id/test/Test Webhook Endpoint
Sends a test ping to the specified webhook endpoint to verify your implementation. Updates `is_verified` to true upon success.
Query / Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | required | The webhook endpoint UUID |
Response Fields
| Field | Type | Description |
|---|---|---|
| message | string | Test outcome message |
| status_code | integer | HTTP status returned by your server |
| is_verified | boolean | Whether the endpoint is now verified |
Example Response
{
"message": "Test succeeded — endpoint responded with 200.",
"status_code": 200,
"is_verified": true
}(your endpoint URL)Signature Verification
Every webhook request includes an `X-Praxicraft-Signature` header. Verify it using the signing secret shown in your webhook settings. Reject requests where the signature does not match.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| X-Praxicraft-Signature | string | required | HMAC-SHA256 hex digest of the raw request body, keyed with your webhook signing secret |
| Content-Type | string | required | application/json |
Response Fields
| Field | Type | Description |
|---|---|---|
| event | string | Event type — see list below |
| created_at | ISO 8601 | Event timestamp (UTC) |
| data | object | Event-specific payload |
Example Response
// Python verification example
import hmac, hashlib
def verify(secret: str, raw_body: bytes, header_sig: str) -> bool:
expected = hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header_sig)(your endpoint URL)Event Types & Payloads
Subscribe to specific events when configuring a webhook endpoint. Unsubscribed events are never delivered to that endpoint.
Response Fields
| Field | Type | Description |
|---|---|---|
| assessment.started | event | Candidate started the assessment (session created) |
| assessment.completed | event | Candidate submitted all cases and completed the assessment |
| candidate.passed | event | Session scored above the passing threshold |
| candidate.failed | event | Session scored below the passing threshold |
| candidate.violation | event | Proctoring violation detected (fullscreen exit or copy/paste) |
| invitation.expired | event | Invitation link passed its expiry date without being started |
| webhook.test | event | Test ping dispatched from the dashboard |
Example Response
// candidate.passed payload
{
"event": "candidate.passed",
"created_at": "2025-03-15T15:21:00Z",
"data": {
"session_id": "e1f2a3b4-...",
"assessment_id": "d290f1ee-...",
"assessment_slug": "senior-backend-screen",
"candidate_email": "jane@example.com",
"candidate_name": "Jane Doe",
"score": 255,
"max_score": 300,
"passed": true
}
}Errors
All error responses follow a consistent format with a machine-readable code and a human-readable message.
https://praxicraft.com/api/v1/public/...Error Response Format
Error responses always return a JSON body with an `error` object containing a `message` and optional `code`.
Response Fields
| Field | Type | Description |
|---|---|---|
| error.message | string | Human-readable explanation |
| error.code | string | Machine-readable code (see table below) |
Example Response
// 401 — Missing or invalid API key
{
"error": {
"message": "Invalid API key.",
"code": "INVALID_API_KEY"
}
}
// 400 — Validation error
{
"error": {
"message": "email is required",
"code": "MISSING_EMAIL"
}
}
// 404 — Resource not found
{
"error": {
"message": "Assessment not found.",
"code": "NOT_FOUND"
}
}
// 403 — Invite quota exceeded
{
"error": {
"message": "Monthly invite quota exceeded. Upgrade your plan to invite more candidates.",
"code": "INVITE_QUOTA_EXCEEDED"
}
}
// 400 — Assessment not in active status
{
"error": {
"message": "This assessment is not active and cannot accept new invitations.",
"code": "ASSESSMENT_NOT_ACTIVE"
}
}
// 429 — Rate limited
{
"error": {
"message": "Request was throttled. Expected available in 8 seconds."
}
}Organisation
Retrieve information about your organisation and team members. Useful for verifying your integration context or building team-aware workflows.
https://praxicraft.com/api/v1/public/org/Get Organisation Info
Returns basic information about the organisation associated with your API key, including your plan and current invite quota.
Response Fields
| Field | Type | Description |
|---|---|---|
| name | string | Organisation name |
| slug | string | URL-safe organisation identifier |
| plan | string | Subscription plan: free | starter | growth | enterprise |
| invite_limit | integer|null | Monthly candidate invite quota (null for enterprise) |
| invites_used | integer | Invites used in the current billing cycle |
| invites_remaining | integer|null | Invites remaining in the current cycle (null for enterprise) |
GET /api/v1/public/org/ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"name": "Acme Corp",
"slug": "acme-corp",
"plan": "starter",
"invite_limit": 100,
"invites_used": 23,
"invites_remaining": 77
}https://praxicraft.com/api/v1/public/org/team/List Team Members
Returns all active members of your organisation with their name, email, and role. Useful for ATS integrations that need to attribute assessments to a specific recruiter.
Response Fields
| Field | Type | Description |
|---|---|---|
| count | integer | Total number of team members |
| results | array | Array of team member objects |
| results[].name | string | Member's full name |
| results[].email | string | Member's email address |
| results[].role | string | owner | admin | recruiter | viewer |
GET /api/v1/public/org/team/ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"count": 3,
"results": [
{ "name": "Sarah Chen", "email": "sarah@acme.com", "role": "owner" },
{ "name": "James Okafor","email": "james@acme.com", "role": "admin" },
{ "name": "Priya Nair", "email": "priya@acme.com", "role": "recruiter" }
]
}https://praxicraft.com/api/v1/public/org/stats/Get Organisation Analytics
Returns high-level aggregate metrics for your organisation's assessment activity, including pass rates and candidate volume.
Response Fields
| Field | Type | Description |
|---|---|---|
| total_invites | integer | Total invitations sent ever |
| total_candidates | integer | Total candidates who started a session |
| completed_assessments | integer | Sessions completed |
| passed_candidates | integer | Sessions that met the passing score |
| pass_rate | float | Percentage of completed sessions that passed |
| active_assessments | integer | Count of currently active assessments |
GET /api/v1/public/org/stats/ Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
{
"total_invites": 450,
"total_candidates": 380,
"completed_assessments": 365,
"passed_candidates": 210,
"pass_rate": 57.5,
"average_score_percentage": 68.2,
"active_assessments": 12
}