PraxicraftDeveloper Docs
Praxicraft Assess API · v1

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.

Base URL: https://praxicraft.com
Format: JSON
Auth: Bearer Token

Authentication

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_`.

GEThttps://praxicraft.com/api/v1/public/assessments/
Auth: Bearer ct_live_…

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
NameTypeRequiredDescription
AuthorizationstringrequiredBearer ct_live_xxxxxxxxxxxxxxxx
Content-Typestringrequiredapplication/json (required on POST / PATCH)
Response Fields
FieldTypeDescription
(varies)object|arrayThe response payload — public API returns the resource directly, not wrapped in a status envelope
Example Response
JSON
// 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.

GEThttps://praxicraft.com/api/v1/public/...
Auth: Bearer ct_live_…

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
FieldTypeDescription
X-RateLimit-LimitintegerMax requests allowed in the window
X-RateLimit-RemainingintegerRequests remaining in current window
Retry-AfterintegerSeconds to wait before retrying (429 responses only)
Example Response
JSON
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.

GEThttps://praxicraft.com/api/v1/public/assessments/
Auth: Bearer ct_live_…

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
FieldTypeDescription
Freeplan20 invitations / month · 3 active assessments · 2 team seats
Starterplan100 invitations / month · 15 active assessments · 5 team seats
Growthplan500 invitations / month · 50 active assessments · 15 team seats
EnterpriseplanUnlimited invitations · Unlimited assessments · Unlimited seats
Example Response
JSON
// 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.

GEThttps://praxicraft.com/api/v1/public/assessments/
Auth: Bearer ct_live_…

List Assessments

Returns assessments for your organization. Defaults to `active` only. Use the `status` param to list drafts or archived assessments.

Query / Path Parameters
NameTypeRequiredDescription
statusstringoptionalFilter by status: active | draft | archived (default: active)
searchstringoptionalCase-insensitive substring match on assessment title
pageintegeroptionalPage number (default: 1)
page_sizeintegeroptionalResults per page, max 100 (default: 20)
Response Fields
FieldTypeDescription
iduuidAssessment UUID
titlestringAssessment title
slugstringURL-safe identifier used in all invite endpoints
statusstringactive | draft | archived
case_countintegerNumber of technical cases in this assessment
time_limit_minutesinteger|nullPer-session time cap, null = no limit
passing_scoreintegerMinimum percentage required to pass
invitation_countintegerTotal candidates invited
enforce_fullscreenbooleanWhether fullscreen mode is enforced during the assessment
copy_paste_disabledbooleanWhether copy/paste is blocked during the assessment
metaobjectPlan quota info: { plan, invite_limit, invites_used, invites_remaining }
HTTP
GET /api/v1/public/assessments/?status=active&search=backend
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "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
  }
}
GEThttps://praxicraft.com/api/v1/public/assessments/:slug/
Auth: Bearer ct_live_…

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
NameTypeRequiredDescription
slugstringrequiredAssessment slug from the list endpoint
Response Fields
FieldTypeDescription
iduuidAssessment UUID
slugstringURL-safe identifier
titlestringAssessment title
descriptionstringCandidate-visible description
statusstringactive | draft | archived
time_limit_minutesinteger|nullSession time cap in minutes
passing_scoreintegerMinimum score percentage to pass
case_countintegerNumber of technical cases
enforce_fullscreenbooleanWhether fullscreen mode is enforced
copy_paste_disabledbooleanWhether copy/paste is blocked
created_atISO 8601Creation timestamp
HTTP
GET /api/v1/public/assessments/senior-backend-screen/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "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"
}
GEThttps://praxicraft.com/api/v1/public/assessments/:slug/cases/
Auth: Bearer ct_live_…

List Assessment Cases

Returns the list of platform or custom technical cases attached to a specific assessment.

Query / Path Parameters
NameTypeRequiredDescription
slugstringrequiredAssessment slug
pageintegeroptionalPage number (default: 1)
page_sizeintegeroptionalResults per page, max 100 (default: 20)
Response Fields
FieldTypeDescription
iduuidCase UUID
titlestringCase title
typestringplatform | org (source of the case)
case_typestringcoding | sql | text | mcq
difficultystringeasy | medium | hard
orderintegerDisplay sequence in the assessment
pointsintegerScore weight of this case
Example Response
JSON
{
  "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.

POSThttps://praxicraft.com/api/v1/public/assessments/:slug/invites/
Auth: Bearer ct_live_…

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
NameTypeRequiredDescription
emailstringrequiredCandidate email address
namestringoptionalCandidate full name (displayed in the email and on the landing page)
rolestringoptionalJob role or position the candidate is being screened for (e.g. Senior Backend Engineer). Shown in the invitation email.
send_emailbooleanoptionalSet true to immediately dispatch the invitation email (default: false)
Response Fields
FieldTypeDescription
messagestringConfirmation message
invite_tokenuuidUnique token for this invitation — use this to fetch results later
invite_urlurlDirect link the candidate opens to start the assessment
expires_atISO 8601Invitation expiry (7 days from creation)
statusstringpending | started | completed | expired
HTTP
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
JSON
{
  "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"
}
POSThttps://praxicraft.com/api/v1/public/assessments/:slug/invites/bulk/
Auth: Bearer ct_live_…

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
NameTypeRequiredDescription
candidatesarrayrequiredArray of candidate objects, each with `email` and optional `name` and `role`
send_emailbooleanoptionalSend invitation email to all newly created invitations (default: false)
Response Fields
FieldTypeDescription
invitedarrayCandidates who received a new invitation — includes email, invite_token, invite_url
skippedarrayCandidates skipped with a reason (already_invited | missing_email | error)
HTTP
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
JSON
{
  "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" }
  ]
}
GEThttps://praxicraft.com/api/v1/public/invites/:token/
Auth: Bearer ct_live_…

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
NameTypeRequiredDescription
tokenuuidrequiredThe invite_token from the create invitation response
Response Fields
FieldTypeDescription
invite_tokenuuidUnique token for this invitation
emailstringCandidate email address
namestring|nullCandidate name if provided
rolestring|nullJob role the candidate was invited for
statusstringpending | started | completed | expired
assessmentobject{ slug, title } — the assessment this invitation belongs to
created_atISO 8601When the invitation was created
expires_atISO 8601Invitation expiry timestamp
started_atISO 8601|nullWhen the candidate started the session
completed_atISO 8601|nullWhen the candidate completed the session
reminded_atISO 8601|nullTimestamp of the last reminder email sent
HTTP
GET /api/v1/public/invites/c7b3e2a1-.../
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "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
}
POSThttps://praxicraft.com/api/v1/public/invites/:token/remind/
Auth: Bearer ct_live_…

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
NameTypeRequiredDescription
tokenuuidrequiredThe invite_token from the create invitation response
Response Fields
FieldTypeDescription
messagestringConfirmation that the reminder was dispatched
HTTP
POST /api/v1/public/invites/c7b3e2a1-.../remind/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "message": "Reminder sent."
}
DELETEhttps://praxicraft.com/api/v1/public/invites/:token/
Auth: Bearer ct_live_…

Cancel Invitation

Permanently deletes a pending invitation. Returns 400 if the candidate has already started or completed the assessment.

Query / Path Parameters
NameTypeRequiredDescription
tokenuuidrequiredThe invite_token to cancel
HTTP
DELETE /api/v1/public/invites/c7b3e2a1-.../
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
HTTP/1.1 204 No Content
GEThttps://praxicraft.com/api/v1/public/invites/
Auth: Bearer ct_live_…

List All Invitations

Returns a global list of all candidate invitations across all assessments in your organisation. Paginated.

Query / Path Parameters
NameTypeRequiredDescription
pageintegeroptionalPage number (default: 1)
page_sizeintegeroptionalResults per page, max 100 (default: 20)
Response Fields
FieldTypeDescription
invite_tokenuuidUnique token for this invitation
assessment_titlestringTitle of the assessment
assessment_slugstringSlug of the assessment
emailstringCandidate email
statusstringpending | started | completed | expired
sent_atISO 8601When the invite was sent
HTTP
GET /api/v1/public/invites/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "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.

GEThttps://praxicraft.com/api/v1/public/assessments/:slug/results/
Auth: Bearer ct_live_…

List Assessment Results

Returns all completed candidate sessions for an assessment, ordered by most recent. Paginated.

Query / Path Parameters
NameTypeRequiredDescription
slugstringrequiredAssessment slug
pageintegeroptionalPage number (default: 1)
page_sizeintegeroptionalResults per page, max 100 (default: 20)
Response Fields
FieldTypeDescription
candidate_emailstringCandidate email
candidate_namestringCandidate name (if provided)
statusstringpending | started | completed | expired
score_percentagefloatAggregated score as a percentage (0–100)
passedbooleanWhether the candidate met the passing threshold
started_atISO 8601Session start timestamp
completed_atISO 8601|nullCompletion timestamp
invite_tokenuuidUse this to fetch the detailed result
HTTP
GET /api/v1/public/assessments/senior-backend-screen/results/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "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-..."
    }
  ]
}
GEThttps://praxicraft.com/api/v1/public/invites/:token/result/
Auth: Bearer ct_live_…

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
NameTypeRequiredDescription
tokenuuidrequiredThe invite_token from the create invitation response
Response Fields
FieldTypeDescription
candidate_emailstringCandidate email
candidate_namestringCandidate name
assessmentstringAssessment title
statusstringInvitation status
scoreintegerRaw score achieved
max_scoreintegerMaximum possible score
score_percentagefloatPercentage score (0–100)
passedbooleanWhether the candidate met the passing threshold
passing_scoreintegerPassing threshold configured for this assessment
started_atISO 8601When the session started
completed_atISO 8601|nullWhen the session completed
violation_countsobject{ fullscreen_violations, copy_paste_violations }
detailed_report_urlurlAPI URL for the full session report (requires session auth)
casesarrayPer-case breakdown: case_order, case_title, score, max_score, status, time_spent_seconds
HTTP
GET /api/v1/public/invites/c7b3e2a1-.../result/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "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.

GEThttps://praxicraft.com/api/v1/public/cases/
Auth: Bearer ct_live_…

List Custom Cases

Returns all custom cases created by your organisation. Paginated.

Query / Path Parameters
NameTypeRequiredDescription
pageintegeroptionalPage number
page_sizeintegeroptionalResults per page
Response Fields
FieldTypeDescription
iduuidCase UUID
titlestringCase title
case_typestringcoding | sql | multiple_choice
difficultystringeasy | medium | hard
Example Response
JSON
{
  "count": 42,
  "results": [
    {
      "id": "e21a...",
      "title": "Custom Algorithm 1",
      "case_type": "coding",
      "difficulty": "hard"
    }
  ]
}
POSThttps://praxicraft.com/api/v1/public/cases/create/
Auth: Bearer ct_live_…

Create Custom Case

Programmatically add a new question to your library.

Request Body
NameTypeRequiredDescription
titlestringrequiredCase title
descriptionstringrequiredInternal description
questionstringrequiredThe actual question text (Markdown supported)
case_typestringrequiredcoding | sql | multiple_choice
Response Fields
FieldTypeDescription
iduuidNew case UUID
titlestringEchoes the title
Example Response
JSON
{
  "id": "new-uuid",
  "title": "My New Case"
}
GEThttps://praxicraft.com/api/v1/public/cases/:id/
Auth: Bearer ct_live_…

Retrieve Custom Case

Retrieves the full configuration of a specific custom case, including the question text.

Query / Path Parameters
NameTypeRequiredDescription
iduuidrequiredCase UUID
Response Fields
FieldTypeDescription
iduuidCase UUID
titlestringCase title
questionstringQuestion text (Markdown)
case_typestringcoding | sql | multiple_choice
Example Response
JSON
{
  "id": "e21a...",
  "title": "Custom Algorithm 1",
  "question": "### Implement Quicksort...",
  "case_type": "coding"
}
PATCHhttps://praxicraft.com/api/v1/public/cases/:id/
Auth: Bearer ct_live_…

Update Custom Case

Updates specific fields of an existing custom case.

Query / Path Parameters
NameTypeRequiredDescription
iduuidrequiredCase UUID
Request Body
NameTypeRequiredDescription
titlestringoptionalNew title
questionstringoptionalNew question text
Response Fields
FieldTypeDescription
iduuidCase UUID
titlestringUpdated title
Example Response
JSON
{
  "id": "e21a...",
  "title": "Updated Title"
}
DELETEhttps://praxicraft.com/api/v1/public/cases/:id/
Auth: Bearer ct_live_…

Delete Custom Case

Permanently removes a custom case from your organization's library.

Query / Path Parameters
NameTypeRequiredDescription
iduuidrequiredCase UUID
Example Response
JSON
HTTP/1.1 204 No Content

Pipelines

Automate multi-stage hiring flows. Pipelines group multiple assessments into ordered stages.

GEThttps://praxicraft.com/api/v1/public/pipelines/
Auth: Bearer ct_live_…

List Pipelines

Returns all hiring pipelines for your organisation.

Response Fields
FieldTypeDescription
iduuidPipeline UUID
titlestringPipeline title
slugstringPipeline slug
stage_countintegerNumber of stages
Example Response
JSON
{
  "count": 2,
  "results": [
    {
      "id": "p1...",
      "title": "Grad Program 2025",
      "slug": "grad-2025",
      "stage_count": 3
    }
  ]
}
GEThttps://praxicraft.com/api/v1/public/pipelines/:slug/
Auth: Bearer ct_live_…

Get Pipeline Detail

Retrieves the full configuration of a hiring pipeline, including the ordered list of assessment stages.

Query / Path Parameters
NameTypeRequiredDescription
slugstringrequiredPipeline slug
Response Fields
FieldTypeDescription
iduuidPipeline UUID
titlestringPipeline title
descriptionstringInternal description
stagesarrayList of stages: { order, assessment_id, assessment_title, assessment_slug }
Example Response
JSON
{
  "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" }
  ]
}
POSThttps://praxicraft.com/api/v1/public/pipelines/:slug/enroll/
Auth: Bearer ct_live_…

Enroll Candidate

Enrolls a candidate into a pipeline and triggers the first stage invitation.

Request Body
NameTypeRequiredDescription
emailstringrequiredCandidate email
namestringoptionalCandidate name
Response Fields
FieldTypeDescription
messagestringConfirmation message
enrollment_iduuidNew enrollment UUID
statusstringHiring status
current_stageintegerZero-indexed stage
Example Response
JSON
{
  "message": "Candidate enrolled successfully.",
  "enrollment_id": "e1...",
  "status": "active",
  "current_stage": 0
}
GEThttps://praxicraft.com/api/v1/public/pipelines/enrollments/:id/
Auth: Bearer ct_live_…

Get Enrollment Status

Retrieves the current progress and history of a candidate within a specific pipeline enrollment.

Query / Path Parameters
NameTypeRequiredDescription
iduuidrequiredEnrollment ID from the enrollment response
Response Fields
FieldTypeDescription
iduuidEnrollment UUID
statusstringactive | completed | rejected
current_stageintegerIndex of the candidate's current stage
historyarrayChronological log of stage transitions and assessment results
Example Response
JSON
{
  "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.

GEThttps://praxicraft.com/api/v1/public/webhooks/
Auth: Bearer ct_live_…

List Webhooks

Returns all configured webhook endpoints for your organisation.

Response Fields
FieldTypeDescription
iduuidWebhook ID
urlstringTarget endpoint URL
is_activebooleanWhether the webhook is enabled
event_typesarrayList of subscribed events
Example Response
JSON
{
  "count": 1,
  "results": [
    {
      "id": "550e8400-...",
      "url": "https://api.acme.com/webhooks",
      "is_active": true,
      "event_types": ["assessment.completed", "candidate.passed"]
    }
  ]
}
POSThttps://praxicraft.com/api/v1/public/webhooks/
Auth: Bearer ct_live_…

Create Webhook

Register a new endpoint to receive event notifications.

Request Body
NameTypeRequiredDescription
urlstringrequiredYour HTTPS endpoint URL
event_typesarrayrequiredList of events to subscribe to
descriptionstringoptionalInternal label
Response Fields
FieldTypeDescription
iduuidNew webhook ID
signing_secretstringUse this to verify X-Praxicraft-Signature headers
Example Response
JSON
{
  "id": "550e8400-...",
  "signing_secret": "whsec_..."
}
POSThttps://praxicraft.com/api/v1/public/webhooks/:id/test/
Auth: Bearer ct_live_…

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
NameTypeRequiredDescription
iduuidrequiredThe webhook endpoint UUID
Response Fields
FieldTypeDescription
messagestringTest outcome message
status_codeintegerHTTP status returned by your server
is_verifiedbooleanWhether the endpoint is now verified
Example Response
JSON
{
  "message": "Test succeeded — endpoint responded with 200.",
  "status_code": 200,
  "is_verified": true
}
POST(your endpoint URL)
Auth: HMAC-SHA256 (X-Praxicraft-Signature)

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
NameTypeRequiredDescription
X-Praxicraft-SignaturestringrequiredHMAC-SHA256 hex digest of the raw request body, keyed with your webhook signing secret
Content-Typestringrequiredapplication/json
Response Fields
FieldTypeDescription
eventstringEvent type — see list below
created_atISO 8601Event timestamp (UTC)
dataobjectEvent-specific payload
Example Response
JSON
// 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)
POST(your endpoint URL)
Auth: HMAC-SHA256

Event Types & Payloads

Subscribe to specific events when configuring a webhook endpoint. Unsubscribed events are never delivered to that endpoint.

Response Fields
FieldTypeDescription
assessment.startedeventCandidate started the assessment (session created)
assessment.completedeventCandidate submitted all cases and completed the assessment
candidate.passedeventSession scored above the passing threshold
candidate.failedeventSession scored below the passing threshold
candidate.violationeventProctoring violation detected (fullscreen exit or copy/paste)
invitation.expiredeventInvitation link passed its expiry date without being started
webhook.testeventTest ping dispatched from the dashboard
Example Response
JSON
// 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.

GEThttps://praxicraft.com/api/v1/public/...
Auth:

Error Response Format

Error responses always return a JSON body with an `error` object containing a `message` and optional `code`.

Response Fields
FieldTypeDescription
error.messagestringHuman-readable explanation
error.codestringMachine-readable code (see table below)
Example Response
JSON
// 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.

GEThttps://praxicraft.com/api/v1/public/org/
Auth: Bearer ct_live_…

Get Organisation Info

Returns basic information about the organisation associated with your API key, including your plan and current invite quota.

Response Fields
FieldTypeDescription
namestringOrganisation name
slugstringURL-safe organisation identifier
planstringSubscription plan: free | starter | growth | enterprise
invite_limitinteger|nullMonthly candidate invite quota (null for enterprise)
invites_usedintegerInvites used in the current billing cycle
invites_remaininginteger|nullInvites remaining in the current cycle (null for enterprise)
HTTP
GET /api/v1/public/org/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "name": "Acme Corp",
  "slug": "acme-corp",
  "plan": "starter",
  "invite_limit": 100,
  "invites_used": 23,
  "invites_remaining": 77
}
GEThttps://praxicraft.com/api/v1/public/org/team/
Auth: Bearer ct_live_…

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
FieldTypeDescription
countintegerTotal number of team members
resultsarrayArray of team member objects
results[].namestringMember's full name
results[].emailstringMember's email address
results[].rolestringowner | admin | recruiter | viewer
HTTP
GET /api/v1/public/org/team/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "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" }
  ]
}
GEThttps://praxicraft.com/api/v1/public/org/stats/
Auth: Bearer ct_live_…

Get Organisation Analytics

Returns high-level aggregate metrics for your organisation's assessment activity, including pass rates and candidate volume.

Response Fields
FieldTypeDescription
total_invitesintegerTotal invitations sent ever
total_candidatesintegerTotal candidates who started a session
completed_assessmentsintegerSessions completed
passed_candidatesintegerSessions that met the passing score
pass_ratefloatPercentage of completed sessions that passed
active_assessmentsintegerCount of currently active assessments
HTTP
GET /api/v1/public/org/stats/
Authorization: Bearer ct_live_xxxxxxxxxxxxxxxx
Example Response
JSON
{
  "total_invites": 450,
  "total_candidates": 380,
  "completed_assessments": 365,
  "passed_candidates": 210,
  "pass_rate": 57.5,
  "average_score_percentage": 68.2,
  "active_assessments": 12
}

Need help integrating?

Email our engineering team at api@praxicraft.com

Get Your API Key