Memos

Saved Views

Save frequently used filters so you can switch views quickly.

Saved Views keep filter expressions for quick reuse. They are useful when you repeatedly open the same slice of your memos, such as work items, incomplete tasks, or recent public posts.

Upgrading from Shortcuts

Saved Views replace Shortcuts in 0.31. Stored shortcuts migrate automatically. Update bookmarks to /views and integrations to MemoViewService, /api/v1/users/*/views/*, and the MCP tool memo_view_list_memo_views.

Create a saved view

Saved Views are created and managed on the Views page at /views, which you open from the sidebar.

  1. open the Views page and choose Create
  2. give the saved view a clear title
  3. enter a filter expression
  4. choose Validate to dry-run the expression — this checks the filter without saving, so you catch typos before committing
  5. Save the saved view

Title and filter are both required, and an invalid expression is rejected on save.

Saved Views appear in the sidebar. Select one to apply its filter to your memo list; the active filter also syncs to the URL, so you can bookmark or share the resulting view. You can also start a new saved view straight from an active filter — the form opens prefilled with the current expression.

Edit and delete saved views

The Views page lists every saved view with its title and filter. Each row has a menu to:

  • Edit — load the saved view back into the form to change its title or filter
  • Delete — remove a saved view you no longer use, after a confirmation step

Filter expression syntax

Saved Views use CEL (Common Expression Language) filter expressions. A saved view filter must evaluate to true for memos you want included.

Supported fields

FieldTypeExample
contentstringcontent.contains("meeting")
creatorstringcreator == "users/1"
creator_idintegercreator_id == 42
created_tstimestampcreated_ts >= now - duration("24h")
updated_tstimestampupdated_ts >= timestamp("2026-01-01T00:00:00Z")
visibilitystringvisibility == "PUBLIC"
pinnedbooleanpinned
tagsstring list"project/backend" in tags
tagtag-tree aliastag in ["work"]
has_task_listbooleanhas_task_list
has_linkbooleanhas_link
has_codebooleanhas_code
has_incomplete_tasksbooleanhas_incomplete_tasks
has_locationbooleanhas_location
spaceresource name or nullspace == "spaces/team-notes"

Operators and functions

  • comparisons: ==, !=, <, <=, >, >=
  • logic: &&, ||, !, and parentheses ()
  • membership: tag in ["work", "team"], "project/backend" in tags, visibility in ["PUBLIC", "PROTECTED"]
  • string functions: contains(), startsWith(), endsWith(), and matches()
  • collection functions: exists(), all(), exists_one(), and size()
  • set functions: sets.contains(), sets.intersects(), and sets.equivalent()
  • time values: now, duration("24h"), and timestamp(...)

Boolean fields can be used directly without == true:

pinned && has_incomplete_tasks

Tag matching

Use tag in [...] when you want to match one or more tag trees:

tag in ["work"]
tag in ["project", "team"]

tag in ["project"] also matches nested tags such as project/backend.

Use tags.exists() for more specific tag patterns:

tags.exists(t, t.startsWith("archive"))
tags.exists(t, t.endsWith("/bug"))
tags.exists(t, t.contains("todo"))

Other collection predicates and set operations are also available:

tags.all(t, t.startsWith("work/"))
tags.exists_one(t, t.startsWith("project/"))
sets.intersects(tags, ["work", "urgent"])
sets.equivalent(tags, ["inbox"])
size(tags) == 0

tags.all() matches only non-empty tag sets. Use size(tags) == 0 when you specifically want untagged memos.

String matching

Content matching is case-insensitive for contains(), startsWith(), and endsWith():

content.contains("meeting")
content.startsWith("TODO")
content.endsWith("done")

Use matches() for regular expressions:

content.matches("v[0-9]+")

Regular expressions are validated before a saved view is saved. Keep patterns portable: SQLite, PostgreSQL, and MySQL use different regular-expression engines.

Time-based filters

created_ts and updated_ts are CEL timestamps. now is a timestamp variable, not a function. Use durations for relative windows and timestamp() for fixed instants or Unix epoch seconds:

created_ts >= now - duration("1h")
created_ts >= now - duration("168h")
updated_ts >= timestamp("2026-01-01T00:00:00Z")
created_ts >= timestamp(1767225600)

Timestamp accessors support calendar filters:

created_ts.getFullYear() == 2026
created_ts.getMonth() == 0
created_ts.getDayOfWeek() == 0
created_ts.getMonth() == now.getMonth() && created_ts.getDate() == now.getDate()

Months are zero-based (0 is January), and days of the week are zero-based (0 is Sunday). Accessors are evaluated in UTC for SQLite and PostgreSQL; MySQL reads timestamp columns in the database session time zone.

Example saved views

NameFilter
Work TODOstag in ["work"] && has_incomplete_tasks
Public postsvisibility == "PUBLIC"
Posts with linkshas_link
Recent notescreated_ts >= now - duration("168h")
Team updatestag in ["team"] && visibility == "PROTECTED"
Unfinished taskshas_task_list && has_incomplete_tasks
Pinned references`pinned && (has_link
Active projectstags.exists(t, t.startsWith("project")) && !tags.exists(t, t.startsWith("archive"))
Untaggedsize(tags) == 0
With locationhas_location
Unassignedspace == null
Members-only notesvisibility == "SPACE"
One Spacespace == "spaces/team-notes"
Release notescontent.matches("v[0-9]+\\.[0-9]+")

Good saved view patterns

  • incomplete tasks for one project
  • pinned or high-signal memos
  • public notes you review before sharing
  • recent memos from a time window
  • resource collections with links or attachments

Management tips

  • keep names short and obvious
  • delete stale saved views you no longer use
  • update filters when your tag vocabulary changes
  • prefer a few durable saved views over dozens of narrow one-off views

Limits to remember

Saved Views are personal filters, not shared Space settings. They apply within the current browsing scope and never grant access to additional memos. If a Space filter conflicts with the selected Space, it can return no results. Switch to the main Memos context when you want to filter across available Spaces. Use the actual stable Space ID in a spaces/... filter; team-notes above is an example custom ID.

Memos supports a deliberate subset of CEL that can be translated consistently to SQLite, MySQL, and PostgreSQL. Use the Validate action before saving; it reports unsupported fields, type mismatches, invalid regular expressions, and operations that cannot be translated.

On this page