Eviworx
Docs

Saved Views API

A saved view captures how a list is cut: the filter, the search term, the sorting, the column layout, the display mode, the row density and the rows per page. It always belongs to exactly one entity — a ticket view stays a ticket view. Views are either personal or shared with others, can be pinned to the sidebar and show their result count there. They are managed under /api/saved-views.

🔖
Features
✓ Filter, search, sorting and layout in one
✓ Eleven lists, from tickets to cost centers
✓ Sharing with users, groups and roles
✓ Up to 10 pinned views in the sidebar
✓ Result counter with the target list rights
✓ Shipped system views for every role
✓ Duplicate into your own personal copy
✓ Version field against mutual overwrites

Which lists have views

Eleven entities have a column schema and therefore saved views. The result counter of a view only answers if the caller may open the underlying list at all — which is why the table names that permission. Where no permission is listed, the list itself has none: there the row-level visibility alone decides which entries somebody sees.

Entity Permission for list and counter
TICKETtickets.viewAll or tickets.viewOwn
INCIDENTincidents.viewAll or incidents.viewOwn
PROBLEMproblems.viewAll or problems.viewOwn
CHANGEchanges.viewAll, changes.viewOwn or changes.viewPendingApprovals
LICENSElicenses.viewAll or licenses.viewOwn
ELIBRARY_ITEMelibrary.view
COST_CENTERcostCenters.view
ASSETno list permission — asset type grants and assignment decide
CONTRACTno list permission — without visibility the list stays empty
KB_ARTICLEno list permission — article visibility, status and grants decide
USERno list permission — with users.viewAll everyone, otherwise only your own entry

Permissions

Permission Allows Default roles
savedViews.createOwnCreate and duplicate personal viewsEnd user, agent, admin, approver
savedViews.createAgentGroupShare views with agent groupsAgent, admin
savedViews.createOrganizationShare views with roles (organization-wide) and with any active group, even without membershipAdmin
savedViews.deleteSharedEdit and delete shared views of others and see their recipientsAdmin

User account only: All routes under /api/saved-views require a signed-in user. A view is bound to a person — ownership, sharing and pins are meaningless without one. An API key is therefore rejected with 403.

Endpoints

Method Endpoint Description
GET/api/saved-viewsAll visible views as { data: [...] }; optionally narrowed to one entity (?entityType=TICKET). System views first, then the most recently changed.
GET/api/saved-views/:idA single view
POST/api/saved-viewsCreate a view (201)
PATCH/api/saved-views/:idUpdate a view; version is required
DELETE/api/saved-views/:idDelete a view (204, no body)
POST/api/saved-views/:id/duplicateCopy into your own personal view (201); body { name }
POST/api/saved-views/:id/pinPin to your own sidebar (204)
POST/api/saved-views/:id/unpinRemove the pin (204)
POST/api/saved-views/pins/reorderSet the order of your own pins (204); body { order: [id, ...] }
GET/api/saved-views/:id/countResult count of the view as { count }
GET/api/saved-views/share-targets/usersSelectable users as { data, total }
GET/api/saved-views/share-targets/agent-groupsSelectable agent groups as { data, total }

List, detail, create, update and duplicate all answer with the same shape — a client only has to know one response. Pin, unpin, reorder and delete answer with 204 and no body.

Fields of a view

Field Type Description
entityTypeEnumTarget list of the view. Required on create and immutable afterwards — a different entity would be a different view.
nameString (1–255)Display name. Names need not be unique; two views may share a name.
descriptionString? (≤500)Short description
icon, colorString? (≤100 / ≤32)Icon and color for the picker and the sidebar
filterObjektFilter tree of the view (required). Structure see below.
searchString? (≤500)Search term stored together with the view
sortArray? Up to three sort levels: [{ field, direction }] with direction asc or desc
columnsArray?Column layout: [{ key, visible, width?, pinned? }], pinned being left or right
displayModeEnum?table, cards, kanban
densityEnum?compact, normal, spacious
perPageInt?Rows per page from the fixed scale 10, 25, 50 or 100. null means: no preference, the list keeps its own default.
scopeEnumPERSONAL, AGENT_GROUP, ORGANIZATION — default PERSONAL
sharedWithUsers, sharedWithAgentGroups, sharedWithRolesString[]Recipients as IDs. They are always present in the response but only filled for those allowed to see them (see below).
isSystemBooleanShipped view: visible to every role and immutable
isOwner, isShared, isPinned, pinOrderBoolean / Int?Response only and always from the perspective of the caller: did they create it, is it shared with anybody, have they pinned it and at which position.
versionIntIncreases with every change and is required on PATCH — saving on a stale state returns 409 instead of overwriting someone else's work.
columnsVersionIntIncreases only on layout changes. It lets a list detect that the view layout changed after a personal column tweak and point that out — renaming does not trigger that notice.

The filter of a view

The filter is a tree of groups and conditions. A group joins its entries with AND or OR, a condition names field, operator and value. Which fields and operators are allowed is decided by the field catalog of the respective entity — the same basis as the filter queries of the list endpoints.

{
  "combinator": "AND",
  "conditions": [
    { "field": "status", "operator": "in", "value": ["OPEN", "IN_PROGRESS"] },
    { "field": "assignedAgentId", "operator": "isNotNull", "value": null },
    {
      "combinator": "OR",
      "conditions": [
        { "field": "priority", "operator": "eq", "value": "HIGH" },
        { "field": "createdAt", "operator": "relative", "value": "last_7_days" }
      ]
    }
  ]
}

Create a view

POST /api/saved-views
{
  "entityType": "TICKET",
  "name": "Offene P1 des Teams",
  "description": "Alle offenen Tickets mit Priorität P1",
  "icon": "Ticket",
  "color": "#F59E0B",
  "filter": {
    "combinator": "AND",
    "conditions": [
      { "field": "status", "operator": "in", "value": ["OPEN", "IN_PROGRESS"] },
      { "field": "priority", "operator": "eq", "value": "HIGH" }
    ]
  },
  "sort": [{ "field": "createdAt", "direction": "desc" }],
  "perPage": 50,
  "displayMode": "table",
  "density": "normal",
  "scope": "AGENT_GROUP",
  "sharedWithAgentGroups": ["clx-group-servicedesk"]
}

Response (201 Created)

{
  "id": "clx-view-123",
  "entityType": "TICKET",
  "name": "Offene P1 des Teams",
  "description": "Alle offenen Tickets mit Priorität P1",
  "icon": "Ticket",
  "color": "#F59E0B",
  "filter": { "combinator": "AND", "conditions": [] },
  "sort": [{ "field": "createdAt", "direction": "desc" }],
  "perPage": 50,
  "columns": null,
  "displayMode": "table",
  "density": "normal",
  "search": null,
  "scope": "AGENT_GROUP",
  "sharedWithUsers": [],
  "sharedWithAgentGroups": ["clx-group-servicedesk"],
  "sharedWithRoles": [],
  "isSystem": false,
  "isOwner": true,
  "isShared": true,
  "isPinned": false,
  "pinOrder": null,
  "version": 1,
  "columnsVersion": 0
}

Visibility and sharing

The visibility decides which kind of recipients a view may carry — it is not a label, it is enforced. Sharing with individual users is additionally possible in every visibility.

Visibility Recipients Required permission
PERSONALOnly the owner, plus optionally individual users. Group or role recipients are rejected with 400.savedViews.createOwn
AGENT_GROUPMembers of the selected agent groups (at least one). Role recipients are rejected with 400.savedViews.createAgentGroup
ORGANIZATIONAll users of the selected roles (at least one).savedViews.createOrganization

Who sees a view is resolved on every request: its owner, everybody in case of a system view, and whoever a share applies to — as a user, through an active group membership or through their own role. Whoever leaves a group or changes role loses the view with the next request.

Who can be selected as a recipient

Selection and validation use the same sets — what the picker endpoint does not offer is not accepted on save either:

  • Users: the caller view on users (with users.viewAll everybody, otherwise only their own entry), narrowed to active login accounts — no anonymized, archived or email-only contacts — and without the caller themselves.
  • Agent groups: active, non-archived groups; without savedViews.createOrganization only your own active memberships.
  • Roles: active roles from the role option list, which savedViews.createOrganization opens.

Both picker endpoints accept q (search inside the target set), ids (comma separated, resolves existing recipients and takes precedence over q) and limit (default 20, at most 50). Besides data the response carries total, so the interface can show that the list is capped.

The target state is what gets checked: The sharing permissions only apply if visibility or recipients actually change. A pure rename therefore stays possible even when a previously set recipient has become invalid — the next sharing change does require a clean list, though. An invalid recipient with the permission in place is 400; if the permission that widens the selection is missing, the API answers 403 without revealing whether the ID exists.

Who may change or delete a view

One single rule applies: the owner, or — on a SHARED view — whoever holds savedViews.deleteShared. The same permission decides whether the recipient lists are delivered filled; everybody else sees them empty and only learns from isShared THAT the view is shared. A personal view of somebody else is not opened by savedViews.deleteShared. System views are immutable: update and delete answer 403 SAVED_VIEW_SYSTEM_IMMUTABLE — the way forward is to duplicate them.

Duplicating

Every visible view can be copied with savedViews.createOwn. The copy belongs to the caller, is always personal and takes over filter, search term and the complete layout — the shares deliberately stay behind, because a copy is a new view and not a second door into somebody else's permissions. The name of the copy comes in the body ({ name }) so it is formed in the language of the interface; the interface suggests "Name (copy)".

Pinning and result counter

Pinned views appear in the sidebar under "My views", across all entities and in a self-defined order. A pin belongs to the individual user: it is invisible to others and causes no update for them. Pinning and unpinning can be repeated without producing an error.

Next to every row stands the result count of the view. It comes from GET /api/saved-views/:id/count and is delivered as { count }, with Cache-Control: private, no-store — the number depends on the rights of the caller and must therefore not be cached anywhere.

  • The counter requires the same permission as the list behind it. Whoever may not open the target list receives 403 SAVED_VIEW_ENTITY_FORBIDDEN instead of a number — otherwise the counter would be a way to learn about stock that the list withholds. The interface simply omits the number badge in that case.
  • What is counted is what the list shows: the row-level visibility of the caller and the base rules of the list apply — for users, anonymized entries stay out, in the eLibrary archived documents do.
  • The number itself may be up to 30 seconds old; any change to the view discards it immediately. The permissions are always checked before the number — so a withdrawn permission takes effect without delay.

Limits

Limit Value
Rows per page10, 25, 50, 100 (fixed scale; other values are 400)
Sort levels3
Columns per layout60
Column width40 to 1200 px
Conditions per filter group50
Nesting of the filter tree10
Recipients per share list100
Pins per user10
Views per reorder call50

Error Codes

HTTP Error Code Description
400SAVED_VIEW_INVALID_SCOPEVisibility and recipients do not match (e.g. a role share on a personal view or a group view without a group)
400SAVED_VIEW_SHARE_TARGET_INVALIDA recipient is not in the selectable set — made-up ID, blocked account, archived group, inactive role, or the caller themselves. details names the affected list and the IDs.
400SAVED_VIEW_PIN_LIMIT_REACHEDMore than 10 pinned views
400UNKNOWN_COLUMN_KEY, UNPINNABLE_COLUMN, PIN_LIMIT_EXCEEDED, UNSORTABLE_FIELD, SORT_LEVEL_LIMIT_EXCEEDED, UNKNOWN_DISPLAY_MODELayout errors: unknown column, column that cannot be pinned, too many pinned columns, field that cannot be sorted, too many sort levels, unsupported display mode. The same codes apply to the personal list layout.
400Validation error: perPage outside the scale, unknown query parameter, missing name when duplicating, exceeded lengths
403SAVED_VIEW_FORBIDDENThe view is not shared with the caller, the permission for the chosen visibility is missing, or they may not manage it
403SAVED_VIEW_SYSTEM_IMMUTABLESystem views cannot be changed or deleted
403SAVED_VIEW_ENTITY_FORBIDDENThe counter was requested without permission to open the target list
403FORBIDDENNo user context (API key), or the stored filter cannot be applied to the target entity
404SAVED_VIEW_NOT_FOUNDThe view does not exist
409SAVED_VIEW_VERSION_CONFLICTThe view was changed in the meantime; details names the expected and the current version. Reload and reapply the change.

Live updates

Views run over the real-time channels of the object kind "saved view": create and duplicate report created, every change — including to the sharing — reports updated with the changed fields, deletion reports deleted. Picker, sidebar and counters therefore refresh without reloading; a newly shared view appears at the recipient on its own, a withdrawn one disappears. The object channel of an individual view is only joined by those allowed to see it. Pinning, unpinning and reordering send nothing — they only change your own state.

Related pages

  • API Overview — the query syntax of the list endpoints a view builds on
  • Permissions & RBAC — roles, permissions and the row-level visibility that applies to views and counters too
  • Global Search — the cross-entity search that evaluates the same read permissions
  • Real-time & Presence — how the channels that keep views current are built