Skip to Content

Custodian Portal Search

Extends Custodian Portal UX with a global search in the app bar and saved searches a team can share.

Two features, one vocabulary

Global search (app bar)List search (on a page)
JobJump to a record from anywhereFilter the list on People, Credentials, or Intake
InputFree textFree text + that list’s filters
ResultA grouped popover of recordsThe page’s own table
Saved?Offers your saved and recent searchesYes — this is what gets saved

They meet at one action: pressing Enter on free text in the app bar opens the top list page for the query, and the page shows it as the active search. A search is always the same object — { text, filters } — whether typed in the app bar, read from a URL, or loaded from a saved search.

Documents and groups are jump-to only: neither has a list page to search, so there’s no saved search for them.

The search object

{ "v": 1, "text": "smith", "filters": { "status": "NOT_INVITED" } }

Filter keys are an allow-list per scope, enforced by the server (an unknown key/value is 400 INVALID_SEARCH):

ScopePageText matchesFilters
people/peoplename, email, reference ID, an active group’s namestatus, documents, group (an id filter)
credentials/credentialscredential type, holder name/email, internal code, id prefixstatus
batches/ (Intake)batch id prefix, date, a document’s filename—

An id filter (e.g. People’s group) accepts any UUID and resolves it inside the caller’s own custodian — a foreign or unknown id is a 404, never data. Raw extracted document content is never searched — it’s unvetted personal data and would bypass the audited per-person detail view.

Global search results

GET /api/v1/custodian/me/search?q= groups People, Groups, Documents, Credentials, Batches — at most 5 per group, 25 total, minimum 2 characters, every group scoped to the caller’s own custodian_id. Opening a result goes through the existing audited detail endpoints — search adds no new way to read a person’s records.

Saved searches

ActionViewerOperatorAdmin
Create a private searchyesyesyes
Share with the team—yesyes
Edit/rename/unshare/delete your ownyesyesyes
Edit/delete someone else’s shared search——yes
Use a shared search, or make it your defaultyesyesyes

A TEAM search is visible to every active team member of the same custodian, never to another custodian. If an owner leaves the team, their shared searches stay (with their name kept as written); their private searches and defaults are left in place, unreachable.

One default per user per scope. It applies only when the page is opened with no search in the URL — a link, an app-bar result, or “Show all” always wins — and it’s always visible as an “Applying your default” chip with a way to show all without deleting the default.

Recent and most-used

A search is recorded when committed (Enter in the app bar, or a filter/text change on a list page that survives a short pause) — never per keystroke. History is deduplicated per user and scope, pruned to the 50 most recently used. The empty search box shows, in order: Saved (default first, then most-used, then name), Most used, Recent.

The batch review dialog and credential detail are URL state, not component state (/batches/:id?item=:itemId, /credentials?tab=register&credential=:id) — so a document or credential is linkable and Back closes it.

Interface

  • App bar: brand and sections left, search centered, account and theme right.
  • Keys: ⌘K/Ctrl+K and / (no field focused) open it; arrows move; Enter opens; Esc closes and returns focus.
  • List pages carry no search box in the page body — the Filter (or Saved searches) menu sits in the header beside the primary action; a chip row under the header shows the active text/filters, the loaded saved-search pill, and Save/Update/Clear.