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 treats documents as first-class resources, not footnotes to cards. The same docs table backs both the description attached to every card and freestanding markdown documents — specs, notes, runbooks — that exist on their own. Both kinds share the same editing API, revision history, and markdown rendition. Understanding how the two kinds diverge (and where they stay identical) makes the rest of the data model click.

One Table, Two Kinds

The docs table has a kind column with two possible values:
KindLives atVisibilityLifecycle
cardOwned by a card via cards.doc_idInherited from the card (NULL in DB)Created and deleted with the card in one transaction
standalone/docs/{id} as a first-class resourceIts own visibility columnIndependent — outlives any board
The schema enforces the visibility rule as a CHECK constraint, so a card doc can never accidentally carry its own visibility and diverge from the card that owns it:
-- docs_visibility_kind_ck
(kind = 'card'       AND visibility IS NULL)
OR
(kind = 'standalone' AND visibility IS NOT NULL)
Card docs are not addressable as /docs/{id} — you reach a card’s description through the card itself (GET /api/v1/cards/{id} returns doc in the composite payload, or edit it via the card’s docId).

Editing Documents

All doc edits go through PUT /api/v1/docs/{id} with a JSON body:
{
  "baseVersion": 4,
  "content": "# My updated document\n\nNew content here.",
  "title": "Optional new title"
}
title is optional — omitting it leaves the title unchanged. baseVersion is required and is the concurrency anchor described below.

Optimistic Concurrency

Doc saves use optimistic concurrency control rather than locking. The service issues a version-gated update:
UPDATE docs
SET content = ?, version = version + 1, updated_by = ?, updated_at = ?
WHERE id = ? AND version = ?
If zero rows are changed — meaning another writer incremented version since you read the document — the endpoint returns 409 Conflict with the current version in the body:
{
  "error": "conflict",
  "currentVersion": 5
}
The client shows a conflict warning. An agent resolves the conflict by fetching the doc at currentVersion (via GET /api/v1/docs/{id} or the .md rendition), merging its changes, and retrying with baseVersion: 5. This is deliberate: there is no CRDT or operational transform. A versioned 409 makes staleness an explicit, recoverable outcome — a live merge would silently interleave an agent’s stale-context write with no conflict signal.

Revision History

Every save that changes the document content appends a row to doc_revisions — a snapshot of the content at that version, attributed to the author. Revision history is append-only and survives doc updates.
GET /api/v1/docs/{id}/revisions
→ [{ id, docId, version, content, authorId, createdAt }, …]
Revisions are listed newest-first. The revision log is the audit trail; the live docs row is always the current version.

Markdown Rendition and YAML Frontmatter

Every doc URL — and every card URL — serves a markdown rendition when requested with Accept: text/markdown or a .md suffix (see Content Negotiation). The rendition opens with YAML frontmatter:
---
id: "9f4a1c2e-83b0-4e1d-bf5a-d0e1234abc56"
title: "Architecture Notes"
version: 7
visibility: "link-read"
linked_cards:
  - "/cards/a1b2c3d4-0000-0000-0000-000000000001.md"
linked_docs:
  - "/docs/b2c3d4e5-0000-0000-0000-000000000002.md"
---

# Architecture Notes

Content follows here…
The version field is load-bearing. When an agent reads a document through the markdown rendition, it must round-trip the version value back as baseVersion in its PUT /api/v1/docs/{id} request. Without it the 409 conflict check cannot fire, and the agent may overwrite concurrent edits silently. Every tool that fetches a doc rendition should extract and preserve version.Every link in the rendition carries the .md suffix — /cards/{id}.md, /docs/{id}.md — so an agent that follows a link stays in markdown without needing to set an Accept header.
Hashboard tracks which docs and cards reference a given document. GET /api/v1/docs/{id}/backlinks returns the reverse index:
GET /api/v1/docs/{id}/backlinks
→ [{ id, kind, title, … }, …]
Backlinks only surface resources that the requesting principal can see — link-shared items are never listed for outsiders, so a backlink result is safe to display directly. Cards can reference standalone documents through the card_doc_links join table. This is a many-to-many relationship distinct from the one-to-one ownership of a card’s description. A card might link to a spec doc, a runbook, or meeting notes without those docs belonging exclusively to the card.
GET  /api/v1/cards/{id}/links      → linked standalone docs
POST /api/v1/cards/{id}/links      → add a link
DELETE /api/v1/cards/{id}/links/{docId} → remove a link
Card description docs (kind: card) are not valid link targets in card_doc_links — service-enforced. A description belongs to exactly one card; surfacing it as a shared link would create two lists (card attachments vs. doc attachments) that nothing keeps in agreement.

Standalone Doc Lifecycle

Standalone docs are created with POST /api/v1/docs and are listed at GET /api/v1/docs. They carry their own visibility and appear in the filing index when assigned a board_id (a filing facet — not a cascade edge; see Visibility). Deleting a standalone doc removes the row and all its revisions; any card_doc_links pointing to it are cascade-deleted, and the activity log retains the docId in its JSON data so history is not rewritten.
MethodPathDescription
GET/api/v1/docsList standalone docs visible to you
POST/api/v1/docsCreate a standalone doc
GET/api/v1/docs/{id}Get doc (composite: { doc, linkedCards, attachments })
PUT/api/v1/docs/{id}Save content — requires baseVersion
GET/api/v1/docs/{id}/revisionsRevision history, newest-first
GET/api/v1/docs/{id}/backlinksDocs and cards that link here
DELETE/api/v1/docs/{id}Delete a standalone doc

Build docs developers (and LLMs) love