Saved Views API
Eine gespeicherte Ansicht hält einen Listen-Zuschnitt fest: den Filter, den Suchbegriff, die Sortierung, das Spaltenlayout, die Darstellung, die Zeilendichte und die Zeilen pro Seite. Sie gehört immer zu genau einer Entität — eine Ticket-Ansicht bleibt eine Ticket-Ansicht. Ansichten sind entweder persönlich oder mit anderen geteilt, lassen sich in der Seitenleiste anheften und zeigen dort ihren Trefferzähler. Verwaltet werden sie unter /api/saved-views.
Für welche Listen es Ansichten gibt
Elf Entitäten haben ein Spaltenschema und damit gespeicherte Ansichten. Der Trefferzähler einer Ansicht antwortet nur, wenn der Aufrufer die zugehörige Liste überhaupt öffnen darf — deshalb steht in der Tabelle, welches Recht das ist. Wo kein Recht steht, hat die Liste selbst keines: dort entscheidet allein die Zeilensicht, welche Einträge jemand sieht.
| Entität | Recht für Liste und Zähler |
|---|---|
TICKET | tickets.viewAll oder tickets.viewOwn |
INCIDENT | incidents.viewAll oder incidents.viewOwn |
PROBLEM | problems.viewAll oder problems.viewOwn |
CHANGE | changes.viewAll, changes.viewOwn oder changes.viewPendingApprovals |
LICENSE | licenses.viewAll oder licenses.viewOwn |
ELIBRARY_ITEM | elibrary.view |
COST_CENTER | costCenters.view |
ASSET | kein Listenrecht — Asset-Typ-Freigaben und die Zuweisung entscheiden |
CONTRACT | kein Listenrecht — ohne Sicht bleibt die Liste leer |
KB_ARTICLE | kein Listenrecht — Sichtbarkeit, Status und Freigaben des Artikels entscheiden |
USER | kein Listenrecht — mit users.viewAll alle, sonst nur der eigene Eintrag |
Rechte
| Permission | Erlaubt | Standard-Rollen |
|---|---|---|
savedViews.createOwn | Persönliche Ansichten anlegen und duplizieren | Endbenutzer, Agent, Admin, Approver |
savedViews.createAgentGroup | Ansichten mit Agent-Gruppen teilen | Agent, Admin |
savedViews.createOrganization | Ansichten mit Rollen teilen (organisationsweit) und mit jeder aktiven Gruppe, auch ohne Mitgliedschaft | Admin |
savedViews.deleteShared | Geteilte Ansichten anderer bearbeiten, löschen und ihre Empfänger sehen | Admin |
Nur mit Benutzerkonto: Sämtliche Routen unter /api/saved-views setzen einen angemeldeten Benutzer voraus. Eine Ansicht ist an eine Person gebunden — Eigentum, Freigaben und Anheftungen ergeben ohne Person keinen Sinn. Ein API-Key wird deshalb mit 403 abgewiesen.
Endpoints
| Method | Endpoint | Beschreibung |
|---|---|---|
GET | /api/saved-views | Alle sichtbaren Ansichten als { data: [...] }; optional auf eine Entität eingegrenzt (?entityType=TICKET). Systemansichten zuerst, danach die zuletzt geänderten. |
GET | /api/saved-views/:id | Eine Ansicht |
POST | /api/saved-views | Ansicht anlegen (201) |
PATCH | /api/saved-views/:id | Ansicht ändern; version ist Pflicht |
DELETE | /api/saved-views/:id | Ansicht löschen (204, ohne Body) |
POST | /api/saved-views/:id/duplicate | Kopie als eigene persönliche Ansicht (201); Body { name } |
POST | /api/saved-views/:id/pin | In der eigenen Seitenleiste anheften (204) |
POST | /api/saved-views/:id/unpin | Anheftung lösen (204) |
POST | /api/saved-views/pins/reorder | Reihenfolge der eigenen Anheftungen setzen (204); Body { order: [id, ...] } |
GET | /api/saved-views/:id/count | Trefferzahl der Ansicht als { count } |
GET | /api/saved-views/share-targets/users | Auswählbare Benutzer als { data, total } |
GET | /api/saved-views/share-targets/agent-groups | Auswählbare Agent-Gruppen als { data, total } |
Liste, Detail, Anlegen, Ändern und Duplizieren antworten mit derselben Form — ein Client muss nur eine Antwort kennen. Anheften, Lösen, Umsortieren und Löschen antworten mit 204 ohne Body.
Felder einer Ansicht
| Feld | Typ | Beschreibung |
|---|---|---|
entityType | Enum | Zielliste der Ansicht. Pflicht beim Anlegen und danach unveränderlich — eine andere Entität wäre eine andere Ansicht. |
name | String (1–255) | Anzeigename. Namen müssen nicht eindeutig sein; zwei Ansichten dürfen gleich heißen. |
description | String? (≤500) | Kurzbeschreibung |
icon, color | String? (≤100 / ≤32) | Symbol und Farbe für Auswahlmenü und Seitenleiste |
filter | Objekt | Filterbaum der Ansicht (Pflicht). Aufbau siehe unten. |
search | String? (≤500) | Suchbegriff, der mit der Ansicht gespeichert wird |
sort | Array? | Bis zu drei Sortierebenen: [{ field, direction }] mit direction asc oder desc |
columns | Array? | Spaltenlayout: [{ key, visible, width?, pinned? }], pinned als left oder right |
displayMode | Enum? | table, cards, kanban |
density | Enum? | compact, normal, spacious |
perPage | Int? | Zeilen pro Seite aus der festen Skala 10, 25, 50 oder 100. null bedeutet: keine Vorgabe, die Liste bleibt bei ihrem Standard. |
scope | Enum | PERSONAL, AGENT_GROUP, ORGANIZATION — Standard PERSONAL |
sharedWithUsers, sharedWithAgentGroups, sharedWithRoles | String[] | Empfänger als IDs. Sie stehen immer in der Antwort, sind aber nur für Berechtigte gefüllt (siehe unten). |
isSystem | Boolean | Mitgelieferte Ansicht: für jede Rolle sichtbar und unveränderlich |
isOwner, isShared, isPinned, pinOrder | Boolean / Int? | Nur in der Antwort und immer aus Sicht des Aufrufers: hat er sie angelegt, ist sie mit irgendjemandem geteilt, hat er sie angeheftet und an welcher Position. |
version | Int | Zählt bei jeder Änderung hoch und ist bei PATCH Pflicht — wer auf einem veralteten Stand speichert, bekommt 409 statt fremde Arbeit zu überschreiben. |
columnsVersion | Int | Zählt nur bei Layout-Änderungen hoch. Daran erkennt eine Liste, dass das Layout der Ansicht nach der eigenen Spaltenanpassung geändert wurde, und weist darauf hin — ein Umbenennen löst diesen Hinweis nicht aus. |
Der Filter einer Ansicht
Der Filter ist ein Baum aus Gruppen und Bedingungen. Eine Gruppe verknüpft ihre Einträge mit AND oder OR, eine Bedingung nennt Feld, Operator und Wert. Welche Felder und Operatoren erlaubt sind, entscheidet der Feldkatalog der jeweiligen Entität — dieselbe Grundlage wie die Filter-Abfragen der Listen-Endpunkte.
{
"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" }
]
}
]
}
Ansicht anlegen
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
}
Sichtbarkeit und Freigaben
Die Sichtbarkeit bestimmt, welche Art von Empfängern eine Ansicht tragen darf — sie ist keine Beschriftung, sondern wird durchgesetzt. Die Freigabe an einzelne Benutzer ist in jeder Sichtbarkeit zusätzlich möglich.
| Sichtbarkeit | Empfänger | Nötiges Recht |
|---|---|---|
PERSONAL | Nur der Eigentümer, dazu optional einzelne Benutzer. Gruppen- oder Rollen-Empfänger werden mit 400 abgelehnt. | savedViews.createOwn |
AGENT_GROUP | Mitglieder der gewählten Agent-Gruppen (mindestens eine). Rollen-Empfänger werden mit 400 abgelehnt. | savedViews.createAgentGroup |
ORGANIZATION | Alle Benutzer der gewählten Rollen (mindestens eine). | savedViews.createOrganization |
Wer eine Ansicht sieht, ergibt sich bei jeder Anfrage neu: ihr Eigentümer, jeder bei einer Systemansicht, und wen eine Freigabe trifft — als Benutzer, über eine aktive Gruppenmitgliedschaft oder über die eigene Rolle. Wer eine Gruppe verlässt oder die Rolle wechselt, verliert die Ansicht mit der nächsten Anfrage.
Wer als Empfänger auswählbar ist
Auswahl und Prüfung benutzen dieselben Mengen — was der Auswahl-Endpunkt nicht anbietet, nimmt das Speichern auch nicht an:
- Benutzer: die Nutzersicht des Aufrufers (mit users.viewAll alle, sonst nur der eigene Eintrag), eingeschränkt auf aktive Anmeldekonten — keine anonymisierten, archivierten oder reinen E-Mail-Kontakte — und ohne den Aufrufer selbst.
- Agent-Gruppen: aktive, nicht archivierte Gruppen; ohne savedViews.createOrganization nur die eigenen aktiven Mitgliedschaften.
- Rollen: aktive Rollen aus der Rollen-Auswahlliste, die savedViews.createOrganization öffnet.
Die beiden Auswahl-Endpunkte nehmen q (Suche in der Zielmenge), ids (kommagetrennt, löst bestehende Empfänger auf und hat Vorrang vor q) und limit (Standard 20, höchstens 50). Die Antwort trägt neben data auch total, damit die Oberfläche zeigen kann, dass die Liste gedeckelt ist.
Geprüft wird der Zielzustand: Die Freigabe-Rechte greifen nur, wenn sich Sichtbarkeit oder Empfänger tatsächlich ändern. Ein reines Umbenennen bleibt deshalb möglich, auch wenn ein früher gesetzter Empfänger inzwischen ungültig ist — die nächste Freigabe-Änderung verlangt dann aber eine saubere Liste. Ein ungültiger Empfänger bei vorhandenem Recht ist 400; fehlt das Recht, das die Auswahl überhaupt erweitert, antwortet die Schnittstelle 403, ohne zu verraten, ob die ID existiert.
Wer eine Ansicht ändern oder löschen darf
Es gilt eine einzige Regel: der Eigentümer, oder — auf einer GETEILTEN Ansicht — wer savedViews.deleteShared hat. Dasselbe Recht entscheidet auch, ob die Empfängerlisten gefüllt ausgeliefert werden; alle anderen sehen sie leer und erfahren über isShared nur, DASS die Ansicht geteilt ist. Eine fremde persönliche Ansicht öffnet savedViews.deleteShared nicht. Systemansichten sind unveränderlich: Ändern und Löschen antworten 403 SAVED_VIEW_SYSTEM_IMMUTABLE — der Weg dorthin führt über Duplizieren.
Duplizieren
Jede sichtbare Ansicht lässt sich mit savedViews.createOwn kopieren. Die Kopie gehört dem Aufrufer, ist immer persönlich und übernimmt Filter, Suchbegriff und das vollständige Layout — die Freigaben bleiben bewusst zurück, denn eine Kopie ist eine neue Ansicht und kein zweiter Zugang zu fremden Berechtigungen. Der Name der Kopie kommt im Body mit ({ name }), damit er in der Sprache der Oberfläche entsteht; die Oberfläche schlägt „Name (Kopie)" vor.
Anheften und Trefferzähler
Angeheftete Ansichten stehen in der Seitenleiste unter „Meine Ansichten", quer über alle Entitäten und in selbst gesetzter Reihenfolge. Eine Anheftung gehört zum jeweiligen Benutzer: sie ist für andere unsichtbar und erzeugt bei ihnen keine Aktualisierung. Anheften und Lösen sind wiederholbar, ohne dass ein Fehler entsteht.
Neben jeder Zeile steht die Trefferzahl der Ansicht. Sie kommt aus GET /api/saved-views/:id/count und wird als { count } geliefert, mit Cache-Control: private, no-store — die Zahl hängt an den Rechten des Aufrufers und darf deshalb nirgends zwischengespeichert werden.
- Der Zähler verlangt dasselbe Recht wie die Liste dahinter. Wer die Zielliste nicht öffnen darf, bekommt 403 SAVED_VIEW_ENTITY_FORBIDDEN statt einer Zahl — sonst wäre der Zähler ein Weg, Bestände zu erfahren, die die Liste verschweigt. Die Oberfläche lässt das Zahlenfeld in diesem Fall einfach weg.
- Gezählt wird, was die Liste zeigt: die Zeilensicht des Aufrufers und die Grundregeln der Liste gelten mit — bei Benutzern bleiben anonymisierte Einträge außen vor, in der eLibrary archivierte Dokumente.
- Die Zahl selbst darf bis zu 30 Sekunden alt sein; jede Änderung an der Ansicht verwirft sie sofort. Die Rechte werden immer vor der Zahl geprüft — ein Rechteentzug wirkt also ohne Verzögerung.
Grenzen
| Grenze | Wert |
|---|---|
| Zeilen pro Seite | 10, 25, 50, 100 (feste Skala; andere Werte sind 400) |
| Sortierebenen | 3 |
| Spalten je Layout | 60 |
| Spaltenbreite | 40 bis 1200 px |
| Bedingungen je Filtergruppe | 50 |
| Verschachtelung des Filterbaums | 10 |
| Empfänger je Freigabe-Liste | 100 |
| Anheftungen je Benutzer | 10 |
| Ansichten je Umsortierung | 50 |
Fehlercodes
| HTTP | Error Code | Beschreibung |
|---|---|---|
| 400 | SAVED_VIEW_INVALID_SCOPE | Sichtbarkeit und Empfänger passen nicht zusammen (z. B. Rollen-Freigabe an einer persönlichen Ansicht oder eine Gruppen-Ansicht ohne Gruppe) |
| 400 | SAVED_VIEW_SHARE_TARGET_INVALID | Ein Empfänger liegt nicht in der Auswahlmenge — erfundene ID, gesperrtes Konto, archivierte Gruppe, inaktive Rolle oder der Aufrufer selbst. details nennt die betroffene Liste und die IDs. |
| 400 | SAVED_VIEW_PIN_LIMIT_REACHED | Mehr als 10 angeheftete Ansichten |
| 400 | UNKNOWN_COLUMN_KEY, UNPINNABLE_COLUMN, PIN_LIMIT_EXCEEDED, UNSORTABLE_FIELD, SORT_LEVEL_LIMIT_EXCEEDED, UNKNOWN_DISPLAY_MODE | Layout-Fehler: unbekannte Spalte, nicht fixierbare Spalte, zu viele fixierte Spalten, nicht sortierbares Feld, zu viele Sortierebenen, nicht unterstützte Darstellung. Dieselben Codes gelten für das persönliche Listenlayout. |
| 400 | — | Validierungsfehler: perPage außerhalb der Skala, unbekannter Query-Parameter, fehlender Name beim Duplizieren, überschrittene Längen |
| 403 | SAVED_VIEW_FORBIDDEN | Die Ansicht ist nicht für den Aufrufer freigegeben, das Recht für die gewählte Sichtbarkeit fehlt, oder er darf sie nicht verwalten |
| 403 | SAVED_VIEW_SYSTEM_IMMUTABLE | Systemansichten lassen sich nicht ändern oder löschen |
| 403 | SAVED_VIEW_ENTITY_FORBIDDEN | Der Zähler wurde abgefragt, ohne die Zielliste öffnen zu dürfen |
| 403 | FORBIDDEN | Kein Benutzerkontext (API-Key), oder der gespeicherte Filter lässt sich nicht auf die Zielentität anwenden |
| 404 | SAVED_VIEW_NOT_FOUND | Die Ansicht existiert nicht |
| 409 | SAVED_VIEW_VERSION_CONFLICT | Die Ansicht wurde zwischenzeitlich geändert; details nennt die erwartete und die aktuelle Version. Neu laden und die Änderung erneut anwenden. |
Live-Aktualisierung
Ansichten laufen über die Echtzeit-Kanäle der Objektart „Gespeicherte Ansicht": Anlegen und Duplizieren melden created, jede Änderung — auch an den Freigaben — updated mit den geänderten Feldern, Löschen meldet deleted. Auswahlmenü, Seitenleiste und Zähler aktualisieren sich dadurch ohne Nachladen; eine neu geteilte Ansicht erscheint beim Empfänger von selbst, eine entzogene verschwindet. Den Objekt-Kanal einer einzelnen Ansicht betritt nur, wer sie auch sehen darf. Anheften, Lösen und Umsortieren senden nichts — sie ändern nur den eigenen Zustand.
Verwandte Seiten
- API-Übersicht — die Abfragesyntax der Listen-Endpunkte, auf denen eine Ansicht aufsetzt
- Permissions & RBAC — Rollen, Rechte und die Zeilensicht, die auch für Ansichten und Zähler gilt
- Globale Suche — die entitätsübergreifende Suche, die dieselben Leserechte auswertet
- Echtzeit & Presence — wie die Kanäle aufgebaut sind, über die Ansichten aktuell bleiben