API Reference
Checklists

Checklists

Checklist tasks (checklist_items) are the individual to-dos attached to an offboarding record - "revoke GitHub access," "collect laptop," and so on. There's no standalone /checklists resource; tasks are created as a side effect of starting an offboarding and managed through the employee/offboarding endpoints below.

These are the same session-authenticated endpoints the PurpletGo web app uses (/api/employees, /api/checklist-templates) - not the API-key-based /api/v1 surface. If you're integrating from a server, you'll authenticate the same way the dashboard does.


How tasks get created

POST /api/employees

When you create an offboarding, its checklist is populated automatically. PurpletGo picks the task list in this order:

  1. checklistTemplateId in the request body, if you pass one explicitly
  2. A role-based template configured under Settings → Role Templates, matched against the employee's role and separation type
  3. A shorter default list for contractors/vendors, or a general default list for regular employees

Each generated task has a task and an assignedTo. The default lists assign to IT Admin, HR, Manager, Employee and Finance; the contractor list also uses HR Admin. You can add more tasks afterwards with the add endpoint below, or by applying a template.

See the Offboarding API for the full create-offboarding request body.


Reading a checklist

Checklist items aren't fetched from their own endpoint - they come back embedded on the offboarding record.

GET /api/employees

Each row in the response includes a checklist_items array:

{
  "id": "e3b0c442-...",
  "employee_name": "Jordan Lee",
  "checklist_items": [
    {
      "id": "f47ac10b-...",
      "task": "Revoke GitHub access",
      "completed": false,
      "assigned_to": "IT Admin",
      "due_date": null,
      "sla_hours": 48,
      "sla_deadline": "2024-06-03T10:00:00.000Z",
      "sla_breached": false
    }
  ]
}

Field names are the raw checklist_items column names (snake_case) - there's no camelCase transformation on this endpoint.


Add a task

POST /api/employees/:offboardingId/checklist

Adds one task to an existing offboarding's checklist. Needs the offboardings_edit or checklists_manage permission (Super Admin, Admin, HR and Manager by default, or a custom role that grants either).

Request body

{ "task": "Collect parking pass", "assignedTo": "Facilities" }
FieldRequiredTypeDescription
task✅string1 to 500 characters
assignedTo-stringFree text, up to 100 characters. A missing or blank value is stored as HR - a task is never unassigned

Response 201 Created - the new row, in the same snake_case shape as the embedded checklist:

{
  "id": "f47ac10b-...",
  "task": "Collect parking pass",
  "completed": false,
  "assigned_to": "Facilities",
  "due_date": null,
  "sla_hours": null,
  "sla_deadline": null,
  "sla_breached": false,
  "not_needed_at": null,
  "not_needed_reason": null
}

Returns 404 if the offboarding isn't in your organisation (or your department scope), and 409 with "code": "case_completed" if the offboarding is already completed - its checklist can no longer change until it is moved back a stage. The addition is written to the audit log.


Update a task

PATCH /api/employees/checklist/:itemId

Updates one checklist item. This is also how you mark a task complete - there's no separate "complete" endpoint.

Request body (all fields optional - send only what you're changing)

FieldTypeDescription
completedbooleanMarking true stamps completed_at and clears any SLA breach; marking false clears completed_at. Requires the offboardings_edit permission
notNeededbooleantrue marks the task as not needed this time: it counts as completed but stays visible with its reason, and reason becomes required. false puts it back (and unticks it, unless you also send completed). Requires offboardings_edit
reasonstringWhy the task is not needed, 3 to 500 characters. Required when notNeeded is true, ignored otherwise
taskstringTask description, 1 to 500 characters
assigned_tostringWho owns the task, up to 100 characters. A blank value is stored as HR
due_datedate or nullYYYY-MM-DD
sla_hoursinteger or nullHours from creation until the task is due (1 to 8760). Pass null to clear it

Who can do what: ticking or unticking (completed) and notNeeded need offboardings_edit. Changing task, assigned_to, due_date or sla_hours needs offboardings_edit or checklists_manage, and setting (not clearing) sla_hours needs offboardings_edit. By default HR, managers and admins hold offboardings_edit; custom roles as the Roles screen shows. Anyone else gets 403.

Response 200 OK

{
  "success": true,
  "completed": true,
  "slaHours": 48,
  "slaDeadline": "2024-06-03T10:00:00.000Z",
  "slaBreached": false,
  "notNeededAt": null,
  "notNeededReason": null
}

Ticking a task that is already done (with nothing else in the body) changes nothing - completed_at and the automation that runs on completion are left alone - and returns { "success": true, "completed": true, "unchanged": true }.

Conflicts (409)

codeWhen
case_completedThe offboarding is completed, so its checklist can no longer change. Move it back a stage first
linked_item_openThe task follows another record (Revoke <app> access, Verify NDA) and completes itself from that record; ticking it by hand while the record still says otherwise is refused. error says what is still open. Marking it notNeeded is allowed

Delete a task

DELETE /api/employees/checklist/:itemId

Removes a task outright. Use this for a task that should never have been on the list; a task that simply doesn't apply this time should be marked not needed with PATCH instead, which keeps it visible with its reason.

Needs the offboardings_edit or checklists_manage permission, plus a reason in the body. Deleting a task that is still open is an Admin decision: anyone else gets 403 with "code": "open_task_delete" and should mark it not needed instead.

Request body

{ "reason": "Duplicate of the IT access task" }
FieldRequiredTypeDescription
reason✅string5 to 500 characters. Missing or out of range returns 400 with "code": "reason_required"

Response 200 OK

{ "success": true }
StatusWhen
403 open_task_deleteThe task isn't completed and you aren't a Super Admin or Admin
404No such task in your organisation or department scope
409 case_completedThe offboarding is completed
409 legal_holdThe offboarding is under legal hold (or was released less than 7 days ago); its checklist is part of the record

The deletion is written to the audit log with the task, whether it was completed, and your reason.


Apply a checklist template

POST /api/checklist-templates/:id/apply/:offboardingId

Copies the template's items onto an already-created offboarding, on top of whatever tasks it already has. Items the offboarding already has (same task text, ignoring case and extra spaces) are skipped, so applying a template twice doesn't duplicate tasks.

Requires the checklistTemplates plan feature (Starter and up); on a plan without it the request returns 403 with "feature": "checklistTemplates" and "upgradeRequired": true.

Response 200 OK

{ "success": true, "itemsAdded": 6, "itemsSkipped": 2 }

Returns 404 if the template isn't yours or the offboarding isn't in your organisation (or department scope), and 409 with "code": "case_completed" if the offboarding is already completed - the same rule as adding a task by hand. The apply is audited in the same transaction as the tasks it adds.

Templates themselves (GET/POST/PATCH/PUT/DELETE /api/checklist-templates) are managed separately - see Settings → Checklist Templates in the app for the full template CRUD surface.

Every template write - create, gallery import, rename (PATCH), full replace (PUT), duplicate, delete and apply - needs the checklists_manage permission: Super Admin and Admin by default, or a custom role that grants it. Anyone else gets 403.


SLA breach report

GET /api/employees/sla-breaches

Returns every incomplete task with an SLA configured, across all in-progress offboardings, sorted by deadline. Requires HR, manager, or admin permissions. Unlike the embedded checklist above, this endpoint camelCases its fields:

[
  {
    "id": "f47ac10b-...",
    "task": "Revoke GitHub access",
    "assignedTo": "IT Admin",
    "slaHours": 48,
    "slaDeadline": "2024-06-03T10:00:00.000Z",
    "slaBreached": true,
    "completed": false,
    "dueDate": null,
    "createdAt": "2024-06-01T10:00:00.000Z",
    "offboardingId": "e3b0c442-...",
    "employeeName": "Jordan Lee",
    "employeeDepartment": "Engineering",
    "exitDate": "2024-06-15",
    "hoursOverdue": 6.2,
    "hoursRemaining": 0,
    "status": "breached"
  }
]

status is computed per-row: breached once the deadline has passed, at_risk when less than 25% of the SLA window remains, otherwise on_track.