Skip to content

Tenant API v1 — Tasks ​

Base path: /api/tenant/v1

Middleware: auth:tenant-api, tenant.user, verified, module:tasks, plus permission middleware / policies.

Assignee scoping: without tasks.assign (and not superadmin), list/board/stats only include tasks where assigned_to is the current user.

assigned_to on create/update/assign must be a tenant user id (or null to unassign). Unlike leads, Tasks do not enforce lead-assignee eligibility (exclude_from_lead_auto_assign / owner exclusion), but the assignee must be active (not suspended) and email-verified.

Tags ​

GET /task-tags ​

List workspace task tags (name, slug, color, sort_order). Permission: tasks.view.

POST /task-tags ​

Create a tag. Body: name (required), optional slug, color, sort_order. Permission: tasks.create.

PUT /task-tags/{taskTag} ​

Rename / update a tag (name, slug, color, sort_order). Workspace owner (superadmin) only.

DELETE /task-tags/{taskTag} ​

Delete a tag and detach it from all tasks. Workspace owner (superadmin) only.

Assign tags on the task via tag_ids, PUT /tasks/{id}/tags, or the SPA list/board inline tag popover. Non-owners may add tags; removing tags from a record requires the workspace owner (422 on tag_ids otherwise). Manage Tags in the SPA is available to anyone with tasks.view; rename/delete controls appear only for the owner.

Stats & board ​

GET /tasks/stats ​

Same filters as list (minus pagination/sort). Payload includes:

total_tasks, my_tasks, due_today, due_this_week, overdue, completed_today, completion_rate, scope (org|mine).

GET /tasks/board ​

Columns keyed by TaskStatusEnum. Open Kanban boards also subscribe to private Reverb channel tenant.{tenantId}.tasks.board (auth: same tenant + tasks.view) for invalidate-on-change refresh.

One column per status (open, in_progress, waiting, completed, cancelled): status, task_count, tasks[]. Honors the same filters as list.

Tasks CRUD ​

GET /tasks ​

Query: search, status, priority, tag_id, assigned_to (unassigned or user id), my_tasks, overdue, trashed, sort, direction, page, per_page.

List/board items include tags[] when loaded, plus project_id / embedded project, optional milestone_id / milestone, and depends_on_task_ids when loaded.

Status values: open, in_progress, waiting, completed, cancelled.
Priority values: low, medium, high, urgent.

overdue=true returns open / in-progress / waiting tasks with due_at in the past (matches the stats overdue definition). When overdue is set, status is ignored.

List and board task cards include latest_note — most recent note (id, body, author, timestamps) or null.

POST /tasks ​

JSON or multipart. Body: title (required), description (optional HTML subset from the workspace editor), status, priority, due_at, assigned_to, tag_ids[], optional project_id (soft — requires the Projects module entitled and a project the actor may see; see LinkableProject), optional milestone_id (same project), optional depends_on_task_ids[] (same-project blockers). Optional multipart attachments[] (max 10) — validated by workspace storage.upload_policy.

Initial due_at on create does not require tasks.change_due_date.

GET /tasks/{id} ​

Includes assignee, creator, notes (with note attachments), task attachments, activities, tags, optional project / milestone, and depends_on_task_ids. Embedded notes and activities are newest-first (created_at DESC, then id DESC).

PUT /tasks/{id} ​

Partial update of task fields (including status / priority / assigned_to / due_at / tag_ids[] / project_id / milestone_id / depends_on_task_ids[]). Optional multipart attachments[] via POST twin /tasks/{id} (PHP does not populate files on true PUT).

Changing due_at after create requires tasks.change_due_date (403 otherwise).

POST /tasks/{id}/attachments ​

Upload additional task files (attachments[], max 10). Permission: tasks.update.

GET /tasks/attachments/{uuid}/download · DELETE /tasks/attachments/{uuid} ​

Download / delete a task attachment (authz via parent task view / update).

PUT /tasks/{id}/tags ​

Sync tags. Body: { "tag_ids": number[] }. Permission: tasks.update.

DELETE /tasks/{id} ​

Soft delete. Permission: tasks.delete.

POST /tasks/{id}/restore ​

Restore a soft-deleted task. Permission: tasks.restore (admin + owner by default).

DELETE /tasks/{id}/force ​

Permanently delete a soft-deleted task (must already be trashed). Permission: tasks.force.delete (owner by default).

Actions ​

POST /tasks/{id}/assign ​

{ "assigned_to": number|null }

POST /tasks/{id}/complete ​

Marks status completed and sets completed_at.

POST /tasks/{id}/reopen ​

Clears completed_at and started_at, returns the task to a non-completed status (typically open), and starts a new timing cycle. Prior status_changed activities remain for audit.

POST /tasks/{id}/notes ​

JSON or multipart: { "body": string } required, optional attachments[] (max 5). Note attachment download/delete: GET|DELETE /tasks/note-attachments/{uuid}.

GET /tasks/{id}/timeline ​

Task activity timeline entries (History / Activity). Entries of type status_changed include properties.duration_seconds (time in the previous status) and resource fields duration_seconds / duration_formatted (compact human text, e.g. 3h, 1d 2h 5m).

Status timing on show / write responses ​

GET|PUT /tasks/{id} (and complete/reopen/assign responses that return TaskResource) include:

  • started_at — first transition into in_progress in the current cycle (null until then; cleared on reopen)
  • timing — computed summary:
    • created_to_completed / started_to_completed: { seconds, formatted } or null
    • segments[]: { from, to, seconds, formatted } for the current cycle (after latest reopen, else from create)
    • time_in_status: map of status → { seconds, formatted } (includes live age in the current status)

Official documentation for the EloSync SaaS Platform.