Skip to main content

Saved Views

A saved view is a named bundle of issue filters and sort preferences a user wants to recall later. It backs the “Star this view” / “Open saved view” surface on the issues list — the FE owns the schema of filters_json and sort_json; the server stores the bytes opaquely. Implementation: internal/api/saved_view_handler.go. Backed by the saved_views table.
All endpoints require an authenticated session and workspace context.

Endpoints

MethodEndpointPurpose
GET/api/v1/saved-viewsList own + shared views
POST/api/v1/saved-viewsCreate a view
PATCH/api/v1/saved-views/{viewId}Update a view (owner only)
DELETE/api/v1/saved-views/{viewId}Delete a view (owner only)

Visibility model

Each row is owned by one user but can optionally be shared with the entire workspace via the shared flag. List returns the union:
workspace_id = <current workspace> AND (user_id = <session user> OR shared = 1)
A view owned by another user but shared is returned read-only — Update and Delete are restricted to the owner (user_id must match the session user, otherwise the handler returns 403 Only the view owner can update/delete it).

View shape

FieldTypeNotes
idstring (CUID)View id.
namestringDisplay name (the user types this when starring the view).
filters_jsonstringOpaque JSON blob — the FE-defined filter expression. Stored as-is, returned as-is.
sort_jsonstring | nullOpaque JSON blob describing the sort. null means “use the default”.
view_typestring"list", "board", etc. Defaults to "list" on create when omitted.
is_defaultboolMarks one view per user/view-type as the default that opens automatically. Only mutable via Update — never set on create.
sharedboolWhen true, the view is visible to every workspace member.
created_atRFC3339

Endpoint reference

CRUD over saved views. Reads return own + shared rows; writes are restricted to the owner.

GET /api/v1/saved-views

List the calling user’s views plus every shared view in the workspace. Defaults pinned to the top, then alphabetical by name. Auth: authenticated session + workspace context. Response: 200 OK — JSON array (never null).
[
  {
    "id": "sv_01HVZ...",
    "name": "My open bugs",
    "filters_json": "{\"status\":[\"backlog\",\"in_progress\"],\"label\":[\"bug\"]}",
    "sort_json": "{\"field\":\"priority\",\"dir\":\"desc\"}",
    "view_type": "list",
    "is_default": true,
    "shared": false,
    "created_at": "2026-05-19T18:22:10Z"
  }
]

POST /api/v1/saved-views

Create a new saved view owned by the calling user. Auth: authenticated session + workspace context + OWNER, ADMIN, or MANAGER role (requireRole("create")). Request body:
FieldTypeRequiredDefaultNotes
namestringYesDisplay name. Empty → 400 name is required.
filters_jsonstringYesOpaque filter JSON. Empty → 400 filters_json is required.
sort_jsonstring | nullNonullOpaque sort JSON.
view_typestringNo"list"Empty string is normalised to "list".
sharedboolNofalseWhen true, the view appears for every member of the workspace.
is_default is always false on create — promote a view to default with PATCH. Response: 201 Created with the saved view object (same shape as a List entry).
StatusCondition
400Missing name, missing filters_json, or malformed JSON body.
401Not authenticated.
403Caller is below the MANAGER role.

PATCH /api/v1/saved-views/{viewId}

Partial update. Only the owner of the view can update it — Update reads user_id from the row and compares it against the session user. Auth: authenticated session + workspace context + OWNER, ADMIN, or MANAGER role + ownership of the row. Request body: every field optional; only provided keys are written.
FieldTypeNotes
namestring
filters_jsonstring
sort_jsonstring | null
view_typestring
is_defaultboolPromote / demote default state. The server does not currently demote a previous default automatically — set it explicitly on the displaced view if you want one-default-only semantics.
sharedboolToggle workspace-wide visibility.
Response: 200 OK with the full updated view object.
StatusCondition
400Malformed JSON body or no fields provided (No fields to update).
401Not authenticated.
403Caller is not the view owner.
404View id does not exist.

DELETE /api/v1/saved-views/{viewId}

Hard delete. As with PATCH, only the owner can delete. Auth: authenticated session + ownership of the row. (No role gate beyond ownership — a user can always delete their own view.) Response: 204 No Content.
StatusCondition
401Not authenticated.
403Caller is not the view owner.
404View id does not exist.

See also

  • Issues — the list this view filters.
  • User Preferences — adjacent per-user state (UI density, last-opened tabs); preferences are a flat key-value store, saved views are first-class rows because they’re shared.