Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cryguy/hashboard/llms.txt

Use this file to discover all available pages before exploring further.

Hashboard organizes work into boards, columns, and cards — but a card does not have to live on a board at all. Loose cards exist independently at their own URL and can be promoted onto a board at any time. Understanding this model, along with how assignees and labels interact with placement, is the foundation for using Hashboard effectively.

Boards

A board is a named container with an optional description and a visibility setting. The principal who creates a board is its owner; the owner’s household plus any card assignees can access cards on it (subject to the board’s visibility). Boards are listed, filtered, and searched independently from their cards.

Board fields

name, description, visibility, createdBy, archivedAt

Board endpoints

GET /api/v1/boards — list visible boards
GET /api/v1/boards/{id} — composite view: { board, columns, cards }
PATCH /api/v1/boards/{id} — update fields
POST /api/v1/boards/{id}/archive — archive

Columns

Columns are ordered containers within a board. They carry a name and a fractional-index pos value that determines their left-to-right order. Columns can be archived independently — archiving a column does not archive the board or its other columns. The archived state is reversible via POST /api/v1/columns/{id}/unarchive.

Cards

A card is the atomic unit of work. Every card has a title, a visibility level, an optional due date, and a linked description document (see Documents). Cards belong to at most one column, and therefore at most one board.

Card placement

A card’s placement is determined by three nullable columns — board_id, column_id, and pos — which are either all set or all null, enforced by a database CHECK constraint:
-- Guaranteed by schema: all three are null together, or all three are non-null
(board_id IS NULL AND column_id IS NULL AND pos IS NULL)
OR
(board_id IS NOT NULL AND column_id IS NOT NULL AND pos IS NOT NULL)
cards.board_id is deliberately denormalized (because a column already implies a board) to make board-scoped queries join-free. The service layer keeps board_id and column_id in sync atomically when cards move between boards.
Loose cards are cards where all three placement fields are null. A loose card exists at its own URL (/cards/{id}) and appears in every eligible principal’s inbox (GET /api/v1/inbox). Loose cards have the full card feature set — description, labels, assignees, attachments — independent of any board. To place a loose card onto a board, call POST /api/v1/cards/{id}/move with a target columnId and optional prevId/nextId neighbors.

Ordering

Card position within a column uses fractional-index text keys — lexicographic keys in the style popularized by Figma. Because keys sort lexicographically, inserting a card between two existing cards or moving a card to a new position writes exactly one row. Move APIs accept prevId and nextId (the IDs of neighboring cards in the target column) rather than absolute positions, so callers never need to re-number their siblings.
prevId ──► new pos key ◄── nextId
     (computed server-side via posBetween)
Passing both as null appends the card at the end of the column. Passing only prevId (with nextId: null) inserts after that card; passing only nextId (with prevId: null) inserts before it.

Card Descriptions

Every card is linked to a row in the docs table (kind card) via cards.doc_id. The description is a full markdown document — not a plain-text field. It can contain headings, fenced code blocks, tables, and links to other Hashboard resources. Card docs inherit the card’s visibility and never hold their own — see Documents for the editing model.

Assignees

Principals (humans or agents) can be assigned to a card. Assignment does two things:
  1. Task attribution — the card surfaces in the assignee’s workload views.
  2. Sharing — an assignee can read the card regardless of the board’s visibility. This grant is durable: it survives visibility changes on the board or the card.
Because assignment is a sharing mechanism, only the card’s own circle can assign — the creator’s household plus principals already assigned to the card. Assigning outside that circle would be an escalation of privilege.
Agents are first-class principals and can be assigned to cards exactly like humans. An agent acts with its owner’s authorization (resolved through principals.owner_id), but its edits and comments are attributed to the agent’s own identity in activity feeds. This means you can hand off a task to an agent and its work remains legible as its own.

Labels

Labels are a global, instance-wide palette with unique names. Any card — board-placed or loose — can carry any label. Because labels belong to the instance rather than to a board, moving a card between boards never invalidates its labels, and loose cards can be tagged before they are placed.
There is no “board-scoped” label in Hashboard. If your workflow requires per-board tag namespaces, use a naming convention (e.g. frontend/bug) rather than relying on board membership.

Archival

Boards, columns, and cards each carry an independent archived_at timestamp. Archiving operates at whichever level you target:
TargetWhat it affects
CardThat card only — column and board remain live
ColumnThat column only — board and other columns remain live
BoardThe board itself — columns and cards retain their own archive state
Archived cards on a board are returned by GET /api/v1/boards/{id}/archived (returns { columns, cards }). All archive operations are reversible via the corresponding unarchive endpoint.

Quick Reference: Card Endpoints

MethodPathDescription
GET/api/v1/inboxLoose cards visible to you
POST/api/v1/cardsCreate a card (loose or placed)
GET/api/v1/cards/{id}Composite view: { card, doc, assignees, labelIds, linkedDocs, attachments }
PATCH/api/v1/cards/{id}Update card fields
POST/api/v1/cards/{id}/moveMove to a column (or make loose)
POST/api/v1/cards/{id}/archiveArchive
POST/api/v1/cards/{id}/unarchiveUnarchive
POST/api/v1/cards/{id}/detachRemove from board (make loose)
GET/api/v1/boards/{id}/cardsCards on a board in position order
GET/api/v1/boards/{id}/archivedArchived cards on a board

Build docs developers (and LLMs) love