Tasks

One shared board belongs to each account. Tasks can optionally link to an event. The fixed statuses are open, in_progress, and done.

All endpoints require Bearer authentication. Send Accept: application/json, and Content-Type: application/json for JSON request bodies. Reads accept a read or write token; mutations require a write token. Personal co-organizer tokens see and manage only tasks for their current assigned events. They cannot create account-wide tasks or remove a task’s event link. Assigning a person never grants them event access.

Task representation

{
  "id": "W1pjAZsT2T1aPLyfFM9pHJZc",
  "title": "Confirm the door briefing",
  "description": "Meet the entrance team at 18:00.",
  "description_html": "<div class=\"trix-content\"><p>Meet the entrance team at 18:00.</p></div>",
  "attachments": [],
  "archived_at": null,
  "status": "in_progress",
  "position": 1,
  "lock_version": 2,
  "due_on": "2026-10-24",
  "event": { "slug": "october-party", "title": "October Party" },
  "assignee": { "id": 42, "name": "Lisa" },
  "created_at": "2026-09-13T16:00:00Z",
  "updated_at": "2026-09-13T16:10:00Z"
}

id is an opaque public ID. event, assignee, due_on, and archived_at may be null. description is plain text (empty string when absent); description_html contains the sanitized rendered rich text. Due dates are calendar dates (YYYY-MM-DD); timestamps are UTC. Assignee IDs identify account memberships. Use the people endpoint below to obtain eligible membership IDs. position is the ordering value within a status column; clients should move tasks through the position endpoint instead of writing this field directly.

List and show

GET /admin/tasks returns { "tasks": [...] }, ordered by position and ID. Optional query parameters:

Parameter Meaning
event_slug An accessible event slug, or none for tasks without an event. Omit for all accessible tasks.
archived true lists archived tasks only. Omit or use false for the active board.
hide_done true hides completed tasks on the active board.
assignee_id An eligible membership ID, me for the current person, or none for unassigned tasks. Omit for everyone.

GET /admin/tasks/:id returns one task using the representation above. Missing or inaccessible tasks and event slugs return 404.

Create and update

POST /admin/tasks accepts a task object. Only title is required for an account-wide task; co-organizers must also provide an accessible event_slug.

{
  "task": {
    "title": "Confirm the door briefing",
    "event_slug": "october-party",
    "assignee_id": 42,
    "due_on": "2026-10-24"
  }
}

Returns 201 Created, the task representation, and a Location header pointing to the task’s JSON URL.

PATCH /admin/tasks/:id accepts the same fields plus lock_version:

{
  "task": {
    "description": "Meet the entrance team at 18:00.",
    "status": "in_progress",
    "lock_version": 1
  }
}

Writable fields: title (maximum 200 characters), description (plain text or HTML; maximum 20,000 plain-text characters), status, due_on, event_slug, assignee_id, and lock_version. Omitted fields are preserved. Updating description replaces all its rich text and attachments; retain existing attachment markup when changing only the text. Set optional values to null or an empty string to clear them. A co-organizer cannot clear event_slug.

Updates return 200 and the current task. Include the last returned lock_version to detect concurrent edits. A stale version returns 409 with { "errors": { "base": ["..."] } }; read the latest task and reconcile changes before retrying. Validation failures return 422 with field errors, for example { "errors": { "title": ["can't be blank"] } }. A person without access to the task cannot be assigned; choose an eligible person first.

Move a card

PATCH /admin/tasks/:task_id/position:

{
  "position": {
    "status": "done",
    "before_id": "B2L8pYqsDHwTrPc93MCzhGNY",
    "lock_version": 2
  }
}

status is required. Optional before_id identifies an accessible task in the destination column. Omit it to place the card last. Moving within the same column reorders the card. The anchor preserves the relative order of cards hidden by filters. Returns 200 and the updated task; stale versions return 409. An invalid destination returns 422 and leaves the task unchanged.

People eligible for assignment

GET /admin/task_assignees?event_slug=october-party returns:

{ "people": [{ "id": 42, "name": "Lisa" }] }

An event includes active owners, managers, and co-organizers assigned to that event. Omit event_slug to get eligible people for account-wide tasks (owners and managers). Scanner, promoter, inactive and foreign memberships are excluded.

Comments and activity

GET /admin/tasks/:task_id/comments returns { "entries": [...] }, ordered oldest first. Entries include comments and creation, status and assignment history. Each entry has exactly these fields:

{
  "id": "LAfgPyCWoSVpJhMF89YAzRTZ",
  "kind": "comment",
  "body": "Entrance team confirmed.",
  "body_html": "<div class=\"trix-content\"><p>Entrance team confirmed.</p></div>",
  "attachments": [],
  "details": {},
  "author": "Lisa",
  "created_at": "2026-09-13T16:15:00Z"
}

kind is created, status, assignment, or comment. For status changes, details contains from and to; for assignments it contains name, which is null when unassigned. body is plain text and body_html is sanitized rendered rich text; both are null for non-comment activity. author may be null if the originating user has been removed.

POST /admin/tasks/:task_id/comments with { "comment": { "body": "Entrance team confirmed." } } adds a comment (maximum 10,000 plain-text characters; plain text or HTML). Returns 201, the entry representation and a Location header pointing to the comments collection. Empty comments return 422; a comment containing only an attachment is valid.

Archive and restore

POST /admin/tasks/:task_id/archive archives a completed (done) task. DELETE /admin/tasks/:task_id/archive restores it to the active board in Done. Both return 200 and the updated task, including archived_at (UTC timestamp or null). Send optional { "lock_version": 4 } to protect against concurrent changes; stale versions return 409.

Archiving an unfinished task returns 422. Archived tasks retain their comments, attachments, assignments and event association. Restore a task before moving it. Archived tasks remain available through their detail URL and follow the same current access rules as the active board.

Both tasks and entries include an attachments array (empty when none). Each attachment contains:

{
  "signed_id": "SIGNED_BLOB_ID",
  "attachable_sgid": "ATTACHABLE_SGID",
  "filename": "door-briefing.pdf",
  "content_type": "application/pdf",
  "byte_size": 4096,
  "url": "/admin/task_uploads/SIGNED_BLOB_ID"
}

Use signed_id for authenticated downloads and attachable_sgid for Action Text markup. These IDs remain subject to current task access.

Images and files in rich text

The dashboard editor supports direct uploads. API clients use the same flow:

  1. POST /admin/task_uploads with a blob object containing filename, byte_size, checksum (base64 MD5), and content_type. Maximum file size is 20 MB. This requires a write token.
  2. The 200 response includes signed_id, attachable_sgid, and direct_upload with url and headers. Upload the file bytes with PUT to that URL using those headers.
  3. Include <action-text-attachment sgid="ATTACHABLE_SGID"></action-text-attachment> in the task’s description or comment’s body HTML. Use the returned attachable_sgid, not signed_id, for this markup.

GET /admin/task_uploads/:signed_id downloads a file with current organizer authentication. API clients must send their Bearer token and Accept: application/json even though the response contains file bytes. Supported image types render inline; ?disposition=attachment forces download. Other files always download. HTML representations contain these authenticated links, which are not public sharing links.

Unattached uploads are accessible only to their uploader in the current account. Attached files follow current task permissions. Only the uploader’s private draft files or files already attached to that same task can be embedded. Standard public Active Storage URLs cannot access task uploads.

Delete

DELETE /admin/tasks/:id returns 204 No Content. Deleting a task also removes its comments, history and assignment notifications. Deleting an event removes its tasks.

Live updates and notifications

API changes appear in the shared board through the same live refreshes as the admin UI. Assigning a task to another person sends a notification through their existing inbox and configured browser push. The actor is not notified about self-assignment. Delivery checks current task access and assignment; old notifications are hidden after assignment or access is removed. Comments update live without sending a notification to the whole team.