# Audits (/docs/administration/audit-trail) Every workspace keeps a running record of who changed what and when. Whenever anyone creates an app, edits data, runs a pipeline, publishes a version or invites a teammate, an entry is added automatically. You read it through the **Audit Analytics** app: a ready-made surface over your workspace's audit trail, with pages of tables and charts. The app is **admin-only** and **system-managed**: only admins see it, its configuration is locked, and it cannot be edited. It is also **permanent** - an admin can open it but cannot archive, restore or delete it, so audit history can never be hidden or removed. It is always live, reading directly from the trail: there is no run to trigger and nothing to refresh, and numbers are current to within a minute or two. It shows the activity of the apps **you can open**. Another member's personal space stays private, an admin included: the entries of an app in it - and of an app that has since been permanently deleted - do not appear in any table, chart or total. Entries that belong to no app, such as a member being invited, always appear. ## Opening it [#opening-it] * **From the apps list.** Admins see Audit Analytics alongside the other apps. * **From a "View audits" button.** A small clock button sits on a dataset row, a pipeline node, a version in the history, a knowledge-base item, a member, an indicator, a Visualisation configuration, a reporting period, and a single indicator value cell. It opens the app **in a new browser tab**, pre-filtered to that resource, so you keep the screen you were on. Two of those deep links matter most in an indicator app. On a **cell** in the data grid, the button opens **Indicator Data Changes** filtered to that one cell - that indicator, that breakdown, that period - so you see every value it has held and who set each one. On a **reporting period**, it shows everything that happened to the period itself: created, renamed, submitted for review, approved, reopened. ## What each page shows [#what-each-page-shows] * **Overview** - activity across the workspace: how many actions over time, who did them, how much was driven by AI. It also carries a **Deletions and bulk operations** table and a **Retained originals** table, which lists every original file that was kept (a dataset upload, or a document you gave Vera) with its name, app, node, who or what uploaded it and when, and a one-click download of the file exactly as it came in. * **All actions** - everything that happened, newest first, one row per event: when, the action, the resource, and who or what did it. Every column filters, so this is where you narrow to a time window, an actor kind, one actor, one app or one kind of action. * **Analytics Data Changes** - every change to an analytic app's data. An inline edit shows one row per changed cell (the value before and after); a deleted row shows one row for the whole deletion, with no column and no before/after; an upload or a replace shows one row for the whole write (see [Uploads and replaces](#uploads-and-replaces)). * **Indicator Data Changes** - every change to an indicator value, one row per changed cell: the indicator, its dimension and reporting period, the value before and after, and who changed it. ## Reading a change [#reading-a-change] Each row answers the same questions: * **Row** - which record changed (on a data-change page, the stable id of the row or cell). A data-change page also carries **Created**, **Last updated** and the **Row ID**, so you can sort or filter by any column to follow one record's full history. * **When** - the time the change happened. * **Actor** - who or what made it. Usually a person; for an AI-made change the person who instructed the AI (see [AI accountability](#ai-accountability)); for a change written by a connected system, that **integration**. * **Actor kind** - **Person**, **Vera**, **External AI**, **Integration**, **Support** or **System**. Filter on it to see, for example, everything last night's connected system did. * **AI** - set when the change was made by AI rather than directly by a person. An integration is not AI, so a machine write does not set it. Most rows include a **Resource link**, which opens the changed resource in its live editor, selected and ready to inspect. A link is **inactive** when the resource was deleted after the change was recorded: the history stays, but there is nothing live to open. ## Uploads and replaces [#uploads-and-replaces] Most data arrives as a file upload or a replace that writes thousands or millions of rows at once. Recording one entry per row would swamp the trail, so a bulk write is **one entry for the whole write**, on the **Analytics Data Changes** page beside the inline edits: * **Row** shows the block of rows the write created, for example `1-51200`. * **Operation** reads `replace_rows` or `append_rows`. This is the one to check when following a single row: a **replace** threw away everything the node held and numbered the new rows from 1 again, so row 4 after a replace is a different row from row 4 before it. An **append** left the earlier rows as they were. * **Rows written** is how many rows the write actually added. * **Changed by** and **When** are the person and time, exactly as for an edit. The block is a **range, not a list**: when an upload skips duplicate rows, some ids inside the block are never given to any row, so **Rows written** can be smaller than the block looks. For the same reason a bulk entry's **Resource link** opens the node's data unfiltered. This is what makes the per-row **View audits** button useful for data that was never edited by hand: on a row that arrived by upload it finds the upload that created it, alongside any later edits, in one list. ## Downloading a table [#downloading-a-table] Every table in the app has an **Export** button, so you can hand the evidence to an auditor or a colleague as a file. Choose **CSV** or **Excel**. The file holds the rows the table shows. Every filter on the table applies, including the ones a **View audits** link set when it opened the page already narrowed to one resource - open **Analytics Data Changes** from a node's link, export, and the file holds that node's changes rather than the whole workspace's. The page's parameters and the search apply too. The file carries every column of the table, not only the ones on screen, and its rows come in the table's own default order (newest first on the pages that list changes), however you have sorted the table on screen. The whole history of a workspace can be more than a spreadsheet holds, about a million rows. Excel is refused for it with a message saying so; download it as **CSV** instead, or filter the table first. Every download is recorded, the audit trail's own included: an admin sees who downloaded what, when and how many rows under **Data downloads** on the [System page](./index.mdx#usage). ## AI accountability [#ai-accountability] When **Vera** or a connected AI makes a change on someone's behalf, the trail shows **both the AI and the person who instructed it**: the **AI** column marks the change as AI-made, the **Actor** is the person who asked for it, and the AI model that made it is recorded too. So even when the AI did the work, it is always clear who is accountable. See [AI transparency](../infrastructure/ai-transparency.mdx). ## Changes made by a connected system [#changes-made-by-a-connected-system] A connected system - an ERP, a warehouse, a nightly job - writes through the Automation API under a credential an administrator issued. Those changes are recorded like any other, with two differences: * The **Actor** is the **integration itself**, by the name it was given when the credential was created, and the **Actor kind** reads **Integration**. Nobody was at a keyboard, so no person is shown as having done it. The name survives the credential being revoked or deleted, so history stays readable. * Who **issued** the credential is a question about the credential rather than about the change, and it is answered on the Access page's **API credentials** tab. Any credential event in the trail - created, secret rotated, scopes changed, revoked - links straight there, attributed to the administrator who performed it. For what a connected system is and how an admin issues its credential, see [Credentials](../integrations/api/credentials.mdx). ## How history is kept [#how-history-is-kept] Audit history is captured on **every change** and kept in the Audit Analytics app with **no time horizon**: older history stays readable however old it gets. # Connected systems (/docs/administration/connected-systems) A **connected system** is another piece of software that works with your workspace on its own: an ERP, a data warehouse, a nightly job. It signs in with a credential of its own rather than with somebody's account, pushes data into a dataset, starts a run, and collects the results on its own schedule. Nobody is at a keyboard and no browser is involved - that is the point of it. The work happens at three in the morning whether or not anyone is around. ## What a connected system can do [#what-a-connected-system-can-do] A connected system works through the **Automation API**, and what it may do there is decided entirely by the access you grant its credential: * **Read the workspace** - list the apps, the datasets it can write into, and the outputs it can read. * **Write data** - import rows into a dataset of an analytic app, as a file it uploads and the workspace ingests. * **Run a pipeline** - start a run and poll it until it finishes. * **Read results** - generate an export of an output and download the file. That is the whole surface. Whatever access you tick, a connected system cannot create or delete apps, change a pipeline, edit a Visualisation, invite people or change workspace settings. Those stay with the people in the workspace. ## Where the credentials live [#where-the-credentials-live] Open the **Access** page and its **API credentials** tab. A credential is a principal of the workspace like a member, so it is managed beside the members: who and what may work here is one page. The tab is for people allowed to manage the workspace's API credentials, which today means admins: a member is not offered it, and every request behind it is refused without that permission. Each credential is one row of the tab's table: its name, its key, its access options, when it was last used, and when it expires or must next be attested. The gear on a row opens the credential's details, and the row's menu holds **Attest**, **Edit access**, **Rotate secret** and **Revoke**. Copy the key with the copy button beside it. The details have three tabs: **Details**, **Access** and **Activity**. **Access** lists every permission its access options give it - reading app configuration, reading data, importing data files, running pipelines, downloading data, publishing - and which option gives each one, the same way a member's access is shown. A credential holds no role: it works in every shared space and every app in one, including apps created later, and never in anyone's personal space. A revoked credential can do nothing. A credential is a **workspace asset**, not a personal one - it keeps working after whoever created it leaves. The list still names the person who issued it, and marks the credential **Former member** when that person is no longer in the workspace, so you have a prompt to review whether the integration is still wanted. [Take the tour of API credentials](https://app.ovidens.com/access?tab=credentials\&tour=integrations) to be shown a credential, what rotating and revoking each cost, and the form that issues a new one. The tour never creates a credential - it stops at the button and waits for you. ## Issuing one [#issuing-one] **New credential** asks for a name, an optional description, and the access to grant. Name it after the system it is for ("ERP nightly import" rather than "test"), because that name is what the workspace's audit trail shows every time the integration writes something. Creating it produces two strings: * a **Client ID**, which identifies the credential and is safe to show; and * a **Client secret**, which is what proves the system is that credential. **The secret is shown once.** It is displayed in the dialog that appears right after you create the credential, and nothing can show it again: it is not stored in a form anyone, including an admin, can read back. If it is lost, rotate the credential to issue a new one. The dialog copies each one, or both at once as the environment variables `OVIDENS_CLIENT_ID` and `OVIDENS_CLIENT_SECRET`, and **I've stored it** closes it and discards the secret from the page. Hand both to whoever builds the integration. Everything after that - how the system signs in with them, which endpoints it calls, what the file has to look like - is the developer's side of the work, and it is described by the API's own published contract rather than by this guide. ## What the access options mean [#what-the-access-options-mean] Each option grants one kind of work and nothing else. The picker also prints the technical name each one carries in the API next to its label, which is what the developer building the integration will ask you about: | Access | What it lets the system do | | --------------------------- | ----------------------------------------------------------- | | **Read the workspace** | List apps, nodes and their configuration. | | **Write data** | Import and change the rows of a dataset. | | **Write indicator values** | Enter and clear collected indicator values. | | **Run published pipelines** | Run an app's published configuration. | | **Run drafts** | Run unpublished configuration that nobody has reviewed yet. | | **Read results** | Query and export the results of a run. | | **Publish** | Change what every person in this workspace sees. | Two badges in the picker tell you how to read that list: * **Elevated** marks an option that reaches beyond reading and running what the workspace has **published** - running a draft nobody has reviewed, for instance, or publishing, which changes what everyone in the workspace sees. An elevated option is an explicit choice you make for one integration, not something a new credential arrives with. Grant one only when the system genuinely needs it. * **Not in effect yet** marks an option that nothing checks today. It is part of the vocabulary, so it can be granted, stored and shown, but granting it changes nothing an administrator could observe. The badge is the honest answer to "what does this actually do right now", and it disappears for an option as soon as that option starts taking effect. **Edit access** changes the grant of an existing credential. Nothing has to be re-issued: the change applies from the credential's very next request, and taking access away binds even on a token the system is already holding. ## Rotating the secret [#rotating-the-secret] **Rotate secret** issues a second secret without breaking anything. The secret in use today keeps working alongside the new one, so you can roll the new one out to the system at your own pace instead of during an outage. A credential never carries more than two secrets, so the **next** rotation is what retires the older one - rotating twice is the deliberate "stop accepting the old secret", and the dialog says so before you confirm it. Rotate when a secret may have been exposed, when the person who set the integration up leaves, or on whatever schedule your own policy sets. The credential's details show when the current secret was created and when the previous one was last used, so you can watch the old secret go quiet before you retire it. ## Telling whether it is working [#telling-whether-it-is-working] Two places answer that, and they answer different questions: * **The credentials table** says whether the credential is being used at all. **Last used** is the last time it authenticated, and reads "Never used" until the integration's first successful call - which makes it the first thing to check when a new integration is silent. * **The audit trail** says what it actually did. Everything a connected system does is recorded like any other change: open the **Audit Analytics** app, go to **All actions**, and filter **Actor kind** to **Integration**. The **Actor** of a machine write is the integration itself, under the name its credential was given - not the administrator who issued that credential, because they authorised the machine, they did not perform the action. For how to read the trail, see [Audit trail](./audit-trail.mdx). Credential events themselves - created, secret rotated, access changed, revoked - are in the same trail, attributed to the administrator who performed them, because there a person really did act. ## Revoking [#revoking] **Revoke** stops a credential immediately, including any token it is already holding: its next request is refused. It cannot be undone, so restoring access means issuing a new credential and giving the system its new secret. A revoked credential stays in the list, marked **Revoked** and with nothing left to change but its key still copyable, and it keeps its name in the audit trail - so the record of what it did while it was live stays readable. # Administration (/docs/administration) Administration is where you manage your **workspace**: who is in it, a record of what happened, and how much of your plan you are using. Most of it is the job of an **admin**; everyone in the workspace can work on the apps. ## Your workspace [#your-workspace] A **workspace** is the shared home for everything your team does in Ovidens: its apps, data, and members. Anyone you invite can see and work on the same apps, so a workspace usually maps to a team or a company. The first time you sign in you set your workspace up: Ovidens greets you with "Name your workspace to finish setup and open it.", asks for an **Organization name** (the name everyone in your organization will see), and optionally lets you **Add billing details** - a postal address for your invoices, which you can skip and add later. **Create workspace** finishes setup and takes you straight in. If you belong to more than one workspace, switch between them from the workspace switcher at the top of the left navigation; each keeps its apps, data, and members completely separate. When you return to [app.ovidens.com](https://app.ovidens.com) while signed in, Ovidens opens your apps automatically. If you belong to several workspaces, it reopens your remembered workspace when you still belong to it; otherwise it asks you to choose one. If your session has ended, sign in again to continue. Any unfinished workspace setup comes first. The workspace name is not permanent. An admin can change it, and pick the icon and colour shown next to it in the sidebar, on the **General** tab of the **System** page under **Identity**. ## Members and roles [#members-and-roles] The **Access** page lists everyone in the workspace on its **Members** tab. Each member has a **role**: * A **Member** can work with the apps their roles open (see **Access** and **Default access for new members** below). * An **Admin** can do everything a member can, plus manage who is in the workspace. Everyone sees the member list. **Invite user**, the **Pending invitations** list and **Remove** are for whoever may manage members: an admin, or a member who has been given **member management**. Deciding what someone may do - the **Workspace role** column, **Manage access**, and inviting someone as an admin - stays with admins. A member without either sees the list and nothing else. The list is one table you can search by name or email and filter by workspace role. Beside each person it shows their **Workspace role** and, for admins, the roles they hold **On spaces**. For an admin, the person's name and the gear on their row (**Manage access**) open a dialog with their **Access** and **What they can do**. The row's menu offers **AI permissions**, **Activity log** and **Remove from workspace**, each only to whoever may use it; when you may use only one of them, it is a button on the row instead. Copy an email with the copy button beside it. To invite a teammate, open **Access**, select **Invite user**, enter their email, pick their role, and send the invitation. If you manage members without being an admin, the dialog has no role choice: everyone you invite joins as a member. They appear under **Pending invitations** until they accept, then move into **Members**. From that list you can **Copy invite link** to send the invitation yourself, or **Cancel invitation** for one that has not been accepted. A new member starts with no access: they have their own personal space and nothing else until somebody gives them a role, unless your workspace sets **Default access for new members** (below). The invite form says so as well. Someone invited as an admin is an admin from the start. An admin changes someone's **Workspace role** in their row: the change saves at once, with **Undo** in the message that confirms it. Making someone an admin, or taking an admin's rights away, asks you to confirm first. Someone whose admin rights are taken away stays in the workspace as a member and keeps their own work. Any invitation they sent to join as an admin that nobody has accepted yet is cancelled with it, and an admin invitation can only be accepted while the person who sent it is still an admin. An admin cannot be removed from the workspace until their admin rights have been taken away. **Remove from workspace** always asks you to confirm. ### Removing a member who has their own apps [#removing-a-member-who-has-their-own-apps] Every member has a personal space that only they can see. In it they build, publish, run and archive their own apps, write and run their own SQL queries against them, protect and delete their runs, and delete trashed knowledge-base pages for good. A query there reads only the app it is run in, never another app. Nobody else can open a personal space, an admin included. When you remove someone whose personal space holds apps, the confirmation tells you **how many** there are - never which ones, since nobody but their owner sees them - and asks what should happen to them before **Remove** is available: * **Move to** a team space moves every one of their apps there, where the people with access to that space can work with them. Only the team spaces you can create apps in are offered. * **Archive them** archives every app where it is. Nobody sees them, they stop counting against your app limit, and they are deleted for good once the archive retention period has passed. If the person rejoins before then, they find the apps in their own archive and can restore them. If their personal space is empty, the confirmation asks nothing more. Removing a member also disconnects every AI tool they had connected to this workspace; if they rejoin, they connect it again. ### Access: a role on the whole workspace, one space or one app [#access-a-role-on-the-whole-workspace-one-space-or-one-app] **Manage access** on a member's row (admins only) opens their **Access**: every role that person holds and where it applies - the whole workspace, a space, or a single app - one row each, with the role changeable in place. It is where you give somebody a narrower role than admin, or a role on less than the whole workspace: for example **App builder** on one team's space so they can configure and publish that team's apps and no others, or **App builder** on a single app so they can change that one app. To give a role, pick the space or app (or the workspace) under **Add access**, then the role. The choices offered are only the ones you are allowed to give there: personal spaces and the Audit Analytics app are never offered, and neither are archived apps. A role that would add nothing - because the person already holds as much there through another role - is not offered, and a row another role already covers is dimmed and says which one. The roles are: * **Admin** - everything, including deciding what everyone else may do. * **App builder** - create, configure, publish and archive apps (with Vera too), and work with their data. * **Analyst** - read an app's configuration and data, run it, and download its data. * **Contributor** - read an app's configuration and data, enter data (by hand or by importing a file), and download it. What a new member holds when they join is the workspace's default access for new members (below). * **Viewer** - read an analytic app's published dashboard, its knowledge base and its data, without its configuration, drafts or history, and without downloading the data or running AI on it. * **Member manager** - invite and remove members (on the whole workspace only). * **Reporting manager** - run an indicator app's collection: its reporting timeline, collection rounds and the people asked, the review of answers, and submitting, approving and reopening reporting periods (with Vera too). It reads the apps it covers and their configuration and data, and changes neither. Give it on one space or one app, for example to a sustainability lead who runs the reporting there and has no other administration. It does not enter figures on its own: somebody who also enters figures holds **Contributor** beside it. An **App builder** does not run collection, so the person who designs an app is not the one who approves what was collected in it, unless they are given this role too. To take a role away, select **Remove** beside it. Every change saves at once, with **Undo** in the message that confirms it. If a change is refused - for example because it would remove the last admin - the message says why and nothing changes. Your own access is shown but cannot be changed here. **What they can do**, the dialog's second view, shows what the person may actually do and why: for the whole workspace, for each space and each app they hold a role on, and for their own personal space, every permission they have there, each with the role that gives it ("App builder on Finance"). A space or an app lists what their roles on the whole workspace allow as well, so each place reads complete on its own. Their personal space shows only that they can always work in it - never what is in it. ### My access [#my-access] Every member can see what they themselves may do on the **Access** page's **My access** tab. It is the same view as **What they can do** in an admin's dialog, about you: for the whole workspace, for each space and app you hold a role on, and for your own personal space, every permission you have there and the role that gives it. Your personal space is listed as yours. To be given more, ask an admin: they give roles through **Manage access** on your row. A role on a space or an app decides what the person may DO there: configure, publish, enter data, download, and what Vera and connected AI may do on their behalf are decided app by app. It also decides which apps the person can see at all. In every app list and in search, each person finds only the apps a role of theirs covers - a role on the whole workspace covers every app, a role on a space covers the apps in it - and an app no role of theirs covers behaves for them as if it did not exist, for Vera and connected AI acting on their behalf too. A **Member manager** role on its own opens no app. What a person may read of an analytic app they can open depends on their role. A role that reads the app's configuration - **Contributor**, **Analyst**, **App builder** and **Admin** - opens the editor (read-only where they may not configure) and reads the draft, every node's configuration and SQL, the version history and the skills. A **Viewer** reads the app as it was last **published**: its dashboard pages, with each SQL widget showing its rows but never the query behind it, its knowledge base and the data behind them. A Viewer does not see that a draft is being worked on, is offered no **Open editor**, and opening the editor's link takes them to the dashboard instead. An app that has never been published shows a Viewer its empty state. An **indicator app** is read the same way. A Viewer reads its indicators, groups, dimensions and checks as they were last **published** - each indicator in full, with its guidance, unit, framework mapping and rules, and a calculated indicator with its calculation - and its collected figures exactly as everyone else does, together with its reporting periods and the collection rounds they are asked in. The reporting units in those rounds, in their own tasks and in the app's **Round progress** table and its companion tables carry the names that were last published, and a unit added only in the draft does not appear to them at all. A Viewer does not see the draft being worked on, the version history, Vera's work or the editor's checking tools, is offered no **Open editor**, and opening the editor's link takes them to the app instead. An indicator app that has never been published tells a Viewer that it has not been published yet. A **survey app** is read the same way. A Viewer reads the form as it was last **published** - every question exactly as respondents were asked it - and every response that has come in, together with the files people attached. The survey's **Information** page, its counts and its collection rules read the same for everyone. A Viewer does not see questions being drafted for the next round, the version history, the share link or the form editor's checking tools, is offered no **Open editor**, and opening the editor's link takes them to the survey instead. Downloading the responses as CSV is not part of the Viewer role. A survey that has never been published tells a Viewer that its responses appear once someone who manages the survey publishes it. If someone follows a link to an app they cannot open, they see **App not found** with the line "This app does not exist, or you do not have access to it." and the names of the admins who can give them access - up to three, then "and N more", or the person who shared the link when there is no other admin. The page never says which of the two it is, so it does not reveal whether the app exists. To let them in, give them a role on the app, on its space or on the whole workspace. The workspace always keeps at least one admin, so removing the last admin's rights is refused before anything changes: make someone else an admin first. Your own workspace role and **Remove from workspace** are not available on your own row - another admin changes your role. In a member's menu, **AI permissions** sets the most that person's AI may do in this workspace (see [MCP](../integrations/mcp.mdx)), and **Activity log** opens that member's history in the Audit Analytics app. ### Roles and spaces [#roles-and-spaces] **Roles**, on the **Access** page, is for everyone: it lists every privilege, grouped by area, with a column per role marking whether it allows it, and says under a privilege when it can be given only on the whole workspace or a space. Click role names in the header to compare those roles (**Add a role** brings back one you no longer see), and choose **Only differences** to see where they differ. Roles cannot be changed. A role given on a space or an app allows only what can apply there - **Admin** on a space, for example, does not let anybody manage members. **Spaces** is one table of the **Workspace** and each team space, with the default space first: who holds a role there, how many apps it holds and what they store. A role on the whole workspace applies in every team space too, so it is listed once, at the top, rather than under every space. Personal spaces are never listed, because only their owners can open them: the tab says only how many members have one and how many apps are filed in them. The tab also shows how much storage the workspace uses against its limit, and what each shared space's apps store. Personal spaces count against the same limit, so the tab says what members' personal spaces store together - never what one person's space stores, and only once at least three colleagues' personal spaces hold data; until then that figure is counted with the files that belong to no app. What belongs to no app (audit originals, attachments and other workspace files) is shown as its own figure. The figures per space are measured once a day, and the tab says on which day. It also shows how many shared spaces the workspace has against its limit, for example "3 of 10 team spaces in use". Admins manage the shared spaces here. Selecting a team space opens **Space settings** - the same space settings as on the **Apps** page - with its **Access** and, on **Details**, its name, its icon and **Delete**: * **New space** creates one. Give it a name of up to 80 characters that no other shared space already has (capital letters do not count as a difference). When the workspace has reached its limit the button is unavailable and says why; the Ovidens team can raise the limit. The default space counts towards it; personal spaces never do. * Changing the **name** on **Details** renames a space, the default space's included. Roles given on the space and the apps in it are not affected. * **Delete**, on **Details**, removes a shared space after you confirm, but only an empty one: if it still holds apps (archived ones included), roles given on it or defaults for new members, the delete is refused with how many of each are left. Move its apps to another space with **Move to another space** on the **Apps** page, where you see who gains and loses access before anything moves, and remove its roles and defaults here on the **Access** page first. The default space cannot be deleted. Every one of these changes is recorded in the activity log. A member who manages members without being an admin sees the **Spaces** tab too, listing the shared spaces they can open - but not who holds which role in them, which stays with admins. Anyone else sees **Members**, **My access** and **Roles**, and following a link to **Spaces** tells them that only someone who manages members or decides what they may do can see it. ### API credentials [#api-credentials] A connected system - a nightly import, a script, a scheduler - signs in with an **API credential** rather than a person's account. Credentials are on the **Access** page beside the members, on the **API credentials** tab, which only admins see. Everything about a credential happens there: creating one and copying its secret (shown once), seeing what it may do, rotating its secret, changing its access and revoking it. The tab is one table - each credential's name, key, access options, when it was last used and when it expires or must next be attested - and the gear on a row opens its details. The **Access** tab of a credential's details lists every permission its access options give it and which option gives each one, the same way a member's does. A credential holds no role, so it works in every shared space and every app in one, including apps created later, and never in anyone's personal space. See [Connected systems](./connected-systems.mdx) for the walkthrough. ### Default access for new members [#default-access-for-new-members] Everyone who joins the workspace from now on - by accepting an invitation with an account they already have, by signing up through an invitation, or by being added by the Ovidens team - receives the roles listed under **Default access for new members** at the bottom of the **Access** page's **Members** tab, plus their own personal space. Someone invited as an admin is an admin as well; any other invitation gives nothing beyond the defaults. Admins see this section and change it there; nobody else sees it. Every workspace starts with no default access: a new member holds no role until somebody gives them one, and until then works only in their own personal space. Members who had already joined keep every role they hold. A sentence above the list says what new members get. To add a default, select **Add default** at the right of the heading, pick the workspace, a space or an app in the dialog (you can search them), then the role, and select **Add default**: it is added with **Undo** in the message that confirms it. For example, **Viewer** on a team's space lets every newcomer read that team's dashboards and nothing else. Only roles you are allowed to give are offered, a role already set there is not offered again, and **Admin** and **Member manager** on the whole workspace are never offered: a default can never make somebody an admin or let them manage members. A workspace can have up to 20 defaults. To stop giving one, select **Remove** beside it; that also offers **Undo**. If a change is refused, the message says why and nothing changes. Changing the defaults never changes the roles of anyone who has already joined - to change those, use **Manage access** on their row. The defaults are read when a person joins, not when they were invited, so an invitation that is still open gives the defaults as they stand when it is accepted. A member whose roles open no app yet - for example in a workspace with no default access - sees "No apps are shared with you yet" on the apps page, with the names of the admins they can ask for access, instead of an empty workspace. If their list of apps could not be loaded, the page says so and offers **Try again**. If you would rather be shown than told, the Access page can walk you through its Members tab: [take the tour of the Access page](https://app.ovidens.com/access?tour=users). A tour only points things out; it never changes anything. ## Activity log [#activity-log] Your workspace records who changed what and when, automatically, on every change: apps created, data edited, pipelines run, teammates invited. Admins read that record in the **Audit Analytics** app, and reach it from anywhere in the product through the **View audits** button. The [Audits](../administration/audit-trail.mdx) page is the full walkthrough. Which AI model powers Vera is a per-workspace choice a workspace admin makes on the **General** tab of the **System** page - see [Choosing the workspace AI model](../infrastructure/ai-transparency.mdx#choosing-the-workspace-ai-model). ## The System page [#usage] The **System** page has these tabs. API credentials are not among them: they are on the **Access** page's **API credentials** tab (see [API credentials](#api-credentials)). * **Usage** names your workspace's compute tier and instance, which is what chiefly drives the readings below it, and shows the workspace-wide caps against a live current value: apps, users, audit log entries, stored data, monthly compute credits, monthly AI credits and team spaces. Below that, a chart of usage over time you can switch between **AI credits**, **Compute credits** and **Storage**, by hour, day, week or month. Admins - whoever may read the workspace's activity records - also see **Data downloads** at the foot of the tab: what members of this workspace downloaded - who, from which app and node, whether it was the current data, a version or a run, how many rows and how much - with the 30-day totals above it. A download that was narrower than its node is marked **filtered** or with the number of columns taken, so a small file is never mistaken for the whole dataset having left. A download from an app you cannot open - one in a colleague's personal space - is neither listed nor counted in the totals. Records are kept for 90 days; the files themselves are removed much sooner. It is the same question the [Credentials](../integrations/api/credentials.mdx) page answers for an automated system, asked about people. * **Limits** shows the fixed caps that apply per app, per version and per Visualisation - how many nodes, knowledge-base items, widgets, indicators, dimensions or skills each may hold - together with how much run data is retained per app and how long an archived app is kept before it is purged. * **General** holds your workspace **Identity** (name, icon, and colour) and its **AI configuration** (the model that powers Vera). An admin edits both and saves them together with one **Save**; everyone else, including a member who manages members, sees them read-only. [Take the tour of the System page](https://app.ovidens.com/system?tour=system) to be shown each tab on the page itself. ### When you reach a limit [#when-you-reach-a-limit] Two of the meters can stop work, and both say so plainly rather than failing silently: * **AI credits** reset monthly. At the monthly limit, AI features stop with a message naming the workspace's limit and pointing you at support; the rest of the workspace keeps working as normal. * **Compute credits** also reset monthly, and pay for running pipelines and reading data. At the limit you get "Monthly compute-credit limit reached for this workspace." until the next month begins, or until an admin arranges a higher budget. This stops reading as well as writing: dashboards, Visualisation pages, data previews and exports all report the limit rather than loading, because reading data costs compute in the same way running a pipeline does. Raising the budget takes effect immediately - the next page load works, with nothing to restart and no waiting period. Other limits, such as the number of apps, members or shared spaces, simply refuse the action that would exceed them and tell you which limit you hit. # Build with Vera (/docs/analytical-apps/build-with-vera) **Vera** is the AI assistant built into Ovidens. Describe what you want to create, change or understand in plain language. Vera chooses the available tools needed for your request across the **Data Pipeline**, **Visualisation** and **Knowledge Base**. You do not need to prepare a knowledge base before you start. ## How to start [#how-to-start] 1. Open your app, click **Open editor**, and open the **Vera** panel. 2. Send a request, such as "Import this orders file, calculate monthly revenue, and show it as a line chart". Attach the file if needed. 3. Answer any question Vera asks about missing details. Vera shows progress while working and gives a short reply when it finishes. If a step cannot be completed, it explains the limitation and any work already done. The **Details** button opens tokens, one steps timeline and recorded times, without repeating your input or Vera's reply. Conversation history shows start/opening times and first-request app context, and searches saved titles across the archive. The compact autonomy menu beside the attachment button inside the composer offers levels 0 to 9; 4 is the balanced default. Documentation links open in a new tab. Shared app changes pause Visualisation editing until the current state is loaded, and stale saves are refused. Pipeline results remain available through the app's pipeline and history controls. ## Keep the conversation going [#keep-the-conversation-going] Send follow-ups in the same panel: "Use the order date for the month", "Make that chart blue", or "Explain how the revenue is calculated". Vera uses your recent conversation to understand references to earlier requests and replies. Your conversations belong to you within the workspace. Use **Conversation history** to reopen an earlier thread, or **New conversation** to start a separate named thread. You can rename it later. Full history is saved; **Load earlier messages** opens older turns. Other users have their own conversations. Vera automatically restores your last conversation only in the same app and within 6 hours of your last interaction. Otherwise it opens a blank chat, saved when you send the first message. **Conversation history** opens a centered, searchable table with page controls; you can manually reopen older conversations or a thread from another app. Each message shows the app it started from, and the current page provides context for your next request. Vera can work across apps your permissions allow. **Stop this request** ends the current task; changing conversations alone leaves its work running on the server. ## Give Vera the details it needs [#give-vera-the-details-it-needs] * **Describe the goal and business rules.** For example, say whether revenue includes tax and which date determines the reporting month. * **Attach useful files.** Ask Vera to read or summarise a document, or explain which data to import. Reading a file leaves the app unchanged; Vera asks if its purpose is unclear. * **Use the knowledge base when it helps.** Existing documentation can supply context, and you can ask Vera to write or update it. A conversation answer is not automatically saved as business documentation. * **Ask how Ovidens works.** Vera can consult the user documentation and link you to a relevant guide, such as uploading data or publishing an app. Vera asks focused questions when an important detail is missing. Clear requests can proceed straight away. It does not always read every document, rebuild the whole pipeline, add example data or run the pipeline for every message. ## Who can use Vera [#who-can-use-vera] Chatting with Vera requires AI access on the app. You can ask questions without permission to change the app's configuration. Each editing tool still requires its own permissions, so Vera can change only what you are allowed to change and what your enabled AI tools allow. See [roles](../administration/index.mdx). You answer questions in your own conversation. Other people's requests and replies are not part of your chat. ## Changes and publication [#changes-and-publication] Vera creates or reuses the app's draft when a requested change needs one. Asking for an explanation or help does not create a draft. A pipeline run Vera starts for authoring is a preview, so viewers keep seeing the published app. Review changes before publishing them. You can click **Publish**, or explicitly ask Vera to publish if that tool is enabled for you. Without such a request, Vera leaves its edits in the draft. **Discard** removes all changes in that draft, including data uploaded while it was open. See [Draft and publish](../overview/apps.mdx#draft-and-publish). An [AI widget](./skills.mdx) is a separate read-only use of Vera. Running it gives an answer without changing data, editing configuration or putting the app into draft mode. See [Interact with Vera](../tips/interact-with-vera.mdx) for outcomes, recovery and AI usage. # Export and import an app (/docs/analytical-apps/export-import) An analytical app can be downloaded as a single **bundle file** and imported into another workspace. Use it to move an app between workspaces, to hand a working setup to a colleague, or to keep an offline copy of a configuration you are happy with. The bundle carries the app's **configuration**, not its data. That distinction is the thing worth understanding before you use it, so it is spelled out below. ## Exporting [#exporting] Open the app and choose **Export app** ("Download this app as a portable bundle"). A dialog asks what to call the file before it is written, filled in with the app's technical id and today's date. The name is yours to change; the `.app-bundle.json` ending is fixed, so a renamed bundle still imports. Export always takes a **published version**. If the app has never been published there is nothing to export yet - publish it first. Any edits sitting in an open draft are not included; what you get is the last published state, so a bundle is always a coherent, complete configuration rather than a half-finished edit. Exporting requires the **App builder** or **Admin** role on the app. ## What travels in the bundle [#what-travels-in-the-bundle] * **The app itself** - name, description, icon, and technical id. * **The whole pipeline** - every node with its full configuration: dataset nodes and their column definitions, SQL transforms with their queries, indicator-data bridges, and analytic-data imports. * **The Visualisation canvas** - the pages and every widget on them. * **The knowledge base** - the folder tree and the content of every page. * **The app's [skills](./skills.mdx)** - each skill's name, description, declared inputs, whether it is offered over MCP, and its full instruction text. ## What does not [#what-does-not] * **Your data.** No dataset rows, no uploaded files, no indicator values. The imported app arrives with its structure in place and its inputs empty, ready for you to upload or connect the data on the new side. * **History.** Versions, pipeline runs and the audit trail stay with the original app. The imported app starts a fresh history of its own. * **Anything workspace-specific.** Members, permissions and AI privileges belong to the workspace, not the app. ## Importing [#importing] On the **Apps** page, choose the import button (**Import an app file**) at the top right, or the one on the header of the space the app should go into (or **Import app** on the welcome screen of a workspace with no apps yet), and pick a bundle file; from the top right, Ovidens asks which space the app goes into when you may import into more than one. The app is created in that space with the configuration from the bundle, and you land on it ready to add data. A few things to expect: * The app keeps the **technical id** from the bundle. If an app with that id already exists in this workspace the import is refused rather than overwriting anything - rename or remove the existing app first, or export from a differently-named one. * The import counts against your workspace's **app limit**, exactly like creating an app by hand. * A file that is not a valid bundle is rejected with a clear message and nothing is created. * Every import node in the file (analytic-app-data or indicator-data) must name an app of this workspace whose data you can read, and not one in a personal space. If one does not, the import is refused, naming those nodes, and nothing is created: remove them from the file, or ask for access to the apps they read from first. See [who can change an app that imports another app](./pipeline/analytic-data.mdx#who-can-change-an-app-that-imports-another-app). Importing requires the **App builder** or **Admin** role on the workspace, or on the team space new apps are filed into. ## What cannot be exported [#what-cannot-be-exported] The **Audit Analytics** app is not exportable. It is system-managed, its sources are reserved for the workspace's own audit archive, and every workspace already has one - there is nothing portable about it. See [Audits](../administration/audit-trail.mdx). ## A note on moving between workspaces [#a-note-on-moving-between-workspaces] Because data does not travel, moving an app is a two-step job: import the bundle to get the structure, then load the data on the new side the same way you would for a new app - upload files into the dataset nodes, or point the indicator-data and analytic-data nodes at the apps they should read from in this workspace. Those connections refer to other apps **by their technical id**, so if the app you are importing reads from another app, that one needs to exist here, and be readable by you, before the import is accepted: import it first, under the same technical id. # Knowledge Base (/docs/analytical-apps/knowledge-base) The **Knowledge Base** is a place inside each app to keep notes, documents, and context about your data. It is where you write down the things that are obvious to you but would not be obvious to anyone (or anything) else: what a column means, where the data comes from, or the rules your business follows. ## Why it matters [#why-it-matters] The Knowledge Base does double duty. * For **people**, it is documentation that travels with the app, so a teammate opening it later understands the data without having to ask. * For **Vera**, it is useful background when its definitions are relevant to your request. You can explain your goals directly in chat and ask Vera to create or update the documentation alongside the pipeline and Visualisation. Writing notes first is optional. ## Where to find it [#where-to-find-it] Open your app, click **Open editor**, and open the **Knowledge Base** panel (or press K). Its left side is a panel with four tabs: * **Pages** - the notes themselves, in folders. * **Favorites** - the pages you starred. * **Skills** - the app's reusable AI tasks. See [Skills](./skills.mdx). * **Trash** - a bin icon holding what you deleted. ## Writing pages [#writing-pages] You organise the Knowledge Base with **folders** and **pages**, much like folders on your computer. **New page** and **New folder** create them at the top level, and the more-actions menu on any folder adds a page or a folder inside it. Rename anything from the same menu, and drag pages between folders as your app grows. A search box above the list filters the pages by name. The page editor supports Markdown: headings, lists, tables, code blocks, and more. It also renders **Mermaid diagrams**, so a flow chart or a small architecture sketch lives with the note that explains it. **Toggle full width** gives a wide table more room. You do not have to save. Pages save themselves as you write; a small indicator shows **Saving…** and then **Saved**. Star a page with **Add to favorites** to pin it to the **Favorites** tab, which is worth doing for the two or three pages people open constantly. **Remove from favorites** unpins it again. ## Importing documents [#importing-documents] You rarely start from a blank page. **Import** brings existing material in, in two shapes: * **Markdown or text** - MD, Markdown, TXT, and CSV files, added as pages directly. * **Document** - PDF, Word, PowerPoint, and Excel files. Documents are converted to Markdown so they are searchable, editable, and readable by Vera, and the original stays available to download exactly as it came in. You can drop several files at once; the dialog shows the progress ("3 of 8") and tells you at the end how many were imported, with **Try again** for any that failed. Keep the dialog open until it finishes. ## Deleting and restoring [#deleting-and-restoring] Deleting a page or a folder does not destroy it: **Move to trash** puts it, and everything inside it, in the **Trash** tab. From there **Restore** brings it back where it was, and **Delete permanently** removes it for good. Writing, moving to trash and restoring are for members with the **App builder** or **Admin** role on the app; **Delete permanently** is for administrators only. Anyone else reads the Knowledge Base without the editing controls and without the **Trash** tab. # Analytical apps overview (/docs/analytical-apps/overview) An **analytic app** takes your raw data, shapes it into the results you care about, and presents those results in a **Visualisation** your team works in. It is the right kind of app when you have spreadsheets or files to clean, combine, and turn into tables and charts. ## The three parts of an analytic app [#the-three-parts-of-an-analytic-app] Every analytic app is made of three parts, each with its own panel in the app. * The **[Data Pipeline](./pipeline/overview.mdx)** is where you shape your data: bring it in, then transform it step by step. * The **[Visualisation](./visualisation.mdx)** is what your teammates work in, built from the results of the pipeline: tables, charts, filters, and inputs. * The **[Knowledge Base](./knowledge-base.mdx)** is a place for notes and context about your data. It helps you, and it gives Vera the background it needs to build a better app. Its **Skills** tab is where you save [reusable AI questions](./skills.mdx) for the app. ## Build with Vera first [#build-with-vera-first] The easiest way to build an analytic app is to ask **Vera**, the AI assistant. Describe what you want in plain language, for example "import this sales file, total revenue by region, and chart it", and Vera sets up the pipeline, the Visualisation, and the knowledge base for you. No SQL or technical knowledge needed. See [Build with Vera](./build-with-vera.mdx). The rest of this section explains each part so you can review what Vera builds, or build and adjust the pieces by hand when you prefer. ## The build loop [#the-build-loop] Building an analytic app by hand follows a simple loop: 1. **Bring in data** by adding a dataset and importing a file. 2. **Transform it** with steps that clean and combine your data. 3. **Run** the pipeline so every step processes and produces fresh results. 4. **Build the Visualisation** on those results with widgets like tables and charts. You repeat this loop as your needs change: adjust a step, run again, and the Visualisation updates. ## The editor, Run, and Publish [#the-editor-run-and-publish] You build in the **editor**: open the app and click **Open editor** in the top right, or press E. The three panels above are its tabs. To see the app the way your teammates will, go **Back to viewer** with the arrow at the far left of the editor bar, or press E again. Everything you change belongs to your private draft until you release it: * **Run** processes the pipeline and refreshes the results. This is a private preview - your teammates keep seeing the last published results. * **Publish** promotes the draft, so everyone sees your changes, and records the app as a version you can return to later. **Publish and run** does both in one step. See [Draft and publish](../overview/apps.mdx#draft-and-publish) for the full cycle, including discarding a draft and restoring an older version. Both touch data as well as configuration: discarding a draft also throws away the rows you uploaded while it was open, and restoring an older version brings that version's data back in place of what has been entered since. Changing the app's configuration and publishing it are for members with the **App builder** or **Admin** role on the app. Anyone else who opens the editor sees the pipeline, the Knowledge Base, skills and the Visualisation read-only, with no **Publish**, **Discard** or **Restore**, and can still enter data in the app's tables. See [Who builds and publishes](../overview/apps.mdx#who-builds-and-publishes). # Analytic app data (/docs/analytical-apps/pipeline/analytic-data) An **analytic-app-data** node imports the result of a node from **another** of your analytic apps into this pipeline. It lets several apps build on one shared, already-published result instead of copying the same data or rebuilding the same steps in every app. A common pattern is a shared "reference data" app whose output several reporting apps re-use, or a heavy data-preparation app whose result feeds a handful of smaller apps. Instead of duplicating that work, each downstream app imports the result with an analytic-app-data node. ## Choose what to import [#choose-what-to-import] When you add an analytic-app-data node, you tell it what to bring in: * the **source analytic app** to read from (one of your workspace's other analytic apps), * the **node** inside that app whose result you want, * optionally, **which columns** to import and in what order. Leave this empty to import every column exactly as it is. The source-app list offers only the apps you may import from - apps whose data you can read, and an app in a personal space only for an app in that same space - and marks any app that has no published result yet, so you can only pick an app that is ready to import from. ## How the data is captured [#how-the-data-is-captured] The data is imported as a **snapshot** of the source app's last successful **published** run, taken at the moment this pipeline runs. Two things follow from that: * Only a **published** result is imported. A private preview run in the source app is never imported, so downstream apps only ever build on results their owners have deliberately published. * The import is a point-in-time copy. When the source app publishes newer results, this app does not change on its own. **Run this pipeline again** to refresh the import with the newer values. The imported node is **read-only**: it shows the imported result as a table, and you edit the data in the source app, not here. From this node the imported columns flow into the nodes connected after it, so you can blend the shared result with the rest of your data. ## Who can change an app that imports another app [#who-can-change-an-app-that-imports-another-app] An analytic-app-data node carries another app's data into this one, so it is checked whenever this app's configuration changes: * You can add an import node, or point one at a different app, only for an app whose data you can read. An app in somebody's personal space can be imported only into an app in that same personal space, and the Audit Analytics app can never be imported. * Adding an import can therefore lock colleagues out. Before you save an import node that points at a new app, the node says how many people would no longer be able to modify this app, for example "3 people will no longer be able to modify this app". It never names them. * Once this app imports from another app, changing anything in it - its pipeline, knowledge base, skills, Visualisation and settings, and publishing it - is open only to people who can read every app it imports from. Anybody else is told which import node stops them, and can ask for access to the app it imports from. * They can still repair the app themselves, one import node at a time: deleting such a node, or pointing it at an app they can read, is always allowed. Nothing else is - not even choosing different columns of the same app. The same holds when the app an import node reads from has been deleted. A notice at the top of the editor names the import nodes that stop them, so they know which ones to change. * Restoring an earlier version, or discarding a draft, is allowed only when the version it makes current imports from no app they cannot read; a version that does import from one stays out of reach, even if it imports from fewer of them. * Running the pipeline, entering data, exporting and archiving are not affected. Everybody who can read this app's data also sees what it imports, even without access to the source app - and only ever the source app's **published** result, never its drafts. Keep that in mind before importing from an app whose readers are a smaller group than this app's. # Datasets (/docs/analytical-apps/pipeline/datasets) A **dataset** node is an input table you fill with your own data. It is where your information enters the pipeline, either by importing a file or by typing it in. Everything downstream builds on what your datasets hold. ## Define columns and their types [#define-columns-and-their-types] A dataset is organised into **columns**, and each column has a **data type** that says what kind of value belongs in it. Choosing the right type keeps your data clean: numbers stay numbers, dates show a date picker, and a dropdown only allows the options you set. The available types include: * **Text** and **Long text** for words and free-form notes, * **Integer** and **Decimal** for whole and fractional numbers, * **Currency** for money and **Percent** for percentages, * **Boolean** for a simple yes/no, * **Date**, **Time**, **Timestamp**, and **Year** for points in time, * **Phone** and **Email** for those specific kinds of text, * **Dropdown** for picking one option from a list, and **Multi-select** for picking several, * **Attachment** for one or more uploaded files. Give each column a clear name and the type that matches the values it will hold. ## Import a CSV or Excel file [#import-a-csv-or-excel-file] The quickest way to fill a dataset is to import a spreadsheet. A step-by-step wizard guides you through six steps: **Select file**, **Choose sheet**, **Parsing options**, **Map columns**, **Preview & validate**, and **Confirm**. You can go **Back** at any point without losing what you have set. Importing a file is available to everyone who may edit the app's data - the same people who can [edit its rows by hand](#edit-data-by-hand). Someone who may only view the app cannot import. ### Select file [#select-file] Drag a CSV or Excel file onto the wizard, or click to pick one. Ovidens detects the delimiter and the encoding for you. If you are starting from nothing, **Template** gives you a file that already has this dataset's columns as headings. Choose CSV or Excel in the dialog, name the file, and fill it in - upload it back and the mapping step has nothing left to guess. If the file is too big or has more rows than a dataset may hold, the wizard says so straight away and names the limit, so you can split the file rather than wait for a failed import. Very large files are imported exactly as parsed instead of being editable cell by cell in the preview. A single row may hold up to 2,000,000 characters. A longer row stops the import with a message naming its line, and nothing is imported, so shorten the longest values on that line or split them across rows. ### Choose sheet [#choose-sheet] An Excel workbook usually has several sheets, so pick the one holding your data. Each is listed with its size, for example "1,204 rows · 12 cols". A CSV skips this step. ### Parsing options [#parsing-options] This step is how you deal with a file that is not perfectly clean. Everything here has a sensible default, and the preview updates as you change it. * **Delimiter** - the character that separates columns (comma, semicolon, tab). * **Encoding** - the file's character set. Use Windows-1252 for older Excel exports whose accents come out garbled. * **Header row** - which row holds the column names, or **No header** if the file starts straight into data. * **Skip leading rows** and **Skip trailing rows** - ignore a title block or a set of notes at the top, and totals or footnotes at the bottom. * **Empty/null tokens** - values that should count as empty, for example `N/A` or `-`. Type one and press Enter to add it. * **Trim whitespace around values** - remove stray spaces from every value. ### Map columns [#map-columns] Line up each column in your dataset with a column from your file. The wizard matches what it can by name and labels how confident it is: an exact match, a name match, or a fuzzy match. Anything you do not want, set to "leave empty". Two shortcuts help with an awkward file: * **Suggest with AI** asks Vera to work the mapping out from the column names. Each suggestion is marked "AI suggestion · needs review", so you confirm rather than trust it blindly. The wizard states plainly what leaves your workspace: "Column names and a few sample values are sent to our AI provider to suggest a mapping." * **Reuse previous mapping** applies the mapping from the last import into this dataset, which is what you want for a file you refresh every month. The wizard warns you if a required column is not mapped, or if columns in your file are going unused. ### Preview & validate [#preview--validate] Now the wizard checks every value against its column's type and shows you the totals: how many rows are valid, how many have errors, and how many carry warnings. Warnings cover things worth a second look rather than outright errors, such as a column that is completely empty, a column with only one distinct value, or duplicate rows. Click any issue to **jump to the cell** behind it and fix it in the preview. The preview shows the first 500 rows, and says so; every row is still checked and imported. One switch decides what happens to bad rows: * **Skip invalid rows** on: the invalid rows are left out and the valid ones are imported. * Off: all rows must be valid, and the import is rejected if any row has errors. If an import is blocked, **Download error report** lists exactly which rows failed, including ones beyond the preview. A very long value is shortened in the report and ends in "...", so the report stays a size a spreadsheet can open; the line number takes you to the full row in your file. **Deduplicate on** is optional: pick one or more columns as a key, and incoming rows whose key already exists are skipped. ### Confirm [#confirm] Finally, choose the **Import mode**: **Replace** overwrites all existing data, or **Append** adds these rows to what is already there (offered only when there is data to append to). Confirm, and Ovidens reports what happened, for example "Imported 1,180 rows · skipped 24". A file needs at least one data row. A file that holds only a header row, or nothing at all, is refused in both modes with the message "The file has no data rows, so there is nothing to import", and the dataset keeps the rows it has. An empty file is usually a mistake - an export that matched nothing, or a file saved too early - so Ovidens does not treat it as an instruction to empty the dataset. The same holds with **Skip invalid rows** on when every row of the file is invalid: the import is refused with a message saying how many rows were invalid, rather than importing none. To empty a dataset on purpose, delete its rows in the data table. You cannot import while a pipeline run is in progress; the wizard says so and waits. ## Edit data by hand [#edit-data-by-hand] You do not have to import everything. You can also type values directly into the dataset and edit cells in place, which is handy for small tables or quick fixes. You can set basic rules on a column too, such as making it required or requiring its values to be unique, so your data stays consistent as you enter it. This works from the very first row. A brand-new dataset that has never received any data shows an empty table with the message "No data yet. Add the first row by hand, or use Import to load a file." and an **Add first row** button. Choosing it opens the editor with the first row already created; type into it and **Save**. From then on the **+ New row** strip at the bottom of the grid adds further rows. No file import is needed first, and importing a file stays available at any time from the same toolbar. For someone who cannot import, the message leaves the import out: "No data yet. Add the first row by hand." There are three ways to add a row. The **+ New row** line at the bottom of the table and the add-row control shown at the left of each row while you are editing both create the new row straight away, with nothing to type first. Using **Row** in the toolbar instead opens a form, and it only adds a row once you have filled in at least one field - submitting it empty adds nothing. However you add a row, it lands at the end of the table - a dataset keeps its rows in the order they were created, so a new row is always appended rather than inserted between two existing ones. The row counts as an unsaved change until you choose **Save**. The only thing you need before you can type is at least one column. A dataset with no columns defined yet shows the import drop zone instead, because importing a file is also what defines the columns. ### Files in an attachment column [#attachments] An **Attachment** column holds files - one or several per row. While you are editing, hover over a cell in the column and choose **Add file** (on an empty cell) or the paperclip (on a cell that already holds files). In the dialog that opens, click to pick files or drag them onto the drop area, then confirm with the upload button, which names how many files it sends. Each file can be up to 10 MB; a bigger file is refused in the dialog with a message saying so, and nothing else you have changed is lost. An uploaded file is not part of the row until you **Save**. Choosing **Cancel** instead drops it, together with the rest of the unsaved changes. Click the file icon in a cell to see all of its files with their names and sizes. From there you can download a file (you are asked what to name it first), see an image in a larger view, attach more files or remove one. To replace a file, remove it and add the new one, then **Save**. Anyone who can read the app's data can open and download its files. Only workspace administrators can add or remove them. The same cells, with the same rules, appear in an editable [table widget](../visualisation.mdx#table-attachments) on the app's dashboard. A file stays downloadable when the app's configuration is edited or a new version is published. Removing a file from a cell, or cancelling after uploading one, does not delete the stored copy: it is kept, still counts towards your workspace's storage, and is not cleaned up automatically today. ## Download the data [#download-the-data] **Export** in the table toolbar downloads what the dataset holds. Every download asks you to name the file first, so what lands in your downloads folder is something you can recognise later rather than a technical id. Downloading takes the rows out of the workspace, so it is a permission of its own, separate from reading them. It comes with the **Contributor**, **Analyst**, **App builder** and **Admin** roles. Someone whose access to an app lets them only view it can read its tables but is not offered **Export**, here, on a table or pivot in the Visualisation, or under the results of a SQL query, and a report offers them no **Export PDF**, because the PDF carries the report's tables in full. **The file matches what you are looking at.** If you have filtered the table, searched it, or hidden columns, the dialog asks what the file should hold and each answer carries its own number: * **Rows** appears when a filter or a search is narrowing the table. **Current filter** holds exactly the rows on screen; **All rows** holds the whole dataset. The one on screen is chosen for you, so confirming without changing anything downloads what you were looking at. * **Columns** appears when you have hidden at least one. **Visible** holds the columns you left on screen, in the order you put them in; **All** holds every column the dataset has. The line under the file name always describes the choice you have made, so the number you read there is the number of rows the file will have. Choosing **All rows** on a very large dataset can also put it past what a spreadsheet holds, and Excel is greyed out while that choice is selected. One view cannot be downloaded on its own yet: with **failing rows only** turned on, **Current filter** is offered greyed out and the download takes all rows. Turn the toggle off to download what a column filter matches. The name is filled in for you from the node's own name plus today's date - a weekly pull of the same dataset no longer overwrites last week's. Edit it if you want something else. The file type sits beside the box and is not part of the name, so the two can never disagree. Choose **CSV** or **Excel**: * **CSV** is the interchange format. It is plain text, every value is written out as text, and it re-imports through the same wizard it came from. * **Excel** carries types. A date arrives as a date, a number as a number and a yes/no column as TRUE or FALSE, so you can sort and total a column without retyping it. A percent column shows its `%` and a currency column its symbol, the same way they read on screen. Dates are written as `2026-08-29` so they mean the same thing in every region. A spreadsheet holds about a million rows. If the dataset has more than that, Excel is offered greyed out with the row count, and CSV takes it. A run's results and a published version's pinned data download the same way, from the same control, and their names say which one you took. # Indicator data (/docs/analytical-apps/pipeline/indicator-data) An **indicator-data** node pulls metrics from one of your indicator apps into this pipeline. It is the bridge between the two kinds of app: it lets an analytic app read the numbers you have been collecting in an indicator app and combine them with other data. If you track metrics like fuel use or electricity in an indicator app, this node is how you bring those figures into an analytic app alongside, say, your financial data. ## Choose what to pull in [#choose-what-to-pull-in] When you add an indicator-data node, you tell it what to fetch: * the **source indicator app** to read from - the list offers only the indicator apps you may import from: those whose data you can read, and an app in a personal space only for an app in that same space, * which **indicators** (metrics) you want, * the **period range** and **period type**, for example monthly values across a span of months or years, * optional **dimension filters**, to narrow the metrics to particular breakdowns, such as one region or site (see [Dimensions](../../indicator-apps/dimensions/other-dimensions.mdx)), * and whether to **Include dimension labels**. Leave **Include dimension labels** off and each breakdown arrives as its short technical id, for example `de_south`. Turn it on and the node adds a second column next to each one holding the readable member name, for example "South". Turn it on when the figures end up in a table or a chart people read: the label is what belongs on the axis or in the column heading, while the id stays the reliable thing to join and filter on. ## How the values are captured [#how-the-values-are-captured] The metrics are captured as a **snapshot** when the pipeline runs. In other words, the node records the indicator values as they stand at the moment of the run, so the rest of your pipeline works from a stable set of figures. Run the pipeline again whenever you want to refresh that snapshot with the latest values. From there, the indicator values flow into the nodes connected after this one, letting you blend collected metrics with the rest of your data. ## Who can change an app that imports another app [#who-can-change-an-app-that-imports-another-app] An indicator-data node carries another app's data into this one, so it is checked whenever this app's configuration changes: * You can add an import node, or point one at a different app, only for an app whose data you can read. An app in somebody's personal space can be imported only into an app in that same personal space, and the Audit Analytics app can never be imported. * Adding an import can therefore lock colleagues out. Before you save an import node that points at a new app, the node says how many people would no longer be able to modify this app, for example "3 people will no longer be able to modify this app". It never names them. * Once this app imports from another app, changing anything in it - its pipeline, knowledge base, skills, Visualisation and settings, and publishing it - is open only to people who can read every app it imports from. Anybody else is told which import node stops them, and can ask for access to the app it imports from. * They can still repair the app themselves, one import node at a time: deleting such a node, or pointing it at an app they can read, is always allowed. Nothing else is - not even choosing different columns of the same app. The same holds when the app an import node reads from has been deleted. A notice at the top of the editor names the import nodes that stop them, so they know which ones to change. * Restoring an earlier version, or discarding a draft, is allowed only when the version it makes current imports from no app they cannot read; a version that does import from one stays out of reach, even if it imports from fewer of them. * Running the pipeline, entering data, exporting and archiving are not affected. Everybody who can read this app's data also sees what it imports, even without access to the indicator app - and only ever that app's **published** structure with its collected values, never its drafts. Keep that in mind before importing from an app whose readers are a smaller group than this app's. # Pipeline overview (/docs/analytical-apps/pipeline/overview) The **Data Pipeline** is the set of steps that take your raw data and shape it into the results your Visualisation uses. Think of it as an assembly line: data comes in at one end, each step does a little work, and finished results come out the other end. ## Nodes and connections [#nodes-and-connections] Each step in the pipeline is a **node**. You connect nodes so that one feeds the next, and the canvas draws those connections as lines, so you can see the flow of data at a glance. A node early in the flow brings data in; a later node takes that data and transforms it. You build the pipeline in the editor, on the **Data Pipeline** tab, by adding nodes and drawing connections between them. A pipeline has three stages: **inputs** - a dataset and an indicator bridge - feed a **consumer**, a **SQL** transform (the only transform type for now), whose result is the **output**. That output is surfaced on the separate **Visualisation** tab as a table, chart, pivot, or SQL editor. ## The four node types [#the-four-node-types] There are four kinds of node, each with its own page. * **[Datasets](./datasets.mdx)** bring data in. A dataset is an input table you fill with your own data, usually by importing a file. * **[SQL nodes](./sql-nodes.mdx)** transform data. They take the output of earlier nodes and reshape it. * **[Indicator data](./indicator-data.mdx)** nodes pull metrics from one of your indicator apps so you can combine those metrics with other data. * **[Analytic app data](./analytic-data.mdx)** nodes import a node's result from another of your analytic apps, so you can build on a shared, already-published result without copying it. ## Run the pipeline and read the results [#run-the-pipeline-and-read-the-results] Adding and connecting nodes describes what should happen, but nothing is processed until you run it. Click **Run** to process every step in order. The run happens in the background, and the status updates on its own when it finishes, so you do not need to wait or refresh. When the run finishes, click any node to view the data it produced. Each node shows whether it succeeded, and your Visualisation widgets show the fresh results. If a step fails, you can open it to read the error, fix it, and run again. ## Run history and what is kept [#run-history-and-what-is-kept] Every run is listed in the app's history, under **History** (the clock button in the editor bar). Click **View full history** for the complete list, with the start time, status, duration, number of nodes, and the size of what the run produced. Click a row to see the pipeline exactly as that run left it. The *list* keeps every run. The *data* a run produced does not stay forever, because it is the biggest thing an app stores. Ovidens keeps the results of the newest few runs (three, unless your workspace is set differently), always keeps the latest successful run, and quietly frees the rest. The older run still shows in the history, it just has no data to open any more. Administrators can override that on any individual run: * **Protect this run's data** pins a run so the clean-up never touches it. A protected run is marked **Protected** in the list. **Stop protecting this run's data** releases it again, and says plainly what that means: the data becomes "eligible for automatic deletion by the next retention sweep". * **Delete this run** frees that run's data immediately and removes it from the history. It cannot be undone, and it is not offered for the latest successful run. Your workspace's **System** page lists both limits under **Limits**: "Run data retained per app" and "Protected runs per app". See [Administration](../../administration.mdx). ## When results are out of date [#when-results-are-out-of-date] If you change data after a run, Ovidens says so instead of quietly showing stale numbers. In the editor you get a banner: "The data changed since the last run. Re-run to refresh the results.", with a **Re-run** button. Individual widgets carry a small **Out of date** badge with the same message. People using the published app see the equivalent note: someone who builds the app, such as a workspace administrator, needs to run it again and publish it. ## Let Vera build it for you [#let-vera-build-it-for-you] You do not have to assemble a pipeline by hand. Describe the data and the result you want, and **Vera** can build the whole pipeline for you. See [Build with Vera](../build-with-vera.mdx). # SQL nodes (/docs/analytical-apps/pipeline/sql-nodes) A **SQL** node is a step that transforms your data. It reads the output of earlier nodes and reshapes it into something new, for example combining two tables, filtering rows, or calculating a total. SQL is the standard language for working with data, and a SQL node uses a query that reads from the nodes feeding into it. ## You do not need to know SQL [#you-do-not-need-to-know-sql] This is the important part: you do not have to write SQL yourself. Describe the transformation you want in plain language, for example "keep only this year's orders and add up revenue per customer", and **Vera** writes the query for you. Vera is the no-code path; this page explains the node for when you want to read or adjust what it produced. See [Build with Vera](../build-with-vera.mdx). ## Working in a SQL node [#working-in-a-sql-node] If you do want to work with the query directly: 1. In the editor, open the SQL node. 2. Write or adjust the query. It reads from the nodes connected into this one. 3. Run the query to see its output and the columns it produces. A SQL node's results feed any nodes connected after it, and your Visualisation widgets. Because downstream steps depend on this node's output, they only have fresh data once the pipeline has run successfully. If the query has not been run yet, nodes that rely on it will not have data to work with until you click **Run**. Running a query in a SQL node is a permission of its own, separate from reading the app. It comes with the **Contributor**, **Analyst**, **App builder** and **Admin** roles on the whole workspace - a role on a single space or app does not give it - and you always hold it in your own personal space. Without it, the node shows its query with no **Run** button and a short note saying why. If the app could not check whether you hold it, the note says so instead; reload the page to try again. ### What the Run button can read [#what-the-run-button-can-read] Running a query in the editor reads **only this app's own nodes**, named directly by their id - the same rule the SQL editor and the SQL widget follow. A query may name a node, or a `WITH` clause it defines itself, and nothing else. An [indicator data](./indicator-data.mdx) or [analytic app data](./analytic-data.mdx) node can be read once the pipeline has run: the query reads the copy of its rows that run took. Before the app has a run there is no such copy, and the name is refused like any other name with nothing behind it. A local development stack never offers these two node types to a query and refuses their names; read one there through a SQL node that selects from it. These are refused before anything runs, with a message saying what to change: * a name with a prefix, such as `other_app.orders` or `main.orders` - write `orders`; * a name that is not a node of this app, such as another app's table, a system table like `duckdb_tables`, or a file path; * a node whose id is also the name of one of the database's own catalog tables, such as `duckdb_tables`, `pg_class` or `sqlite_master` - rename the node to read it; * a table function in a `FROM` clause, such as `read_parquet(...)`, `range(...)` or `unnest(...)` - `unnest(...)` in the `SELECT` list is fine; * `DESCRIBE`, `SHOW` or `SUMMARIZE` used as a subquery, `PIVOT` and `UNPIVOT`, reading an earlier version with `AT (VERSION => ...)`, a `WITH ... AS MATERIALIZED` clause, and an aggregate's internal state with `EXPORT_STATE`; * a function that reads settings, session details or the database catalog, runs SQL handed to it as text, or changes something - for example `current_setting(...)`, `current_database()`, `json_serialize_plan(...)` or `nextval(...)`. Ordinary functions for text, numbers, dates, lists and JSON, aggregates and window functions all work. A function written after a value, as in `name.upper()`, is refused too - write `upper(name)`; * a type that belongs to a schema, such as `CAST(x AS other_app.mood)`, or that is not one of DuckDB's built-in types; * a number too large for the database to hold, such as `1e400` - the query cannot be checked, so it is not run. The SQL keywords written without parentheses - `current_user`, `current_schema`, `current_date` and the like - still work, including as column names. They report only this query's own connection, never another app. A pipeline run and the live preview of a node hold its saved query to the same rule. Saving a query that uses one of these forms is not refused, but when the pipeline runs that node fails with the same message, and the nodes that read from it are skipped. Rewrite it without them - a conditional `sum(CASE WHEN ...)` in place of a `PIVOT`, or `SELECT unnest(range(10)) AS n` in place of `FROM range(10)`, for example. To use another app's data, add an [analytic app data](./analytic-data.mdx) node and read that node by its id. ### How much a page of results can hold [#how-much-a-page-of-results-can-hold] A query's results are shown one page at a time, with a count of every row the query produced. One page may hold at most **4 MB** of data and at most **262,144 values**. A value is measured as it would be shown as text, so a long text, a large list or a value nested inside another counts in full; and every element of a list, every field of a struct and every key and value of a map counts as a value of its own. A query whose page is over either limit is refused with a message saying so. Nothing is cut short: you never see part of a value, or some of the rows, presented as if it were the whole result. To bring the page under the limits: * select fewer columns, or shorter ones - for a long text, `left(text, 1000)` returns its first 1,000 characters and `length(text)` its length, and `len(list)` the length of a long list; * filter to fewer rows with a `WHERE` clause. A few column types cannot be shown as a query result at all: a `UNION`, a `VARIANT` and a fixed-size `ARRAY`, on their own or inside a list, struct or map. A query returning one is refused with a message naming the column. Convert it in the query - `CAST(... AS VARCHAR)` shows its value as text, and `CAST(... AS INTEGER[])` turns a fixed-size array into a list. The limits apply wherever a query's results are shown straight away: the **Run** button, the SQL editor, a SQL widget, the AI tools that query an app, and the preview of a SQL node described below. They limit what one page holds, not how many rows a query may produce. ## Seeing what a query produces before you run [#seeing-what-a-query-produces-before-you-run] You do not have to run the pipeline to see whether a query is right. Open a SQL node's **Data** tab when there is no run to show and it previews the node's CURRENT query over the CURRENT contents of the app's datasets, right then. A strip above the rows says so, because these are not run results: nobody else sees them, your Visualisation does not use them, and they change the moment you edit the query or the data. That makes the ordinary loop tight. Edit the query, look at the rows, fix it, look again - and only click **Run** when it is right. Type a value into a dataset's grid and the next preview already reflects it. A few things to know about it: * **It is for people who can edit the app.** A viewer sees the run results, as before. * **It shows the first page, in the query's own order.** Sorting, filtering and the search box are not offered here - a preview that quietly ignored a sort would be worse than one that does not offer it. Run the pipeline for the full grid. * **The columns come from what the query actually returns**, not from the Output columns list, so a query you have just rewritten shows its new columns immediately. * **If the query cannot run, it says why.** The message names the relation that did not resolve, which is usually a typo or a missing entry in `manifest.yml` (see below). ### What a preview can read [#what-a-preview-can-read] A preview binds **every dataset node in the app**, exactly as a run does - so a query reading a dataset works whether or not `manifest.yml` mentions it. The **SQL nodes** it binds are the ones your `manifest.yml` declares, and their inputs in turn. So a query that reads ANOTHER SQL node's output without listing that node in `manifest.yml` will not preview, and the message names it. Add it to `input_tables` and the preview works - which is worth doing anyway, since that entry is also what draws the arrow on the canvas and what tells the pipeline to run the two steps in the right order. Two kinds of input cannot be previewed at all, and the message says so rather than looking like a broken query: an [analytic-data import](./analytic-data.mdx) and an [indicator-data](./indicator-data.mdx) bridge. Their rows are fetched when the pipeline runs, so there is nothing live to read. Run the pipeline to see a node that depends on one. ## `manifest.yml` has to match the query [#manifestyml-has-to-match-the-query] `manifest.yml` is where a SQL node declares which other nodes it reads. That list is not a convenience: it is the only thing that draws the arrows on the canvas and the only thing that tells a run which step has to finish before this one starts. A query that reads a node the list does not mention still runs, but the app's picture of itself is then wrong, and two steps that must run in order are free to run in the wrong one. So saving a SQL node checks the two against each other, and refuses the save with a message naming the node and the table when they disagree: * **The query reads a node `input_tables` does not list.** Add it to the list. * **`input_tables` lists a node the query never reads.** Remove it, or read it in the query. * **`input_tables` names something that is not a node of this app.** Usually a typo in the id. A node that reads no other node needs no list, and an empty `manifest.yml` is right for it. If you build apps through Vera or through an AI connector, this is handled for you: the tools require the list and are told the same rule, so the arrows are there from the first version rather than after you ask for them. ## Reading a dataset's row id [#reading-a-datasets-row-id] Every dataset node's table carries three extra columns after your own: | Column | Meaning | | ------------- | ------------------------------------------------------ | | `_row_id` | The row's stable id - the same one the data grid uses. | | `_created_at` | When the row was first created. | | `_updated_at` | When the row was last changed. | They sit AFTER every column the dataset declares, in the order you declared them, and they can never clash with one of your own columns: a column name you write always starts with a letter. `SELECT * FROM orders` returns them alongside your columns. They do not become the node's output columns, though - a SQL node's **Output columns** list ignores them, so a query that selects `_row_id` saves and runs exactly like one that does not. The row id is what lets you join a row back to something said ABOUT that row - most usefully the [validation results](./validation.mdx). The validation node is normally called `validation_errors`, but an app where one of your own nodes already holds that name gets a numbered variant instead, so use the name your app actually shows: ```sql title="orders_with_findings.sql" SELECT o.*, e.validation_error FROM orders o LEFT JOIN validation_errors e ON e.dataset_technical_id = 'orders' AND e.row_id = o._row_id ``` It is available wherever you write SQL against the app - in a SQL node's script, in the node's preview, and in the ad-hoc query box - so the same query works in all three. A dataset with no rows yet still has the columns, so a query reading them returns no rows rather than failing. Rows uploaded before row ids existed read as empty: a made-up id that looked stable would be worse than an absent one. ## Output columns and how they are shown [#output-columns-and-how-they-are-shown] The **Output columns** list shows one entry per column the query produces. You cannot add, remove or reorder them here - that is what the query decides. What you can set is how each column READS: * **Display name** and **Description**, shown in the Data tab and in any picker that offers this column. * **Type**, which decides how the column's cells are rendered and filtered. A `Currency` column shows a currency symbol and grouped digits, a `Yes / No` column shows a check or cross instead of the words true and false, a date renders in your language's format, and an email column offers a mail link. Numeric types filter with the number filter, dates with the date filter, and the rest with a text filter that suggests the values actually present in the results. * **Currency code** and **Decimals**, shown for a `Currency` or `Percent` column. ### Every column the query returns needs a usable name [#every-column-the-query-returns-needs-a-usable-name] A column's identifier comes straight from the query, so the query has to give each one a name that can be used as an identifier: lowercase letters, digits and underscores, starting with a letter, up to 32 characters. `order_id`, `revenue` and `region_2024` are all fine. Most queries already satisfy this, because selecting a column keeps its name. What does not is an expression you have not named. `SELECT count(*)` returns a column literally called `count_star()`, and `SELECT a + b` returns one called `(a + b)`. Give those an alias: ```sql SELECT count(*) AS order_count ``` Saving a node whose query returns a name that cannot be used is refused, and the message says which column it means. You will usually see it sooner than that: as soon as you run the query, a line above the results names the columns that need an alias. The alias is worth having for its own sake, not only to satisfy the rule. Later nodes and your Visualisation widgets refer to this column BY NAME, so `order_count` is something you can read and reference, while `count_star()` is not. Adding one costs nothing - you are naming the column you already meant. ### The type list is narrowed to what the query returns [#the-type-list-is-narrowed-to-what-the-query-returns] Under the type picker, a line states what the query returns for that column - text, a whole number, a decimal number, yes/no, a date, a time, or a date and time. The picker offers only the display types that fit it: | The query returns | You can display it as | | ----------------- | ------------------------------------------------------------ | | Text | Text, Long text, Email, Phone number, Dropdown, Multi-select | | Whole number | Whole number, Decimal, Percent, Currency, Year | | Decimal number | Decimal, Percent, Currency | | Yes / No | Yes / No | | Date | Date | | Time | Time | | Date and time | Date and time | The restriction is not cosmetic. A column's filter is applied by the database, so displaying a number as text would send text comparisons to a numeric column and the filter would stop working. A node that has never been run yet has no detected type, so it offers every display type until the first run tells it what the column really is. You do not have to set anything. Leave a column's type alone and it follows what the query returns, which is already better than an unformatted result: whole numbers are grouped, dates use your language's format, and yes/no values show a glyph. ### When the query changes under a display type [#when-the-query-changes-under-a-display-type] The display type is yours; the detected type belongs to the query. Rewriting the SELECT can therefore change what a column returns while its display type stays as you set it - a money column that becomes text, for instance. That never fails a run. The data is correct and only its label is stale, so refusing to show you your own data would be the wrong answer. Instead the run stays successful and says so in four places: the node's card on the canvas turns amber with a tooltip, the column shows a **Type mismatch** chip in the Output columns list, its detail pane shows an amber note, and a strip appears above the results grid. Picking a display type that fits - or changing the query back - clears all four immediately, with no re-run needed. # Validation results (/docs/analytical-apps/pipeline/validation) When you configure validations on a dataset node - "this column is required", "these two columns must be unique together", "this code must exist in that other dataset" - the platform executes them on every pipeline run and publishes the results as two nodes in your pipeline (it also runs them whenever you look at the data, which is [further down this page](#in-the-data-grid)): | Node | What it holds | | --------------------- | --------------------------------------------------------------------------------- | | **Validation rules** | One row per validation you have configured, across every dataset node in the app. | | **Validation errors** | One row per dataset row that fails a validation. | Both appear on the canvas, have a Data tab, can be read by a SQL node and charted by a Visualisation widget. What is different is that the platform writes them: they are their own kind of node, with their own icon, and they are not offered when you add a node. They open read-only and update themselves whenever you change a validation - there is nothing to maintain, and nothing to break. Opening one shows the query the platform wrote, and you can **Run** it to see the current results, exactly as you would in a SQL node. They appear the moment your app has its first configured validation, and they stay after that. If you later remove every validation, both nodes remain and simply return no rows, because your own queries may already read them. ## What the nodes are called [#what-the-nodes-are-called] Normally they are called `validation_rules` and `validation_errors`, and the examples on this page use those names. They are not guaranteed, though. If one of your own nodes already holds the name, the platform takes the next free one - `validation_rules_2`, `validation_errors_2` and so on - rather than touching your node. Your existing node keeps its name and its contents; the generated pair simply sits beside it. If you are writing a query against these nodes, check the name on the canvas first and use the one your app actually has. ## Validation errors [#validation-errors] | Column | Meaning | | ---------------------- | ------------------------------------------------------------------- | | `dataset_technical_id` | The dataset node the failing row belongs to. | | `row_id` | That row's stable id. | | `column_technical_id` | The column, for a per-column rule. Empty for a condition-tree rule. | | `rule_id` | Joins to `validation_rules.rule_id`. | | `check_type` | The check that failed. | | `validation_error` | The message describing the failure. | `row_id` is the point of the table. It is the same stable row id the data grid uses, and every dataset node's table carries it as `_row_id` (see [SQL nodes](./sql-nodes.mdx#reading-a-datasets-row-id)), so you can join a finding back to the row it belongs to - substituting the name your app actually gave the node, if it is not the usual one: ```sql title="orders_with_findings.sql" SELECT o.*, e.validation_error FROM orders o LEFT JOIN validation_errors e ON e.dataset_technical_id = 'orders' AND e.row_id = o._row_id ``` A row with no matching finding is a trusted row. A row with several failing rules appears once per rule, so a row that breaks two checks gives you two rows, told apart by `rule_id`. ### Gating a SQL node on the findings [#gating-a-sql-node-on-the-findings] The most useful thing to build from this is a node that carries only the rows nothing reported - so everything downstream of it works on data you have already checked: ```sql title="trusted_orders.sql" SELECT o.* FROM orders o WHERE NOT EXISTS ( SELECT 1 FROM validation_errors e WHERE e.dataset_technical_id = 'orders' AND e.row_id = o._row_id ) ``` **Declare `validation_errors` as an input table**, beside the dataset: ```yaml input_tables: - orders - validation_errors ``` The validation nodes always run first, before any node you write, so a query that reads them always sees the findings for the run it is part of - whether or not you declare them. What the declaration buys is the arrow on the canvas: leave it out and the node still gives the right answer, but nothing on the diagram shows that it depends on the validation results, and the next person to read your pipeline will not see the link. To gate on a specific check rather than on any failure at all, add `AND e.check_type = 'required'` (or join `validation_rules` and filter there). ## In the data grid [#in-the-data-grid] You do not have to query the node to see what failed. Open a dataset node's **Data** tab and the failing cells are marked there, with the reason on hover: a strong mark on the cell a per-column rule named, a lighter one across every cell of a row a condition tree rejected. Hovering a marked cell shows the reason and only the reason - the usual tooltip that repeats the cell's own content steps aside for it. **The marks arrive with the rows.** The grid asks for a page of data and gets that page's findings back in the same answer, checked against the very rows it is about to show you. There is no run to name and no "checked at" date to read past, because the rows and the marks come from one and the same read. So: * **Nothing is stale, and nothing is withdrawn.** Save a change in the grid, or complete an import, and the rows you touched are re-checked the next time the grid reads them: a mark you fixed is gone, a rule you have just broken appears, and every other row keeps its own marks. You do not run the pipeline to see this. * **A row someone else added or changed** is checked the same way, on the next read. * **Every failing row in front of you is marked**, at any dataset size. A row's findings travel with the row, so a block of rows is marked the moment it appears - there is no fixed first slice, no partially marked screen and nothing that arrives late. * **The line above the grid describes the whole dataset**, not the page: either no findings, or how many rows fail something. That count does not move as you scroll, search or filter. Beside the count is **Show only failing rows**, which narrows the grid to the rows that fail at least one check, and back again. A dataset that has no validations of its own shows no line at all, even when other datasets in the same app have plenty. **A run's data view and a version's data view are marked too**, each by its own answer. A run shows exactly what that run found. A version is checked by the validations as they were configured when it was published, over the data as it stood then - so a historical view is never re-judged by today's rules. If the checks cannot be run at all - the platform could not resolve one of the tables a rule reads, say - the line says the validations could not be checked and nothing is marked. **Your data is unaffected**: every row is there, in order, with the correct total. A failing validation costs you the marks, never the data. ## On a Visualisation page [#on-a-visualisation-page] The same marks appear on a **table widget** backed by a dataset, so the people who read the app - rather than build it - see what the validation found without opening the pipeline. The reason is on hover there too, and the marks are checked against the same rows the table is showing. Three differences from the Data tab, all deliberate: * **The line above the table appears only when there is something to warn about**: that findings exist, or that the checks could not be run. A clean dataset is simply shown without a strip, so a page of tables does not turn into a page of notices. * **A grouped or summarised table is not marked.** Its rows are totals over many source rows, not the rows a rule named. Neither is a table over a SQL transform: a finding belongs to a dataset row, and a transform's output rows are not dataset rows. * **There is no "show only failing rows" control.** That is the author's tool, and it stays on the Data tab; a page is laid out by the person who built it. ### While a draft is open, the published app is checked as it was published [#marks-and-drafts] An open draft makes every dataset table on the published app read the **published snapshot** of the data rather than the live rows, so that a half-finished edit of yours cannot leak to readers (see [Draft and publish](../../overview/apps.mdx#draft-and-publish)). The marks follow that exactly: the table is checked by the validations of the version it is showing, over the snapshot it is showing. Your team therefore sees what the published app says about the published data, while the **Data** tab in the editor goes on checking the live rows against the rules you are editing. Neither borrows the other's answer, and neither has to wait for a run. ## Validation rules [#validation-rules] | Column | Meaning | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `rule_id` | The rule's identity within this run - `col:..` for a per-column rule, where `n` is its position in that column's list of checks, and `tree:.` for a condition tree, where `n` is its position in that dataset's list. | | `dataset_technical_id` | The dataset node the rule is configured on. | | `column_technical_id` | The column, for a per-column rule. Empty for a condition-tree rule. | | `rule_kind` | `column`, `complex`, or `overflow`. | | `check_type` | The operator for a per-column rule, or the top-level group for a condition tree. | | `parameter` | The value the check compares against, where the check takes one. | | `error_message` | The message the rule reports when it fires. | | `compilable` | Whether the rule actually runs. | | `skip_reason` | Empty when the rule runs, otherwise why it does not. | This table is not decoration: **Validation errors** reads `check_type` and `error_message` from it. So editing a condition-tree rule's message changes what your findings say, without anything else changing. ## What each check means [#what-each-check-means] Every check below shares one rule, which is worth reading before the table: > **A blank value passes every check except `Required`.** A blank value is an empty cell, or one holding only spaces. Without this rule an optional column with a pattern check would report every empty cell as an error, which is not what "optional" means. If blanks matter for a column, add `Required` beside the other check. | Check | Fails when | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Required | The cell is empty. What "empty" means per column type is the table below. This is the only check a blank fails. | | Unique | Another row holds the same value. Every copy of a duplicate is reported, not just the later ones. Blank cells are never duplicates of each other. | | Equals | The value is not equal to the one you gave. | | Greater than | The value is not greater than the one you gave. | | Less than | The value is not less than the one you gave. | | Greater or equal | The value is below the one you gave. | | Less or equal | The value is above the one you gave. | | Matches pattern (`like`) | The text does not match the pattern. `%` stands for any run of characters, `_` for one character. | | Matches pattern (any case) (`ilike`) | The same, comparing without regard to upper and lower case. | | Starts with | The text does not start with the value you gave. | | Ends with | The text does not end with the value you gave. | | Matches expression (`regex`) | The text does not match the regular expression. | Two consequences follow from these, and both are deliberate: * **A value that cannot be compared fails a comparison.** If a column holding `abc` carries "greater than 0", that row is reported. The value cannot be compared, and an uncomparable value is not greater than zero. * **A check whose answer is unknown counts as a failure.** The platform never reads "unknown" as "fine". ### Which checks a column offers [#which-checks-a-column-offers] The checks a column offers follow from what that column holds, so a date is compared against a date and a yes/no against a yes/no: | Column type | Required | Unique | Equals | Greater / less than | The five text patterns | | ------------ | -------- | ------ | ------ | ------------------- | ---------------------- | | Text | yes | yes | no | no | yes | | Long text | yes | yes | no | no | yes | | Phone | yes | yes | no | no | yes | | Email | yes | yes | no | no | yes | | Integer | yes | yes | yes | yes | no | | Decimal | yes | yes | yes | yes | no | | Percent | yes | yes | yes | yes | no | | Currency | yes | yes | yes | yes | no | | Year | yes | yes | yes | yes | no | | Date | yes | yes | yes | yes | no | | Time | yes | yes | yes | yes | no | | Timestamp | yes | yes | yes | yes | no | | Boolean | yes | no | yes | no | no | | Dropdown | yes | yes | yes | no | no | | Multi-select | yes | no | no | no | no | | Attachment | yes | no | no | no | no | The rule behind the table: **Required** is offered everywhere; **Unique** wherever a cell holds one value that could identify a row (so not on a boolean, which has two values, nor on a multi-select or an attachment); **Equals** wherever there is one exact value to compare against; the ordered comparisons wherever values have an order; and the five text patterns on the four free-text types. On a text column, **Matches pattern** with no `%` already IS an exact match, which is why Equals is not offered there as a second way to spell the same check. **A check you configured earlier is never removed.** If you change a column's type after configuring its checks, the checks stay, keep running, and keep showing in the editor - they are simply no longer offered as something to add. ### What to type as the value [#what-to-type-as-the-value] A comparison's value has to be a value the column itself could hold: | Column type | Type this | Example | | ----------------------------------- | --------------------------- | --------------------- | | Integer, Decimal, Percent, Currency | a number | `1500`, `-3.5` | | Year | four digits | `2026` | | Date | `YYYY-MM-DD` | `2026-01-01` | | Time | `HH:mm` or `HH:mm:ss` | `12:30` | | Timestamp | `YYYY-MM-DDTHH:mm:ss` | `2026-01-01T08:00:00` | | Boolean | picked from the two options | `True` | | Dropdown | the option's exact text | `EU` | | Text, Long text, Phone, Email | any text (the pattern) | `A%` | A value outside that format leaves the rule **skipped** rather than guessed at: it appears in **Validation rules** with `compilable` false and `skip_reason` `operand_type_mismatch`, and every other rule keeps running. The editor marks the field as you type, so you normally see it before you save. Two details worth knowing: * The value is stored normalised, and that is what **Validation rules** reports back in `parameter`: `12:30` becomes `12:30:00`, `2026-01-01T08:00:00` becomes `2026-01-01 08:00:00`, and `YES` becomes `true`. * Decimal, Percent and Currency compare as floating-point numbers. A value carrying more precision than that can represent may not compare exactly equal, so **Equals** on a money column is best kept for round amounts. ### What "Required" means, per column type [#what-required-means-per-column-type] Required is one check - "the cell is not empty" - and what empty means follows from what the column stores: | Column type | Required fails when | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Multi-select | No option is selected. A multi-select cell stores its options as one text, so no selection stores nothing at all - "at least one option selected" is exactly what Required means here. | | Boolean | The cell is empty. `false` is a VALUE and PASSES: to require the box be ticked, use **Equals** `True`. | | Attachment | The cell holds no attachment. | | Dropdown | No option is picked. | | Everything else | The cell is empty or holds only spaces. | A cell that holds the literal text `[]` in an attachment column is NOT treated as empty. The "blank" rule is one rule for every column type, and giving attachments their own would be a second rule to keep in step with it. ### Condition trees [#condition-trees] A complex validation combines checks into a tree, and the groups mean: | Group | Fails when | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | AND | Any child check fails. | | OR | Every child check fails. | | NOT | The child check passes. | | UNIQUE | Another row holds the same combination of the listed columns. A row where ANY of those columns is blank is exempt - a partial key is not a key. | | REFERENCE | The row's value does not exist in the target dataset's column. A blank source value is exempt. The comparison ignores surrounding spaces, so `" 1"` matches `"1"`. | A check inside a tree behaves exactly like the per-column version, blank rule included - which has one consequence worth knowing: > `NOT (column matches "x%")` **fires on a blank column**, because the inner check > passes on a blank and `NOT` inverts it. If that is not what you want, put the check inside an `AND` beside `Required`. ## When a rule cannot run [#when-a-rule-cannot-run] A rule the platform cannot turn into a correct check is never run as a broken one. It is skipped, and the skip is visible: the rule is still a row in **Validation rules**, with `compilable` false and a `skip_reason`. Every other rule keeps running, and the pipeline run itself is unaffected. Find them with a SQL node, a Visualisation widget or an ad-hoc query: ```sql title="skipped_rules.sql" SELECT rule_id, dataset_technical_id, column_technical_id, check_type, skip_reason FROM validation_rules WHERE NOT compilable ``` | `skip_reason` | What to do | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `operand_missing` | The check needs a value to compare against and none was given. Fill it in. | | `operand_not_numeric` | A check on a number column was given something that is not a number. Correct the value. | | `operand_type_mismatch` | The value is not one the column's type could hold - a date that is not `YYYY-MM-DD`, a year that is not four digits, a time outside 24 hours, a yes/no that is neither. See "What to type as the value" above. | | `column_not_found` | The rule names a column the dataset no longer has. Point it at an existing column, or remove it. | | `reference_target_not_found` | The dataset the reference points at no longer exists. | | `reference_target_not_dataset` | A reference must point at a dataset node, not at a SQL node or an import. | | `reference_column_not_found` | The reference names a column the target dataset does not have. | | `tree_incomplete` | A group has no children, a `NOT` has more than one, a `UNIQUE` lists no columns, or a `REFERENCE` maps no columns. Finish the rule. | | `rule_budget_exceeded` | See below. | ### The overflow row [#the-overflow-row] There is a ceiling on how many validations one app's generated nodes can represent. An app that goes past it gets one extra row in **Validation rules** with `rule_id` `overflow`, whose `parameter` is how many configured validations are not represented and whose message says so. The validations that did not fit are simply absent; your dataset still saves normally. If you see it, the fix is to have fewer, more meaningful validations - or to split the datasets across apps. ## Things this does not do [#things-this-does-not-do] * **A failing validation never blocks a write.** Validation is observational: a run reports what is wrong, it never refuses your data. You decide what to do about it. * **There is no warning level.** Every configured validation is an error when it fires. * **The stored messages are in English.** They belong to a shared table that has no reader's language at the moment it is written. The structured columns beside them (`check_type`, `parameter`, `column_technical_id`) are what a translated view renders from - which is what the data grid does: its cell reasons are in your language for every per-column check, and are the message you typed yourself for a condition tree. # Skills & the AI widget (/docs/analytical-apps/skills) A **skill** is a reusable, app-specific AI task that you author once and offer to other people as a one-click action. The motivating example is an emission-factor app: a user types a material description, and the skill walks the factor taxonomy and returns the best-matching factor with its value and source. Other apps have other skills. Skills are first-class parts of your app, managed on the **Skills** tab of the [Knowledge Base](./knowledge-base.mdx) panel and versioned, drafted, and published together with everything else in the app. One definition reaches two audiences: * **In-app end users** run a skill from an **AI widget** on the Visualisation canvas and read the answer inline. * **External AI clients** (your own AI connected to your workspace) can invoke the same skill as an **MCP prompt**. ## What a skill is made of [#what-a-skill-is-made-of] A skill carries a little structured metadata on top of a markdown body: * **Name** - the display name shown as the widget title and in pickers. * **Technical ID** - a stable, lowercase id (for example `find_emission_factor`). It is fixed once the skill is created, because the widget and the MCP prompt both refer to the skill by it. The display name stays editable; to change the technical id itself, delete the skill and create a new one. * **Description** - a one-line summary shown under the widget and to external AI clients. * **Inputs** - the questions the skill asks the user. Each input has a label (what the user sees), an argument key (a lowercase id), an optional helper text, and a required flag. A skill can have zero inputs (just a Run button) or several. * **Expose as an MCP prompt** - on by default; turn it off to keep a skill in-app only. * **Instructions** - the body, written in plain language in the same rich text editor as Knowledge Base pages and stored as markdown. This is the task you are giving the AI ("You help the user map a material to an emission factor. Filter by domain, then activity, and return the single best match with its factor value and source."). ## Authoring a skill [#authoring-a-skill] Open your analytical app in the editor, open the **Knowledge Base** panel, and switch to its **Skills** tab. The left rail lists the app's skills; the right side is the editor. 1. Click the add button to start a new skill. 2. Type a **name**. The technical id is suggested from the name; you can adjust it while creating, but it is fixed afterwards. 3. Write a short **description**. 4. Add the **inputs** the skill needs, one row each. Mark the ones that must be filled in as required. 5. Decide whether to **expose it as an MCP prompt**. 6. Write the **instructions** in the body editor. 7. Save. Skills are part of your app configuration, so they are edited on a draft and become available to end users and external AI clients when you **publish** the app. When you pin a past version in the app's history, the Skills section shows that version's skills read-only. See [Draft and publish](../overview/apps.mdx#draft-and-publish). ## Adding an AI widget [#adding-an-ai-widget] An AI widget lets end users run a skill from a Visualisation. 1. Open the **Visualisation** section and add an **AI** widget from the palette. 2. In the widget settings, pick the **skill** it should run. The widget title defaults to the skill's name; you can override it. 3. Publish the app so your viewers see the widget. On the published Visualisation the widget shows one field per skill input and a **Run** button. A short line under the button tells the viewer how long a run may take: a skill is **bounded to about 30 steps and about 150 seconds**, so it always finishes promptly rather than spinning. Running the widget is **read-only**. Vera reads existing configuration and queries published data to answer. It cannot edit data, change the pipeline or Knowledge Base, publish, or create a draft, even when the user has editing permissions. Previewing a skill reads an existing draft without changing it. If a task needs a change, Vera explains briefly that the widget cannot make it. Widget answers are separate from the user's conversation in the editor's Vera panel. ### What the viewer sees while it runs [#what-the-viewer-sees-while-it-runs] While the skill runs the widget stays transparent. It shows: * the **current action** Vera is taking (for example "Querying the data" or "Inspecting the node columns"), * an **elapsed timer** counting up from the start, and * a **live step trace** - the sequence of actions Vera has taken so far, including the SQL queries it runs against your data. This means a viewer can see, at a glance, what is happening and roughly how far along the run is. ### The result [#the-result] When the run finishes the widget leads with **the answer**, rendered as formatted text. Below the answer a collapsible **"How Vera got this"** section reveals the full step trace and a subtle line showing the AI credits the run used, so a viewer can check how the answer was reached. The widget makes each outcome distinct: * **Answer ready** - the answer is shown, with the "How Vera got this" trace available and a **Run again** action. * **No answer** - the run finished but Vera did not produce a result. The widget says so plainly and offers **Try again**. It never falls back to unrelated text. * **Ran out of steps** - the run reached its step or time limit before finishing. The widget shows the partial result, notes that it was cut short, and offers **Try again** (try a more specific request). * **Error** - something went wrong; the message is shown with **Try again**. If the assistant needs more information it asks a follow-up question right in the widget, and the person who started the run answers to continue. Running a skill uses AI, so the person running it needs AI access on the app. Every role but **Viewer** has it. Someone with the **Viewer** role sees the widget's inputs disabled and a line saying that running it needs AI access, with no **Run** button. If their access could not be checked (for example on a connection problem), the line says so instead. An administrator can also switch a person's AI off in their **AI permissions**; the widget then explains that AI is not enabled for them. ## Skills as MCP prompts [#skills-as-mcp-prompts] Every published skill that has **Expose as an MCP prompt** turned on is available to your own AI over your workspace's MCP connection as a prompt named `{app_technical_id}.{skill_technical_id}` (for example `emission_factor_hub.find_emission_factor`). The skill's inputs become the prompt's arguments. Your AI client fetches the prompt, fills in the arguments, and runs it against the tools your workspace exposes. Skills that are still on a draft, or that have the toggle turned off, are not offered as prompts. A skill's instructions are your workspace's own content, so the prompts are covered by the same AI privileges as everything else: the connection needs **List skills** to see them and **Get skill** to fetch one. Both are part of the standard read permissions. If a connected AI reports no prompts even though you have published skills with the toggle on, that connection is missing those two - a workspace admin can add them on the **AI privileges** settings for that person, and the prompts appear on the next request without reconnecting. ## Built-in prompts [#built-in-prompts] Beyond skills you publish yourself, your workspace's MCP connection also offers five ready-made prompts covering common tasks, available on every workspace: `create-node` (create a pipeline node on an app's draft), `run-pipeline` (run an app's pipeline and report the result), `query-data` (answer a data question over an app's data with SQL), `collect-indicator-values` (walk through entering missing indicator values for an open reporting period), and `report-status` (summarise reporting-period completion for an indicator app). Each asks for anything it needs, such as which app, if you have not said already. Each of these needs its own AI privileges to appear, the same as a tool: your AI client only sees the ones your effective permissions cover, whether or not you have published any skills of your own. See [Connect your own AI](../integrations/mcp.mdx) for how to connect an external AI client to your workspace. # Visualisation (/docs/analytical-apps/visualisation) The **Visualisation** is where your team works with a finished analytic app. It is built from **widgets** on a grid, tables, charts, text, and inputs, that turn the results of your pipeline into something people can actually use. It is more than a report to read: from the Visualisation your team can run the app, see the latest run results, filter the data, type values back into a table, and download what they need. An app can hold more than one page of widgets, so a single app can cover several related topics. ## How the Visualisation is built [#how-the-visualisation-is-built] You build it in the editor, on the **Visualisation** tab. Add widgets, arrange them on the grid by dragging and resizing, and give each data widget a **data source**: the pipeline step (called a node) whose results it should show. A Table, Chart, Pivot or Figure widget can read any node that holds data, and so can a parameter whose dropdown options load from a node: a [dataset](./pipeline/datasets.mdx), a [SQL node](./pipeline/sql-nodes.mdx), and the two nodes that import data from elsewhere, [indicator data](./pipeline/indicator-data.mdx) from one of your indicator apps and [analytic app data](./pipeline/analytic-data.mdx) from another analytic app. You can show imported figures directly, without a SQL node in between. An imported node gets its columns from the pipeline's last successful run, so run the pipeline once after adding one: until then the widget's column settings ask you to run it first. For data to appear, the pipeline must have run at least once. If a widget prompts you to run the pipeline, switch to the **Data Pipeline**, click **Run**, and the widget fills with fresh results. See the [Pipeline overview](./pipeline/overview.mdx) for how running works. While you work, all of this lives in your private draft. Click **Publish** to hand the new layout to your team; until you do, they keep seeing the previous one. See [Draft and publish](../overview/apps.mdx#draft-and-publish). ## When the Visualisation shows nothing [#empty-visualisation] An app your team opens shows results from the latest **published** run. When there is no such run, the widgets are empty and a notice at the top of the page says which of three things is missing, and what fills it: | The notice says | What it means | What fills it | | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | Not published yet | The app has no published version, so your team has nothing to see. Running it in this state only runs your draft. | **Publish** the app from the editor (a member with the **App builder** or **Admin** role on the app). | | Not calculated yet | The app is published but its pipeline has never run. | Click **Run** on the Visualisation, or run it from the Data Pipeline. | | No run has produced results yet | The pipeline has run, but no run has produced results for your team. A preview run of the draft keeps its results private to the editor; a run that failed or is still going has none to show yet. | Click **Run** on the Visualisation - it runs the published version. | If there is no notice and a widget is still empty, the app IS populated and the question is the data itself: the filter or query matched no rows. The **Run** button appears only for people who may run apps. Everyone else sees the same notice ending in "Ask a workspace administrator...", because the run would be refused for them. ## When a widget names a column that no longer exists [#missing-column] A chart or pivot that groups its data names the columns it groups by and the columns it adds up, and how it adds each one up. If one of those columns is not in the widget's data source any more - it was renamed or removed in the pipeline, or Vera or an import created the widget with a column that was never there - or a summary names a function other than sum, count, avg, min or max, the widget does not guess. It shows a short message instead of data: | Where | What you see | | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Pivot | "This widget's grouping can't be read: it names a column its data source doesn't have, an aggregate with no column, or a function other than sum, count, avg, min or max. An editor can fix it in the widget's settings.", in the pivot's grid. | | Chart | "This chart's grouping can't be read: it uses a column its data source doesn't have, or a function other than sum, count, avg, min or max. An editor can fix it in the chart's settings.", in place of the bars or slices. | | Report | A pivot block keeps its column headings, and the line under it says "The grouping can't be read: it names a column its data source doesn't have, or a function it can't apply" where the row count would be. A chart block shows the chart's message. | There is no **Try again**: asking again gives the same answer until the widget changes. To fix it, open the widget's settings in the editor. The column that no longer exists shows as an empty choice: pick one of the data source's columns there, or remove that row with its **X**. The widget reads its data again and draws as soon as the answer arrives. People reading the published app see the message until you [publish](../overview/apps.mdx#draft-and-publish) the corrected version. ## When a widget uses an unsupported feature [#unsupported-feature] A widget whose saved settings use a feature Ovidens no longer supports shows "This widget uses a feature that is no longer supported" in its own space. The other widgets on the page keep working. The same notice appears wherever a report or printout places that widget. Vera and connected AI clients receive the notice as a note, with no data for that widget. An unsupported widget does not stop you editing the rest of the Visualisation: the editors keep saving your other changes and leave that widget exactly as it was saved. To clear the notice, remove the widget or replace it with a new one. If an edit leaves a widget with a setting its type does not accept, a persistent banner names the widget and its page, and changes are not saved until you correct or remove it. Each widget accepts only the settings for its own type. When Vera, a connected AI client or an app import supplies an invalid setting, Ovidens refuses the write and names the widget and the field. Choosing a different type in the editor starts with that type's settings. ## Widget settings for AI clients [#widget-settings] The tool schema describes nine authored widget types selected by `type`. All share `id`, `type`, `title`, `description`, `descriptionAsTooltip`, `width` and `height`. Widths are `full`, `half`, `third` or `quarter`; heights are steps 1 through 4 or `auto`. Each alternative adds only these fields: | Type | Settings | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `table` | `nodeId`, `fixedFilters`, `parameterOptOut`, `visibleColumns`, `pinnedColumns`, `linkColumns`, `columnWidths`, `editable`, `contextSources` | | `chart` | `nodeId`, `fixedFilters`, `parameterOptOut`, `chartType`, `xColumn`, `chartGrouped`, `chartStacked`, `chartOrientation`, `chartLabelAngle`, `chartCategoryOrder`, `chartShowLegend`, `chartCategoryLabels`, `chartAggregates` | | `pivot` | `nodeId`, `fixedFilters`, `parameterOptOut`, `tableGroupBys`, `tableAggregates`, `columnWidths` | | `stat` | `nodeId`, `fixedFilters`, `parameterOptOut`, `statColumn`, `statAggregation`, `statFormat`, `statComparisonFilters` | | `parameter` | `params` | | `sql` | `nodeId`, `textContent` | | `text` | `textContent`, `textAlign` | | `ai` | `skillTechnicalId` | | `knowledge_base` | `kbItemKey` | Nested entries also have their own schemas. Filters use `id`, `column`, `operator` and `value`; aggregates use `id`, `column`, `function` and an optional `label` (a chart aggregate can also have `color`). Aggregate functions are `sum`, `count`, `avg`, `min` and `max`. A table has no grouping settings: use a pivot to summarise rows. The tool schema states which fields can be omitted. Omitted settings on an existing widget are kept; a new widget starts with its type's defaults. An unsupported widget is a read-only fallback, outside the tool schema. A write that leaves it as it is - sending it back as exactly its `id` and `type`, or not naming it in an edit - keeps it exactly as stored, so it never blocks a change to another widget. Sending it back with all its stored settings is refused. A write that changes it without making it valid is refused, naming the widget and its settings: remove it, or change its `type` to rebuild it. Changing a widget's type keeps its title, description and size and, when the new type reads data, its node, fixed filters and parameter opt-out; every other setting starts from the new type's defaults. An AI write is limited in length for what it adds or changes: a new page or widget id is at most 64 characters, a widget title 200, a page name 120 and a widget description 2,000. A value sent back unchanged is not limited, so a longer name someone typed in the editor stays. A text widget's content has no limit. ## The widgets [#the-widgets] Every widget is added in the editor and positioned on the grid. Table, Chart, Pivot and Figure widgets use a pipeline node as their data source; the other types have the settings described below. ### Text [#text] The **Text** widget puts formatted text on the page: headings, short notes, and instructions. Use it to give the page structure and to explain what people are looking at, for example a title, a note on where the data comes from, or how to use a filter. It shows no data, so it needs no data source. ### Table [#table] The **Table** widget shows your data as rows and columns, straight from a node. You choose which columns appear. To summarise rows by group, use a [pivot](#pivot). A table works in one of two ways: * **Read-only** tables display data and cannot be changed, the usual choice for a report. * **Data-entry** tables let people type values directly into the table from the app. Edits are saved when the pipeline runs. This is how a Visualisation becomes a place to enter data, not just read it. Only a data-entry table offers **Import** for loading a file into it, and in the editor its header carries an **Editable** label so you can tell it apart from a read-only table at a glance. A table with fixed filters is always read-only: its settings show a short note saying why instead of the **Editable** switch. Adding fixed filters also switches **Editable** off, and removing them later leaves it off, so switch it back on if the table should take entries again. Anyone whose role lets them enter data in the app - **Contributor**, **App builder** or **Admin** - can edit the rows of a data-entry table: change a value, add a row, or delete one. Each change is recorded in the audit trail under the name of the person who made it. **Import**, loading a file into the table, stays with workspace administrators, so other members do not see it. Tables respect your [parameters](#parameters): with a region filter set to "North", the table shows only North rows, and a small label shows which filter is active. A table keeps the rows it holds in view. With an automatic height it grows with its rows, and while someone is editing it (**Edit**) it grows a little more to make room for the **Save** and **Cancel** buttons and the **New row** strip, so a table filtered to the one or two rows being corrected keeps them on screen. With a fixed height the card keeps its size; if it is too small for the table, the table scrolls inside the card, under the widget's title, so every row, the page controls and **Save** and **Cancel** can still be reached. With an automatic height, a table is never taller than the window it is read in. It shows as many whole rows as fit below its title, together with its page controls and its **Show all** button, and always at least five rows, so in a very small window the page scrolls instead. A [pivot](#pivot) with an automatic height does the same. Resize the window or zoom the browser and the table follows, showing more or fewer rows, without reloading anything. **Show all** shows up to 100 rows at once, and the page scrolls to reach them. If a table's data cannot be loaded, the table says so in its own space, where the column names and rows would be, with a **Try again** button that asks again. The message stays while the request is repeated; then the rows come back, or the message shows again if the data still cannot be loaded. Search, sort and filter stay available, because changing any of them asks for the data again. **Edit** and **+ Row** are unavailable until the data loads, and anything typed but not yet saved is kept. A table can be downloaded from the widget itself. You choose CSV or Excel - the Excel file keeps the column types and the percent and currency formatting the table shows on screen, so it opens ready to sort and total. See [Download the data](pipeline/datasets.mdx#download-the-data). Which rows the file holds depends on where the table is. A table in the [Audit Analytics](../administration/audit-trail.mdx) app, or on a survey's **Results** page, downloads the rows it shows: its filters (including the ones a link set when it opened the page already narrowed to one resource), the page's [parameters](#parameters) and the search all apply, so a table showing 312 rows downloads 312. The file carries every column of the table's data source, not only the ones on screen, and its rows come in the table's own default order rather than one you sorted them into. A table in any other app downloads its whole data source, whatever is filtered on screen. A spreadsheet holds about a million rows, so Excel is refused for a bigger table, with a message saying so - choose CSV for it. #### Files in a table [#table-attachments] A table over a dataset with an **Attachment** column shows each row's files exactly as the dataset's own data view does. Click the file icon in a cell to see the files with their names and sizes, download one (you are asked what to name it first) or see an image in a larger view. Downloading works on every table, read-only ones included, for anyone who can read the app. On a data-entry table, administrators can also add and remove files while editing (**Edit**): choose **Add file** on an empty cell or the paperclip on a filled one, then pick files or drag them onto the upload dialog. Each file can be up to 10 MB; a bigger one is refused in the dialog and nothing else you changed is lost. To replace a file, remove it and add the new one. A file is uploaded when you confirm the dialog, but it becomes part of the row only when you choose **Save**; **Cancel** drops it with the rest of the unsaved changes. Other readers of the app can download files but not add or remove them. See [Files in an attachment column](pipeline/datasets.mdx#attachments) for what happens to a file that is removed. #### Context columns [#context-columns] **Context columns** put facts from other datasets beside each row, so the people correcting a table see what they need without leaving it. A table over a dataset, read-only or data-entry, can carry up to 10 of them in total, shown after its own columns with a lock in the header that names the dataset each one comes from. Context columns come from **context from a dataset**: you choose a dataset of the same app, and how its rows match this table's rows, once, and then pick as many of its columns as you need. Every column of one context reads the same matching rows. Each column is one of: * **Value** shows a column of that dataset from the first matching row: each order's product category from your products dataset, for example. * A **function** counts or adds up all the matching rows: how many customers bought each product, or the revenue of all its orders, for example. The functions are **Count**, **Count distinct**, **Sum**, **Average**, **Minimum** and **Maximum**, and that list is complete: a calculation beyond them, such as a ratio or a count of only some rows, belongs in a SQL node. One context can mix both: from your orders dataset, the first customer, the number of customers and the revenue, all matched on the product. To add context, open the table's settings and find **Context columns** in the Data group, under the columns. Click **Add context from a dataset**. Choose **From dataset**, then **Match rows where** a column of this table equals a column of that dataset (**Match another column** adds a second or third pair). Then, for each column, choose its **Column** and its **Function** - **Value** or one of the functions - and give it a **Name**; **Add column** adds another column from the same dataset. The editor offers only the functions the dataset has a column for and, per function, only the columns it can use. Renaming a column, or changing its function, keeps it the same column. If you later switch a context to another dataset, the columns that dataset also has stay - found by the same column, or by a column of the same name - and the others go; removing a context's last column removes the context. One context is open for editing at a time. **How values match.** A dropdown is compared by the label you see. A number and a text are compared as numbers, so `1` matches `"1"`, `"1.0"` and `"1.00"`, while a text that is not a number matches nothing - that includes a number written with a decimal comma, such as `1,5`. A date and a timestamp are compared by the calendar day, so the date 5 January 2024 matches every timestamp on that day. A text and a date or a timestamp are compared as that date or timestamp: `2024-01-05` matches the date 5 January 2024, and against timestamps it matches only midnight of that day. Two timestamps must be exactly equal. Rows of the other dataset whose match value is empty, or does not read as the type it is compared as, are left out. **What a column shows.** A row with no match shows 0 for a count and stays empty otherwise. A row whose own match value is empty shows empty in every column, counts included, because "no customer" is not "no orders". **Count** without a column counts the matching rows, and with a column counts its non-empty values. **Average** is the mean of the non-empty values. **Minimum** and **Maximum** of a text compare by character code, so `Zebra` comes before `apple`, and a dropdown compares by its label, not by the order of its options. A sum keeps its column's type: a sum of a currency column shows that currency, a sum of a percent column a percent, and a sum of whole numbers a whole number; an average keeps a currency or a percent and is otherwise a decimal. A Value, a minimum and a maximum of a currency or percent column show its format too. **What is refused.** **Sum** and **Average** take a number, currency or percent column, but not a year: the sum of years is not a number anyone reads. A multi-select column holds several options in one cell, so it cannot be a match column, and **Count distinct**, **Minimum** and **Maximum** cannot read it; **Count** and **Value** can. Context columns sort, filter and search like any column, but they are never edited: they are not part of **Edit**, the new-row form or the record panel, and the text filter suggests no values for them. They always show, since hiding and pinning apply to the table's own columns. Their values come from the same data as the rows - your draft in the editor, the published version for your team - and follow an edit the next time the table loads, with no pipeline run. A download carries the table's own columns only. When a match value appears more than once in a context's dataset, a **Value** column shows the first row's value and an amber **!** in its header says how many values repeat; a function reads every matching row and shows no warning. A column that cannot be read - its dataset or column was deleted, or it asks for something refused above - stays empty with a red **!** in the header saying why, while the rest of the table loads as usual. [Vera](../tips/interact-with-vera.mdx) can add context columns too; one that names something that does not exist, or asks for something refused above, is refused with the reason. ### Chart [#chart] The **Chart** widget draws your data as a **bar**, **line**, **area**, **scatter**, **pie** or **donut** chart, so a trend or a ranking is obvious at a glance. Bar compares values across categories, line and area show change over time, pie and donut show how a whole splits into parts, and scatter plots two number columns against each other, to show how one measure moves with another. A chart needs a data source plus the column that labels each point or bar, the column that supplies the values, and optionally more than one series to compare. You can **group by a category**, and the chart redraws to match your [parameters](#parameters). A scatter's horizontal axis and each of its series must be **number** columns: over a text or date column there is nothing to plot, so the chart says so instead of drawing an empty plot. A scatter can carry several series, each plotted at its own values. A chart saved with a type Ovidens does not draw, by another tool for example, names that type in place of a drawing until an editor picks a chart type in its settings. A pie, a donut, or a grouped bar, line or area chart whose label or value column is no longer in its data source shows a message instead of a drawing, and an editor fixes it in the chart's settings. See [When a widget names a column that no longer exists](#missing-column). Each series can also carry its own **label**: open the pencil on the series row to type one (or clear it back to the column name with Reset), and the legend and the tooltip both use it - handy when two series aggregate the same column in different ways. Five settings control how a chart reads: * **Stacked** (bar and area) places the series on top of one another instead of side by side, so each bar shows a total that is split into parts rather than several values to compare. * **Bars** (bar only) chooses vertical columns or horizontal bars. Horizontal is the one to reach for when your category names are long: they are read down the side, where there is room, instead of packed across the bottom. * **Label angle** (vertical columns only) turns the category labels flat, at an angle, or upright. Turning them fits far more categories across the bottom. When there are still more labels than will fit legibly, the chart shows every second or third one rather than overlapping them. * **Category order** decides the order the categories are drawn in, and it changes what the chart says: left to right as the data came back, A to Z, highest value first, or lowest first. Highest-first turns a chart into a ranking. Line and area charts offer only the first two, because their horizontal axis usually runs in time and sorting those points by value would destroy the trend they exist to show. * **Legend** (every chart type) shows or hides the colour key naming each series under the chart. It is on by default; switch it off when the tile is tight and the series are obvious - pie and donut charts included, whose legend is how their categories are read. ### Pivot [#pivot] The **Pivot** widget groups rows together and shows a summary for each group. An editor sets it up in the pivot's settings: one or more **group-by** columns (such as region) and one or more summary columns, each a sum, count, average, minimum or maximum of a column (such as the sum of revenue). A year column offers count, minimum and maximum only, because a sum or average of years is not a number anyone reads. People reading the page see the result, one row per group. Use a [table](#table) when people need the detail, and a pivot when they need the totals. A new pivot starts on a default so it shows something straight away: it groups by the first column that is not a number and adds up the first number column that is not a year. The default only fills a part you have left empty. A column you chose is always used as you chose it and never quietly swapped for another, so if it is no longer in the data source the pivot shows a message instead of a grid, and an editor fixes it in the pivot's settings. See [When a widget names a column that no longer exists](#missing-column). **Formats.** A pivot shows its figures the way the data source defines them. A sum of a currency column is shown in that currency with its decimal places, and a sum of a percent column with its decimal places and a % sign. A count is always a whole number, because it counts rows. An average of whole or decimal numbers is rounded to two decimal places. The earliest or latest date of a date column is shown as a date, and a year is shown as a year (2023, never 2,023). A group-by over a dropdown column shows the option's name and colour. **Every group, page by page.** A pivot shows every group its data has, a page at a time, and its pager says how many groups there are. **Searching, filtering and sorting.** The search box finds groups by the values of the group-by columns, and a dropdown group-by by the option names you see. A filter on a group-by column narrows the rows before the summaries are taken, so the totals follow the filter. A filter on a summary column keeps the groups whose summary passes it, such as the regions whose revenue is above 10,000. Sorting by a summary column puts the groups in order of their summary. The pivot's **Fixed filters** (its settings' Data group) and the page's [parameters](#parameters) narrow the rows before the summaries are taken too. **Full screen and download.** Open a pivot in full screen from its header (in the editor, from the buttons over the tile); your search stays as it was. The download holds every group the pivot shows under its current search, filters and sort, under the same column headers. Figures are written as plain numbers so a spreadsheet can calculate with them; an Excel file keeps a currency or percent column's format, and a date is written as a date (2024-01-01). If a pivot cannot read its data, for example because the query took too long, it says so in place of the grid and offers **Try again**. A report block shows a pivot exactly as the tile does, formats, years and dropdown names included. ### Naming a summary column [#aggregate-labels] Each summary column of a pivot can have its own header. In the widget's settings, open the pencil button next to the summary and type the header into **Column header**, for example "Revenue in EUR". The header is shown above that column on the page, in full screen, in reports and in a downloaded file; the small badge with the summary (SUM, AVG, ...) stays beside it. Leave the field empty, or choose **Reset**, and the column takes its data source column's name again. A report and a download have no badge, so there an unnamed summary column is headed by the column's name and its summary, such as "Amount (sum)" beside "Amount (max)", and a filter on it reads the same way above the page. ### Parameters [#parameters] A **Parameter** widget adds input controls, a dropdown, text box, number field, date picker, or range, that let people filter or adjust the page without changing anything you built. Rather than build one page per region, you add a region parameter: people pick a region and every table and chart updates to match. One page serves everyone. Connect a parameter to the **column** it filters on, and provide a dropdown's options either as a fixed list or loaded live from a pipeline node. A parameter filters every widget that uses the matching column, so one control can drive the whole page. A widget that should always show everything (for example a grand total) can be set to ignore parameters. ### SQL [#sql] The **SQL** widget puts a query box on the page so someone comfortable with SQL can ask ad-hoc questions of the pipeline output. Pick a node as the source and it fills in a starter query to adjust. A query can name a dataset or a SQL node, but not an imported node (indicator data or analytic app data): to query imported data, add a SQL node after the import in the pipeline and query that. If you are not comfortable with SQL you do not need this widget: describe your question to [Vera](../tips/interact-with-vera.mdx) instead, or use the other widgets, which cover most needs without a single query. A query here reads **only this app's own nodes**, named directly by their id: a name with a prefix, another app's table, a system table, a file path or a table function in `FROM` is refused with a message saying what to change, and so is a function that reads settings or the catalog, runs SQL given as text, or changes something. The full rule is in [What the Run button can read](./pipeline/sql-nodes.mdx#what-the-run-button-can-read). Writing and running a query of your own is a permission of its own, separate from reading the app. It comes with the **Contributor**, **Analyst**, **App builder** and **Admin** roles on the whole workspace - a role on a single space or app does not give it - and you always hold it in your own personal space. Someone who may only view an app sees each SQL widget's results - the rows of the query saved on the widget - without the query box, so they read what the page's author wrote and cannot change it. In the editor, someone who may change the app but not run queries edits the saved query with no **Run** button, and a short note says why. ### AI [#ai] The **AI** widget runs a curated AI [skill](./skills.mdx) and shows the answer right on the page. Its only setting is which skill it runs; the widget takes the skill's name as its title. If the skill asks for something, for example a region or a date range, the widget shows one labelled box per question, so the reader fills those in and clicks **Run**. While it works you see what Vera is doing, how long it has been running, and how far it may go. When it finishes you get the answer, with a foldout showing how Vera arrived at it. It needs no data source: the skill already knows which data to look at. A reader whose account is not allowed to use AI sees the widget explain that instead of a Run button. ### Knowledge Base [#knowledge-base-widget] The **Knowledge base** widget embeds a single page from the app's [Knowledge Base](./knowledge-base.mdx) on the Visualisation, rendered read-only. Use it to keep guidance next to the numbers: what the data means, how the app is meant to be used, or the rules behind a figure, so people do not have to leave the page to find it. It shows no pipeline data, so it needs no data source; you simply pick which page it displays. # Build with Vera (/docs/indicator-apps/build-with-vera) **Vera** is the AI assistant built into Ovidens. Describe what you want to create, change or understand in your indicator app. Vera uses the tools available to you for that request, without requiring a prepared knowledge base or a fixed setup sequence. ## How to start [#how-to-start] 1. Open your indicator app, click **Open editor**, and open **Vera**. 2. Send a request, such as "Track monthly electricity use per site and calculate the percentage change from the same month last year". 3. Answer any focused question about missing metrics, units or rules. Vera can work with [indicators](./indicators/input.mdx), [dimensions](./dimensions/other-dimensions.mdx), [groups](./groups.mdx) and [calculations](./indicators/formula.mdx) as your request needs. It shows progress while working and finishes with a short reply about the result. If it cannot do something, it explains the limitation and any work already completed. ## Continue your conversation [#continue-your-conversation] Ask follow-ups such as "Add another site", "Use kWh as the unit" or "Explain that calculation". Your recent requests, answers to Vera's questions and Vera's replies provide context for the next message. Closing the panel or reloading restores the conversation and any question waiting for your answer. Your conversations belong to you within the workspace. Use **Conversation history** to reopen an earlier thread, or **New conversation** to start a separate named thread. You can rename it later. Full history is saved; **Load earlier messages** opens older turns. Other users have their own conversations. Vera automatically restores your last conversation only in the same app and within 6 hours of your last interaction. Otherwise it opens a blank chat, saved when you send the first message. **Conversation history** opens a centered, searchable table with page controls; you can manually reopen older conversations or a thread from another app. Each message shows the app it started from, and the current page provides context for your next request. Vera can work across apps your permissions allow. **Stop this request** ends the current task; changing conversations alone leaves its work running on the server. ## Give Vera useful context [#give-vera-useful-context] * Name the metrics, units and collection frequency. * Describe the breakdowns you need, such as region or site. * Explain calculation rules and what should happen when a value is missing. * Ask Vera how Ovidens works when you need guidance. It can consult the user documentation and link to the relevant instructions. You can explain your rules directly in chat. If a key choice is ambiguous, Vera asks before making that decision for you. ## Who can use Vera [#who-can-use-vera] Chatting with Vera requires AI access on the app. You can ask questions without permission to change the app's configuration. Each editing tool still requires its own permissions, so Vera can change only what you are allowed to change and what your enabled AI tools allow. See [roles](../administration/index.mdx). You answer questions in your own conversation. Other people's requests and replies are not part of your chat. ## Changes and publication [#changes-and-publication] A requested structure change creates or reuses the app's draft. Asking a question or requesting help does not create a draft. Review the result and click **Publish**, or explicitly ask Vera to publish if that tool is available to you. Otherwise its edits stay in the draft. **Discard** removes everything changed in that draft, including values collected while it was open. See [Draft and publish](../overview/apps.mdx#draft-and-publish). See [Interact with Vera](../tips/interact-with-vera.mdx) for outcomes, recovery and AI usage. # Consistency checks (/docs/indicator-apps/checks) A [validation rule](./indicators/input.mdx) guards a single value: this number is never negative, that one is at most 100. A **consistency check** guards a relationship *between* datapoints: renewable plus non-renewable energy equals total energy, diverted waste never exceeds waste generated, a share stays under 100%. Checks are what turn "every cell is filled" into "the report holds together", so a failing check can block a reporting period from being approved. ## Where they live [#where-they-live] Checks are part of the app's definition, so they live in the editor. Open your indicator app, click **Open editor**, and go to the **Checks** tab. You get the familiar two-panel layout: a searchable list of checks on the left, the selected check on the right. Delete lives on the row in the list. Like everything else in the editor, a new or changed check sits in your private draft until you **Publish**. See [Draft and publish](../overview/apps.mdx#draft-and-publish). ## Start from a common pattern [#start-from-a-common-pattern] A new check offers **Start from a common pattern** with three ready-made shapes: * **Breakdown sums to total** - components add up to a total. * **Part stays within whole** - a part cannot exceed the whole. * **Share stays under 100%** - a ratio or percentage stays in range. Picking one fills the form in for you; you then swap in your own datapoints. Nothing is locked, so a template is a starting point, not a constraint. ## Writing a check [#writing-a-check] A check is a comparison with a left side, an operator, and a right side, and each side is **built from blocks** rather than typed - the same building blocks a [formula indicator](./indicators/formula.mdx#you-build-a-calculation-you-do-not-write-one) uses, so there is one thing to learn and no language to remember: * **Name** - a short label so it is easy to recognise in the list, for example "Totals reconcile". A technical id is filled in from the name as you type and stays fixed once the check is created. * **Left side** and **Right side** - each a calculation over your datapoints. For a check that renewable and non-renewable energy add up, the left side is a **Calculation** block adding two **Reference** blocks, and the right side is a single Reference to total energy consumption. * **Operator** - `=`, `!=`, `<`, `>`, `<=`, or `>=`. * **Tolerance** - only offered for `=` and `!=`. A small allowance so a rounding difference of a few decimals does not trip an equality. * **Message shown on failure** - the sentence a collector reads when the check fails, for example "Renewable energy cannot exceed total energy use." Ovidens offers a **Suggested message** derived from the comparison; **Use** accepts it. * **Severity** - **Block** ("a failing cell blocks Approve & lock for that period") or **Warn** ("shows a warning; sign-off stays possible"). As you build, Ovidens asks the **same question the save asks** and shows one line: either a **Reads as:** sentence restating the check in plain words - which is how you confirm it means what you intended - or the reason it cannot be evaluated, naming the datapoint, the cadence or the breakdown that does not line up. Beside the reads-as line, a footprint line tells you how often it will be evaluated, for example "checked per site, per year". ## Reading the results [#reading-the-results] Below the form sits the **Per-slice evaluation** table: the check applied to your real data, one row per period and breakdown slice, with the result and, for a numeric comparison, the difference (Δ) against the tolerance. Four chips filter it: **All**, **Fail**, **Unknown**, and **Pass**, each with its count. **Unknown** is not a problem - it means at least one of the datapoints the check refers to has no value yet, so there is nothing to compare. Those rows name what is missing, and an unknown check neither blocks nor warns. A failing row offers **Fix in Data Input**, which takes you straight to the exact datapoint and period behind it, so you go from "these numbers disagree" to the cell that needs correcting in one click. ## What a check does to your report [#what-a-check-does-to-your-report] * A **Block** check that fails keeps the reporting period from being approved. **Approve & lock** stays disabled and the period's **Status & approval** tab lists what is blocking it, by datapoint and by your check message. * A **Warn** check that fails is flagged but never blocks approval. * Either way, collectors see the message you wrote while they are entering data, so the person who can fix the inconsistency is told about it at the moment it matters. See [Review and approve a reporting period](./collection-overview.mdx#sign-off) for the approval flow itself. # Collection Overview (/docs/indicator-apps/collection-overview) **Collection Overview** answers one question at a glance: how is data collection going? Open an indicator app and it is the first thing you land on, on the **Overview** tab, beside **Questionnaire** and **Contributors**. It shows completeness, who is contributing, and where the gaps are, all in one place. It is built for you from the data you collect, and there is nothing to configure. The time window comes from the **reporting period** you pick in the header, which is shared with the other two tabs; the app derives its periods from its reporting interval, so there is always one to pick. Beyond that you only have a **group filter** and a **Refresh** button, and, on the heatmap itself, a **View** control that sets how fine the columns are. ## The headline numbers [#the-headline-numbers] Across the top sit three summary cards: * **Overall completeness** - the share of expected values that have actually been collected, with the filled and expected cell counts behind it. * **Active contributors** - how many people have entered data, so you can see collection is a shared effort and not resting on one person. * A combined card for what needs attention: how many indicators are **Overdue**, how many are **Indicators at risk** (below 50% complete), and when the **Last entry** was made. ## Find the gaps [#find-the-gaps] Below the headline numbers, several panels show where collection needs attention: * **Completeness heatmap** - a grid of indicators against periods, coloured from well-covered to empty. Filter it by group, search by name, expand or collapse groups, change the column resolution with **View**, and open it **Fullscreen** for a busy app. * **Coverage by dimension** - for indicators broken down by a [dimension](./dimensions/other-dimensions.mdx), bars showing how complete each member is, worst first, so a lagging region or site stands out. * **At-risk & overdue** - a short table of the least-complete indicators, with how many of their expected values are in. * **Submission trend** - a chart of how many values have been entered day by day, so you can see whether collection is keeping pace. ## Reporting periods [#reporting-periods] A **reporting period** names the period you are reporting - for example "Reporting year 2025" - so the collection effort is scoped to one well-defined window. You never create periods by hand: the app knows its **reporting interval** (chosen when the app is created - calendar year, quarter, month, or a custom start month and length) and derives the timeline of periods from it, into the past and the future alike. Every period is there the moment the app exists. A derived period starts as a plain calendar entry. It becomes a **record** the moment you act on it - rename it, submit it for review, approve it, or run a [round](./collection-rounds.mdx) against it - and keeps its name and window from then on. Renaming works on any open period; the window itself always comes from the interval and cannot be moved or resized. **Changing the interval** takes effect from a month you choose, forward: periods before that month keep their windows, and the timeline after it re-tiles at the new rhythm. If the new boundary cuts an old period short, the shortened remainder simply becomes its own (shorter) period. A change that would re-tile the window of a period you have already acted on is refused with the period's name - reopen or delete that period first, or pick a later start. **Collection is never blocked by a missing period.** You can enter data for any date at the datapoint's own collection cadence - past or future - without setting anything up first. The only thing that locks a cell is a period that has been **submitted for review or approved**: every bucket that touches such a window is read-only until the period is reopened. **Reporting periods are managed in the editor.** Click **Open editor**, then open the **Reporting periods** tab (the calendar icon, the last of the editor's tabs). The **Reporting interval** card at the top of the list shows the interval history and, for admins and reporting managers, offers **Change interval from …** and removing the latest change. Below it, the list shows every period with its status badge - a period nobody has acted on is simply **open** - and the detail panel has two tabs: **Configuration** (the label) and **Status & approval** (the sign-off flow below). Deleting is offered on an acted-on period's row while it is still open and not bound to a round; deleting it returns the window to a plain derived period, and the values collected in that window stay. The period you pick is shared across the app, so whichever one you select in the header is the one every tab shows, and it rides in the page address, so a link you share opens on the same period. ### Last year's figure, and carrying it forward [#last-years-figure-and-carrying-it-forward] Ovidens puts last year's number where you need it: on the cell itself. Wherever you enter a value, a small clock icon appears; hover it and you see that exact cell's value in the preceding calendar years, most recent first, up to three years back. Only years that actually hold a value are listed, and the icon does not appear at all when there is no history. It reads the same for a single figure and for every cell of a breakdown. If a figure is unchanged from last year, you can **carry it forward**. Any empty cell in an open reporting period offers a "carry forward from last year" action that copies the previous window's value across and marks the cell as carried forward. The marker keeps the provenance visible to reviewers, and the carried-forward value is checked by the same rules as a value you type yourself. The action is not offered on a period that has been approved and locked. ## Review and approve a reporting period [#sign-off] When collection for a period is finished, you close it with **Approve & lock**: a locked, versioned record of what you reported. Open the [Reporting periods](#reporting-periods) tab in the editor, select the period, and use its **Status & approval** tab. It states in one line what Approve & lock does (it freezes the period's data and publishes an immutable version), shows the period's status with a plain-language explanation, and lists what still blocks it. * **Submit for review** (optional) hands the period from collection to review. While a period is in review its values are locked for everybody, administrators included, and only an administrator or a reporting manager of this app can reopen it - so what the reviewer reads is exactly what gets approved. Because it freezes the whole window, submitting is open to the people who collect or govern here and not to the whole workspace: an administrator, a reporting manager of this app, or somebody who holds an assignment on one of this app's open or finished [collection rounds](./collection-rounds.mdx). A member who is none of these has entered no figures here and is asked to have an administrator or reporting manager submit the period, or to be assigned to a round. You may submit while a collection round is still open - the panel says so, and says the figures may still change until it closes. * **Approve & lock** freezes the period. TWO things gate it. First, no collection round bound to the period may still be **open**: approving freezes the data, so approving mid-collection would freeze the report while people are still answering. The panel names the rounds that are open and the confirm stays disabled until you close them. Second, readiness must be green: every applicable datapoint is filled and every blocking [consistency check](./checks.mdx) passes. Until then the button is disabled and the panel lists what still blocks it, by datapoint and by check message. Approving is for an administrator or a reporting manager of this app; a small team can approve straight from open without a separate review step. * **If you were the last person to edit the period's figures, the approve dialog says so** before you confirm. You may still approve: the warning is there because an assurance review asks whether the person declaring the figures also entered them, and the activity log records on the approval whether the approver was the last editor. Approving through Vera or a connected AI records the same. If your organisation separates entering figures from approving them, ask somebody else to approve. * **An outstanding change request never blocks approval.** If a reviewer has asked for a change on some answers, the panel says how many and says in words that it does not block: those figures are still editable and the period is still approvable. It is there so you decide knowingly, not so the platform decides for you. These three lines are shown only to the people who govern the app's collection - administrators and its reporting managers - so a collector submitting a period for review sees no warning about an open round; the hard block is on Approve & lock, which only they can do. A period whose rounds have since been deleted keeps its change requests but has no round left to count them off, so the line is absent there. * Approving captures an immutable **version** of the whole app, so the report can always be reopened exactly as it was approved. In the approve dialog you may optionally expand **Add a declaration** to record who is declaring the report, their role, and, depending on your reporting framework, the basis and module option - each field explained inline. A declaration is not required and can be added on a later approval, so a one-click approval on a ready period is valid. The approved state shows who approved it and when, a link to that version, and the declaration when present. An approved period is **locked**: its cells become read-only in the grid and a banner names the status, so no one can quietly change a figure after it was declared. To make a change, **unlock & reopen** the period. Reopening is for an administrator or a reporting manager of this app, behind a confirmation, because it unlocks the period for editing and sets aside the approval; the version you approved stays in history, and approving again produces a fresh version and a fresh approval record. Every submit, approval, and reopen is recorded in the audit log with who did it. ## Consistency checks [#consistency-checks] Where a [validation rule](./indicators/input.mdx) guards a single value, a **consistency check** guards a relationship between datapoints, for example that a renewable and a non-renewable energy figure add up to the total. A failing check can block **Approve & lock** for the period, so checks are what turn "all cells filled" into "the report holds together". See [Consistency checks](./checks.mdx) for how to write and read them. ## Contributors [#contributors] **Contributors** is a tab of its own, beside Overview and Questionnaire. It lists who has been entering data and how active they have been, for the same reporting period. It is the quickest way to see who to thank, or to nudge, as a collection deadline approaches. # Collection rounds (/docs/indicator-apps/collection-rounds) A **collection round** is how an app asks for its figures. Instead of asking the whole workspace for an app's figures, a round asks NAMED PEOPLE for the figures of the unit they are responsible for - a subsidiary, a site, a department - and lets an administrator or a reporting manager approve each answer, reject it, or send it back to the same person with a reason. Four words carry the whole feature, and nothing else means the same thing: | Word | What it is | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Reporting unit** | The *who from* of a figure, exactly as a reporting period is the *when*. A member of the ONE dimension the APP collects by. | | **Respondent** | A person, named by email, responsible for a reporting unit ON ONE ROUND. Who you ASKED. | | **Round** | One collection round: a reporting period, the datapoints it asks for, and the people it asks. It does not choose a dimension - the app has one, and every round of it asks along that one. | | **Review** | Your verdict on one unit's answer to one datapoint. | Because the ROUND owns its period, its datapoints and its people, you can set next year's round up while this year's is still running, and change your mind about who answers it without touching this year's. The one thing a round does NOT choose is the dimension its units come from: the app has one and every round asks along it, and once a round of the app has left draft it can no longer be moved elsewhere. A round still in draft holds nothing - you can still change your mind while nobody has been asked anything yet. Rounds are set up in the app's **editor**, in one section called **Collection**, and reviewed on the app's own **Collection** tab. They are ANSWERED somewhere else entirely: on **Tasks**, a top-level page outside any app. **Setting a round up, and reviewing it, is for administrators and reporting managers.** A **Reporting manager** is a role an administrator gives on the whole workspace, on one space or on one app (see [Members and roles](../administration/index.mdx#members-and-roles)): it runs rounds, reviews answers and approves reporting periods there, and nowhere else. The editor's Collection section shows the email address of everybody responsible for each unit, so only somebody who manages the app's rounds is offered it - and the app's Collection tab, which is where answers are judged, is theirs alone for the same reason. Answering is for everybody who is asked, and they need neither the editor nor the app: what they were asked for appears under **Tasks** (see [Step 5](#step-5-answer-it)). ## Step 1: choose the app's reporting-unit dimension [#step-1-choose-the-apps-reporting-unit-dimension] A reporting unit is not a new list you type out. It is a **dimension member**: you pick which of the app's dimensions holds its reporting units, and its members become them. If you collect per site, the `Site` dimension is it; if you collect per subsidiary, that one is. **The app has one, and every round of it collects along that one.** Open the editor, go to **Structure -> Dimensions**, open the dimension you want and turn on **Reporting-unit dimension**. Turning it on for another dimension moves it - a dimension list never shows two. Two things follow, and the second is worth knowing before you start: * The app has to be **published** for a round to leave draft, because collection runs against the published structure. Set the dimension, publish, then open the round. * Once a round of this app is running or finished, the dimension can no longer be **moved**. Every answer and every review decision names one of its members, so re-pointing it would strand them. Setting it back to the dimension the published structure carries is still allowed, and that is the repair if a draft has drifted away from it. To collect along a different axis, return those rounds to draft - resetting their review decisions first - or use a separate app. ## Step 2: define the round [#step-2-define-the-round] Switch to **Collection** and add a round. It carries: * a **name** and a technical id, * an optional **description** - what this round asks for, and of whom, * exactly one **reporting period**, which is the window it collects for, and * the **datapoints** it asks for. The app's reporting-unit dimension is shown here for reference; it is not something the round chooses. Choosing the datapoints IS defining what the round asks. Three kinds of datapoint cannot be asked for, and each is listed greyed out with its own reason rather than hidden: * one you cannot choose yet because the app has **no reporting-unit dimension**. Set that first - it decides what can be asked at all. * one that is **calculated** rather than answered. The app works it out from other datapoints, so there is no answer for anybody to give. Change it to a collected data type on the Indicators page if you want it in a round. * one that is **not collected by the app's reporting-unit dimension**. It has a single organisation-wide answer and so no unit to review it against. Assign that dimension to it on the Indicators page. If a round already holds one of those - because the datapoint changed after you chose it - you can still untick it while the round is a draft. While a round exists on a reporting period, that period cannot be deleted: deleting it would take the round and every verdict recorded on its answers with it. Delete the rounds first, or keep the period. ## Step 3: say who is responsible [#step-3-say-who-is-responsible] Switch to the round's **People** tab. It shows the members of the app's reporting-unit dimension, indented the way they are nested - a region above its sites. Only the units at the level the app collects at can be given people, and the tab says which level that is; the units above them are shown for orientation and are not selectable. Select a unit, then pick the people responsible for it. The picker searches by name and by address, and offers only people this unit does not already have. **Assigning somebody does not give them access to the app.** It says which units they answer for; whether they may answer at all is their access to the app. A person sees the task and can answer only if they may edit the app's data - **Contributor** or wider, on the app, on its space or on the whole workspace. Somebody you assign without that sees no task for this round, cannot open it and can neither answer, comment, mark it done nor hand the period over for review. Give them access under **Access** first, or as well. So that this never goes unnoticed, the round says so: on the **People** tab and on the **Tasks** list under **Collection**, a person who is assigned but may not edit the app's data carries a **Cannot answer** chip, and hovering it says what to give them. The chip disappears as soon as they hold that access. **Copy respondents from** on the Definition tab takes another round's people onto this one in one go, skipping anybody it already holds, so setting next year's round up does not mean retyping forty addresses. It is offered while the round is a draft. The result names every kind of skip, so nothing is left behind silently: * Somebody this round has **withdrawn** stays withdrawn - the copy never restores write access - so you can restore them under People if they should be asked again. * A unit that is **no longer one of the app's reporting units** is left behind, which is what happens when the app's collection level has moved since the round you copied from. * An address the workspace **can no longer place** is left behind: nobody here holds it any more, or two people do, or the invitation it was made from was taken up by somebody who has since left, or it expired before anybody accepted. The copy makes the same check the picker makes, and a person the workspace cannot place cannot be given a unit by a copy any more than by hand. If somebody should still answer for that unit, invite them or pick them under People. **You can pick a member of this workspace, or somebody you have invited who has not accepted yet** - and the copy applies exactly the same rule. An open invitation is offered in the same list, marked **Invited**. If the person you need is neither, invite them on the **Access** page - the picker links to it - and pick them once the invitation exists. There is no free-text address field: you always pick a person the workspace already knows, so an address nobody here has cannot be made responsible for a unit. **Assigning an invited person works, and it starts granting when they accept.** Until then the assignment is held under their address and grants nothing - they cannot sign in yet. The moment they accept the invitation and answer for the first time, the assignment becomes theirs by identity. If you CANCEL the invitation instead, the assignment stays on the unit, grants nothing, and shows as **Not in workspace**; remove it, or invite the person again. Assigning a member names the PERSON rather than the text of their address, so the address is only a label from then on: if they change their email address they keep their units, and if their old address is later given to a new colleague, that colleague inherits nothing. Remove an assignment when somebody leaves. Each address shows where it stands: * **Member** - they are in this workspace and can sign in now. * **Invited** - they have been invited and have not accepted yet. * **Not in workspace** - nobody here holds this address. A row can only read this way if it was made before responsibility was assigned by picking a member, or if the person has since been removed from the workspace. Such a row grants nothing. Remove it, and - once the person is a member - pick them again. If two members of the workspace happen to share one address, assigning either of them is refused and names the address, because the assignment is held under that address and it does not say which of them you mean. Give each of them their own address, then pick the one you meant. An invitation that has already been accepted, whose address no current member uses any more, is refused too - the person may have left, or changed the address on their account. Invite them again, or pick a current member instead. **You can only assign somebody a REPORTING UNIT** - an active member of the app's designated dimension, at the level the app collects at. The rail is what shows you which those are: anything else is drawn as structure and cannot be selected at all, so a parent above that level, a member below it, or a deactivated one is never something you can pick a person for and be refused afterwards. A figure for a region is not typed beside its sites, it is rolled up from them by the pipeline, so the person who answers is the person who holds a site. Somebody responsible for three sites is assigned all three. A unit with nobody assigned is in no round. That is deliberate: a unit a round asks always has somebody to ask. **Removing somebody from a round that is already running WITHDRAWS them rather than erasing them.** The round did ask them, and their answers and any verdict on those answers outlive the assignment - so the row stays on screen, struck through and marked **Withdrawn**, and they simply stop being able to answer and stop seeing the round. Put them back with the restore control beside the row, or by adding the same address again. On a round that is still a draft the same control removes the row outright, because a draft round has asked nobody. ## Step 4: open it [#step-4-open-it] A round has three states, and the buttons say what each move does. | State | What it means | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Draft** | Nobody has been asked anything yet. **Nobody sees a task for a draft round**, however many people you have already put on it - so you can staff it, change your mind and staff it again without anybody being told. A draft changes no one's access and carries no verdicts, so you can edit it freely. | | **Open** | The round is running. Each unit's questions can now be answered only by the people responsible for that unit. | | **Closed** | The round is over. Every answer it governs is read-only, for everyone, administrators included. | Once a round leaves draft its definition is fixed - it is what every verdict was recorded against. A round that has run is ended by **closing** it, never by deleting it, so no verdict you recorded is ever destroyed. You can return an open round to draft only while no answer carries a verdict. ### Two rounds may not ask the same question at the same time [#two-rounds-may-not-ask-the-same-question-at-the-same-time] Opening a round is refused when another round that is already open or closed asks for one of the same datapoints over an **overlapping** reporting period. The message names that round, the first datapoint they share and how many they share, and it names the way out: narrow this round's datapoint list while it is still a draft, or return the other round to draft. A round you are REOPENING can no longer be edited, so there only the second one is open to you. The reason is that a figure has to have ONE round deciding who may answer it and whether the answer is frozen. Two rounds over one answer means two verdicts, and no way to say which is in force. Rounds over the same datapoints in DIFFERENT reporting periods are fine - that is the ordinary year-on-year case - and so are rounds over different datapoints in the same period. ### Publish the app before a round leaves draft [#publish-the-app-before-a-round-leaves-draft] You define a round against what you see in the editor. It is ANSWERED against the app's **published** structure - the same structure every figure is collected against. A round that is open or closed while those two disagree is one nobody can answer, so the move out of draft is refused, and the message says which case you are in: * the app has **no reporting-unit dimension** at all - set one first (see [Step 1](#step-1-choose-the-apps-reporting-unit-dimension)), * its reporting-unit dimension is **not in the published structure**, * the draft designates a **different** reporting-unit dimension from the published one, * a unit you assigned somebody to is not in the published structure yet, or * a unit you assigned somebody to is in the published structure but is **not one of the app's reporting units** - it sits at another level, or it is at the collection level and has been deactivated. The message names the unit, the dimension and the level. Withdraw that person under **People**, give them the units at the collection level they really answer for, or make the unit active again. Somebody responsible for a unit ABOVE the collection level would answer for every unit beneath it, including ones nobody gave them, which is why this is refused rather than allowed through. Set the draft's reporting-unit dimension back to the published one, or - while no round of the app is open or closed yet, and while the draft designates a dimension of its own - publish the app, and move the round again. The check runs whenever a round **leaves draft**, and again whenever a closed round is **reopened**, because a closed round governs its answers exactly as an open one does. Closing a round that is already open is not checked: it is already collecting, so closing it starts nothing new, and being unable to STOP a round would be the worse answer. The buttons here take a draft round to **Open** and no further, so leaving draft means opening it; the rule is stated over the states rather than over that one button because the API and the AI tools can also take a draft round straight to **Closed**, and the same check covers that move. While a round is open or closed, publishing or restoring a version that would move the app's reporting-unit dimension, or move a unit out from under the person responsible for it, is refused for the same reason: the answers and verdicts already recorded name those units. Adding a **new** unit is fine, and so is every other structure change. A running round does not freeze the app. Deleting the app's reporting-unit dimension is refused while the app holds ANY round, a draft one included. Delete those rounds first, then the dimension - and a round that has left draft has to be returned to draft before it can be deleted at all, so the full sequence is reset its review decisions, return it to draft, delete it. (Moving the dimension needs less than that: returning the round to draft is enough on its own.) A dimension the app has not designated deletes as it always did. Opening a round sends nobody an email. The people responsible for a unit see the round the next time they open the app, so tell them it is there the way you would tell them anything else. ## Step 5: answer it [#step-5-answer-it] A respondent opens **Tasks** in the left-hand navigation. They never enter the app that asked, and they need no access to the editor: providing a figure is not a reason to be walked into somebody else's app. The Tasks entry appears only once somebody has actually been asked something, and it carries the number of open rounds. The page is ONE row per **task** - one round, one person, however many reporting units that person holds. A site manager responsible for three plants sees ONE row and opens ONE questionnaire covering all three, rather than chasing three. Each row names the round, the app that asked, the due date, and how far along it is in CELLS - "6 of 8 cells filled" - so a person who has answered for one of their two sites reads as half done rather than as nearly finished. Opening a row opens **the questionnaire** for the whole task, with one column per unit on every question. The way back is the same page; nothing on the task leads into the app. Nothing else about the questionnaire changes - it is the same guided form described in [Answering a round](./questionnaire.mdx) - but it is now narrowed to the questions the round asks **their own unit** (see "Why a unit is asked some questions and not others" below) and to that unit's own figures. The reporting period follows the round, because a round collects for exactly one, so there is no period picker on that screen at all. **A task appears when the round is OPENED, and not before.** Putting somebody on a draft round tells them nothing; opening it is what asks them, and everybody on it gets their task at that moment. Closing the round does not take it away - the task stays readable so answers, verdicts and the discussion on them can still be read back - it only stops the answers being editable. A round somebody has been **withdrawn** from stops appearing for them. If a reviewer asks for a change, the reason is on the answer and you can reply to it in the discussion beside it - see [Step 6](#step-6-review-the-answers). Asking Vera, or a connected AI assistant, to fill a round in works the same way and is bounded the same way: it reaches exactly as far as **you** do and no further - your own units if you are a respondent, every unit if you manage the app's rounds. Assigning a respondent and recording a verdict are not things an assistant can do at all - the first would widen who may enter data, and the second records a named person's judgement. ## Step 6: review the answers [#step-6-review-the-answers] An administrator or a reporting manager opens the same **Collection** tab. The table is your worklist: one row per round and unit, which you can search, sort by any column, page through and export like any other table in the app. | Column | What it says | | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Round** / **Status** / **Reporting period** | which round the row belongs to, whether it is running, and the window it collects for | | **Unit** | the reporting unit, in the order the app's reporting-unit dimension lists it - the same order as the round's People tab, so a unit sits where you expect it | | **Responsible** | who holds the unit TODAY, one address per line so every one is readable (past six, the first five plus a "+N more" line that carries the rest in its tooltip). It reads "nobody is responsible for this unit today" when no one does. Shown to administrators only | | **Participation** | where the unit stands in the round - see the chips below | | **Answered** | **N of M answered** with a bar: how many of the questions this unit is asked already hold at least one figure inside the round's reporting period. Hover the cell for the coverage reading - how many of the round's questions reach this unit at all. If progress cannot be computed right now the cell says so, rather than claiming zero | | **Approved** / **To change** / **Open** | how its answers have been judged, and how many are still waiting | | **Review** | the clipboard button in the last column - always visible, and the one way to open that unit's questionnaire for that round | The search box matches the round name, the unit name and the responsible addresses, so on a large tree you can type a site name or a colleague's address and get straight to their row. Sorting a column is yours to do; until you do, the rounds sit newest first with each round's units in the app's reporting-unit dimension's own order. | Chip | What it means | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Asked** | somebody is responsible for this unit on this round now | | **Withdrawn** | the round asked it and everybody responsible for it has since been withdrawn. Its answers and the verdicts on them stay readable, and you can still reopen a verdict | | **Not asked** | the round holds nobody for this unit at all, and it is listed only because a verdict was recorded on one of its answers - so **an approval can always be found and, if it was wrong, reset**. No answer is ever left frozen by a judgement nobody can reach | Who is responsible for a unit TODAY is a different question from the chip: a **Withdrawn** unit has a chip and no address. ### Talking about one answer [#talking-about-one-answer] Every answer carries its own **discussion** - one thread per question per reporting unit, opened from the answer itself on the questionnaire and on the review screen. It is where a change request gets answered: the reviewer's reason arrives on the answer, and the person who holds the unit replies there rather than by mail, so what was asked and what was said back sit beside the figure they are about. * **Anybody who holds that unit can post, and so can whoever manages the app's rounds.** Nobody else sees the thread. * **It is append-only.** A message cannot be edited or deleted by anybody, which is what makes it usable as a record of what was agreed. * **It stays open when the answer does not.** An approved answer and a closed round can both still be discussed - a frozen figure is exactly the kind you need to be able to ask about. * **Nothing is gated on it.** A thread never blocks a sign-off, changes no verdict and locks no cell; it is the conversation, not a decision. ### Why a unit is asked some questions and not others [#why-a-unit-is-asked-some-questions-and-not-others] You do not configure this and there is nothing to keep in step. Each datapoint is collected at one **level** of the app's reporting-unit dimension - some at Site, some at Region - and the rule follows from that: > A unit is asked a datapoint when at least one member of that datapoint's > collection level sits at or beneath that unit. Read against a Region -> Site tree: | The unit | A datapoint collected at Site | A datapoint collected at Region | | --------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | **Europe** (a region with Hamburg and Rotterdam under it) | asked - the questionnaire gives it a column per site, and its respondent answers for both | asked - one answer, for Europe itself | | **Hamburg** (a site) | asked - one answer, for Hamburg | **not asked** - Hamburg is not a region, and no region sits beneath it | | **Iberia** (a region with no site under it yet) | **not asked** - there is nothing under it to answer for | asked | A question a unit is not asked is simply not shown to that unit, and does not count against its completeness: a unit asked 1 of 2 questions reaches 100% by answering that one - and the worklist's **Answered** column counts against the same asked set, so its denominator is what the unit can actually answer. A unit asked **none** of them still appears in the worklist, reading a muted `0 of M questions` with the reason on the row beneath it, in place of the proportion bar - most often because its part of the tree has no member at the level the round collects at. Add the missing units, or collect the datapoint at a level the unit reaches, and the questions appear. Deactivating a member takes it out of collection, so a region whose only site is deactivated stops being asked that site-level question. Open a unit and you land on **the same questionnaire that person filled in** - the same questions in the same order, each with its guidance, its unit, its own grid and the figures they entered. There is no separate list of values to judge, because judging a flattened list is judging something other than what was answered. While you review, the questionnaire is read-only. That is deliberate: correcting a figure and then approving your own correction under somebody else's name is not a review. If a figure really has to be corrected, leave review and edit it as you normally would. Tick any number of answers and give one verdict: | Verdict | What it does | | ------------------- | ----------------------------------------------------------------------------------------- | | **Approve** | Accepts the figure and freezes its cells, for everyone including administrators. | | **Request changes** | Hands the answer back with your reason and reopens it, so the same person can correct it. | | **Reopen** | Removes the verdict, so the answer is open again with nothing recorded against it. | There are two verdicts and no third one for "not acceptable": saying so while freezing the figure would leave a number the workspace had refused sitting in the report with nobody able to correct it. **Request changes** says the same thing, carries the reason, and leaves the answer open - so the round ends in a corrected figure or an honest gap. A request for a change must carry a **reason**. It is not a note to yourself: the person who answered reads it beside the question they have to fix. The verdict applies to every answer you ticked, in one go, and each answer then carries its verdict beside the question. For the person who answered, an approved answer is read-only under a chip naming who judged it and when, and one you sent back is editable again under your reason. Only an administrator or a reporting manager of the app can record a verdict. Somebody else opening a review link simply gets their own questionnaire, with their own answers to fill in. ## Where to read who approved what [#where-to-read-who-approved-what] The verdict shown on an answer is the one in force **now**. If you need the record - who approved a figure, when, and whether anyone reopened it first - open the **Audit Analytics** app from your apps list and go to its **Indicator Data Changes** tab. Under the table of value changes there is a second table, **Answer Review Decisions**. It has one row per answer a verdict named, so a reviewer who approved a unit's five answers in one press leaves five rows, all sharing one audit id. Each row says the reporting period, the reporting unit, the datapoint, the verdict, and who decided it - under the name that person had at the time, not the name they use today. Reopening a verdict is recorded like any other decision, so filtering the table to one unit and one datapoint reads as that answer's whole history, newest first. The link on a row opens the app's Data tab, where the judged figures live. Two things it deliberately does not show. The **reason** you gave for a rejection or a change request is not in it - a reason is written to the person who answered, and it stays beside the question they read it on. And nothing here is ever recorded against an assistant: recording a verdict is something only a person can do, so the AI column is always empty. The Audit Analytics app is visible to workspace administrators only. If your workspace does not have it yet, an administrator can create it from the apps list. ## Reading verdicts with SQL [#reading-verdicts-with-sql] Verdicts are data, not only chips on a screen, and two tables carry them for anything that speaks SQL. **Inside the app**, the pipeline carries a **Cell reviews** table: one row per collected figure with the verdict currently in force over it - approved, changes requested, or none - plus the reason, who decided, when, and the reporting period it belongs to. A verdict matches the figure's OWN reporting unit exactly, with no walk up or down the tree, and covers every other breakdown of that question inside the period - exactly as the questionnaire shows it. It is always current, and it exists on every indicator app whether or not a round is running. **In your workspace's data lake**, the same verdicts are mirrored to a table named `ireview_app` beside the app's own figures, refreshed within about five minutes of a decision - so a BI tool or an export reading the lake can join "what was reported" to "what was approved" without touching the app at all. The mirror is a copy for reading: what a verdict PERMITS is always decided from the platform's own records, never from the lake. ## Measuring round progress [#measuring-round-progress] Progress is data too. Every indicator app's pipeline carries a **Round progress** table: one row per round, reporting unit and question, saying whether the unit was asked it, whether at least one figure inside the round's reporting period answers it, how many cells that answer holds, and the verdict recorded on it - beside the round's name, status and reporting period and the unit's respondent counts. A unit that is asked nothing still appears, as a single row with no question, so nobody drops off a dashboard. It is always current, it exists on every indicator app, and an app running no round simply reads an empty table. Three companion tables carry the definitions it is joined from - the rounds, the units each round covers, and the questions each unit is asked - so your own SQL can start from either end. **To chart it in an analytics app**, add an **Indicator data** node to that app's pipeline and set its **Content** to **Round progress**: the node imports the whole progress table from the indicator app you pick, and a run snapshots it exactly like any other data - re-run to refresh. Narrow or reshape it with an ordinary SQL step, then put a table or chart on the Visualisation: "Hamburg has answered 12 of 15" is a filter and a count away. Progress is counts, deliberately: none of these tables carries a respondent's or a reviewer's name or address. Who is responsible stays on the surfaces of the people who manage the rounds, and who decided stays on the review record. ## What a round does and does not keep private [#what-a-round-does-and-does-not-keep-private] Worth being exact about, because it is easy to assume more. * **Writing** is scoped. While a round is open, a respondent can enter figures only for their own units, and a workspace member with no assignment cannot enter any of that app's collected figures at all. * **The views that list the figures cell by cell are scoped the same way**: the questionnaire, the app's own **Data** tab and the editor's data grid. On an app running a round, somebody who does not manage its rounds sees their own units' figures there and nobody else's - and the Data tab SAYS so rather than showing an empty grid, naming the units it is showing you, or explaining that you are responsible for none, or that the datapoint you picked has one organisation-wide answer that is not scoped to a unit at all. Reading a **past version's** pinned snapshot is not scoped. * **Totals and roll-ups stay open.** Anyone who can open the app still sees the overview, the completeness views, the activity feed and every total and roll-up, exactly as they could before you ran a round - including figures that add up units they are not responsible for. * **Cell-by-cell figures do not.** Wherever the app names a single cell rather than a total - last year's value behind a question, the cells a validation rule or a consistency check flagged, with the numbers that made them fail, and the cells a rule marked not applicable - somebody who does not manage the app's rounds sees only their own units' cells. The counts beside those lists stay whole, so a reader can still tell that more exist than they are being shown. A round decides who is ASKED and who may WRITE, and it keeps one unit's cell-by-cell figures from another. It does not hide the app's totals from the rest of the workspace. # Data (/docs/indicator-apps/data) The **Data** tab shows one datapoint at a time, as a **record** rather than as a form: what the datapoint is, and the figures collected for it over time. It is where you go to read a number, check where it came from, or correct one - without working through a questionnaire to reach it. It is also where an app that runs **no round** collects. If nobody has set up collection rounds, this is the only place figures are entered - and on such an app only an administrator, or a reporting manager of the app who may also enter data there, can enter them, because entering a figure needs either standing over the app's collection or an assignment, and an assignment comes from a round. ## Finding your way around [#finding-your-way-around] On the **left** is the list of the app's datapoints, in their groups, with a search box over them. It is a reader's list: there is nothing to add, drag or reorder here. Clicking a datapoint opens its record on the right. On the **right** are three things, in order: * the datapoint's **name** and technical id, * a collapsed **Configuration** disclosure, and * the **figures**. Open **Configuration** to read how the datapoint is defined: its data type, its unit, its guidance, which dimensions it is broken down by, its validators and its applicability rule. It is read-only here - the editor is where a datapoint is changed - and closing it puts it away again. Below that is the same grid the editor's Data Input tab uses: a table or a chart, a period navigator to move through time, transpose, and the aggregation controls for a number. Your view of it rides in the address, so a link you send somebody opens on the same datapoint, the same years and the same table-or-chart choice you were looking at. On a **required** datapoint, every cell still owed for the selected reporting period carries a thin red line on its left edge - the counterpart of the green line a filled cell shows - and one line above the grid counts how many cells are still empty in the current view. Typing a value clears the mark straight away. A cell the datapoint's applicability rule excludes is never counted, and a comment on its own does not fill a cell: approval asks for a value. ## Reading is open, writing is not [#reading-is-open-writing-is-not] **Everybody who can open the app reads every unit's figures here**, whether the app runs a round or not - there is no strip, no hidden column and no narrowed table. **Who may CHANGE a figure is a different question**, and the answer is: an administrator, a reporting manager of the app who may also enter data there, or somebody holding an assignment that covers it. That is narrower than a dataset row, which anyone holding **Contributor**, **App builder** or **Admin** can edit, because a round decides who answers for which reporting unit. ## Editing here [#editing-here] You can enter and correct figures on this tab exactly as you can in the editor's data grid, subject to the same rules: * the figure has to fall inside an open [reporting period](./collection-overview.mdx#reporting-periods), * you have to be an administrator, a reporting manager of the app, or hold an assignment covering the figure - which means your **own** units, for the datapoints the round asks for, in the period it collects, * a figure that names no reporting unit at all - a company-wide datapoint - is for administrators and the app's reporting managers only, and * an answer that has been **approved** is frozen for everybody, including administrators, until a reviewer reopens it. An answer sent back for **changes** stays writable - that is the point of sending it back. When one of those refuses a change, it says which and what to do about it, in the place you tried to make it. # Other dimensions (/docs/indicator-apps/dimensions/other-dimensions) A **dimension** is a way to break a metric down so you can track it in more detail. Besides time, you can give an indicator dimensions like **Region** or **Site**. Without a dimension, an indicator holds a single value each period. With one, it holds one value each period for each part of the breakdown, so instead of a single company-wide total you collect a value per region. ## Members and levels [#members-and-levels] A dimension is made of **members** and, optionally, **levels**. * A **member** is one entry in the breakdown, for example "North" or "South". * A **level** groups members into a hierarchy, with each member sitting under a parent one level up. A hierarchy can be **up to five levels deep**. For an organisation you might build a Location dimension like this: ``` Country Germany Switzerland Region South North Central Site Munich Stuttgart Berlin Zurich ``` Here **Country** is the top level with two members (Germany, Switzerland), **Region** sits under it, and **Site** is the bottom level where you actually enter data. You build the dimension by adding its levels top to bottom, then the named members within each. Collecting at the bottom (per site) lets Ovidens roll the figures up to the top (per country) automatically. ## Assign a dimension to an indicator [#assign-a-dimension-to-an-indicator] When you assign a dimension to an indicator, that indicator becomes **collected per member**. In practice this means data entry asks for a value for each member rather than a single value. A Region dimension on your energy indicator, for example, means you enter energy use for North, South, and so on, each period. An indicator can have more than one dimension, in which case you collect a value for each combination, for example each region in each period. ### One level, and every dimension named [#one-level-and-every-dimension-named] An assignment also picks **the level the indicator collects at**. Values are entered at that level and nowhere else: totals for the levels above it are calculated from what you entered, never stored a second time, which is what keeps a country total and the sites under it from drifting apart. Two things follow, and the platform enforces both rather than storing a value nothing can read: * A value must name a member **at the collection level**. A member above or below it is refused, with a message saying so. * A value must name a member **for every dimension** the indicator is assigned. There is no "unspecified" cell on a dimensioned indicator. The data-entry grid shows exactly those cells, so what you can see is what you can fill in. Two things follow from that: * **A level with no members yet shows no columns.** If you assign a `Country > Site` dimension while only countries exist, pick Country as the collection level - or add the sites. The grid says which of the two it is rather than showing columns nothing can be entered into. * **A member above the collection level is not a cell.** A country with no sites under it does not get a column on a site-level indicator. Collect at country level, or add a site. If you change the collection level after values have been entered, the values already stored stay where they are and stop being read. The **Stranded data** list on the Pipeline tab names them, and the remedy is to bring them back onto the grid: set the collection level back to the one they were entered at, and they are cells again. Once they are, you can clear them the way you correct any other value - empty the cell and save - and then change the level. A cell the grid does not show cannot be cleared from the grid, which is exactly what makes it stranded. ## The reporting-unit dimension [#the-reporting-unit-dimension] If you run [rounds](../collection-rounds.mdx), one of an app's dimensions is its **reporting-unit dimension**: the one whose members are the units you ask named people to answer for. There is exactly one per app, and every collection round of that app asks along it - so everybody's responsibility is expressed in one vocabulary. Set it in the editor under **Structure -> Dimensions**: open the dimension and turn on **Reporting-unit dimension**. Turning it on for another dimension moves it; a dimension list never shows two. Two rules follow: * **The app has to be published for a round to leave draft.** Collection runs against the published structure, so set the dimension, publish, then open the round. * **Once a round is running or finished, the dimension can no longer be moved** - because its answers and review decisions name members of it. Setting it back to the dimension the published structure carries stays allowed, and that is the repair if a draft has drifted away from it. A round still in draft holds nothing. To collect along a different axis, reset those rounds' review decisions and return them to draft, or use a separate app. * **Deleting the dimension is refused sooner than that**: while the app holds ANY round, a draft one included, because a draft round's people are already assigned to its members. The two rules have different starting points on purpose - a round leaving draft is what pins the CHOICE, having a round at all is what protects the DIMENSION. ## Why hierarchies stay shallow [#why-hierarchies-stay-shallow] The five-level cap is deliberate. A hierarchy of Country, Region, and Site is easy to read and quick to enter; going deeper multiplies the cells a collector has to fill and rarely adds insight. Keep yours shallow and focused on the breakdowns you actually report on. # Time dimension (/docs/indicator-apps/dimensions/time) Every indicator in an indicator app is tracked over time, one value per **period**. The **time dimension** is what makes an indicator app a record of how a metric moves rather than a single snapshot. Before you collect any values, you choose how often each indicator is measured. ## Choose the collection interval [#choose-the-collection-interval] The **collection interval** (also called the period type) is how often an indicator gets a value. You pick one of five: **Daily**, **Weekly**, **Monthly**, **Quarterly**, or **Yearly**. Choose the interval that matches how the real-world measurement happens. A meter you read monthly should be a monthly indicator; an annual target should be yearly. ## How periods work [#how-periods-work] Once an indicator has an interval, you collect and see its values period by period. You move between periods to enter a value for each one, for example typing in January's figure, then February's, and so on. When you look at the data, you see the value laid out across the periods, so the trend over time is easy to follow. You can move forward and back through the periods, and focus on a particular range of them. ## How time grains relate [#how-time-grains-relate] Indicators that are measured at different intervals can still relate to each other. A finer interval can roll up into a coarser one: twelve monthly values add up to a yearly total, for example. Ovidens understands these relationships, so a yearly metric can be computed from monthly data without you doing the arithmetic by hand. You will see this in action when you build a [calculation](../indicators/formula.mdx) that combines indicators tracked at different intervals. # Groups (/docs/indicator-apps/groups) A **group** is an optional label that organises your indicators into sections. As an indicator app grows, its list of indicators can get long; groups let you sort them into tidy sections like "Energy", "Emissions", and "Finance" so the list stays easy to scan. ## What a group does [#what-a-group-does] A group is about tidiness: it decides where an indicator sits in the list, the indicator-app equivalent of putting related items into folders so you can find them quickly. That is all it does - a group never changes whether a datapoint is collected. To make a datapoint apply only under a condition, put an [applicability rule](./indicators/input.mdx) on the datapoint itself; to exempt a whole section, put the same rule on each of its datapoints. ## Create a group and move indicators [#create-a-group-and-move-indicators] To organise your indicators: * create a group and give it a name that describes the section, * move indicators into the group so they appear under it, * move them back out, or into a different group, whenever you reorganise. Indicators that you do not put in any group still work exactly the same; they simply appear on their own rather than under a section heading. You can group as many or as few of your indicators as you like. # Formula indicator (/docs/indicator-apps/indicators/formula) A **formula** indicator is a metric Ovidens works out for you from other indicators, instead of one you enter by hand. You build the calculation once - "revenue minus costs" - and Ovidens evaluates it for every period and dimension member automatically. Pick the **Formula** data type on the indicator and the **Calculation** panel appears in place of the collection settings. A calculation is evaluated **once per coordinate**. A coordinate is a single **period** (for example, March 2026) plus **one member per dimension** when the indicator has dimensions (for example, period March 2026 with Region "North"). Each reference in the calculation is resolved at that coordinate. If an input has no value there, the result is **empty**, unless you supply a fallback (see [Missing inputs](#missing-inputs)). New to indicator apps? Start with the [overview](../overview.mdx), which explains indicators, [dimensions and hierarchies](../dimensions/other-dimensions.mdx), and [periods and time grain](../dimensions/time.mdx). ## You build a calculation, you do not write one [#you-build-a-calculation-you-do-not-write-one] There is no formula language to learn. A calculation is built from a small set of **building blocks**, each chosen from a menu: * **Reference** - read another indicator. This is the only block that reads data. * **Number** - a value you type. * **Calculation** - two blocks with `+`, `-`, `*` or `/` between them. * **Absolute** - the magnitude of a block, without its sign. * **Rounded** - a block rounded to a number of decimal places. * **Fallback** - a block, with a second block used instead when the first is empty. * **Condition** - compare two blocks, and take one of two other blocks depending on the answer. Every block is picked from a dropdown, and every block can hold other blocks, so a calculation like "round renewable energy divided by total energy, times 100, to one decimal place" is built by nesting. The editor never asks you to type anything but a number, and there is nothing you can spell wrong. Beneath the blocks the editor shows two lines: what your calculation **reads as** in plain language, and the same calculation in Ovidens' own short notation. The first is the one to check your work against; the second is what error messages quote back to you. ## Let Vera build it for you [#let-vera-build-it-for-you] Click **Describe your metric**, type the calculation in plain words - for example "revenue minus costs" or "year-on-year growth in revenue" - and Vera builds it for you. What comes back is a real calculation in the editor, so you can open it up, change one part of it, and keep the rest. Ovidens checks Vera's answer before it reaches you, so a suggestion you accept is one that will save. ## Reading data: the Reference block [#reading-data-the-reference-block] A **Reference** block reads one other indicator. You choose the indicator from a list, and then choose **how** to read it - which is where the reference functions come in. You can reference a collected [numeric](./input.mdx#numeric) indicator, a collected [boolean](./input.mdx#boolean) indicator, or another formula indicator. A boolean reference resolves to a number: **yes becomes 1, no becomes 0**, and a missing value behaves exactly like a missing number. So `SUM` over a boolean counts how many periods met the target and `AVG` is the fraction that did. A [select](./input.mdx#select) indicator is compared with `SELECTED`, and a [text](./input.mdx#text) or [file](./input.mdx#file) indicator can only be asked whether it has been answered at all, with `HAS_VALUE` - both below. ### The reference functions [#the-reference-functions] | Function | What it reads | | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | `VALUE` | the value at this coordinate | | `SUM` `AVG` `MIN` `MAX` `FIRST` `LAST` `COUNT` | a **finer-grained** indicator, rolled up to this indicator's period | | `ANCESTOR_SUM` `ANCESTOR_AVG` `ANCESTOR_MIN` `ANCESTOR_MAX` | a dimension rolled up **N levels above** the current member | | `LEVEL_SUM` `LEVEL_AVG` `LEVEL_MIN` `LEVEL_MAX` | a dimension rolled up to the **absolute level N** (1 is the top) | | `PERIOD_AGO` | the value **N periods back** | | `YEAR_AGO` | the value exactly **one year back** | | `DIM_SUM` | the **total across every member** of a dimension | | `MEMBER` | the value of **one named member** | | `SELECTED` | whether a **select** indicator holds one particular option - 1 if it does, 0 if it holds another | | `HAS_VALUE` | whether an indicator of **any type** has been answered - 1 or 0, never empty | | `MEMBER_COUNT` | **how many members** a dimension has at a level (this one names a dimension, not an indicator) | The editor only asks for the arguments the function you picked actually takes. Choose `PERIOD_AGO` and it asks how many periods; choose `MEMBER` and it asks which member; choose `VALUE` and it asks for nothing more. Change the function and the arguments follow - there is no way to leave an argument behind that the new function does not take. **Level numbers** are capped by the dimension itself. A hierarchy is at most five levels deep, so an ancestor distance is at most 4 and an absolute level at most 5; `MEMBER_COUNT` cannot name a level past the number of levels that dimension has. The editor offers only the levels that exist. **Time shifts.** For `PERIOD_AGO`, the count is a whole number of periods, at least 1. `YEAR_AGO` lands on the matching period one year earlier, with two small adjustments so there is always a period to compare against: a February 29 period compares against February 28 a year earlier, and when the earlier year has no 53rd week, a 53rd-week period compares against that year's last week instead. **Naming a dimension.** When an indicator is collected across more than one dimension, a roll-up needs to know which one. The editor asks - and when there is only one possibility it fills it in for you and says so. **Selecting a member.** `MEMBER` reads one slice. The member must be a **collection member**, that is, one at the level values are stored at; a parent member is a roll-up, which is what the `ANCESTOR_*` and `LEVEL_*` functions are for. Selecting one member collapses that dimension, so an indicator with no dimensions of its own can read a single member of a dimensioned indicator. **Reading a select.** `SELECTED` asks whether a select indicator holds **one particular option** - you pick the option from that indicator's own list. It answers 1 when the cell holds it, 0 when the cell holds a different one, and **nothing** when the cell has not been answered, because an unanswered question is unknown rather than "not that option". Wrap it in a **Fallback** if you want an unanswered cell to count as 0. The option is remembered by its own identity, not by its position, so **reordering an option list changes nothing** about the rules that name it. Removing an option a rule names is refused, with a message naming the option, rather than quietly leaving a rule that has stopped meaning anything. **Asking whether anything was answered.** `HAS_VALUE` works on an indicator of **any** type, including [text](./input.mdx#text) and [file](./input.mdx#file), and answers 1 or 0 at every cell - never empty, even where the indicator has no value stored there at all. It is what makes the commonest questionnaire rule expressible: *if you answered yes, you must attach the evidence*. ## Missing inputs [#missing-inputs] A missing input makes the **whole result empty**, and the **Fallback** block is the one way to say otherwise: it takes a block and a second block to use when the first comes out empty. Put a Fallback around a single Reference and it substitutes a number for that missing value; put it around a whole sub-calculation and it also covers the cases arithmetic itself produces - a division by zero, a result too large to represent. ## Worked examples [#worked-examples] Each example below is the **blocks you assemble**, and under each is the short notation Ovidens prints back beneath the editor once you have. You never type that line - it is there to read your work back to you, and it is the form an error message quotes. **Margin.** Both `revenue` and `costs` are monthly with the same dimensions, so each is a Reference read with `VALUE`. * **Calculation** with `-` between them * **Reference** -> `revenue`, read as `VALUE` * **Reference** -> `costs`, read as `VALUE` ``` ([revenue] - [costs]) ``` **Yearly total from monthly data.** Inside a yearly indicator, read the monthly series with a time aggregation. * **Reference** -> `monthly_sales`, read as `SUM` ``` SUM([monthly_sales]) ``` **Ratio with a divide-by-zero guard.** A **Condition** block returns 0 when the target is zero. * **Condition** * *If this*: **Reference** -> `target`, read as `VALUE` * *Compared with* `>`: **Number** -> 0 * *Then take*: **Calculation** with `/` between `actual` and `target` * *Otherwise take*: **Number** -> 0 ``` IF([target] > 0; ([actual] / [target]); 0) ``` **Year-over-year growth.** The same indicator read twice, once as it stands and once a year back. * **Calculation** with `/` between them * **Calculation** with `-`: **Reference** -> `revenue` (`VALUE`) and **Reference** -> `revenue` (`YEAR_AGO`) * **Reference** -> `revenue`, read as `YEAR_AGO` ``` (([revenue] - YEAR_AGO([revenue])) / YEAR_AGO([revenue])) ``` **Share of a total.** `energy_consumption` is collected across an `energy_source` breakdown. The renewable share is the renewable member over the total across all members, as a percentage, rounded to one place. * **Rounded** to 1 decimal place * **Calculation** with `*` * **Calculation** with `/`: **Reference** -> `energy_consumption` read as `MEMBER` of `renewable`, and **Reference** -> `energy_consumption` read as `DIM_SUM` * **Number** -> 100 ``` ROUND(((MEMBER([energy_consumption]; renewable) / DIM_SUM([energy_consumption])) * 100); 1) ``` With 120 renewable out of a 300 total, this computes 40. ## Grain rules [#grain-rules] A calculation's **grain** is its time grain (the period type) and its dimension scope (which dimensions, and at which level). References have to line up with that grain, or Ovidens refuses to save the indicator and says which reference does not fit. The time hierarchy, from finest to coarsest, is: ``` daily < weekly < monthly < quarterly < yearly ``` The rules: * A plain `VALUE` reference must be at the **same** time grain as the indicator. * A **finer** indicator has to be read with a time aggregation (`SUM`, `AVG`, `MIN`, `MAX`, `FIRST`, `LAST`, `COUNT`). * A **coarser** indicator is read as it stands and repeated at each finer coordinate. It is not divided up, so divide it yourself - `MEMBER_COUNT` is there for exactly that. * A reference collected at a **finer** hierarchy level on a shared dimension has to be rolled up with `ANCESTOR_*` or `LEVEL_*`. * A reference that keeps a dimension this indicator is not collected across cannot be compared with it, and Ovidens says which dimension. ## What Ovidens refuses, and when [#what-ovidens-refuses-and-when] **When you save.** Everything about how a calculation FITS is decided at save time, and every refusal names what did not fit: * a reference to an indicator this app does not hold, or to one that carries no numeric value; * a **cycle** - a calculation that, directly or through others, reads itself; * a **cadence** that does not reconcile: a finer reference with no aggregation, or an aggregation over a coarser one; * a **dimension** that cannot be lined up: an extra one the indicator is not collected across, or a finer level with no roll-up; * a **member** that does not exist, or is a parent rather than a collection member; * a **level** past the depth the dimension actually has; * a calculation that is simply **too large** - there is a cap on how many blocks one calculation may hold, and on how many references it may make. The editor asks the same question the save does, while you build, so a refusal reaches you as a red line under the calculation rather than as a failed save. **When the indicator is a formula.** A cell has a number or it has none. There is no per-cell reason, and that is deliberate rather than an omission: your calculation is ONE statement evaluated over the whole grid, so a blank cell is one whose inputs had no value there or whose arithmetic was undefined there, and the statement cannot tell those two apart without being run a second time to ask. What does get a reason is a calculation that could not run **at all**. Then no cell in the grid has a result, the reason is shown **once** above the grid, and each cell reads **No result** - so one sentence about the request is not repeated into every cell, and a cell with no result never looks like a cell containing zero. ## Where your calculation actually runs [#where-your-calculation-actually-runs] Every calculation becomes **one SQL statement** that Ovidens generates and keeps in step with your configuration. You can read it in full, and see the rows it produced, on the app's [pipeline](../pipeline.mdx) tab - which is what makes every figure in the app checkable. The statement is there to be **read**, not edited or run on its own: an indicator app's pipeline belongs to Ovidens, it is rewritten whenever your configuration changes, and each step runs only as part of the app's own evaluation. If the building blocks cannot express something you need, read the data into an **analytic app** with an [Indicator data](../../analytical-apps/pipeline/indicator-data.mdx) node. An analytic app has a real SQL editor with previews and typed output columns, and its statements are yours to write and keep. # Input indicators (/docs/indicator-apps/indicators/input) **Input indicators** are the metrics you collect by hand: one value per **period**, and one value per member if the indicator has [dimensions](../dimensions/other-dimensions.mdx). They are the raw figures your indicator app is built on. Values that are calculated for you instead come from a [formula](./formula.mdx) indicator. There are five input types, picked by the kind of value you collect: * [**Numeric**](#numeric) - a number. * [**Text**](#text) - a word or short note. * [**Boolean**](#boolean) - a yes/no. * [**Select**](#select) - one option from a list you define. * [**File**](#file) - an uploaded document. ## Settings every input indicator shares [#settings-every-input-indicator-shares] Whatever the type, a few settings shape how an indicator behaves: * **Technical ID:** a short, stable name used to refer to the indicator, for example in a [formula](./formula.mdx). It stays the same even if you rename the indicator's display name. * **Description:** a plain explanation of what the metric is, so collectors know what to enter. * **Unit:** for a numeric indicator, the unit its values are measured in, picked from a fixed list (for example MWh, tCO2e, %, EUR). The unit is stored as a structured setting rather than being written into the name, and it is shown beside the value when collectors enter data. Leave it unset for a unitless figure. * **Guidance:** optional longer help text, written in a formatting editor (headings, bold, lists, links). When a datapoint has guidance, collectors see it in a **Guidance** panel above the data grid, open by default and collapsible, so it is read at the point of entry. Use it for standards-grade explanations that are too long for the description. * **Collection interval:** how often a value is collected, daily through yearly. See [Time dimension](../dimensions/time.mdx). * **Required:** whether a value must be entered each period. * **Conditional applicability:** an optional rule that decides, **per cell** (per dimension slice and period), whether the datapoint applies at all, based on other datapoints' values. The rule is a two-sided comparison with a left side, an operator (`= != < > <= >=`), and a right side, each side built from the same [building blocks](./formula.mdx#you-build-a-calculation-you-do-not-write-one) a formula indicator uses - for example a **Reference** to headcount on the left, operator `>=`, and a **Number** 50 on the right. Because it is evaluated per cell, a small country can be exempt while a large one is required in the same period (a per-country trigger is never netted into one total). A rule about a **yes/no** or a **select** datapoint gets a guided row instead of the blocks: Yes / No buttons for the first, and the option's own label for the second, so nobody has to know that a yes is a 1 or that an option is addressed by its position. When it is false for a cell, collectors see that cell marked "not applicable" with the reason, instead of being asked for a value; when a referenced value has not been entered yet, the cell is applicability-unknown, stays optional, and is never silently hidden. The rule belongs to the datapoint: a [group](../groups.mdx) organises datapoints and never gates them, so to exempt a whole section put the same rule on each of its datapoints. A datapoint that carries a rule shows a **Conditional** badge in the structure list. Readiness on the [Reporting periods](../collection-overview.mdx#reporting-periods) tab uses these rules to decide whether a reporting period is complete: a datapoint that does not apply is not counted as missing. * **Value constraints and value rules:** two different things, and only one of them can stop a value being saved. **Value constraints** are structural: a numeric indicator can require whole numbers, and a select indicator can restrict entry to a subset of its options. A value that is not a value of that shape is refused at entry. **Value rules** (numeric datapoints only) are two-sided comparisons built from the same blocks, and each must name **this datapoint** on one side - the reference picker offers it as **This datapoint**, so "at most Office capacity" is two picks. One-click templates cover the common non-negative, minimum, maximum, 0-100 and year-on-year cases. **A value rule never stops you saving.** Your figure is stored, and the cell is marked with a warning icon whose tooltip names the rule it breaks; correct the figure and the mark goes away on the next read. That is deliberate: a rule can compare your value with one nobody has entered yet, or with the total across a breakdown you are halfway through, so a rule you cannot satisfy this minute must not cost you the number you have. The same marks are summarised on the [Reporting periods](../collection-overview.mdx#reporting-periods) tab. Consistency between related datapoints is handled separately by [consistency checks](../checks.mdx), and only a **blocking** check stops anything - it stops sign-off, never an entry. * **Requires a comment:** whether the collector must add a note alongside the value, useful when figures need explaining. * **Framework mapping:** an optional set of key/value entries that map the datapoint to an external reporting framework (for example a standard, disclosure, paragraph, or XBRL concept). Add, edit, and remove entries directly in the datapoint's configuration; the common keys are suggested as you type. The mapping is opaque reference metadata carried with the datapoint - it does not affect collection or validation. * **Archived:** hides an indicator you no longer collect, without deleting its history. ## Numeric [#numeric] A **numeric** indicator holds a number. It is the most common kind and the foundation of most tracking: energy used, revenue earned, litres of fuel burned, and so on. Numeric values are real numbers, so they can be used in calculations. Together with [formula](./formula.mdx) indicators, they are the only inputs a formula can compute with. If you want a metric to feed into a calculation, collect it as a numeric indicator. ## Text [#text] A **text** indicator records a value in words: a short note, a status, or an explanation. Use it when the thing you are tracking is best captured in words, for example "on track" or "delayed", or a short note on what happened that period. Because its value is words rather than a number, a text indicator cannot be used directly in [formula](./formula.mdx) math. If you need something countable, model it as a [boolean](#boolean), a [select](#select), or a [numeric](#numeric) indicator instead. ## Boolean [#boolean] A **boolean** indicator records a simple yes/no. Use it for things that are either true or false: a target met, a checklist item done, an audit passed. Each period, the collector marks yes or no. A boolean can be used directly in a [formula](./formula.mdx): **yes becomes 1 and no becomes 0**. That makes booleans surprisingly useful in calculations. Summing one over a span of periods counts how many times the answer was yes; averaging it gives the fraction of periods that were yes. A period with no value behaves like any other missing input (a gap, not a "no") unless your formula supplies a default. ## Select [#select] A **select** indicator lets the collector choose one option from a list you define, for example "Low", "Medium", "High". You set the options when you create it, and the collector can only pick from that list. A select keeps data consistent, so "High", "high", and "HIGH" do not become three different answers, and it makes the data easy to group and filter. Prefer a select over a [text](#text) indicator whenever the answer should come from a known set rather than be typed freely. ## File [#file] A **file** indicator stores an uploaded document as each period's value: a PDF, an image, a spreadsheet, or any file you need on record alongside your metrics. Use it for evidence behind a reported figure, a certificate that arrives each period, or a form you must retain. Each period, the collector uploads that period's file and can replace it if a newer version arrives. The most recent upload is the one that stands. ## Which types a formula can use [#which-types-a-formula-can-use] Only [**numeric**](#numeric) and [**boolean**](#boolean) inputs carry a value a [formula](./formula.mdx) can compute with (a boolean as 1 or 0), alongside other formula indicators. [Text](#text), [select](#select), and [file](#file) indicators have no numeric value and cannot be referenced in a calculation. # Indicator apps overview (/docs/indicator-apps/overview) An **indicator app** is a place to collect and track **metrics** over time. A metric, which Ovidens calls an **indicator**, is something you measure again and again, such as fuel use, electricity, or revenue. Where an analytic app reshapes data you already have, an indicator app is built to gather a measurement period by period and watch how it moves. ## The building blocks [#the-building-blocks] An indicator app is made of three building blocks, each with its own page. * **[Indicators](./indicators/input.mdx)** are the metrics themselves, the things you track. * **[Dimensions](./dimensions/time.mdx)** are the ways a metric is broken down, such as by region, by site, or over time. * **[Groups](./groups.mdx)** are optional labels that tidy a long list of indicators into sections. ## Collected and formula indicators [#collected-and-formula-indicators] Indicators get their values in one of two ways. * **Collected** indicators are the ones you (or a teammate) enter by hand, value by value, period by period. These are the [input indicators](./indicators/input.mdx), in five types (numeric, text, boolean, select, file). * **Formula** indicators are calculated for you. A **formula** indicator works out its value from other indicators automatically, for every period. For example, margin can be computed from revenue and costs without anyone typing it in. See [Formula](./indicators/formula.mdx). ## Where things live [#where-things-live] An indicator app has two sides, and one button moves you between them. **The app itself** is where the collecting happens, and it is what everyone opens. It has four tabs: * **[Overview](./collection-overview.mdx)** - how collection is going: how complete your data is, who is contributing, and where the gaps are. It is generated for you from the data you collect, so there is nothing to set up. A **reporting period** picker in the header scopes it. * **[Data](./data.mdx)** - one datapoint's record: what it is, and its figures over time. This is where you read and correct a number without opening a form, and it is where an app that runs no round collects. * **[Collection](./collection-rounds.mdx)** - the collection rounds. Open a round to see its units, and open a unit to answer or review its [questionnaire](./questionnaire.mdx). * **Contributors** - who has been entering data, and how much. The header's reporting-period picker scopes this one too. **The editor** is where the app is defined. Click **Open editor** in the top right, or press E. The editor is offered to everyone whose role reads the app's configuration; a **Viewer** reads the app as it was last published and is offered neither. It has five tabs: * **Indicators** - the metrics, sorted into their [groups](./groups.mdx). * **Dimensions** - the breakdowns. * **[Checks](./checks.mdx)** - the consistency rules that guard how your figures fit together. * **Reporting periods** - the windows you collect for, and where a finished period is approved and locked. * **[Pipeline](./pipeline.mdx)** - the SQL pipeline Ovidens generates from this app's configuration, readable and downloadable step by step, alongside any collected values that no longer fit the structure they were collected against. Changes you make in the editor stay in a private draft until you **Publish** them, exactly as in an analytic app. See [Draft and publish](../overview/apps.mdx#draft-and-publish). ## Build with Vera first [#build-with-vera-first] The easiest way to set up an indicator app is to ask **Vera**, the AI assistant. Describe the metrics you want to track in plain language, for example "track our monthly electricity use per site, and compute the year-on-year change", and Vera sets up the indicators, dimensions, groups, and calculations for you. No technical knowledge required. See [Build with Vera](./build-with-vera.mdx). The rest of this section explains each building block so you can review what Vera builds, or build and adjust the pieces yourself. # The generated pipeline (/docs/indicator-apps/pipeline) Every indicator app carries a **pipeline** that Ovidens generates from the app's own configuration. You do not build it and you cannot edit it - it is rebuilt whenever you change the app's structure - but you can read all of it, and that is the point. Open the editor and choose the **Pipeline** tab (or press P). ## What you are looking at [#what-you-are-looking-at] The pipeline has three kinds of node. * **Relations** are tables rather than statements. Each one carries a part of the app's own configuration as rows: its indicators and their cadence, the dimensions they are collected by, every member of every dimension, its rules, and the values collected against it. * **Your datapoints** are one node each - a step named after the datapoint, carrying just the cells collected for it. * **Engine steps** are single SQL statements over the tables before them. A few are the platform's own groundwork - the period calendar, the dimension member tree, the collection members, the stranded-data findings. The rest are **one step per thing you configured**: one for each formula datapoint's calculation, one for each datapoint's applicability and value rules, one for each consistency check. Each is a single statement you can read in full. Click a node to read it. ## Your datapoints, one step each [#your-datapoints-one-step-each] Every datapoint you **collect** - a number, a text, a yes/no or a single choice - has a step of its own. Open it and you see that datapoint's cells and nothing else: the period they were collected for, one column for each dimension you collect that datapoint by, and the value. **Each dimension column is named after your dimension.** Underneath, every cell of the app is stored with an address that says which dimension each of its members belongs to - so `region` means `region` in the stored cell, in this step, and in anything you write against it. These steps unpack that address into one column per dimension, in the order you assigned them. It is also what makes the SQL worth reading: a statement written against `region` still means `region` tomorrow. **A datapoint can be collected by as many dimensions as your workspace allows.** There is no separate storage limit underneath - the only ceiling is the one your workspace's settings state. Three columns come from Ovidens rather than from you, and they start with an underscore so they can never collide with a dimension of your own: `_period_type` (the cadence), `_period_start` (which period) and `_value` (the collected value itself). A dimension you call `value` is perfectly legal and gets a column called `value`, right beside `_value`. Two kinds of datapoint have no step here, each for its own reason. A **formula** datapoint has no collected cells at all - its figures come out of its own engine step, which carries the calculation you configured. A **file** datapoint's stored value is a pointer to the uploaded file rather than a value, so it is not something a table of results can carry; the files themselves stay on the datapoint, where you uploaded them. Reading one of these steps is cheap: it reads your collected cells directly rather than running the whole engine, so opening a datapoint's data is quick even on a large app. **Every node opens the same way a node opens in an analytics app** - the same panel, drawn the same way, down to the last control: a **Configuration** tab, and a **Data** tab showing that node's rows in the ordinary table. Nothing there is editable - the pipeline is Ovidens's, not yours - but everything the engine reads, and everything each step of it produced, is in front of you. Rows are read a page at a time as you scroll, and you can sort, filter and search them exactly as you would any other table; nothing is left out and nothing is summarised. What differs is the Configuration tab. A **relation** names which part of the app's configuration it carries. An **engine step** shows its statement **in full** - not a summary and not an excerpt - alongside the list of tables it reads, so you can read what produced the rows on the tab beside it. **The pipeline is Ovidens's, whole.** No step can be edited, handed over or taken on, in this editor or anywhere else. If the building blocks cannot express something you need, read the data into an **analytic app** with an [Indicator data](../analytical-apps/pipeline/indicator-data.mdx) node: that app has a real SQL editor with previews and typed output columns, and its statements are yours to write and keep. **This pipeline is not a picture of how your app is computed - it is what computes it.** Every number your app shows comes out of these steps: the value in a formula datapoint's cell, the pass or fail of a value rule and a consistency check, whether a conditional datapoint applies for a period. Open a formula datapoint's own step and you are reading the figures the collection grid shows you, at the coordinates it shows them at; open a step above it and you are reading what they were computed from. There is no second, private calculation anywhere behind them. **One object, one statement.** A calculation is not interpreted at run time - it is turned into SQL once, when you save it, and that statement is what runs. So the step you can read IS your calculation, and there is no engine between the two that could read it differently. That is also why a refusal reaches you at **Save** rather than as a finding you have to go looking for. A reference to a datapoint the app does not hold, a cadence that does not reconcile, a breakdown that cannot be lined up, a calculation that reads itself: each is refused while the statement is being generated, naming what did not fit. Nothing unsound is ever written, so there is no "findings" step to consult - the refusal happened before there was a step at all. ## Why Run is greyed out on an engine step [#why-run-is-greyed-out-on-an-engine-step] A step's SQL sits in the same editor an analytics app's SQL node uses, with the same **Run** control underneath it - and on an engine step that control is deliberately switched off, with the reason beside it. An engine step is not a query against tables that sit somewhere waiting to be read. It runs as one part of the app's own evaluation, over relations that are assembled for the length of that evaluation and then gone, so there is nothing for a stand-alone run to read. What that statement produced is on the **Data** tab beside it, which is a page of the real result rather than a rehearsal of it. The statements themselves are ordinary SQL for [DuckDB](https://duckdb.org) and nothing about them is special to Ovidens: select the text in the editor to take a copy into an editor of your own and read it at your own pace. ## If the tab is empty [#if-the-tab-is-empty] An app's pipeline is generated from its structure, and an app that existed before the pipeline did does not carry one yet. Saving any change to its structure generates it: add or edit a datapoint, a dimension, a group or a consistency check, and the tab fills. Every app created since carries its pipeline from the moment it exists. ## Stranded data [#stranded-data] A value is stored together with the structure it was collected against: which indicator, which dimension members, and which collection interval. Change that structure afterwards and the values already collected do not move with it - they are left behind, outside the grid the indicator now asks for, where nothing reads them. The Pipeline tab reports them, with the indicator and how many rows are affected. | What it says | What happened | What to do | | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- | | Collected at a collection interval the datapoint no longer uses | The indicator's collection interval was changed after these values were entered | Set the interval back to reach them, or clear them | | Collected against a dimension the datapoint no longer has | A dimension was taken off the indicator, and these rows still name one of its members | Assign the dimension again, or clear them | | Collected before a dimension was added | A dimension was added after these values were entered, so they name no member for it | Enter the values again against the new dimension, or clear them | | Collected against a member that is no longer active | A dimension member was deleted or deactivated while values still named it | Make the member active again, or clear them | | Collected against a member at another level of the hierarchy | The indicator collects the dimension at one level, and these rows name an active member above or below it - usually because the collection level was changed after the values were entered | Collect the indicator at that level again, or clear them | When there is nothing to report, the tab says so. If the check could not run - the platform was busy, or your workspace has used up its compute allowance for the month - it says **that** instead, rather than reporting a clean result over data it did not read. **The check is taken each time you open the Pipeline tab**, so there is no refresh button to press. Nothing that could change the answer is reachable while the tab is open: values are entered on the data-entry grid and the structure is edited on the other tabs, so coming back here is what takes a fresh reading. **Clearing one of these values.** Where the value still has a cell on the data-entry grid - most often after a collection level or a member changed - empty the cell and save, exactly as you would correct any other value. That works even though entering a NEW value at the same place is refused: clearing is always allowed, because it is how you act on what this list tells you. The cell then reads as empty rather than disappearing from the grid. ## What needs compute, and what happens without it [#what-needs-compute-and-what-happens-without-it] Because the pipeline is what computes the app, reading it and changing it both use your workspace's compute allowance, and a workspace that has used the allowance up (or a platform-wide pause) will say so rather than answering with something it did not compute. * **Reading a step's Data tab** runs the engine for that step. Reading a **relation**'s rows does not - a relation is your own configuration, so its columns and rows are free. * **Changing the structure** does **not**. The pipeline is rebuilt from your configuration alone, and a calculation is checked by generating the statement for it - both of which happen in Ovidens itself. So a datapoint, a dimension, a group or a check can be saved while compute is unavailable, including one carrying a calculation. What you cannot do meanwhile is *read the results*. * **Entering a value** checks the app's rules against what you typed, so that needs compute as well. This is deliberate: accepting a value the platform could not check is worse than telling you to come back. The refusal always names itself and what to do about it; nothing is saved or shown on a half-answer. ## Why a cadence change can be refused [#why-a-cadence-change-can-be-refused] Because a value carries the interval it was collected at, changing an indicator's **collection interval** would strand every value already collected. When you edit the indicator, Ovidens refuses that change while such values exist and tells you how many rows are involved. The way forward is to clear those values first and then change the interval. There is deliberately no "change it anyway" option: it would be a button whose only effect is to lose data quietly. **Restoring an earlier version is not held to that check.** A restore puts the whole app back the way it was, collection intervals included, so it can strand values that an edit would have been refused for. It is deliberately not blocked - a rollback you are not allowed to perform is its own kind of trap - so what it strands is reported rather than prevented: check the **Stranded data** list above after restoring a version. # Answering a round (/docs/indicator-apps/questionnaire) **You open the questionnaire from a round.** Go to the indicator app's **Collection** tab and press **Answer** on the row naming the round and the reporting unit you want to fill in - one row per pair, so being responsible for several units is several rows, not a second menu. That takes you into the guided form: the app's questions, one section at a time, so you never have to understand how the app is built to fill it in. See [Collection rounds](./collection-rounds.mdx) for how a round is set up and what that table shows. The questionnaire is always about ONE round, and about the reporting units you were asked for - both come from the round and your own assignments rather than from anything you choose here. **The round fixes the reporting period**, so there is no period picker on this screen, and it narrows the questions to the ones your units are actually asked. Somebody responsible for two sites answers both in one questionnaire, with a column for each. If nobody has asked you anything yet, there is no **Tasks** entry in the navigation at all. **A round you were asked on shows up only once you can edit that app's data.** Being assigned to a unit says what you answer for, not that you may open the app. If you expected a round and do not see it, or opening it says the round is not there, ask a workspace administrator for access to the app - Contributor or wider is what answering needs. If you only want to read or correct a figure - and your workspace has given you permission to - the **Data** tab is the other way in: it shows one datapoint's record, its configuration and its figures over time, rather than a form. An app that runs no round at all collects entirely through that tab. ## Finding your way around [#finding-your-way-around] The page has two halves. On the **left** is your progress and your map: * an **Overall completeness** card, with a progress ring and the plain count of how many required items are done, for example "12 of 20 required complete", * a search box over the sections and questions, and * one card per section, each showing its own completion and, when you open it, its questions as a list. Clicking a section switches the form to it; clicking a question scrolls straight to it. You can hide the whole rail with **Hide sections** when you want the form full width; Ovidens remembers the choice, and which sections you left folded. On the **right** is the form itself: the current section as a stack of question rows, with **Section 3 of 8** above it and **Previous section** / **Next section** buttons to move through them. ## A question row [#a-question-row] Each row frames one datapoint: its number and name, a marker if it is required, its unit, and any guidance the app author wrote for it. Beside that sits its status: **Required**, **Complete**, **Optional**, **Not applicable**, or **Trigger unknown**. The last two are worth knowing. An app can make a question conditional, so it is only asked when something else is true. When the condition is false you see "Not applicable" with the reason spelled out, instead of being asked for a number you do not have. When the condition depends on an answer nobody has given yet, the question stays open and is marked "Trigger unknown" rather than being quietly hidden. A whole section can be conditional the same way. The value control matches the kind of datapoint: a number box, a text box, a yes/no, a dropdown, or a file upload. Values save as you go, with a small **Saving…** then **Saved** next to the field, and **Clear** empties one again. ### Last year's number [#last-years-number] Next to a value sits a small clock. Hover it and you see what this exact figure was in the preceding calendar years, most recent first, up to three years back. It only appears when there is history to show, and it reads the same on a single figure as on every cell of a breakdown. ### Comments [#comments] **Add comment** attaches a note to a value: why the number moved, what was excluded, where it came from. Some questions require one, and then the app tells you. A value that carries a comment shows it, and **Edit comment** or **Remove** change it later. ### Questions with a breakdown [#questions-with-a-breakdown] If a datapoint is broken down by a [dimension](./dimensions/other-dimensions.mdx), for example per site, the row holds a small grid instead of a single box: one line per breakdown member, with a **Total** so you can see the figures add up. The grid shows your own reporting unit and anything beneath it, and nobody else's. A **required** breakdown counts as complete only when every one of its cells holds a figure, in every period of the reporting window - a cell marked not applicable is the one legitimate gap. An optional breakdown is looser: one figure per period is enough. Each cell in that grid takes its own comment, and an empty one offers **Carry forward from last year**, which copies the previous window's figure across and marks the cell so reviewers can see where it came from. When a breakdown is too large to be comfortable inline, **Edit breakdown** takes you into the editor's full data grid for that datapoint and period, where the same values are editable with more room. ## When a question cannot be answered here [#when-a-question-cannot-be-answered-here] A datapoint collects at its own cadence - monthly, quarterly, yearly. If the round's reporting period does not contain a whole period at that cadence, the question tells you so rather than accepting a figure that would not mean anything. An administrator or a reporting manager of the app fixes that by creating a reporting period that covers it. An answer that has been **approved** is frozen, for everybody including an administrator: the notice above it says who decided and when, and only a reviewer reopening it makes it writable again. An answer **sent back for changes** is the opposite - it is open again, with the reviewer's reason beside the question so you know what to fix. # AI transparency (/docs/infrastructure/ai-transparency) This page explains, in plain terms, how AI works inside Ovidens: what the AI is, which models power it, what data reaches those models, and the controls that keep a person in charge. It is informational and describes the product as it works today; it is not legal advice. ## What the AI is [#what-the-ai-is] Ovidens has one AI, named **Vera**. You meet it in three places, but it is the same assistant and the same controls behind all of them: * The **in-app assistant** you chat with while building or querying an app. * **Skills**: reusable AI tasks an app author saves so anyone can run them. * The **hosted connector**, a per-workspace door that lets your own AI (for example Claude) work in your workspace on your behalf. See [Connect your own AI](../integrations/mcp.mdx). ## Models and provider [#models-and-provider] Which model powers Vera is a **per-workspace choice**. A workspace admin selects it; every member can see the active model. These are the models in the catalog: | Model | Provider | Where your data is processed | | ------------------------------- | ----------------------- | ---------------------------- | | **Claude Haiku 5.5** | Anthropic API, directly | Not limited to the EU | | **Claude Sonnet 5.5** (default) | Anthropic API, directly | Not limited to the EU | | **Claude Opus 5.5** | Anthropic API, directly | Not limited to the EU | These three models are the available choices. Vera uses **one configured model per workspace for all AI work**: chat, AI widgets executing skills, background helpers and conversation summarisation. There is no separate helper model. The **default** is Claude Sonnet 5.5. The API's default inference routing is global. **Vera's AI processing is not limited to the EU**, even though Ovidens' application is hosted in Frankfurt. See [Anthropic's data-residency documentation](https://platform.claude.com/docs/en/manage-claude/data-residency). Anthropic does not train on commercial API inputs or outputs by default; see its [commercial data policy](https://privacy.claude.com/en/articles/7996868-is-my-data-used-for-model-training). Ovidens is the **provider** of this AI system and a **deployer** of general-purpose AI models. Ovidens does not train its own models; it uses the model providers' models as delivered through their APIs. ### Choosing the workspace AI model [#choosing-the-workspace-ai-model] A workspace admin picks the model on the **General tab of the System page** (the workspace configuration page in the sidebar), under **AI configuration**: the "Workspace AI model" card lists every selectable model with a short description and its processing location, and shows which one is active. Members see the same card read-only. The choice applies to the whole workspace - every Vera chat, setup job, skill run and internal AI call uses the selected model. Each new request or resumed request resolves the workspace's current setting, including when you reopen an older conversation. Conversation history does not pin a model. The audit log records the model used for every AI action, so a change of model is visible in the workspace's history. See [Administration](../administration.mdx) for the activity log. ## Intended use and limitations [#intended-use-and-limitations] Vera assists with **building and querying analytics**: setting up an app's data steps, indicators, and Visualisation, and answering questions about your data. Its outputs are **AI-generated and may be inaccurate**. Treat them as a fast first draft, not a final answer. As the in-app notice puts it: Vera is an AI assistant, its responses are AI-generated and may be inaccurate, so please verify before acting on them. You remain responsible for what you publish or decide. ## Human oversight and controls [#human-oversight-and-controls] Vera is built so a person stays in charge: * **Changes follow your request and permissions.** Requested edits use the app's draft. Vera publishes only when you ask and its publication tool is enabled for you. AI widgets are read-only and never edit data or create a draft. * **Per-user AI permissions and opt-outs.** An admin sets the maximum any member's AI may ever do, and each member can switch off anything they do not want their AI to do. * **External AI is deny-by-default.** The hosted connector is off for every member until an admin grants it per person, per connection. * **Per-surface rate limits** keep AI use bounded. * **Bounded skill runs.** A skill run is capped at roughly 30 steps and about 150 seconds, so it always finishes in a predictable envelope. * **A tenant audit log** records every AI action with per-resource AI provenance: which surface ran it, which model or client was used (the workspace's selected model, per action), and which person instructed the AI. See [Administration](../administration.mdx) for the activity log. ## Data handling [#data-handling] Vera sends your request, relevant conversation context, attachments and tool results to Anthropic's API. Depending on the task, those results can include app configuration, knowledge-base content and queried data. The complete stored conversation is not sent on every request; the model receives a bounded recent portion automatically. Older turns remain available to you in conversation history; restate important details when they fall outside the model's recent context. Every call uses the workspace's configured model through this direct API, whose processing is **not limited to the EU**. Ovidens checks your current permissions before every resource operation and keeps workspace conversations private to their owner. Anthropic's API data handling follows its [commercial data policy](https://privacy.claude.com/en/articles/7996868-is-my-data-used-for-model-training) and [data-residency controls](https://platform.claude.com/docs/en/manage-claude/data-residency). An external AI connected through MCP uses its own provider and terms. Review those before connecting it; it is separate from Vera's model selection. ## Transparency markers [#transparency-markers] Two markers make AI use visible: * **The "you are interacting with AI" disclosure** shown wherever Vera answers. * **The audit-log AI-provenance marker**, which tags each AI-made change with the surface, the model or client, and the person who instructed it. ## How this maps to the EU AI Act [#how-this-maps-to-the-eu-ai-act] For the AI features described here, Ovidens treats its obligations as **transparency-risk** obligations under **Article 50** of the EU AI Act: people are told when they are interacting with an AI system, and AI-generated actions are logged and attributable. # Sub-processors and AI data (/docs/infrastructure/sub-processors) This page names the third parties (sub-processors) Ovidens uses to process customer data for its AI and core hosting, and where that processing happens. It is informational and describes the service as it works today; it is not legal advice. The list below covers **AI and core data processing**. ## AI sub-processors [#ai-sub-processors] | Sub-processor | Purpose | Processing location | Trains on your API data? | | ----------------------------- | ------------------------------------------------------------------------------------------- | --------------------- | -------------------------------------------------- | | **Anthropic** (Claude models) | All Vera inference using the workspace's configured model, through the direct Anthropic API | Not limited to the EU | No, by default under Anthropic's commercial policy | Ovidens does not train models on your data. See [Anthropic's commercial data policy](https://privacy.claude.com/en/articles/7996868-is-my-data-used-for-model-training) for its default no-training policy and exceptions for explicitly shared feedback. Which Claude model Vera uses is chosen per workspace. See [Choosing the workspace AI model](./ai-transparency.mdx#choosing-the-workspace-ai-model). ## Core hosting and data sub-processors [#core-hosting-and-data-sub-processors] | Sub-processor | Purpose | Region | | ----------------------------- | ------------------------------------------------------------------------------- | ------------------------ | | **AWS** (Amazon Web Services) | Application hosting, storage, and database | eu-central-1 (Frankfurt) | | **AWS Cognito** | User sign-in and authentication | eu-central-1 (Frankfurt) | | **AWS SES** | Transactional email (workspace invitations) | eu-central-1 (Frankfurt) | | **MotherDuck** | The managed DuckDB data warehouse that stores and processes your analytics data | Hosted on AWS in the EU | ## Data residency and retention [#data-residency-and-retention] Ovidens' core application hosting, storage and database run on AWS in **Frankfurt (eu-central-1)**. Analytics data is stored and processed by **MotherDuck**, hosted on AWS in the EU. Data sent to Vera's Anthropic API follows a separate processing boundary: **AI processing is not limited to the EU**. The API's default inference routing is global; see [Anthropic's data-residency documentation](https://platform.claude.com/docs/en/manage-claude/data-residency) and [Vera's data handling](./ai-transparency.mdx#data-handling). Ovidens' retention follows your agreement. Provider-side retention follows the applicable provider terms; an EU-hosted application does not establish EU-only AI processing or zero provider retention. ## No training on your data [#no-training-on-your-data] Ovidens does not train models on workspace data. Anthropic's commercial API does not use inputs or outputs for training by default, as described in its [commercial data policy](https://privacy.claude.com/en/articles/7996868-is-my-data-used-for-model-training). Ovidens checks each user's permissions before sending resource data and keeps workspace data isolated from other workspaces. ## Data Processing Agreement [#data-processing-agreement] {/* TODO: The Data Processing Agreement (DPA) link and its legal terms must be provided by legal counsel. Do not draft DPA terms here. Replace this placeholder with the counsel-approved DPA reference (a linked document or a contact route) once available. */} A Data Processing Agreement (DPA) is available to customers on request. Contact us at [contact@ovidens.com](mailto:contact@ovidens.com) to request a copy. The DPA terms are provided by our legal counsel; this page does not restate them. *** This page is a factual, good-faith disclosure and is **informational, not legal advice**. It does not create any contractual term. # Credentials (/docs/integrations/api/credentials) This page is for the workspace **administrator** who issues the credential. The developer's side is [Automation API](./overview). A **connected system** is another piece of software that works with your workspace on its own: an ERP, a data warehouse, a nightly job. It signs in with a credential of its own rather than with somebody's account, pushes data into a dataset, starts a run, and collects the results on its own schedule. ## What a connected system can do [#what-a-connected-system-can-do] What it may do is decided entirely by the access you grant its credential: **read the workspace**, **write data** into a dataset of an analytic app, **run a pipeline**, and **read results** as a downloadable export. That is the whole surface. Whatever access you tick, a connected system cannot create or delete apps, change a pipeline, edit a Visualisation, invite people or change workspace settings. ## Issuing one [#issuing-one] Open the **Access** page and its **API credentials** tab, beside the members. The tab is **admin-only**: a member is not offered it, and every request behind it is refused for anyone but an admin. A credential is a **workspace asset**, not a personal one - it keeps working after whoever created it leaves. The list still names the person who issued it and marks the credential **Former member** once that person is gone, so you have a prompt to review whether the integration is still wanted. **New credential** asks for a name, an optional description, how long the credential lasts (30, 90, 180 or 365 days; 365 unless you choose otherwise), and the access to grant. Name it after the system it is for ("ERP nightly import" rather than "test"): that name is what the audit trail shows every time the integration writes something. Creating it produces a **Client ID**, which identifies the credential and is safe to show, and a **Client secret**, which proves the system is that credential. **The secret is shown once**, in the dialog that appears right after you create the credential, and nothing can show it again - it is not stored anywhere anyone, including an admin, can read it back. If it is lost, rotate the credential to issue a new one. The dialog copies each one, or both at once as the environment variables `OVIDENS_CLIENT_ID` and `OVIDENS_CLIENT_SECRET`, and **I've stored it** closes it and discards the secret from the page. Hand both to whoever builds the integration, along with one link: [Automation API](./overview). Everything after that - how the system signs in, which endpoints it calls, what the file has to look like - is written down there. ## What the access options mean [#what-the-access-options-mean] Each option grants one kind of work and nothing else. The picker also prints the technical name each one carries in the API, which is what the developer will ask you about. | Access | What it lets the system do | | --------------------------- | ----------------------------------------------------------- | | **Read the workspace** | List apps, nodes and their configuration. | | **Write data** | Import and change the rows of a dataset. | | **Write indicator values** | Enter and clear collected indicator values. | | **Run published pipelines** | Run an app's published configuration. | | **Run drafts** | Run unpublished configuration that nobody has reviewed yet. | | **Read results** | Query and export the results of a run. | | **Publish** | Change what every person in this workspace sees. | Two badges tell you how to read that list: * **Elevated** marks an option that reaches beyond reading and running what the workspace has **published** - running an unreviewed draft, or publishing, which changes what everyone sees. A new credential never arrives with one; grant it only when the system genuinely needs it. * **Not in effect yet** marks an option nothing checks today. It can be granted, stored and shown, but granting it changes nothing an administrator could observe. The badge disappears for an option as soon as that option starts taking effect. **Edit access** changes the grant of an existing credential. Nothing has to be re-issued: the change applies from the credential's very next request, and taking access away binds even on a token the system is already holding. ## Rotating the secret [#rotating-the-secret] **Rotate secret** issues a second secret without breaking anything: the secret in use today keeps working alongside the new one, so you can roll the new one out at your own pace. A credential never carries more than two secrets, so the **next** rotation is what retires the older one - rotating twice is the deliberate "stop accepting the old secret", and the dialog says so before you confirm. Rotate when a secret may have been exposed, when the person who set the integration up leaves, before the credential expires, or on whatever schedule your own policy sets. The credential's details show when the current secret was created and when the previous one was last used, so you can watch the old secret go quiet before retiring it. A rotation also sets a **new expiry** - the dialog asks how long, as creating one does - and counts as confirming the credential's access (below). ## Expiry and confirming access [#expiry-and-confirming-access] Every credential **expires**, at most 365 days after it was created or last rotated. On the day it expires the connected system is refused, and the only way back is to **rotate** it, which sets a new expiry. Rotate before then and nothing stops. Every **90 days** somebody who manages credentials must also **attest** it: confirm that the system still needs the access it holds. **Attest** in the credential's menu lists its access. You can keep only access you hold yourself across the whole workspace, so anything you do not hold is shown unticked and is removed when you attest; untick anything else the system no longer needs. You never add access this way - **Edit access** does that. Anybody who manages credentials may attest, whether or not they issued it - so a credential whose issuer has left is attested by whoever covers it, and keeps only the access that person holds. Nothing about the credential depends on the issuer still being a member. When an attestation is missed the credential keeps working for **14 more days**, marked **Attestation overdue**. After that it is **Suspended**: the system is refused until somebody attests it, which restores it at once. Creating or rotating a credential counts as attesting it. The credentials table shows when each credential expires or must next be attested, and marks it from **30 days** ahead of either date; its details also say who attested it last. While any credential needs attention, everybody who manages credentials also sees a notice on the **Apps** page linking here. There is no email about it, so keep an eye on that notice. A connected system is told which of the two stopped it - see the error codes in [Automation API](./overview) - and can ask for both dates in advance. ## Telling whether it is working [#telling-whether-it-is-working] Four places answer that, and they answer different questions: * The **Access** tab of the credential's details says what the credential may do: every permission its access options give it, and which option gives each one. It holds no role, so it works in every shared space and every app in one, including apps created later, and never in anyone's personal space. A revoked credential can do nothing. * **The credentials table** says whether the credential is used at all. **Last used** is the last time it authenticated and reads "Never used" until the integration's first successful call - the first thing to check when a new integration is silent. * **Activity** says how much it is doing and what it has taken out (below). * **The audit trail** says what it actually changed: open the **Audit Analytics** app, go to **All actions**, and filter **Actor kind** to **Integration**. The **Actor** of a machine write is the integration itself, under its credential's name, not the administrator who issued it. See [Audits](../../administration/audit-trail.mdx). Credential events themselves - created, secret rotated, access changed, attested, revoked - are in the same trail, attributed to the administrator who performed them. A credential expiring or being suspended is in the trail too, attributed to the platform, since nobody performed it. Runs are attributed in the place you would look for them: in an app's **History**, a run a connected system started carries that system's name beside it, and keeps it after the credential is revoked. A run with no marker was started by a person or by the assistant, or is old enough that the trail entry it was marked from has aged out. ## What one integration has actually done [#what-one-integration-has-actually-done] The **Activity** tab of a credential's details has three parts. **Request limit** is how many requests per minute that credential may make. Requests beyond it are refused until the next minute begins, and the system is told how long to wait. This is the number to quote when whoever built the integration reports being throttled. **Volume over the last 30 days** counts runs it started, exports it made and the rows and data those exports contained, and imports that were applied with the rows they wrote. A failed import is not counted. These are **volumes, not costs** - compute is measured for the workspace as a whole, so the **Usage** tab remains the place to see what was consumed - but an integration whose export row count doubled last month is where to look first when a bill jumps. **What this integration took out** lists its exports, newest first: when, which app and which output, which run the file came from, how many rows and how large, and whether it succeeded. An export is a read rather than a change, so it does not appear in the audit trail - this list is the answer to "did this integration pull something it should not have, and when". A row marked with an asterisk was cut off at the export size limit and holds only part of the output; "File removed" means the export succeeded but its file has since been cleaned up (files go within days, the record is kept for 90 days); hovering a **Failed** status shows the reason the platform recorded, which is the string to quote back to whoever built the integration. ## Revoking [#revoking] **Revoke** stops a credential immediately, including any token it is already holding: its next request is refused. It cannot be undone, so restoring access means issuing a new credential and giving the system its new secret. A revoked credential stays in the list, marked **Revoked** and with nothing left to change but its key still copyable, and it keeps its name in the audit trail - so the record of what it did while it was live stays readable. # Automation API (/docs/integrations/api/overview) This guide walks the whole loop with commands you can paste. The exact shape of every call is in the [API reference](./reference/overview), generated from the contract the API publishes. The contract it describes - field names, scope names, error codes - is English, so this guide and the reference are not translated. ## What the Automation API is [#what-the-automation-api-is] The machine door of the platform, at `https://api.ovidens.com`. An unattended system authenticates with a credential of its own, pushes data into a dataset, runs a pipeline and pulls the result. Its surface is deliberately narrow: read the workspace, write dataset rows, run a pipeline, export a result. It cannot create or delete apps, change a pipeline, edit a Visualisation, invite people or change workspace settings. * **Use the Automation API** when a system acts on its own schedule with its own credential - a nightly ERP export, a warehouse loader, a scheduled reconciliation. * **Use MCP** when a PERSON's AI assistant should act as that person: a different door with a different authorization model, in [Connect your own AI](../mcp). The choice between the two is laid out in [Integrations](../../integrations). Everything a connected system WRITES appears in the workspace's audit trail, attributed to the integration by its credential's name rather than to the administrator who issued it. Reads are not audited; an export leaves an export record instead. ## Getting a credential [#getting-a-credential] A workspace administrator issues one on the **Access** page's **API credentials** tab. Ask for a credential named after the system you are building, its **client id** (`ovc_...`) and **client secret** (`ovs_...`) - the secret is shown once, at creation, and nothing can show it again - and the **access** it should hold, granted as named scopes: | Scope | What it grants | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `workspace:read` | List apps, dataset and output nodes, and their configuration. Read import status, and the status of runs of PUBLISHED configuration. | | `data:write` | Stage, upload and commit a CSV import into a dataset node. | | `pipeline:run` | Run an app's PUBLISHED configuration. | | `pipeline:run_draft` | Run unpublished configuration nobody has reviewed, and see the runs of it - a draft run is invisible to a credential without this scope, including one an editor started in the app. Not granted by default. | | `exports:read` | Generate and download an export of a run result. | | `indicators:write` | Reserved. Nothing checks it today. | | `admin:publish` | Reserved. Nothing checks it today. | A new credential starts with `workspace:read`, `data:write`, `pipeline:run` and `exports:read`; the other three are an explicit choice. Narrowing a credential's access - or revoking it - binds on its very next request, including on a token it is already holding. The administrator's side is [Credentials](./credentials). ## Minting a token [#minting-a-token] OAuth 2.1, `client_credentials`. The authorization server is `https://id.ovidens.com`; the API is a separate resource server that only validates tokens. ```bash title="POST /oauth/token" curl -sS -X POST https://id.ovidens.com/oauth/token \ -u "ovc_YOUR_CLIENT_ID:ovs_YOUR_CLIENT_SECRET" \ -d "grant_type=client_credentials" \ -d "resource=https://api.ovidens.com" \ -d "scope=workspace:read data:write pipeline:run exports:read" ``` * **The credential goes in HTTP Basic** (`-u`). Form fields also work; Basic is the registered method. * **`resource=https://api.ovidens.com` is REQUIRED.** Omitting it, or naming any other resource, is a `400` with `error: "invalid_target"`. * **`scope` is optional.** Omit it for the credential's whole granted set. Naming a scope the credential does not hold fails with `400 invalid_scope` and mints nothing. * **Cache the token for its `expires_in`** rather than minting one per request; the token endpoint is rate limited by source address. * **A credential that has expired or is suspended is refused here too**, with the OAuth error `invalid_client` and, once the secret itself was right, a `code` naming which: `credential_expired` or `credential_attestation_overdue` (see [Error codes](#error-codes)). A wrong secret never carries a `code`. Present it on every API call as `Authorization: Bearer `. Three endpoints need no token, so you can read them before you hold a credential: `https://api.ovidens.com/.well-known/oauth-protected-resource` (where to authenticate), `https://api.ovidens.com/v1/openapi.json` (the contract) and `https://api.ovidens.com/v1/docs` (a redirect to this guide). ## Confirming it worked [#confirming-it-worked] ```bash title="GET /v1/whoami" curl -sS https://api.ovidens.com/v1/whoami -H "Authorization: Bearer $TOKEN" ``` ```json { "clientId": "ovc_7f3a9c1d2e4b6a8c0d5e7f91", "name": "ERP nightly import", "tenantId": "3f1c9a52-4d7e-4b60-9a1f-2c8d5e7b0a34", "workspaceName": "Contoso", "scopes": ["workspace:read", "data:write", "pipeline:run"], "credentialScopes": ["workspace:read", "data:write", "pipeline:run", "exports:read"], "tokenExpiresAt": "2026-08-02T09:30:00.000Z", "credentialExpiresAt": "2027-06-30T09:30:00.000Z", "attestationDueAt": "2026-10-01T09:30:00.000Z" } ``` This is the one call that needs a valid token and no scope, so it separates a bad secret from a missing permission. `scopes` is what THIS token can do (what you asked for, intersected with what the credential holds now); `credentialScopes` is what the administrator granted. On an unexpected `403 insufficient_scope`: in `credentialScopes` but not in `scopes` means your token asked for too little, so mint a new one; in neither means the administrator has to widen the credential. The last two fields say when the CREDENTIAL stops working unless a person acts. Every credential expires (`credentialExpiresAt`, at most a year after it was issued or last rotated), and a workspace administrator must confirm every 90 days that it still needs its access (`attestationDueAt`); a credential whose confirmation is more than 14 days overdue is suspended. A job that reads `whoami` can warn its owner well before either date instead of discovering it as a refusal. ## Discovery [#discovery] ```bash title="Discovery" curl -sS https://api.ovidens.com/v1/apps -H "Authorization: Bearer $TOKEN" curl -sS https://api.ovidens.com/v1/apps/sales_analytics/datasets -H "Authorization: Bearer $TOKEN" curl -sS https://api.ovidens.com/v1/apps/sales_analytics/outputs -H "Authorization: Bearer $TOKEN" ``` Every app, node and column is addressed by its **`technicalId`**, a stable snake\_case identifier that survives a rename. There is no numeric id on this door. **A dataset node's declared column schema IS the integration contract.** `listDatasets` reports, per column, its technical id, its type, whether a value is required, its permitted values when it is an enumeration, and which CSV value options apply. Your file's header must name exactly those columns. Build the file from this response rather than from a sample, and a schema change becomes a diff you detect instead of an import rejected at three in the morning. **A cell of a `dropdown` or `multi_select` column may carry the option's LABEL or its ID**, and either way what the platform stores is the id. `enumValues` lists the labels; beside it, `options` reports the same options in the same order, each with the stable `id` and the `description` its author wrote. A `multi_select` cell packs several of them separated by `,` or `;`. Write the label when a person authors the file and it should read like a spreadsheet; write the `id` when a system generates it, because a label can be renamed and an id cannot. Anything that is neither is refused with the row named. `listOutputs` is the mirror image: the output nodes of the app's latest successful published run, with the columns an export will carry. An app that has never published a successful run answers `200` with `publishedRun: null` and an empty list - a normal state to poll through, not an error. ## The import loop [#the-import-loop] ```text createImport -> PUT the file -> commitImport -> poll getImport -> getImportErrors ``` **1. Stage the import.** The dialect is validated and fully resolved here, before a byte moves. ```bash title="POST /v1/apps/sales_analytics/datasets/customers/imports" curl -sS -X POST \ https://api.ovidens.com/v1/apps/sales_analytics/datasets/customers/imports \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"mode":"replace","filename":"customers.csv","csv":{"delimiter":";","decimalSeparator":","}}' ``` `mode` is `replace` (the file becomes the dataset's contents) or `append`. The response carries the `importId`, the `uploadUrl` and both expiry times. **2. Upload the file** with a plain `PUT`. Send no `Authorization` header - the URL carries its own signature, and a bearer token makes the upload fail. ```bash title="PUT {uploadUrl}" curl -sS -X PUT --upload-file customers.csv -H "Content-Type: text/csv" "$UPLOAD_URL" ``` **3. Commit.** Validates the file against the declared columns and, if every row parses, applies it. ```bash title="POST /v1/apps/sales_analytics/datasets/customers/imports/imp_8c2f4a1d9e63/commit" curl -sS -X POST \ https://api.ovidens.com/v1/apps/sales_analytics/datasets/customers/imports/imp_8c2f4a1d9e63/commit \ -H "Authorization: Bearer $TOKEN" ``` * **Validation is all or nothing.** One unparsable row applies nothing and leaves the dataset as it was. * **Committing the same import id twice is safe.** The first commit answers `202`; a repeat answers `200` with the import's current record and writes nothing, so retrying a timed-out commit is correct rather than a second write. * **Only one import per dataset node validates at a time**; a second is `409 import_in_progress`. **4. Poll the import** until `status` leaves `validating`: ```bash title="GET /v1/apps/sales_analytics/datasets/customers/imports/imp_8c2f4a1d9e63" curl -sS \ https://api.ovidens.com/v1/apps/sales_analytics/datasets/customers/imports/imp_8c2f4a1d9e63 \ -H "Authorization: Bearer $TOKEN" ``` `applied` reports `rowsParsed`, `rowsApplied` and `rowsRejected`. `failed` reports `errorCode`, `likelyCause`, a grouped `errorGroups` summary and, when the header was wrong, a `structure` block naming the expected, detected, unknown and missing columns. A file with no data rows - a header alone, or no bytes at all - fails with `errorCode: "no_data_rows"` in either mode and leaves the dataset exactly as it was; a file whose every row is invalid fails with `row_validation_failed` and leaves it as well. An import never empties a dataset, because an empty file is far more often a failed export than an intent; check the export that produced it before you retry. A row longer than 2,000,000 characters fails the import with `errorCode: "row_too_long"` and leaves the dataset as it was. The file is read row by row and one row is never held whole past that length, so the limit is per row, not per file: shorten or split the longest values on the row the message names, then stage the file again. This read needs only `workspace:read`, so a monitoring job can watch an integration without being able to feed it. If the calling token ALSO holds `data:write` and the import is still awaiting its file, the response carries a fresh `uploadUrl`. **That is how you recover an expired upload URL** - re-read the import, do not stage a new one. **5. Get the full error report** when the grouped summary is not enough: the same path with `/errors` appended answers a short-lived `downloadUrl` for a CSV of every rejected row. There is no report for an import that applied cleanly. Codes that matter here: `upload_missing` (nothing staged, or the staged file expired - stage a fresh import), `file_too_large`, `import_in_progress`, `app_locked`, and the four `csv_dialect_*` codes. ## The run loop [#the-run-loop] ```bash title="POST /v1/apps/sales_analytics/runs" curl -sS -X POST https://api.ovidens.com/v1/apps/sales_analytics/runs \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"target":"published"}' ``` `target` is required. `published` needs `pipeline:run`; `draft` runs unpublished configuration and needs `pipeline:run_draft`, which a credential does not hold by default. **This door never falls back between the two**: an app with no configuration of the target you asked for is refused with `409 no_runnable_version`. The `202` echoes the target it accepted. Only one run per app may be active. A second trigger answers `409 app_locked`, and when the blocker is another run your credential may read, the `detail` carries `activeRunId`. **On `app_locked` WITH an `activeRunId`, poll that run instead of starting another; without one, back off.** That is also the recovery when a `POST` timed out but succeeded: this endpoint is not idempotent, so a blind retry starts a second run. An editor previewing unpublished work in the app locks it the same way, and that run is a draft one: it blocks your trigger, but without `pipeline:run_draft` you cannot read it, so no `activeRunId` is named and backing off is the answer. You are never denied the id of a run you could have polled. Then poll: ```bash title="GET /v1/apps/sales_analytics/runs/4821" curl -sS https://api.ovidens.com/v1/apps/sales_analytics/runs/4821 \ -H "Authorization: Bearer $TOKEN" ``` The run is finished when `status` is no longer `running`. Every node starts `pending`, so a poll taken mid-run reports that for most of the array; on failure, `error` names the node and the message. `GET /v1/apps/sales_analytics/runs` is the paged history, and every run reports `triggeredBy` - an object naming the credential when a system started it, `null` when a person or the workspace assistant did. Both reads need `workspace:read`, and both show runs of published configuration. A run whose `target` is `draft` is a preview of work nobody has reviewed: it is listed, counted in `total` and readable by its id only for a credential that also holds `pipeline:run_draft`. Without that scope, the run id of a draft answers `404 not_found`, the same as an id that names nothing - so a run you started yourself is always readable, because a credential that cannot run a draft cannot have started one. ## The export loop [#the-export-loop] ```bash title="POST /v1/apps/sales_analytics/outputs/revenue_by_region/exports" curl -sS -X POST \ https://api.ovidens.com/v1/apps/sales_analytics/outputs/revenue_by_region/exports \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"runId":4821,"compression":"gzip"}' ``` Every field is optional and the body itself may be omitted, which means "the latest published run, default dialect". An **empty** payload declared as `application/json` is refused by the JSON parser as a `400 invalid_request` before the endpoint sees it, so dropping only the `-d` fails. Send either no body and no `Content-Type`, or an explicit `{}` with the header. This is a property of the HTTP layer, so it applies to any call you strip the body from - if your client sets a JSON content type for every request, send `{}`. ```bash title="POST /v1/apps/sales_analytics/outputs/revenue_by_region/exports (defaults)" curl -sS -X POST https://api.ovidens.com/v1/apps/sales_analytics/outputs/revenue_by_region/exports \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}' ``` **Whether you name a `runId` is the decision that matters.** With none, the file comes from the latest successful published run, and that answer can move between two otherwise identical requests - right for "the current numbers", wrong for a reproducible load. **Naming the `runId` you got from `startRun` pins the file to an immutable run snapshot**, so the same load a month later produces the same bytes. The `202` echoes the resolved `runId` and a `runPinned` flag. Then poll and download: ```bash title="GET /v1/apps/sales_analytics/exports/exp_5b1e7d0a3c92" curl -sS https://api.ovidens.com/v1/apps/sales_analytics/exports/exp_5b1e7d0a3c92 \ -H "Authorization: Bearer $TOKEN" ``` When `status` is `ready` the response carries a `downloadUrl`; fetch it with no `Authorization` header. `rowLimitReached` says the file was cut off at the export size limit and holds only part of the output; `runPinned` says whether the run was yours or resolved. An export writes nothing to the audit trail, but it does leave an export record the administrator can see: which app, which output, which run, how many rows, how large. Codes that matter here: `no_published_run` (nothing published successfully yet - run the pipeline), `run_data_unavailable` (the run exists but its outputs have been removed by retention - run it again), and `503 compute_unavailable`. ## The CSV dialect [#the-csv-dialect] One dialect object describes both directions: what an import PARSES with, an export WRITES with. It is validated at request time, so a bad option is a `400` with nothing uploaded or generated. **Structural options are file-level only.** | Option | Default | Notes | | ----------- | ------- | -------------------------------------------------------------------------------------------------------------- | | `delimiter` | `,` | One character. Must differ from every other single-character option. | | `quote` | `"` | One character. | | `escape` | `"` | One character, and in v1 it must EQUAL `quote`. A distinct escape character is refused rather than mis-parsed. | | `encoding` | `utf-8` | `utf-8` or `windows-1252` on import. Export accepts `utf-8` only. | | `hasHeader` | `true` | Only `true`. Every other option is validated against the declared column names. | **Value options are file-level defaults with an optional per-column override**, under `csv.columns.`. An override may name only value options, and only ones relevant to that column's type: an irrelevant one is `csv_dialect_irrelevant_option`, an undeclared column is `csv_dialect_unknown_column`. | Option | Default | Applies to | | -------------------- | -------------------- | --------------------------------------------------- | | `decimalSeparator` | `.` | `integer`, `decimal`, `percent`, `currency` columns | | `thousandsSeparator` | empty | the same numeric columns | | `dateFormat` | `%Y-%m-%d` | `date` columns | | `timestampFormat` | `%Y-%m-%dT%H:%M:%SZ` | `timestamp` columns | **The supported date and timestamp tokens are `%Y`, `%m`, `%d`, `%H`, `%M` and `%S`.** Anything else is `csv_dialect_unsupported` at request time. Month names are a later addition. Two collisions are refused because they would produce plausible-looking WRONG data: a `thousandsSeparator` equal to the `delimiter` (one value becomes two fields), and one equal to the `decimalSeparator` (`1.5` would become `15`). Beyond the separators, a value in a numeric column may carry a leading `+` or `-`, a parenthesised negative (`(1234)` is `-1234`), surrounding whitespace, a `$`, `€`, `£`, `¥` or `%` symbol, and scientific notation (`2E10`). A TRAILING minus sign is not recognised. On the way OUT, the writer refuses what it cannot produce: `encoding` other than `utf-8`, any decimal or thousands separator outside the set it emits, and a date or timestamp pattern STARTING with a spreadsheet formula trigger (`=`, `+`, `-`, `@`, tab or carriage return) - a pattern is a per-column constant, so it would land a live formula in every cell. ## Error codes [#error-codes] Every failure answers the same body, whatever the endpoint: ```json { "type": "https://ovidens.com/problems/insufficient_scope", "title": "The access token does not carry a scope this operation requires", "status": 403, "code": "insufficient_scope", "requestId": "944debf6-805a-400c-bde6-4a1f437ee42d", "detail": { "requiredScope": "data:write" } } ``` **Branch on `code`, never on `title`.** The code is the contract; the title is the English sentence beside it and may be corrected. `type` is a stable identifier, not a URL to fetch. `requestId` is what to quote to support - it is also on every response as the `x-request-id` header. **`detail` is keyed by the code, and every key in it is optional.** Read `code` first: the keys are not shared between codes, and a given code may answer with no `detail` at all. | Code | `detail` keys, when present | | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `insufficient_scope` | `requiredScope` | | `app_locked` | `reason`, plus `activeRunId` when the blocker is a run this credential may read | | `no_runnable_version` | `target` | | `quota_exceeded` | `resource`, for the three platform-metered ceilings | | `credential_expired` | `expiredAt` | | `credential_attestation_overdue` | `attestationDueAt`, `suspendedAt` | | `compute_unavailable` | `resource`, unless the pause names no ceiling | | `file_too_large` | `bytes`, `max` | | `rate_limited` | `retryAfterSeconds` | | `csv_dialect_conflict` | `fields`, `character`, plus `column` for a per-column override | | `csv_dialect_unsupported` | `field`; then `token` and `supportedTokens` for an unrecognised date pattern, or `reason` plus `quote` or `supported` for a restriction; plus `column` for a per-column override | | `csv_dialect_unknown_column` | `column`, `declaredColumns` | | `csv_dialect_irrelevant_option` | `column`, `option`, `columnType`, `relevantOptions` | Every other code answers with no `detail`. The contract declares the WHOLE code vocabulary on every failure response rather than a per-endpoint subset, so this table, not the contract, is where you learn what a code means: | Code | Status | What it means | What to do | | -------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `invalid_request` | 400 | The body, a parameter or the content type is not valid. | Fix the request. Retrying it unchanged cannot work. | | `invalid_token` | 401 | The token is missing, malformed, expired, or its credential no longer exists. | Mint a fresh token. If that also fails, the credential was revoked. | | `invalid_client` | 401 | The client id or secret was not accepted at the token endpoint. | Check the credential. A rotated secret may have been retired. | | `credential_expired` | 401 | The credential itself has expired. `detail.expiredAt` says when. | Stop and ask a workspace administrator to rotate the credential, which sets a new expiry and issues a new secret. No token will be accepted until then. | | `credential_attestation_overdue` | 401 | The credential is suspended because its periodic confirmation is more than 14 days overdue. `detail` gives the due date and when the suspension began. | Stop and ask a workspace administrator to attest the credential on the Access page. It works again the moment they do; the secret does not change. | | `invalid_target` | 400 | The `resource` indicator is missing or names something this grant may not request. | Send `resource=https://api.ovidens.com`. | | `insufficient_scope` | 403 | The token's effective scope set is too narrow for this operation. | Call `whoami` and compare `scopes` with `credentialScopes`. | | `not_found` | 404 | No such resource. Covers unknown, archived, wrong-type and other workspaces' identifiers alike. | Re-run discovery; do not guess identifiers. | | `conflict` | 409 | A state conflict with no more specific code. | Re-read the resource and decide from its current state. | | `quota_exceeded` | 422 | A workspace limit was reached. `detail.resource` names it for a metered ceiling; the open-import and open-export slot limits name none. | Tell the workspace administrator which limit. Retrying will not clear it. | | `rate_limited` | 429 | Too many requests for this credential. | Wait `Retry-After` seconds. Do not tighten the loop. | | `internal_error` | 500 | An unexpected fault. | Retry once with backoff, then escalate with the `requestId`. | | `csv_dialect_conflict` | 400 | Two dialect options claim the same character. `detail.fields` names both. | Change one of them. | | `csv_dialect_unsupported` | 400 | The dialect asks for something this version cannot do (an unknown strptime token, a distinct `escape`, a non-writable export option). | Read `detail.field`, then the rest of `detail` for the restriction it hit. | | `csv_dialect_unknown_column` | 400 | A per-column override names a column the dataset does not declare. | Re-read `listDatasets`; the schema has changed. | | `csv_dialect_irrelevant_option` | 400 | A per-column override is not relevant to that column's type. | Drop the override, or move it to the right column. | | `upload_missing` | 409 | No staged file is available - none was uploaded, or the staged file expired. | Stage a fresh import and upload again. | | `file_too_large` | 400 | The staged file exceeds the maximum upload size. | Split the file. | | `app_locked` | 409 | A run or an agent job owns the app. `detail.activeRunId` names the run when it is one you may read. | Poll `activeRunId` if present; otherwise back off and retry. | | `import_in_progress` | 409 | Another import for this dataset node is still validating. | Wait for it to finish, then commit. | | `no_runnable_version` | 409 | The app holds no configuration of the requested `target`. | Ask the workspace to publish. The identical request then succeeds. | | `compute_unavailable` | 503 | The platform is deliberately paused, or a platform-wide ceiling was reached. | Back off and retry. This is NOT a fault to escalate. | | `no_published_run` | 409 | The app has no successful published run to export. | Run the pipeline, then export. | | `run_data_unavailable` | 409 | The run exists but its output data has been removed by retention. | Run the pipeline again and export the new run. | **Back-off.** `429` carries a `Retry-After` header and three `X-RateLimit-*` headers reporting the allowance, what is left and when the window resets - respect them rather than guessing. `503 compute_unavailable` means wait, not page someone; no workspace administrator can lift it. `422 quota_exceeded` is the opposite: retrying never helps, somebody in the workspace has to act. So are the two credential codes: `credential_expired` and `credential_attestation_overdue` are 401s, but unlike `invalid_token` minting a new token cannot clear them, so a job that meets one should stop and alert a person rather than retry. A queued import or export whose credential lapses before it runs fails with the same code as its `errorCode`. One whose app was archived or moved into somebody's personal space before it ran fails with `errorCode: "app_not_found"` and writes nothing - the same answer a new request for that app gets. Retrying does not help; ask a workspace administrator where the app went. ## Limits and lifetimes [#limits-and-lifetimes] | Thing | Value | | ----------------------------------- | ------------------------------------------------------------------------------------------------- | | Requests per credential | 120 per minute by default; a workspace can be raised by an operator. `429` carries `Retry-After`. | | Maximum upload | 5 GB per staged file. | | Upload URL | Valid 1 hour. Re-read the import to mint a fresh one. | | Staged file | Kept 7 days, then the import moves to `expired`. | | Download URL (export, error report) | Valid 15 minutes. Re-read the record to mint a fresh one. | | Generated export file | Kept 7 days. | | Import and export records | Kept 90 days after the file is gone. | | Credential | Expires at most 365 days after it was issued or last rotated (`credentialExpiresAt` on `whoami`). | | Credential confirmation | Due every 90 days (`attestationDueAt`); suspended 14 days after a missed one. | **An expired URL never means an expired object.** Every record read mints a fresh URL, so the recovery is always "read the record again", never "start over". ## A worked example [#a-worked-example] The whole loop as one script: mint a token, replace a dataset, run the published pipeline, export an output and download the file, polling at each step and failing loudly. ```bash title="automation-loop.sh" lineNumbers #!/usr/bin/env bash set -euo pipefail API="https://api.ovidens.com" ISSUER="https://id.ovidens.com" CLIENT_ID="${OVIDENS_CLIENT_ID:?set OVIDENS_CLIENT_ID}" CLIENT_SECRET="${OVIDENS_CLIENT_SECRET:?set OVIDENS_CLIENT_SECRET}" APP="sales_analytics" DATASET="customers" OUTPUT="revenue_by_region" FILE="customers.csv" # 1. Mint a token: HTTP Basic, the resource indicator, and the scopes this job needs. TOKEN=$(curl -sS -X POST "$ISSUER/oauth/token" \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d "grant_type=client_credentials" \ -d "resource=$API" \ -d "scope=workspace:read data:write pipeline:run exports:read" \ | jq -r .access_token) AUTH=(-H "Authorization: Bearer $TOKEN") # 2. Prove the credential works before doing anything with it. curl -sS "${AUTH[@]}" "$API/v1/whoami" | jq '{name, scopes, credentialScopes}' # 3. Stage the import. STAGED=$(curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \ -d '{"mode":"replace","filename":"customers.csv","csv":{"delimiter":";"}}' \ "$API/v1/apps/$APP/datasets/$DATASET/imports") IMPORT_ID=$(jq -r .importId <<<"$STAGED") UPLOAD_URL=$(jq -r .uploadUrl <<<"$STAGED") # 4. Upload the bytes. NO Authorization header. curl -sS -X PUT --upload-file "$FILE" -H "Content-Type: text/csv" "$UPLOAD_URL" # 5. Commit. curl -sS -X POST "${AUTH[@]}" \ "$API/v1/apps/$APP/datasets/$DATASET/imports/$IMPORT_ID/commit" >/dev/null # 6. Poll the import. while :; do IMPORT=$(curl -sS "${AUTH[@]}" "$API/v1/apps/$APP/datasets/$DATASET/imports/$IMPORT_ID") STATUS=$(jq -r .status <<<"$IMPORT") [ "$STATUS" = "validating" ] || [ "$STATUS" = "awaiting_upload" ] || break sleep 5 done if [ "$STATUS" != "applied" ]; then echo "import $STATUS: $(jq -r '.errorCode // "unknown"' <<<"$IMPORT")" >&2 curl -sS "${AUTH[@]}" \ "$API/v1/apps/$APP/datasets/$DATASET/imports/$IMPORT_ID/errors" | jq -r .downloadUrl >&2 exit 1 fi echo "applied $(jq -r .rowsApplied <<<"$IMPORT") rows" # 7. Run the PUBLISHED pipeline. On 409 app_locked, adopt the active run. RUN=$(curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \ -d '{"target":"published"}' "$API/v1/apps/$APP/runs") RUN_ID=$(jq -r '.runId // .detail.activeRunId // empty' <<<"$RUN") [ -n "$RUN_ID" ] || { echo "no run: $RUN" >&2; exit 1; } # 8. Poll the run. while :; do DETAIL=$(curl -sS "${AUTH[@]}" "$API/v1/apps/$APP/runs/$RUN_ID") RUN_STATUS=$(jq -r .run.status <<<"$DETAIL") [ "$RUN_STATUS" = "running" ] || break sleep 10 done [ "$RUN_STATUS" = "success" ] || { echo "run $RUN_ID failed" >&2; exit 1; } # 9. Export the output, PINNED to the run we just made: that is what makes it reproducible. BODY=$(jq -nc --argjson runId "$RUN_ID" '{runId: $runId, compression: "gzip"}') EXPORT=$(curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \ -d "$BODY" "$API/v1/apps/$APP/outputs/$OUTPUT/exports") EXPORT_ID=$(jq -r .exportId <<<"$EXPORT") # 10. Poll the export, then download (again, no Authorization header). while :; do RECORD=$(curl -sS "${AUTH[@]}" "$API/v1/apps/$APP/exports/$EXPORT_ID") EXPORT_STATUS=$(jq -r .status <<<"$RECORD") [ "$EXPORT_STATUS" = "generating" ] || break sleep 5 done [ "$EXPORT_STATUS" = "ready" ] || { echo "export $EXPORT_STATUS" >&2; exit 1; } curl -sS -o "$OUTPUT.csv.gz" "$(jq -r .downloadUrl <<<"$RECORD")" echo "downloaded $(jq -r .rowCount <<<"$RECORD") rows" ``` Next: the [API reference](./reference/overview) for the exact shape of any call, or the live contract at [`https://api.ovidens.com/v1/openapi.json`](https://api.ovidens.com/v1/openapi.json). # Validate and apply the staged CSV (asynchronous; poll the import for the outcome) (/docs/integrations/api/reference/commit-import) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Generate an output node's result as CSV (asynchronous; poll the export) (/docs/integrations/api/reference/create-export) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Stage a CSV import and mint its upload URL (/docs/integrations/api/reference/create-import) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # One app with its inbound and outbound port counts (/docs/integrations/api/reference/get-app) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Redirect to the human integration guide (/docs/integrations/api/reference/get-docs-redirect) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # One export's status and a short-lived download URL once it is ready (/docs/integrations/api/reference/get-export) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # A short-lived download URL for the failed import's full error report (/docs/integrations/api/reference/get-import-errors) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # One staged import's status and outcome, with a fresh upload URL while staging (/docs/integrations/api/reference/get-import) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The machine contract for this API, generated from the live route schemas (/docs/integrations/api/reference/get-open-api-document) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Protected-resource metadata for the automation API (/docs/integrations/api/reference/get-protected-resource-metadata) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # One run's status, per-node outcomes and failure summary (/docs/integrations/api/reference/get-run) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The calling credential's identity, workspace, effective and granted scopes (/docs/integrations/api/reference/get-whoami) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # List the workspace's apps (/docs/integrations/api/reference/list-apps) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The app's dataset nodes and their declared column schemas (/docs/integrations/api/reference/list-datasets) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The app's output nodes from its latest successful published run (/docs/integrations/api/reference/list-outputs) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The app's run history, newest first (/docs/integrations/api/reference/list-runs) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # All operations (/docs/integrations/api/reference/overview) {/* Generated by scripts/generate-api-reference.mts from the published contract. Do not edit; regenerate instead. */} ## Contract [#contract] The three routes that need no token: where to authenticate, the machine contract itself, and a redirect to the human guide. Read them before obtaining a credential. Protected-resource metadata for the automation API The machine contract for this API, generated from the live route schemas Redirect to the human integration guide ## Identity [#identity] What the presented credential is and what it may do. The first call to make with a fresh token, and the first call to repeat when one is refused. The calling credential's identity, workspace, effective and granted scopes ## Discovery [#discovery] The workspace's apps and their inbound and outbound ports. A dataset node's declared column schema is the contract a file has to satisfy. List the workspace's apps One app with its inbound and outbound port counts The app's dataset nodes and their declared column schemas The app's output nodes from its latest successful published run ## Imports [#imports] The staged CSV import loop: stage, upload, commit, poll. Validation is all or nothing and a commit is safe to retry. Stage a CSV import and mint its upload URL Validate and apply the staged CSV (asynchronous; poll the import for the outcome) One staged import's status and outcome, with a fresh upload URL while staging A short-lived download URL for the failed import's full error report ## Runs [#runs] Pipeline execution: trigger a run against the published or the draft configuration, then poll it. One run per app at a time. Run the app's pipeline (asynchronous; poll the run for the outcome) The app's run history, newest first One run's status, per-node outcomes and failure summary ## Exports [#exports] Generated CSV of an output node's result: request, poll, download. Pin a run identifier to make a load reproducible. Generate an output node's result as CSV (asynchronous; poll the export) One export's status and a short-lived download URL once it is ready # Run the app's pipeline (asynchronous; poll the run for the outcome) (/docs/integrations/api/reference/start-run) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Integrations (/docs/integrations) Other software can work with your workspace in two ways. They look similar from the outside and they are not interchangeable, because they answer different questions about **who is acting**. ## An AI assistant acting as a person [#an-ai-assistant-acting-as-a-person] Someone connects their own AI assistant - Claude, or another tool that speaks MCP - to the workspace. The assistant then acts **as that person**: it can see what they can see and change what they are allowed to change, and nothing more. A human is in the loop, consenting to the connection and reading what comes back. Choose this when the value is a person working faster: asking questions of the data, building an app by describing it, drafting a Visualisation. Set it up under [MCP](../integrations/mcp.mdx). ## An unattended system acting on its own [#an-unattended-system-acting-on-its-own] An ERP, a data warehouse, a nightly job. It signs in with a **credential of its own** rather than with anybody's account, and it runs on its own schedule with nobody at a keyboard. What it may do is decided entirely by the access an administrator grants that credential, and every change it makes is recorded in the audit trail under the credential's name. Choose this when the value is work happening reliably without anyone present: a recurring import, a scheduled run, a nightly extract. Two pages under **API** cover it, one per role: * **[Credentials](../integrations/api/credentials.mdx)** - for the workspace administrator: issuing the credential, choosing its access, rotating its secret, and seeing what it has done. * **[Overview](./integrations/api/overview)** - for the developer building the integration: minting a token, pushing a CSV, running a pipeline and pulling the result. The per-operation **API reference** sits beneath it. ## Choosing between them [#choosing-between-them] | Question | AI assistant (MCP) | Unattended system (Automation API) | | ------------------------------- | ----------------------------------------------- | ----------------------------------------- | | Who is acting? | A person, through their assistant | A system, under its own credential | | Who authorises it? | The person, by connecting and consenting | An administrator, by issuing a credential | | What bounds it? | That person's own permissions and AI privileges | The scopes granted to the credential | | When does it run? | While somebody is working | On a schedule, unattended | | What does the audit trail show? | The person, and which assistant | The integration, by its credential's name | If both fit, they are not in competition: a workspace commonly has both, for different work. [Overview](./integrations/api/overview) and the API reference beneath it are not translated. They describe a machine contract whose field names, scope names and error codes are English, and a translation would eventually describe something the API does not do. # Connect your own AI (/docs/integrations/mcp) Besides [Vera](../tips/interact-with-vera.mdx), the AI built into Ovidens, you can connect **your own AI** - for example Claude, or another MCP-capable AI - to your workspace. Once connected, it works in the workspace **as you**: it sees what you can see and can only do what you and your admin have allowed. Setting this up takes three steps: an **admin** enables external AI for you, **you** choose what your AI may do, and then you add your workspace's connector URL in your AI. ## Step 1: An admin enables external AI [#step-1-an-admin-enables-external-ai] External AI is **off by default** for every member; an admin has to enable it per person. As an admin: 1. Open the **Access** page and find the member's row on the **Members** tab. 2. Choose **AI permissions** and switch to the **External AI** tab. 3. Tick the capability groups (or expand a group and tick single capabilities) that the member's AI may use at most, then choose **Save**. This sets the maximum you as an admin allow: the most this member's external AI may ever do in this workspace. The member can only choose within it, never beyond it. **Reset to default** puts the member back on the platform default. (See [Administration](../administration.mdx) for the Access page in general.) ## Step 2: Choose what your AI may do [#step-2-choose-what-your-ai-may-do] Next, the member reviews their own AI settings: 1. Open **Profile** at the bottom of the left navigation and choose **Account**, then pick the **AI** section. 2. The section opens on the workspace you are currently working in. If you belong to several, the dropdown at the top switches to another one. Then switch to the **External AI** tab. 3. Everything your admin enabled is **on by default**. Switch off anything you do not want your AI to be able to do. ## Step 3: Connect your AI [#step-3-connect-your-ai] At the top of the **External AI** tab you find your workspace's **connector URL**. It is unique per workspace and looks like `https://mcp.ovidens.com/t//mcp`. The line just below it names the workspace the URL connects to, so you can check you are about to hand your AI the right one. Choose **Copy**, then - using Claude as the example: 1. In Claude, open **Settings > Connectors** and choose **Add custom connector**. 2. Enter a name (for example the workspace name) and paste the copied URL. 3. Choose **Connect**, sign in with your Ovidens account, and **Authorize** the connection. That is it - Claude can now work in your workspace. Other MCP-capable AIs connect the same way: add the connector URL wherever they accept a custom MCP server, then sign in and authorize. Once connected, you can point your AI at anything in the workspace by its ID instead of describing where it is: copy a widget's, a page's or a node's ID in Ovidens and ask your AI to `fetch` it. See [Object IDs and AI agents](./object-ids.mdx). ## Good to know [#good-to-know] * Your AI only sees the tools your **effective permissions** allow - the maximum an admin allows you, minus anything you switched off yourself. * Your AI can never do more in an app than **you** could do yourself there. A tool your permissions allow is still refused when you could not make the same change yourself in the app it touches, and the refusal says so. An app you cannot open does not exist for your AI: it is not listed, and every tool answers that it was not found. * On an indicator app that is running a [round](../indicator-apps/collection-rounds.mdx), your AI enters figures for the reporting units **you** are responsible for and no others, unless you manage that app's rounds (an administrator, or a reporting manager of the app), in which case it may enter any unit's figures, exactly as you could in the app. Five things no AI can do at all: assign who is responsible for a unit, choose which dimension the app's reporting units come from, record a verdict on somebody's answer, write a message on an answer, or mark somebody's collection work finished. The last three would each put words in a named person's mouth. Your AI can READ a round - its name, its status, the period it collects into and the figures it asks for - and it can set one up; who a round asks, and what anybody said about an answer, stay with people. * Each tool tells your AI client whether it only READS and whether it can DESTROY something, so a client that asks before acting can approve safe reads on its own and still stop to ask you before a deletion. You do not configure this; it comes from the same permission model the workspace already enforces. * When a list is longer than one answer can carry, the answer says so: it reports how many items it contains and, where the number is known, how many exist in total. So a capable AI can tell you it has seen 100 of 4,312 values and narrow its question, instead of answering as if it had seen everything. * Every timestamp your AI reads about your workspace - when an app was created or edited, a version published, a run started or a value last changed - reaches it as one exact moment in UTC (the time ends in `Z`). Ovidens shows you that same moment in your own browser's time zone and names the zone beside it, for example **08:43 PM GMT+2**. Your AI is told to convert the time to your time zone and say which zone it means, or to give it in UTC, so the time it quotes is visibly the one on your screen. Dates and times you typed into a dataset column are different: they have no time zone, and your AI is told to show them exactly as they were entered. * Before it queries an app's data, your AI can ask the workspace what that app actually holds: which tables it may read, and the exact type of every column, taken from the query engine rather than guessed from the app's setup. That is why a connected AI usually writes a working query on the first attempt, and why it can tell you a table is not ready yet - the pipeline has not been calculated - instead of reporting that the table does not exist. * Your AI can also check what a query would cost **before** running it. So when a question would sweep an app's whole history, a capable AI can narrow it first, or tell you the question is too broad and suggest a tighter one - instead of leaving you waiting and then failing. Nothing is read and nothing is calculated when it checks. * Your AI can search an app's knowledge base by keyword instead of opening page after page: it gets back the pages that mention your words, each with the passage that matched, and reads only those. It searches what you would see in the app - the working version, and never a page you moved to the trash. Matching is on whole words and does not guess at word forms, so "reports" does not find "report". If your AI comes back empty on a topic you know is written up, ask it to try the wording the page itself uses. * Changes an admin makes, and your own switches, apply on your AI's **next request**; no reconnect is needed. * Every connected AI appears under **Connected AI** in the same settings section, where you can **disconnect** it at any time. * Everything your AI does is recorded in the workspace's activity log - as the AI **plus you** as the person behind it, just like with Vera. * When your AI queries an app that has no results yet, it gets an empty answer that says WHY: the app is not published, has never been calculated, or has only ever been run as a private draft preview. A capable AI will tell you which it is and offer the matching next step (publish, or run the published version) instead of reporting "there is no data". An empty answer with no such reason means the app IS populated and the query simply matched nothing. # Object IDs and AI agents (/docs/integrations/object-ids) Every widget, page, report, node, knowledge-base page and skill in Ovidens - and every indicator, dimension, group, consistency check, reporting period, collection round and survey question - has an ID of its own, such as `wdg_k3m9x2qd7f`. Instead of describing where something is ("the Sales app, the fourth page, the table called Open orders"), copy its ID and give it to your AI. The AI reads exactly that object, in one step, without searching for it. This works with any AI you have connected to your workspace - Claude, ChatGPT or another AI that speaks MCP (see [Connect your own AI](./mcp.mdx)) - and with [Vera](../tips/interact-with-vera.mdx) in an analytic app. ## What an ID looks like [#what-an-id-looks-like] An ID starts with a few letters that say what kind of object it is, then an underscore and ten letters and digits. It has no spaces and no hyphens, so a double-click selects all of it. | Starts with | Object | Starts with | Object | | ----------- | ------------------------------- | ----------- | ------------------- | | `app_` | an app | `ind_` | an indicator | | `page_` | a page of a Visualisation | `dim_` | a dimension | | `wdg_` | a widget | `grp_` | an indicator group | | `rpt_` | a report | `chk_` | a consistency check | | `node_` | a pipeline node | `per_` | a reporting period | | `kb_` | a knowledge-base page or folder | `rnd_` | a collection round | | `skl_` | a skill | `qst_` | a survey question | ## Where to copy an ID [#where-to-copy-an-id] * **A widget:** hover over the widget - in the app and in the editor - and a small ID icon appears right after its title. Click it to copy the ID. On a touch screen the icon is always shown. * **A page or a report:** hover over its row in the app's sidebar. The ID icon appears among the row's other actions. * **The app:** on the apps list, hover over the app and click **Edit app** (the pencil). The dialog shows an **App ID** row under the Technical ID, with its own copy button. This works for every app type. In an analytic app, the ID icon also sits next to the app's name in the header. * **A knowledge-base page:** open the row's menu in the knowledge-base tree and choose **Copy ID**. An open page, and an open report, also show the ID icon next to their name. * **Everything else** - a node, a skill, an indicator, a dimension, a group, a consistency check, a reporting period, a collection round and a survey question: its panel shows an ID row under the Technical ID (a group, which has no Technical ID, shows it under its name). The row is labelled with the kind, such as **Node ID** or **Question ID**, and has its own copy button. An object gets its ID the moment it is saved. A reporting period that only exists in the timeline - nobody has renamed it, handed it over for review, approved it or started a round in it - gets its ID once it is saved for one of those reasons. **Copy link** in the app header's menu and in the knowledge-base row menu copies a link instead. The link opens the object in Ovidens, and your AI accepts it just like an ID, because every link Ovidens copies carries the object's ID. ## Hand an ID to your AI [#hand-an-id-to-your-ai] Paste the ID into your AI with the word `fetch` in front of it: ``` fetch wdg_k3m9x2qd7f ``` Or use it in a question - "what does wdg\_k3m9x2qd7f show for March?" - or paste a copied link. A connected AI hands the ID to Ovidens' **fetch** tool and gets the object back. Two optional additions change what it reads: | Add | What it does | Example | | ----------------- | ----------------------------------------------------------------------- | -------------------------------- | | `@published` | Read the published version's setup instead of your open draft. | `fetch wdg_k3m9x2qd7f@published` | | `@draft` | Read the open draft. With no draft open, your AI is told there is none. | `fetch page_x2kq7m1c0d@draft` | | `:p2`, `:p3`, ... | Read the next page of a long answer. | `fetch node_7m1c0dx2kq:p2` | A version and a page can be combined: `fetch wdg_k3m9x2qd7f@published:p2`. ## What your AI gets back [#what-your-ai-gets-back] * **The object's name and a link** that opens it in Ovidens. A widget's link opens its page, scrolls to the widget and highlights it for a moment. * **How it is set up** - a widget's type, the node it reads, its columns and filters; a node's configuration and columns; a skill's instructions; a question's text and options. * **What it shows, as the app shows it** - a table widget's rows with its filters, visible columns, grouping and totals applied; a chart's series; a pivot's cells; a Figure widget's value; the choices a Parameters widget offers; an SQL widget's result; a node's rows; a knowledge-base page's text; an indicator's collected values; a dimension's members. An AI widget answers with the skill it runs, and reading it never runs the skill. * **The IDs of related objects** - the app, the page a widget sits on, the node it reads, the pages of an app - so your AI can fetch those next. * **Where each part came from** - which version the setup was read from, and which calculation the data came from. ### Which version your AI sees [#which-version-your-ai-sees] Your AI sees what the app would show you: the setup from your open draft if there is one, otherwise from the published version, and the data from the latest successful calculation of the published version. So a widget you have just added to a draft comes back with its setup and a note that there is no published data for it yet. Add `@published` to read exactly what everyone else sees today. ### Long answers [#long-answers] One answer holds up to 100 rows. When there are more, the answer says so and names the ID of the next page - for example `wdg_k3m9x2qd7f:p2` - which your AI can fetch next. A long knowledge-base page is split into pages the same way. ## IDs stay with the object [#ids-stay-with-the-object] * **An ID never changes while the object exists.** Renaming it, moving a widget to another page, publishing, opening a new draft and restoring an older version all keep it. * **A copy of an app gets new IDs.** Importing an app, installing one from the [Marketplace](../marketplace.mdx), duplicating a survey and the sample apps all create objects with IDs of their own, so an ID always points at one object in one app. * **A deleted object's ID stops working.** Your AI is told the object was deleted - or, if you removed it in your open draft, that it is still published. * **IDs belong to your workspace.** An ID copied in another workspace is not found in yours. ## Good to know [#good-to-know] * Your AI reads by ID with your permissions and only reads - it changes nothing. An admin allows it with **Fetch an object by ID** in the **Read** group of the member's **AI permissions**. If your AI could already read an app's Visualisation configuration, it can fetch by ID too. * Archived apps and the Audit Analytics app are not available to AI agents; their IDs are not found. * Reading data by ID counts toward your workspace's compute credits, like any other question your AI asks of the data. # Marketplace (/docs/marketplace) The **Marketplace** is a catalogue of ready-made apps. Instead of building an app from an empty canvas, you pick a template that already does roughly what you need, install it into your workspace, and change it from there. You open it from **Marketplace** in the left navigation. Everyone in your workspace can browse it. ## What a template is [#what-a-template-is] A template is a **complete, working app** that someone has published: its data pipeline, its Visualisation, its Knowledge Base, and its Skills. Many templates also ship with data, so the charts have something in them the moment you open the app. Each card in the catalogue tells you what you are looking at: * The **name** and a one-line summary. * **Sample data** - the template brings example rows so the app is not empty on the first open. They are there to be replaced by yours. * **Reference data** - the template brings a lookup table it needs to work, such as emission factors or unit conversions. This data stays. * A **standard** badge where the template follows a published reporting framework. Click a card to open its detail page, which is where the publisher explains what the template does, what data you will need to supply, and how to read the result. ## Installing a template [#installing-a-template] On the template's detail page, choose **Install**. Give the new app a name and a technical id, and confirm. If you may install into more than one space, choose the space the app goes into too - the dialog starts on **Workspace** where you may install there. Ovidens creates a **new app in your workspace** and opens it. That new app is entirely yours. It is a copy, not a subscription: nothing about it is shared with anyone else, and nothing you do to it affects the template or anyone else's copy of it. Rename it, add nodes, rewrite the SQL, change the Visualisation - it is an ordinary app from the moment it exists. Installing needs the same permission as importing an app, which comes with the **App builder** and **Admin** roles on the space the app goes into - and every member may install into **My space**. A template that ships data also needs permission to change data, because the install writes those rows into the new app's datasets; everyone who may install apps holds it. If you are missing either, the detail page's **Use template** button says which, before anything is created. If the technical id you chose is already taken in your workspace, Ovidens says so and nothing is created. Pick another one and try again. ## The data an installed app comes with [#the-data-an-installed-app-comes-with] Two kinds, and they behave in opposite ways. Knowing which is which is the one thing worth reading before you start entering your own figures. ### Sample data - yours to clear [#sample-data---yours-to-clear] Sample data exists so the app renders on the first open, so you can see the shape of the result before you have supplied anything. It is example data, not your data. When you are ready to work with real figures, open the app and use **Clear sample data**. Every dataset the template filled with samples is emptied in one step; the datasets keep their columns, so the app's structure is untouched and you can upload or type your own rows straight away. Clearing sample data, and bringing in an updated reference table (below), finish setting up the app, so they are offered to whoever may configure it: the **App builder** and **Admin** roles on the app or its space. Someone who only enters data does not see either. You do not have to clear it. The moment you write to a dataset yourself - an upload, a paste, or a single edited cell - Ovidens stops treating that dataset as sample data and it is no longer offered for clearing. Your data is never something the app offers to throw away. ### Reference data - yours to keep [#reference-data---yours-to-keep] Reference data is a lookup table the template needs in order to compute anything: a list of emission factors, a conversion table, a set of category codes. It is part of what the template is for. **Clear sample data never touches it.** You can edit it if your organisation uses different values, and editing it changes nothing about how it is treated. If the template's publisher later ships a corrected version of a reference table, the app tells you and offers to bring it in - see below. That is the only content that can ever move into an installed app after you have installed it. ## When the template is updated later [#when-the-template-is-updated-later] Because an installed app is a copy, an update to the template never arrives on its own. What you get instead is a notice on the app: **a newer version of this template is available**, with what the publisher changed. You then choose what to do with it: * **Bring in an updated reference table.** Where the update includes new reference data for a dataset your app holds as reference data, the app offers to replace that table's rows with the current ones. One dataset at a time, and only when you ask. Your own data is never touched by this. * **Take the rest by hand.** Configuration changes - a new node, a changed calculation, a redesigned page - are not applied for you, because your app has moved on since you installed it and only you know which of your changes to keep. Read what changed, and make the ones you want. * **Stop being told.** Choose **Do not show this again** and the notice goes away for this version of the template. If the publisher releases another one after that, the notice comes back, because that is genuinely new information. Closing the notice with its **X** hides it for the current session only. The app also shows you where it came from, so months later it is still clear which template an app started life as. ## Publishing a template [#publishing-a-template] Publishing is not part of an ordinary workspace. The catalogue is curated: templates are published centrally by Ovidens, so every listing has been checked to install cleanly before anyone sees it. If you have built something you would like to see in the catalogue, get in touch. ## Related [#related] * [Apps](overview/apps.mdx) - what an app is, and the draft-and-publish cycle every app follows, including one you installed. * [Datasets](analytical-apps/pipeline/datasets.mdx) - uploading and editing the data an installed app expects from you. * [Export and import an app](analytical-apps/export-import.mdx) - moving one of your own apps between workspaces, which is the same portable format a template uses. # Apps (/docs/overview/apps) An **app** is a self-contained home for one analysis. It holds everything that analysis needs in one place: its data, the logic that shapes that data, and the **Visualisation** your team works in. Because each app is self-contained, you can build, share, and change one without touching any of the others. You manage all of your apps from the **Apps** page in the left navigation. ## The two kinds of app [#the-two-kinds-of-app] Ovidens has two kinds of app, and you pick the kind that matches what you are trying to do. * An **analytic app** is for combining data intelligently. Use it when your answer comes from bringing several sources together, cleaning them, and reshaping them, for example joining a sales export with a list of regions and presenting revenue by region. * An **indicator app** is for collecting key figures over time. Use it when you record values by hand, period by period, and the data is multidimensional, such as monthly fuel use per site or quarterly headcount per team, and want Ovidens to compute totals, ratios, and trends for you. If you are unsure, ask which sentence fits better: "I need to combine and reshape data into a report" points to an analytic app, while "I need to record figures by hand every period and watch how they move" points to an indicator app. ## The app and its editor [#the-app-and-its-editor] Every app has two faces, and one button moves you between them. * The **app** itself is what everyone opens: the finished Visualisation with the data in it, and none of the building controls. This is what your teammates see. * The **editor** is where you build it: the data pipeline, the Knowledge Base, and the Visualisation you are assembling. To go from the app to the editor, click **Open editor** in the top right, or press E. To go back, point at the app icon in the top left of the editor bar (it turns into a **Back to viewer** arrow) and click it, or press E again. See [Keyboard shortcuts](../tips/keyboard-shortcuts.mdx). ## Draft and publish [#draft-and-publish] Everything you change in the editor goes into your **draft**. A draft is private: it is saved for you continuously as you work, and nobody else sees any of it. The editor bar says **Draft (private)** while you have one, and spells out why: > Only you can see this draft. Viewers keep seeing the published version. That includes running an analytic app. Running is a private preview of your draft, not a release: your teammates keep seeing the last published results until you say otherwise. When you are happy, click **Publish**. Ovidens asks you to confirm, lets you give the version an optional name (something like "Q3 metrics" is enough), and warns you that your viewers will now see these changes. For an analytic app the dialog also offers **Publish and run**, which publishes and then immediately runs the pipeline, so viewers get the new logic and fresh results in one step. Each publish creates a **version**: a snapshot of the whole app at that moment. Versions are kept in order under **History**, the clock button in the editor bar, so you can look back at how the app used to be. Two more controls sit next to the draft: * **Discard** throws the draft away and brings back the last published state. If you also entered data while the draft was open, that data goes back to the published data as well. * **Restore**, on any older version in the history, brings that version back and publishes it as the newest one. It restores the data of that version too, so any data entered since that version was published is replaced. Ovidens tells you this in the confirmation before anything happens. Before it moves anything, Ovidens makes a safety copy of the current state as an automatic version, so the data you entered since the last publish is still in the history afterwards. If your workspace has reached its limit on versions per app, that copy cannot be made - and rather than replace data it could not keep, Ovidens refuses the restore and says so. Ask a workspace administrator to raise the limit. In an analytics app, a restore then recalculates the app automatically: the editor shows the run in progress and, without a reload, the restored results when it finishes - or, if the restored version cannot be calculated, which node failed, with a link to that node and a **Re-run** once you have fixed it. A restore is refused while a pipeline run is in progress; wait for the run to finish, then restore. Restoring is for workspace administrators, so the control is shown only to them. While an app has an open draft, data entry in the app is paused for everyone else: "This app is being edited. Data editing is locked while the draft is open - you are seeing the latest published data." Publish or discard the draft to release it. ## Who builds and publishes [#who-builds-and-publishes] Building apps is for members who hold the **App builder** or **Admin** role. With the role on the whole workspace, either can create an app, edit its details, put the apps of a shared space in order, and archive and restore apps. In an analytic app they can also change everything in the editor - the pipeline, the Knowledge Base, skills and the Visualisation - and publish, discard the draft, and restore an older version. Removing a Knowledge Base page for good, from its trash, stays with administrators. In an indicator app they can build the structure - indicators, groups, dimensions, the reporting-unit dimension and consistency checks - and in a survey app the form, its questions, the collection settings and the share link; in both they can publish, discard the draft and restore an older version. Running data collection in an indicator app - collection rounds, reporting periods and their sign-off, and the reporting interval - is for administrators and for **reporting managers**, a role an administrator gives on a space or an app; the indicator AI assistant stays with administrators. Everyone else opens the published app and works with its data. Ovidens shows them only the controls they can use: the **Apps** page has no create, reorder or archive controls in a shared space for them, and in the editor the app's configuration is read-only while entering data still works. Everyone keeps their own favourites, puts the spaces in their own order, and orders the apps in **My space**. ## Create and open an app [#create-and-open-an-app] Vera can create a fully built app for you from a plain-language description. See [Build with Vera](../analytical-apps/build-with-vera.mdx) for the analytic path and [Build with Vera](../indicator-apps/build-with-vera.mdx) for indicators. To create one yourself: 1. Open the **Apps** page from the left navigation. 2. Choose **New app** at the top right and pick the space the app should go into under **Create in** - or select the **+** on that space's own header - then pick the kind you want. In **My space** only you can see it; in a shared space everyone with access to that space can. Only the spaces where you are allowed to create apps are offered. (In a workspace with no apps yet, the welcome screen's create cards put the app into **My space**.) 3. Give it a name (and an optional description so teammates know what it is for). The dialog shows which space the app goes into. 4. Confirm to create it. Your new app appears in its space's group on the **Apps** page. Click its row to open it and start building. ### Spaces [#spaces] The **Apps** page groups your apps by **space**: * **My space** comes first. Every member has one, and every member can create apps in it, whatever else they may do in the workspace. Nobody else sees the apps in it - not a colleague, and not a workspace admin. * Below it are the shared spaces you can reach, such as **Workspace**, the space every workspace starts with. A shared space you cannot reach is not shown at all. **New app** at the top right asks which space an app goes into, and so does the import button (**Import an app file**) unless only one space is shown, because the space decides who can open it; each space's header also carries its own **+** and import button, wherever you are allowed to create or import apps in that space. Use **Spaces** to show only some of your spaces. If there is no space you may create apps in, or your spaces could not be loaded, the page says so. Admins create, rename and delete shared spaces on the **Access** page - see [Roles and spaces](../administration/index.mdx#roles-and-spaces). An indicator app in **My space** collects figures from you alone: you can enter values into it, but it cannot run a collection round, because nobody you asked could see the app. ### Stars and the order of your page [#stars-and-the-order-of-your-page] * **Stars.** Point at any app you can open and select the star (**Star**). A starred app carries a small star on its icon, and **Starred only** narrows the page to your starred apps. Stars are yours alone: a colleague sees their own, never yours, and a star changes nothing about the app. Select the star again to remove it. * **The order of the spaces is yours.** Choose **Move up** or **Move down** in a space's ⋮ menu (or the space header's own arrow button, when that is the only thing you can do with the space). The order is kept for you alone; a space you have never moved keeps its place after the ones you arranged. * **The order of the apps in a space** follows the column you sort by: click a column header to sort by it. The page opens on **Last opened**, newest first. ### Move an app to another space [#move-an-app-to-another-space] Moving an app is how you share it: an app you built in **My space** reaches your colleagues once you move it into a shared space. Choose **Move to another space** in the app's ⋮ menu on the **Apps** page (or the row's own folder-with-arrow button, when moving is the only thing you can do with the app), then pick where it goes. You can also drag the app onto another space's group: the spaces it may move into are highlighted, and releasing it there opens the same dialog. Moving an app changes who can open it, so before anything is saved the dialog tells you the effect as numbers - for example "12 people lose access" and "30 people gain access". When other apps import this one, it also says how many of the people who build those apps could no longer change them after the move, because they would no longer be able to read this app where it goes. It never names anybody, and it never counts an app in somebody else's personal space: if one of those stands in the way, it says only "An app you cannot see imports this one". Nothing moves until you select **Move**, and every move is recorded. You can move an app: * out of **My space** into any shared space where you are allowed to create apps; * from one shared space into another where you are allowed to create apps, if you may change the app; * into **My space** only if you are a workspace admin, because that takes the app away from everyone else. Somebody else's space is never offered. The dialog also tells you when a move is not possible, and why: * An app moving into **My space** cannot take a collection round with it, nor a role given on the app itself (remove the role on the **Access** page first). * An app in **My space** can be imported only by other apps in **My space**, the same rule that applies when you add an import node. So moving an app into **My space** is refused while an app outside it imports it, and moving an app out of **My space** is refused while it imports an app that stays behind there. Imports from shared apps you can read are allowed wherever the app goes. ### See which apps import an app [#see-which-apps-import-an-app] An app can be imported by other apps (see [Analytic app data](../analytical-apps/pipeline/analytic-data.mdx)). Everybody who can read an importing app sees what it imports, so the apps importing yours are part of who can see its data. The **Imported by** column of the **Apps** page counts them; click the count to list them in the app's settings. You see by name the importing apps you can open yourself; a workspace admin is also told how many other apps in shared spaces import it. An app in somebody else's personal space is never named or counted, and nobody else is told anything about apps they cannot open. ## Leaving an app [#leaving-an-app] Getting back to the **Apps** page catches people out, because there is no separate Back button. The app's own **name**, at the top of the left navigation, is the control: point at it and it turns into a **back arrow**. Click it and you are back on the **Apps** page. It is the same in every app, analytic and indicator alike, so it is worth learning once. (Inside the **editor** the same idea applies one level down: the app icon in the top left of the editor bar turns into **Back to viewer**.) If you would rather be shown than told, the app can walk you through it: [take the tour of going back](https://app.ovidens.com/apps?tour=back-to-apps). The tour runs on a practice app, so nothing you click during it touches your own work. ## Archive and restore an app [#archive-and-restore-an-app] Nothing you remove from the **Apps** page is thrown away immediately. Choosing **Archive** in an app's ⋮ menu (or the row's own archive button, when archiving is the only thing you can do with the app) opens **Archive Analytic App**, which explains what really happens: > The app will be archived and removed from your workspace. Its data is kept during > the recovery window, and you can restore it from Archived apps until it is > permanently deleted. An archived app disappears from the Apps page and stops counting towards the number of apps your workspace is allowed, so archiving one immediately frees a slot for a new app. Members with the **App builder** or **Admin** role on the whole workspace get an **Archived** button in the Apps page header, with the number of archived apps on it. It opens a list where each app shows **Archived** on a date, **Will be permanently deleted** on a date, and a **Restore** button. Restore brings the app back to the Apps page exactly as it was, and takes its app slot back, so it can fail if your workspace is at its app limit. The recovery window is a workspace setting (30 days unless your administrator has changed it). Once it passes, the app and its data are permanently removed and cannot be recovered. Its technical id is not freed: no new app in the workspace can take it, so an app that imported data from the deleted one can never start reading a different app under the same id. ## Export and import an app [#export-and-import-an-app] An app can be moved between workspaces as a single file. * **Export app** lives in the editor's more menu (the ⋮ button in the editor bar) and downloads the app as a portable bundle: a `.json` file named after the app. It exports the app's published version, so publish before exporting. The bundle carries the structure of the app - its pipeline nodes, the Knowledge Base, and the Visualisation - but not the rows of data in it. * The import button (**Import an app file**) sits at the top right of the **Apps** page, and each space's header carries its own import button wherever you may import into that space. Pick a bundle file and Ovidens creates a new app from it and opens it - in that space, from a space's header or when only one space is shown; otherwise, if you may import into more than one space, Ovidens first asks which space the app goes into, starting on **Workspace** where you may import there. In a workspace with no apps yet, the welcome screen offers **Import app** the same way. If the file is not a valid bundle you get "That file is not a valid app bundle." and nothing is created. Exporting needs the **App builder** or **Admin** role on the app. Importing needs it on the space the app goes into - and every member may import into **My space**. # What is Ovidens (/docs) Ovidens is where your team does all of its analytics, at a speed that used to be out of reach. You describe what you want, AI builds it, and you have a working result in minutes instead of weeks. What makes that safe to rely on is that nothing is a black box: every logic block, from a data transform to a computed metric, stays **configurable**, **auditable**, and **versioned**. You can always see how a number was produced, and change it with confidence. Ovidens also takes care of the parts you should not have to think about. There are no servers to size, no databases to run, and no pipelines to keep alive. You bring your analytics; Ovidens runs the infrastructure underneath. ## Everything is an app [#everything-is-an-app] Everything you build in Ovidens is an **app**: a self-contained home for one piece of analysis, holding its data, the logic that shapes it, and the **Visualisation** your team works in. Because each app is self-contained, you can build, share, and change one without touching the others. There are two kinds. * **Analytic apps** are for combining data intelligently. Reach for one when the answer comes from bringing several sources together, cleaning them, and reshaping them, for example joining a sales export with a list of regions and presenting revenue by region. * **Indicator apps** are for collecting key figures over time. Reach for one when you record values by hand, period by period, and the data is multidimensional, for example fuel use or electricity per site each month, with Ovidens computing totals, ratios, and trends for you. You can use one kind on its own, or feed the metrics from an indicator app into an analytic app to combine them with other data. ## Meet Vera, your AI assistant [#meet-vera-your-ai-assistant] **Vera** is the built-in AI assistant, and the fastest way to build. You describe what you want in plain language, for example "track our monthly electricity use per site and chart it", and Vera sets the app up for you: the data steps, the metrics, and the Visualisation. It pauses to ask whenever it needs more detail, and everything it builds is yours to review or adjust. The rest of this guide explains each part of the product, so you can check what Vera builds, or build things yourself when you prefer. ## Your first hour [#your-first-hour] You do not start with an empty workspace. Ovidens sets one up for you at sign-up, already filled in so you can learn on something real: * **Sample Sales Analytics**, an analytic app with data in it, a pipeline that has already run, and a finished Visualisation. * **Sample ESG Indicators**, an indicator app whose metrics the sales app reads. Both are yours: change them, break them, delete them. Nothing about them is protected. ### Guided tours [#guided-tours] A tour walks you around a real screen, highlighting one thing at a time: what a pipeline node is, how a chart is wired to a result, how to ask Vera a question, and what each control on the admin pages actually does before you press it. One covers a basic worth knowing early - [how to get back out of an app](apps.mdx#leaving-an-app), which has no separate Back button. Three teach an app editor: building and running a pipeline, turning results into charts and tables, and working with Vera. Three more show you around an administration page: the Access page, the System page, and API credentials. Open **Guided tours** at the bottom of the sidebar to see them all, grouped under headings - Basics, Analytic app for the three editor tours, and Access and System for the administration pages - so look under the heading that matches what you want to learn rather than scanning one long list. The count beside it says how many you have finished, and each tour in the list is ticked once you have been through it - a finished tour stays in the list, because watching it again costs nothing. The three administration tours also carry a compass in the page header, so you can start one while you are standing on the page it describes - the API credentials tour on the Access page's **API credentials** tab. A tour only shows you around; it never changes anything. Where a tour has you press things for yourself - the editor tours, and the one about leaving an app - it moves you to a **practice** app first, marked with a **Practice mode** badge. Everything you do there is thrown away when the tour ends, and none of your own apps is touched. **Skip tour** leaves at any point, and a tour never starts by itself. Occasionally Ovidens tells you about something new with a small note marked **New**; **Got it** dismisses it. ## Where to go next [#where-to-go-next] * **[Administration](../administration.mdx)** covers your workspace, teammates, the activity log, and usage. * **[Analytical Apps](../analytical-apps/overview.mdx)** walks through combining data and building the Visualisation your team works in. * **[Indicator Apps](../indicator-apps/overview.mdx)** explains collecting metrics and working new ones out with calculations. * **[Tips](../tips/interact-with-vera.mdx)** has guidance on working with Vera, keyboard shortcuts, and search. # Survey apps overview (/docs/survey-apps/overview) A **survey app** collects answers from people outside your workspace. You build a form, publish it, and share one link; anyone with the link can answer it without an account and without signing in. Every response lands in your workspace as a row of data, ready to summarise or feed into an analysis. Where an analytic app reshapes data you already have and an indicator app gathers a measurement period by period, a survey app is built to ask **people** something and keep what they say. ## The three parts [#the-three-parts] A survey app has three surfaces, and the split between them is the thing worth learning first. * The **form** is what you build: the questions, their order, and the rules about which ones appear. Editing it changes a **draft**, and a draft is invisible to respondents until you publish. * **Settings** is what you operate: whether the survey is accepting answers, when it closes, how many responses it takes, and the links to send. Change what you like and press Save; a saved rule takes effect at once, with no publish involved. * **Responses** is what you read: a summary per question, the individual answers, and the way into an analytics app. The reason the first two are separate is practical. "Stop accepting responses" is a switch you want to work instantly. "Add a question" is a change respondents should only meet when you are ready. Who sees which part follows their role on the survey. Everyone who can open a survey app reads its published form and its responses. **Open editor**, the way into the form and its version history, is offered to people who may read the survey's configuration; the share link, and the Settings that change the survey, are for people who may configure it. Someone who may only view a survey never sees a draft. ## Building the form [#building-the-form] The **Questions** page is a canvas: every page of your form is a card, and every question a card inside it, laid out as a respondent will read them. An outline of the form sits on the left (fold it with the button above the canvas, or with ⌘), and clicking a question or a page's title opens its settings in a panel on the right. Hover a card for its actions: drag it by the grip to reorder it (a question can be dragged onto another page), duplicate it, or delete it. Press Escape, or click the selected card again, to close the panel. Each row of the form is either a **question** or a **page break** (a section). A page break starts a new page, so a long survey can be read a section at a time. Add a question with the **Question** button at the foot of the page it belongs to - you pick its type first - and a new page with the **Section** button under the last one. Switch the canvas to **Preview** (or press P) to walk the form exactly as a respondent will, including unsaved edits: the same page, the same Next and Back, the same required checks. Nothing you enter in the preview is stored. A phone width is a click away in the same bar. A question has a **type**, and the type is what a respondent sees: | Type | What the respondent gets | | ----------------------- | ----------------------------------------------------------- | | Short text, Long text | A single line, or a box | | Number | A number field, with an optional unit and range | | Single choice, Dropdown | One choice from a list | | Multiple choice | Any number of choices from a list | | Yes / no | Two buttons | | Scale | A row of numbered buttons, with labels at each end | | Email, Date | A field that checks its own format | | File upload | A button that attaches one file, up to 10 MB | | Table | A grid of columns you define, with rows the respondent adds | A question can also be **required**, and it can be **conditional**: "show this only if an earlier question was answered a particular way". A conditional question a respondent never sees is never required of them, and its answer is never stored. A **table** question is the one to reach for when the answer is a LIST: "tell us about each of your suppliers", "list the sites this applies to". You define the columns - a header and an answer type each, up to ten of them - and the respondent adds a row per supplier, up to fifty. A column can be a short text, a paragraph, a number, a date or an email address, and any of them can be required, which means a row somebody started has to fill it in. On the Results page and in the CSV each column becomes a column of its own, holding that respondent's entries in the order they typed them. A **file upload** question asks for a document: a certificate, a signed form, a photograph. The respondent picks one file per question and it is stored with the survey; on the Results page and in the CSV the answer reads as the file's name, and opening a single response gives you a button that downloads it. You choose what a respondent may attach - **Any file**, **Documents** (pdf, doc, docx, xls, xlsx, csv, txt) or **Images** (png, jpg, jpeg, gif, webp) - and that choice narrows the file picker they see. It is a guide for them rather than a security check. A survey link can be shared with anybody, and Ovidens does not scan what is uploaded for viruses or look inside it. A file is always downloaded rather than opened in your browser, so nothing runs by being listed - but treat what arrives the way you would treat an email attachment from somebody you do not know. A choice question can **allow another answer**. That adds an Other choice with a text box, under the label you give it, and it is stored in two columns: the choice column records that Other was picked, and a second column beside it holds what was typed. Filters and charts over the choice column keep meaning what they meant, and an analytics app importing the responses still resolves every choice to its label. Other catches outliers in a list you have already thought through - it does not recover a category you forgot, so it is worth reading what people typed. Once answers exist you can still ADD choices and reword them; removing a choice, or switching Other off, is refused, because either would leave answers pointing at something that is no longer there. ## Publishing, and editing afterwards [#publishing-and-editing-afterwards] Publishing takes a snapshot of the form and makes it the one respondents see. It is the same versioning every Ovidens app has: each published version is immutable, previous versions stay in History, and you can restore one. Until a survey is published for the first time, its link answers "not open yet" rather than an empty form. **You can keep editing after you publish.** An edit opens a draft, respondents go on answering the published form while you work, and your changes reach them when you publish again. Nothing you collected is lost or changed by any of it, and restoring an older version brings its QUESTIONS back as a new version - it never brings back old answers or removes new ones. Two things are worth knowing before you edit a live survey. * **A question somebody has already answered is partly fixed.** You can reword it, change its help text, move it to another page, make it required or optional, add a choice and rename one. On a table question you can add a column, rename one and change whether it is required. You cannot change its answer type, its column name, remove one of its choices, switch its Other choice on or off, remove one of its table columns or change a table column's type - each of those would leave answers pointing at something that no longer exists. Deleting an answered question hides it from the form and keeps its answers. * **Some changes mean the answers are no longer one measurement.** Moving a rating scale from 1-5 to 1-10, changing the condition that decides who is asked, making a required question optional, or changing a table question's columns at all - adding one included - all change what an answer MEANS. Ovidens tells you before you publish - the publish confirmation lists which questions would be "redefined" - and afterwards it marks that question on the Results page rather than averaging the two sets together. Rewording a question or adding a choice is not such a change: those answers still belong together. A respondent who had the form open when you republished is asked to reload before their answers are accepted, so nobody's answers are recorded against a questionnaire they were not shown. ## Starting a new survey from this one [#starting-a-new-survey-from-this-one] To run the same questions again - next year's edition, or a variant for another group - open the survey with Open editor and choose **Duplicate survey...** from the **More actions** menu (⋮) in the top bar. You name the new survey and, if you may create apps in more than one space, choose the space it goes into. The copy takes the questions in their order, the form's description and its messages, and starts as a draft. Everything else is new: it gets its own link and its own responses, and it holds no answers. This survey keeps its link and its answers. The menu appears only for people who may create apps in at least one space, because a duplicate is a new app. ## Sharing [#sharing] Every survey has **one** link. Anyone holding it can answer without an account, and every answer counts. To walk your own form before you send it, use **Preview** in the builder: it is the respondent's page over your draft, unsaved edits included, and it checks your answers against the same rules the live form applies - but nothing it produces is stored, so a rehearsal never becomes a response. If a link ever needs to stop working, regenerate it: the old address stops resolving immediately and cannot be brought back. ## What happens to the answers [#what-happens-to-the-answers] Each response becomes one row in the survey's **responses** table, with one column per question plus a few facts about the submission itself - when it arrived and which published version of the form it answered. That table is an ordinary Ovidens dataset. It is what the Responses page reads, what a CSV export writes, and what an analytics app imports through an **analytic-app-data** node. You can look at the table itself on the editor's **Pipeline** page: the responses table drawn as the one node this app holds, its columns and their types on the node's Configuration tab, and the answers on its Data tab. It is read-only - a respondent is the only writer - and it shows exactly what every other reader sees, so deleted responses are not in it. **When** a response arrived is recorded in UTC, in the `submitted_at` column. It is the moment the form was submitted, to the millisecond, and it reads back as that same moment wherever you look at it - the grid, the CSV export, and a query in an analytics app. Deleting a response keeps the row and marks it deleted, so the delete can be undone and the audit trail stays honest. A deleted response is excluded from every read. ## The limits a survey works inside [#the-limits-a-survey-works-inside] Five numbers bound one survey, and all five are your workspace's rather than the platform's - a workspace administrator can see them under **System -> Usage and limits**, and an operator can raise them. | Limit | Default | What it bounds | | ---------------------- | ------- | ----------------------------------------------------------- | | Responses per survey | 100,000 | how many responses one survey may collect | | Questions per survey | 100 | rows on one form - questions and page breaks together | | Pages per survey | 20 | page breaks on one form | | Upload size per file | 10 MB | the largest single file a respondent may attach | | Upload size per survey | 500 MB | everything one survey has ever been sent, across every file | A respondent whose file is over the per-file limit is told so while they still have the right file to hand, and one who arrives after the survey's total is reached is told the form cannot take more files - never what the limit is. The response limit works alongside the optional **response limit** you can set on the survey itself in Settings, and the LOWER of the two is what closes the form. A respondent who arrives after either is reached is told the survey is closed; they are never shown which limit it was. Setting your own limit above the workspace's is refused rather than quietly reduced, and the field says what the workspace allows. The editor's **Preview** is unaffected by either, because it stores nothing - so you can always rehearse a form that has filled up. Two more bounds are fixed and not per workspace: 50 options on one choice question, and 4,000 characters in one free-text answer. # Reading responses (/docs/survey-apps/responses) Opening a survey app lands on two pages, switched in the rail on the left: **Information** - the share link, how many responses have arrived, how many questions are asked, when it closes and whether it is still accepting - and **Results**, what people answered. Everyone who can open the survey reads both: the form as it was last published and every response. **Open editor**, the way into the form editor, is offered to whoever may read the survey's configuration - someone who may not change it opens the editor read-only - and building the form needs permission to configure the survey. Someone who may only view the survey never sees the questions being drafted. Information is the page you open to send the link to someone. The link there is shown only to people who may configure the survey, and it is read-only - copy it - while regenerating it lives in the editor's Settings, behind the same permission, because it invalidates every link already sent. The collection rules are read out the same way for everyone; changing them is done in Settings, and the note saying so is shown only to people who can. Results is the page you open to read what people said, and it has two readings of the same survey: the **Summary**, a set of charts built for the form you published, and **Individual**, one row per response. ## Summary [#summary] The Summary is a dashboard, built for your survey and kept up to date with it. You do not lay it out: it follows the form. Each of your form's pages becomes a page of charts, with an **Overview** page in front of them. Questions you ask before the first page break get a page of their own, called **Questions**. Pick the page in the bar above the charts. Every question takes one row, in the order the form asks them, and every tile is the same height, so the page reads at one steady rhythm however many options a question offers. The width control in the page header narrows the dashboard to a centred reading column; it is your own preference, the same one the analytics viewer offers, and it follows you from app to app. The Overview carries four figures - how many responses, how many answers, when the first one arrived and when the last one did - a chart of responses per day, and a bar of how many answers each question drew. Above them sits a **filter strip**: the time the response arrived, plus a filter for each of the first few single-choice questions your form asks. Narrowing there narrows the four figures and the two charts **on the Overview page** - so you can read the whole picture for one team, one site, or one month. It does not reach the per-question charts on the pages after it: those are drawn over the complete option list of each question, including the options nobody picked, which is a different question from "what did this team say". To read one group's answers question by question, take the answers into an analytics app - see the end of this page. Each question then gets the chart its answer type deserves: * A **choice** question gets a ring when it offers two or three options and a horizontal bar chart otherwise, over its complete option list - an option nobody picked is still drawn, at zero, because that is worth seeing. A multi-select answer counts once for each option it names, so the bars can add up to more than the number of respondents. If the question allows another answer, the words people typed are listed under the chart. * A **number** or **scale** question gets a distribution, one bar per value given. * A **date** question gets a chart over time, because that is what a date answer is. * A **text** or **email** question gets a searchable, sortable table of what people wrote, with its own **Export** as CSV or Excel. The file holds the answers the table shows - that question's and no other's - under the table's own filters and search. * A **file** question lists the file each respondent attached, by name; open the response in Individual to download it. * A **grid** question gets a pivot of its rows by its columns. If you change what a question MEASURES - a scale's end points, whether it is required, who it is shown to - the answers given before and after are not the same measurement. The charts count only the current one; the earlier answers are still in Individual, in the record panel and in the export. Correcting the WORDING of a question changes nothing: all of those answers are still comparable. Questions you have **archived** get no chart, because they are no longer part of the form; their answers are still in Individual and in the export. Questions you have added to the draft but not yet published get no chart either - the Summary follows the form your respondents are actually answering. ## Individual [#individual] One row per response, over the same columns an analytics app would see. Click a row to open the **record panel**, which shows that one person's answers end to end - including the questions they were never shown, marked as not answered so a blank never has to be guessed at. The grid carries the usual table controls: search, per-column filters, hiding columns, and **Export CSV**. The **time window** above it - all time, 7, 30, or 90 days - narrows what the grid and the export read; on the Summary the same question is the filter strip's own control. Downloading the responses is a permission of its own, which comes with the **Contributor**, **Analyst**, **App builder** and **Admin** roles; someone who may only view the survey reads the answers but is not offered **Export CSV**. ## Deleting a response [#deleting-a-response] From the record panel. Anyone whose role lets them enter data in the survey - **Contributor**, **App builder** or **Admin** - can delete a response and restore it. The delete is reversible: the row is kept and marked deleted, a toast offers **Undo**, and the audit trail records both halves. A deleted response is excluded from the grid, from the summary, from an export, and from anything an analytics app imports. ## Using the answers in an analytics app [#using-the-answers-in-an-analytics-app] A survey app collects answers; it does not transform them. Its pipeline holds only what the platform generates - the responses and the views over them, which the editor's **Pipeline** page shows read-only - with no SQL to write and nothing to run. That is deliberate, because the job of a form is to be answerable, and every question you could ask of the answers is a question about **data**. So when you want to join the answers to another dataset, chart them, or feed them into an indicator, you move them into an analytics app, which is where transforms and runs live. An **analytic-app-data** node does that. In the analytics app's editor, add one to the pipeline and pick the survey as its source: the source list names every published survey beside your analytic apps, marked *(survey)*, and a survey nobody has published yet is greyed out because there is nothing to import. From there the answers are ordinary pipeline input: a SQL node can filter or reshape them, a Visualisation can chart them, and another app can import the result. It is a **two-step** action, and the second step is yours: 1. The node lands on the analytics app's **draft**, exactly like any node you add by hand. Adding it publishes nothing and runs nothing. 2. Run the pipeline to see the answers flow through it, and publish when the result is what you want. Because a survey has no runs of its own, the import always reads the responses **as they are now**, and each run of the importing app captures them into its own immutable history. Deleted responses never travel. # Interact with Vera (/docs/tips/interact-with-vera) **Vera** is the AI assistant built into Ovidens. It is the no-code way to get things done: you describe what you want in plain language, and Vera does the work, whether that is building a whole app or making one small change. You will find Vera in the **Vera (AI)** panel in analytic and indicator app editors. ## What to ask Vera [#what-to-ask-vera] Vera can handle most of the things you would otherwise build by hand. For example, you can ask it to: * **build an app** from a description of your data and your goal, * **add a widget** to the Visualisation, such as a chart of revenue by month, * **write a transformation**, so you do not need to know SQL, * **build a calculation** that works a new metric out from the ones you collect, * **explain the data**, to help you understand what an app is showing, * **write or update documentation** in the Knowledge Base, * **explain how Ovidens works**, with links to the user documentation. Vera uses the tools enabled for you and allowed on their targets. It chooses the steps your request needs. If a tool or permission is missing, it says so briefly. ## How to phrase requests well [#how-to-phrase-requests-well] Vera does its best work when your request is clear and well supported. A few habits help: * **Be specific.** "Total revenue by region for this year, as a bar chart" gives Vera far more to work with than "make me a report". * **Give context directly in chat.** Explain what your columns mean and the rules you want to use. The [Knowledge Base](../analytical-apps/knowledge-base.mdx) can help when relevant, but preparing it is optional. Ask Vera to document a rule if you want it saved there. * **Attach a file** when it helps, for example a sample spreadsheet so Vera learns your column names. * **Answer Vera's questions.** When it pauses to ask for a detail, your answer guides the rest of its work. ## Keep the conversation going [#keep-the-conversation-going] A request is one turn in your conversation. When Vera finishes, send a follow-up in the same message box, for example "Make that chart blue" or "Explain the last calculation". Your recent requests, clarification answers and Vera's replies help it understand what you mean. For a much older topic, restate the important details. Your conversations are personal to you within the workspace. **Conversation history** opens a centered table with titles, the initial app context, creation time, last activity and last opening time. Search by title and use the page controls to browse the archive; only one page is loaded at a time. Choose **Select** beside a conversation to reopen it. **New conversation** creates another named thread, and **Rename conversation** changes its title. Full history is saved; **Load earlier messages** lets you read older turns. Vera automatically reopens your last conversation only in the same app and within 6 hours of your last interaction. Opening the panel or background polling does not extend that window. Otherwise you start with a blank chat; its conversation is saved when you send the first message. You can manually open any older conversation from history, including one started in another app. The current page provides context for your next request. Vera can use tools on other apps when your permissions allow it; if the target is ambiguous, it asks which app you mean. An unsent message is kept in this browser tab for the selected conversation, or for the current app before the first message, separately for each user and workspace. Changing conversations does not cancel a running task. Choose **Stop this request** if you want to stop it. Your full conversation history remains available for browsing. When you continue, Vera uses the latest saved rolling memory and recent messages, including after a few days. Earlier messages are for browsing; continuing uses the app's current state and current workspace model. Memory compression can lose detail, so restate an important fact if Vera no longer has it in context. Vera condenses older context as it approaches the selected model's available context capacity, rather than after a fixed number of chat messages. Tool results also occupy context, so the visible message count alone does not describe its size. Condensing changes the working memory sent to the model; it does not delete the saved conversation. The current instructions and authorized tool definitions remain available on every request. If an older tool-discovery result leaves working memory, Vera can discover the current tools again. ## How Vera works [#how-vera-works] Vera shows progress while working. It reads relevant context and uses available tools according to your request, without a fixed sequence of setup steps. It asks a focused question if an important detail is ambiguous, then waits for your answer. Clear requests can proceed without a confirmation round. Vera finishes with a short answer about what happened or what it could not do. The **Details** button opens a dialog with a separate AI usage section, timestamps and one steps timeline. Progress and actual tool results appear together; a failed attempt shows its operation, target and reason, and a later success stays a separate attempt. Ordinary progress carries no success claim. Older tool records without an event time say that the time was not recorded. The dialog does not repeat your message or Vera's answer. Requested edits create or reuse the app's draft and follow the same authorization, audit and versioning rules as manual edits. Asking for help or an explanation does not create a draft. Review changes before publishing; Vera publishes only when you ask and the publication tool is enabled for you. Visualisation saves cannot overwrite a newer shared app state. The editor pauses while an AI write is active or shared changes are being refreshed. If a refresh fails, editing stays paused until the retry succeeds. This app-wide coordination does not expose another person's private conversation. Vera searches a bundled copy of the public user guides and reads relevant passages through documentation tools. It has no general internet-browsing tool. The bundled guides are updated with the application release; Vera does not fetch the live site when answering. Source links are underlined and open in a new tab, including plain URLs, so you can read them without leaving your conversation. ## Uploaded files [#uploaded-files] Files attached to sent requests remain private and currently have no automatic age-based expiry while their history records exist. Sending or importing a file does not delete its original bytes. Abandoned uploads that were never sent become eligible for cleanup after 24 hours. Deleting an app preserves your private conversation and its source files. Chat attachment chips currently show the file name and size, without a download action. A dataset export downloads the imported data, rather than the original workbook or document. The six-hour conversation-restoration window does not delete history or uploaded files. ## Task details and version history [#task-details-and-version-history] Open **Details** beside the response timestamp to inspect tokens, step results and times. Messages show when requests were sent and responses finished; clarification questions and answers use their recorded times. Conversation history shows when the conversation started, when its saved metadata was updated and when you last opened it. On compact screens, Vera fills the screen; use **Close** to return to the editor. Resizing back to desktop keeps your saved pane width and draft. Open the compact **Autonomy** menu beside the attachment button inside the composer to choose how independently Vera acts with its slider: **0 · Ask first**, **4 · Balanced** (the default), or **9 · Independent**, with intermediate levels. You can choose the level before sending the first message. This private conversation setting applies to new chat requests. Important missing business details still prompt a question at every level. The version history shows **Version publisher**, **Version contributors** and **AI models and clients** directly in both the Versions and Runs tables. The contributor list records people and AI involved in changes made for that version. Each model appears with the person who instructed it; an external AI connection shows its client name because its exact model is not reported. In the Runs table, these columns describe the linked version, whose number and name appear in the row. Contributors are not copied from an inherited or restored base. Version history has no coarse AI icon or link to a private Vera task. The app's change audit remains available where your permissions allow it. If you publish a draft that Vera helped edit, the publication is yours. Finishing a Vera request does not require or force publication. ## Attach original data files [#attach-original-data-files] You can attach the original CSV (up to 512 MiB) or Excel `.xlsx` file (up to 64 MiB) to a message. Convert older `.xls` files to `.xlsx` first. The upload remains private until you ask Vera to import selected data into the shared app; the browser does not recreate the file's rows as chat text. You can also attach documents, such as a PDF, and ask Vera to read, summarise or answer questions about them. Reading an attachment does not add it to the knowledge base or change the app. Ask explicitly if you want information imported or saved. Pending page and title edits are saved before Vera starts from them. If a save fails, your request stays in the composer. When someone else changes a saved page or layout, your editing copy is kept: choose **Load saved** to use their changes, or explicitly replace the saved copy with yours. Unsaved skill changes stay in your form; Vera uses the saved skill until you save them. ## Ask for help with Ovidens [#ask-for-help-with-ovidens] You can ask "How do I upload data?", "How do I publish this app?" or "What does an indicator dimension do?". Vera can consult the maintained user documentation and link to the relevant guide. You do not need to copy a help page into your app's knowledge base to ask about it. ## Model and data processing [#model-and-data-processing] Vera uses Anthropic's API directly. A workspace administrator chooses one model: Claude Haiku 5.5, Claude Sonnet 5.5 (the default), or Claude Opus 5.5. Every chat, AI widget and internal AI task uses that workspace model. New and resumed requests use the current setting, including in older conversations. **AI processing is not limited to the EU.** Your request, relevant conversation context, attachments and tool results may reach the provider. See [AI transparency](../infrastructure/ai-transparency.mdx) for processing details. ## AI widgets are read-only [#ai-widgets-are-read-only] An [AI widget](../analytical-apps/skills.mdx) runs a reusable task against the app's existing configuration and published data. It returns an answer without changing data or configuration, running the pipeline, publishing, or creating a draft, even if the person using it has permission to edit the app. A preview can read an existing draft skill without changing it. A widget request needing a change receives a short explanation that it cannot make that change. Widget answers are separate from your conversation in the Vera panel. ## When Vera cannot finish [#when-vera-cannot-finish] Vera gives a brief reply explaining what it could do or what stopped it. If a closing reply is unavailable, a short explanation appears instead. Open **Details** for recorded tool attempts, their reasons, token usage and times. Earlier failed attempts remain visible even when a later attempt succeeds and the task completes. An **Internal AI request error** means the provider could not accept the request Vera prepared. Try again; contact Ovidens support if it keeps happening. This is reported separately from unavailable AI connections, missing configuration or a busy provider. **Details** remains available even if the request failed before its first tool call. If token usage was not recorded, the dialog says so; its counters do not establish the provider's full usage. Continue in the normal message box to supply a missing detail, rephrase a request or try again. Requested edits remain in the app's draft until you publish or discard them; review those changes through the app's own controls. ### If your connection drops [#if-your-connection-drops] A lost connection does not stop Vera: the request keeps running on the Ovidens side. If the panel cannot reach Vera for a moment, it says **Lost connection to Vera - retrying** and keeps trying. If it still cannot reach Vera after a while, it says **Couldn't reach Vera. The run may still be going.** and offers a **Check again** button, which picks the request up wherever it has got to. ### When the Vera panel is closed [#when-the-vera-panel-is-closed] In an analytic app's editor, the **Vera** button shows a small dot while the panel is closed: blue while Vera is working, amber when Vera is waiting for your answer, and red when a request ended without doing everything you asked. The red dot stays until you open the panel. ## Vera knows the system, not your use case [#vera-knows-the-system-not-your-use-case] Vera understands Ovidens inside out: how pipelines, indicators, and the Visualisation work, and how to build them. What it does **not** automatically know is your business, your data, and what you are actually trying to achieve. Give it that context and it can build very quickly and very well; leave it out and it has to ask you for missing details. So before you send a request, ask yourself whether you have told Vera everything it needs from a business point of view: what the numbers mean, which rules apply, and what a good result looks like. Putting that context in your request, or in the [Knowledge Base](../analytical-apps/knowledge-base.mdx) when useful, is the single biggest thing you can do to get a better result. And like any AI, **Vera can make mistakes**. Everything it builds lands in your app's draft, which viewers do not see until it is published, so always check its work before you rely on it, especially the logic behind an important number. ## A note on AI usage [#a-note-on-ai-usage] Working with Vera uses **AI credits**, which your workspace has a monthly allowance of. You can see how much you have used on the [Administration](../administration.mdx#usage) page. If the monthly limit is reached, AI features pause until the allowance resets at the start of the next month, while the rest of the app keeps working as normal. Vera requests five-minute prompt caching for reusable input. Cache reuse depends on the provider, an unchanged input prefix and the cache lifetime; saved chat history is separate from that temporary cache. Condensing memory can require a new cache write and another model call, so a shorter context is not always cheaper. # Keyboard shortcuts (/docs/tips/keyboard-shortcuts) Ovidens has keyboard shortcuts in two places: a few that work **anywhere** in the app, and a fuller set for the **editors** when you are building. Every one of them can be changed to whatever suits you. **mod** means the modifier key for your computer: Command on a Mac, and Ctrl on Windows and Linux. ## Anywhere [#anywhere] These work throughout Ovidens, whatever page you are on. | Shortcut | What it does | | ------------------------------- | ----------------------------------------------------------------- | | mod + K | Open search, and switch between apps | | mod + B | Toggle the sidebar between the wide dock and the narrow icon rail | | Shift + / | Show the keyboard-shortcuts help | Search jumps you straight to any app, indicator, or node. See [Search](./search.mdx). The sidebar toggle has nothing to do in an app editor, because the editor replaces the sidebar with its own full-width bar. ## In the app editor [#in-the-app-editor] The rest apply while you are editing an app. ### Panels [#panels] Panel toggles are single keys - they fire only when you are **not** typing in a field, so they never clash with your text. | Shortcut | What it does | | ------------------------------------------------ | ------------------------------------- | | P | Toggle the **Data Pipeline** panel | | V | Toggle the **Visualisation** panel | | K | Toggle the **Knowledge Base** panel | | mod + J | Toggle **Vera (AI)** | | mod + Shift + . | Maximize or restore the current panel | ### Editing and navigation [#editing-and-navigation] | Shortcut | What it does | | ------------ | ----------------------------------------------------------------------- | | E | Open the editor from the app, and go **Back to viewer** from the editor | | / | Search the view you are on - the pipeline canvas, or the list beside it | ## Changing a shortcut [#changing-a-shortcut] Open **Settings** from your account menu and choose **Keyboard shortcuts**. Click the keys on any row and press the combination you want. * Esc cancels and leaves the shortcut as it was. * Backspace removes the shortcut, so that action has no key at all. * The **Reset** button beside a changed row puts it back to its original key, and **Reset all to defaults** puts everything back at once. Your shortcuts are saved to your account, so they follow you to every browser, every computer, and every workspace you belong to. A few combinations cannot be used, because your browser takes them before Ovidens ever sees them - mod + W, T, N, Q, L, R and the number keys among them. Ovidens tells you when you press one rather than letting you set a shortcut that would never work. If the combination you press is already used by something else on the same screen, Ovidens asks whether you want to move it. If you say yes, the other action is left with no shortcut, and it says so. ## Good to know [#good-to-know] * Press Shift + / at any time to open a help window listing the shortcuts that work where you are standing. The full list is in Settings. * Single-key shortcuts are ignored while you are typing in a text box, an editor, or a query, so they never get in the way. That holds even for the few shortcuts that otherwise work while you type: if you give one a single key, it stops working inside text fields, and Ovidens says so when you set it. * There is no shortcut for running or for publishing, on purpose: both are deliberate steps you take from the **Run** and **Publish** buttons in the editor bar. Your edits are saved to your private draft as you go, so leaving the editor never loses work. * mod + S does nothing in the editor. Ovidens catches the keystroke only so your browser does not open its own "save this page" window out of habit. In a data grid it saves your edited rows. # Search (/docs/tips/search) Search is the fastest way to get where you want to go. Rather than clicking through menus, you type a few letters and jump straight to what you need. There are a couple of search boxes, each for a different job. ## Search across your workspace [#search-across-your-workspace] The main search box jumps you to anything in your workspace: an **app**, an **indicator**, or a **node** in a pipeline. Open it from the left navigation (or with its keyboard shortcut, see [Keyboard shortcuts](./keyboard-shortcuts.mdx)), start typing, and the results update as you go, grouped by what they are. Pick a result to go straight there. ## Search the documentation [#search-the-documentation] These docs have their own search box at the top of the page. Type a word, for example "pivot" or "formula", and it finds every page that mentions it, so you can land on the right guide without browsing the menu. ## Find a node on the canvas [#find-a-node-on-the-canvas] A large pipeline can hold many nodes, which can make a particular one hard to spot. The canvas has its own node search for exactly this. Open it, type part of a node's name, type, or id, and the matching nodes are highlighted. You can step through the matches and the canvas brings each one into view, so even a big pipeline stays easy to navigate. # Use the docs with an AI assistant (/docs/tips/use-the-docs-with-ai) These docs are written for people, but they are just as useful to an AI assistant. Every page exists in a plain-text form an assistant can read directly, and the whole site can be pasted into one conversation. Whether you use ChatGPT, Claude or another assistant, the steps are the same. ## Copy a page [#copy-a-page] Every guide has a **Copy page** menu at the top, above the text. It offers four actions: * **Copy as Markdown** copies the page to your clipboard as plain Markdown, ready to paste into a chat. * **View as Markdown** opens that plain-text version in a new tab, so you can read or save it. * **Open in ChatGPT** and **Open in Claude** start a new conversation in that assistant with the page's address already in the prompt, so it reads the guide before you ask anything. ## Every page has a Markdown address [#every-page-has-a-markdown-address] The plain-text version of a page lives next to the page itself: take the page's address and add `.md` at the end. For example, the Datasets guide is at `/docs/analytical-apps/pipeline/datasets`, and its Markdown version is at [`/docs/analytical-apps/pipeline/datasets.md`](/docs/analytical-apps/pipeline/datasets.md). The home page is the one exception: it has no page name of its own to add `.md` to, so its Markdown version is at [`/docs/index.md`](/docs/index.md), and in German at [`/docs/de/index.md`](/docs/de/index.md). An assistant that can read web addresses can be pointed at that file directly, and it will see the same text you see, without the menus around it. ## Give an assistant the whole site [#give-an-assistant-the-whole-site] Two addresses cover all of the docs at once: * [`/docs/llms.txt`](/docs/llms.txt) is the index: every page with its title and address, as a short list. Paste it into a conversation so the assistant knows what exists and can ask for the pages it needs. * [`/docs/llms-full.txt`](/docs/llms-full.txt) is every page in one file. Paste it, or point the assistant at it, when you want it to answer from the complete documentation rather than from a single guide. ## What to paste, and what to ask [#what-to-paste-and-what-to-ask] Start with the page that matches your question. If you are setting up an import, copy the Datasets guide; if you are writing a transform, copy the SQL nodes guide. Paste it, then ask in your own words: "Which column types can I use?" or "Why would my import be rejected?". For a question that spans the product, use the index or the full file instead, and name the feature you are working on so the assistant can find the right section. Not sure which guide you need? The docs search finds any page by a word it contains; see [Search](./search.mdx). # Audits (/docs/de/administration/audit-trail) Jeder Workspace führt eine laufende Aufzeichnung darüber, wer was wann geändert hat. Immer wenn jemand eine App erstellt, Daten bearbeitet, eine Pipeline ausführt, eine Version veröffentlicht oder ein Teammitglied einlädt, wird automatisch ein Eintrag hinzugefügt. Sie lesen das Prüfprotokoll über die App **Audit Analytics**: eine fertige Ansicht darüber, mit Seiten voller Tabellen und Diagramme. Die App ist **nur für Admins** zugänglich und **systemverwaltet**: nur Admins sehen sie, ihre Konfiguration ist gesperrt und kann nicht bearbeitet werden. Sie ist außerdem **dauerhaft** - ein Admin kann sie öffnen, aber nicht archivieren, wiederherstellen oder löschen, sodass die Prüfhistorie niemals ausgeblendet oder entfernt werden kann. Sie ist immer live und liest direkt aus dem Protokoll: Es gibt keinen Lauf auszulösen und nichts zu aktualisieren, und die Zahlen sind auf ein bis zwei Minuten aktuell. Sie zeigt die Aktivität der Apps, **die Sie öffnen können**. Der persönliche Bereich eines anderen Mitglieds bleibt privat, auch für Admins: Die Einträge einer App darin - und einer App, die inzwischen endgültig gelöscht wurde - erscheinen in keiner Tabelle, keinem Diagramm und keiner Summe. Einträge, die zu keiner App gehören, etwa die Einladung eines Mitglieds, erscheinen immer. ## Sie öffnen [#sie-öffnen] * **Über die App-Liste.** Admins sehen Audit Analytics neben den anderen Apps. * **Über eine Schaltfläche "Prüfprotokolle anzeigen".** Eine kleine Uhr-Schaltfläche sitzt auf einer Datensatzzeile, einem Pipeline-Knoten, einer Version in der Historie, einem Wissensdatenbank-Element, einem Mitglied, einer Kennzahl, einer Visualisierungs-Konfiguration, einem Berichtszeitraum und einer einzelnen Kennzahlen-Wertzelle. Sie öffnet die App **in einem neuen Browser-Tab**, vorgefiltert auf genau diese Ressource, sodass Ihre Ansicht bestehen bleibt. Zwei dieser Direktlinks sind in einer Indikator-App besonders nützlich. Auf einer **Zelle** im Datengitter öffnet die Schaltfläche die Seite **Indicator Data Changes**, gefiltert auf genau diese Zelle - diese Kennzahl, diese Aufschlüsselung, dieser Zeitraum -, sodass Sie jeden Wert sehen, den sie je hatte, und wer ihn gesetzt hat. Auf einem **Berichtszeitraum** zeigt sie alles, was mit dem Zeitraum selbst geschehen ist: angelegt, umbenannt, zur Prüfung eingereicht, freigegeben, wieder geöffnet. ## Was jede Seite zeigt [#was-jede-seite-zeigt] * **Overview** (Überblick) - die Aktivität im Workspace: wie viele Aktionen im Zeitverlauf, wer sie ausführte, wie viel von KI getrieben wurde. Dort finden Sie außerdem die Tabelle **Deletions and bulk operations** sowie **Retained originals**, die jede aufbewahrte Originaldatei auflistet (einen Datensatz-Upload oder ein Dokument, das Sie Vera gegeben haben) - mit Name, App, Knoten, wer oder was sie hochgeladen hat und wann, samt Download der Datei genau so, wie sie hereinkam. * **All actions** (Alle Aktionen) - alles, was geschehen ist, neueste zuerst, eine Zeile pro Ereignis: wann, die Aktion, die betroffene Ressource und wer oder was gehandelt hat. Jede Spalte lässt sich filtern; hier grenzen Sie also auf einen Zeitraum, eine Akteur-Art, einen Akteur, eine App oder eine Art von Aktion ein. * **Analytics Data Changes** - jede Änderung an den Daten einer analytischen App. Eine direkte Bearbeitung zeigt eine Zeile pro geänderter Zelle (Wert davor und danach); eine gelöschte Zeile zeigt eine einzige Zeile für die gesamte Löschung, ohne Spalte und ohne Werte davor/danach; ein Upload oder ein Ersetzen zeigt eine Zeile für den gesamten Schreibvorgang (siehe [Uploads und Ersetzungen](#uploads-und-ersetzungen)). * **Indicator Data Changes** - jede Änderung an einem Indikatorwert, eine Zeile pro geänderter Zelle: der Indikator, seine Dimension und der Berichtszeitraum, der Wert davor und danach und wer ihn geändert hat. ## Eine Änderung lesen [#eine-änderung-lesen] Jede Zeile beantwortet dieselben Fragen: * **Zeile** - welcher Datensatz sich geändert hat (auf einer Datenänderungsseite die stabile ID der Zeile oder Zelle). Eine Datenänderungsseite führt außerdem **Erstellt**, **Zuletzt aktualisiert** und die **Zeilen-ID**, sodass Sie nach jeder Spalte sortieren oder filtern können, um die Historie eines Datensatzes zu verfolgen. * **Wann** - der Zeitpunkt der Änderung. * **Akteur** - wer oder was sie vorgenommen hat. In der Regel eine Person; bei einer von einer KI vorgenommenen Änderung die Person, die die KI angewiesen hat (siehe [KI-Verantwortlichkeit](#ki-verantwortlichkeit)); bei einer Änderung, die ein verbundenes System geschrieben hat, diese **Integration** selbst. * **Akteur-Art** - **Person**, **Vera**, **External AI** (externe KI), **Integration**, **Support** oder **System**. Filtern Sie danach, um zum Beispiel alles zu sehen, was ein verbundenes System letzte Nacht getan hat. * **KI** - gesetzt, wenn die Änderung von einer KI statt direkt von einer Person vorgenommen wurde. Eine Integration ist keine KI, ihre Schreibvorgänge setzen die Markierung also nicht. Die meisten Zeilen enthalten einen **Ressourcen-Link**, der die geänderte Ressource in ihrem Live-Editor öffnet, ausgewählt und bereit zur Prüfung. Ein Link ist **inaktiv**, wenn die Ressource nach der Aufzeichnung gelöscht wurde: die Historie bleibt, aber es gibt nichts Lebendiges zu öffnen. ## Uploads und Ersetzungen [#uploads-und-ersetzungen] Die meisten Daten kommen als Datei-Upload oder als Ersetzen an, das Tausende oder Millionen Zeilen auf einmal schreibt. Ein Eintrag pro Zeile würde das Protokoll überfluten, daher wird ein Massenschreibvorgang als **ein Eintrag für den gesamten Vorgang** aufgezeichnet, auf der Seite **Analytics Data Changes** neben den direkten Bearbeitungen: * **Zeile** zeigt den Block der erzeugten Zeilen, zum Beispiel `1-51200`. * **Operation** lautet `replace_rows` oder `append_rows`. Das ist der wichtigste Wert, wenn Sie eine einzelne Zeile verfolgen: ein **Ersetzen** hat alles verworfen, was der Knoten enthielt, und die neuen Zeilen wieder ab 1 nummeriert - Zeile 4 nach einem Ersetzen ist also eine andere Zeile als Zeile 4 davor. Ein **Anhängen** hat die früheren Zeilen unverändert gelassen. * **Geschriebene Zeilen** ist die Anzahl der tatsächlich hinzugefügten Zeilen. * **Geändert von** und **Wann** sind Person und Zeitpunkt, wie bei einer Bearbeitung. Der Zeilenblock ist ein **Bereich, keine Liste**: Wenn ein Upload doppelte Zeilen überspringt, werden manche IDs innerhalb des Blocks keiner Zeile zugewiesen, sodass **Geschriebene Zeilen** kleiner sein kann, als der Block aussieht. Aus demselben Grund öffnet der **Ressourcen-Link** eines Masseneintrags die Daten des Knotens ungefiltert. Genau das macht die Schaltfläche **Prüfprotokolle anzeigen** pro Zeile für Daten nützlich, die nie von Hand bearbeitet wurden: Auf einer per Upload entstandenen Zeile findet sie den Upload, der sie erzeugt hat, zusammen mit allen späteren Bearbeitungen. ## Eine Tabelle herunterladen [#eine-tabelle-herunterladen] Jede Tabelle der App hat eine Schaltfläche **Exportieren**, damit Sie den Nachweis als Datei an Prüfende oder Kolleginnen und Kollegen weitergeben können. Wählen Sie **CSV** oder **Excel**. Die Datei enthält die Zeilen, die die Tabelle zeigt. Alle Filter der Tabelle wirken sich aus, auch die, die ein Link **Prüfprotokolle anzeigen** beim Öffnen der Seite bereits gesetzt hat, etwa auf eine bestimmte Ressource - öffnen Sie **Analytics Data Changes** über den Link eines Knotens und exportieren Sie, enthält die Datei die Änderungen dieses Knotens und nicht die des ganzen Workspace. Auch die Parameter der Seite und die Suche wirken sich aus. Die Datei enthält jede Spalte der Tabelle, nicht nur die am Bildschirm sichtbaren, und ihre Zeilen stehen in der Standardreihenfolge der Tabelle (auf den Seiten mit Änderungen die neuesten zuerst), egal wie Sie die Tabelle am Bildschirm sortiert haben. Die gesamte Historie eines Workspace kann mehr Zeilen umfassen, als eine Tabellenkalkulation aufnimmt (rund eine Million). Excel wird dafür mit einer entsprechenden Meldung abgelehnt; laden Sie sie stattdessen als **CSV** herunter oder filtern Sie die Tabelle zuerst. Jeder Download wird aufgezeichnet, auch der aus dem Prüfprotokoll selbst: Ein Admin sieht unter **Daten-Downloads** auf der [Seite System](./index.mdx#usage), wer was wann und mit wie vielen Zeilen heruntergeladen hat. ## KI-Verantwortlichkeit [#ki-verantwortlichkeit] Wenn **Vera** oder eine verbundene KI eine Änderung im Auftrag von jemandem vornimmt, zeigt das Protokoll **sowohl die KI als auch die Person, die sie angewiesen hat**: die Spalte **KI** markiert die Änderung als von KI vorgenommen, der **Akteur** ist die Person, die darum gebeten hat, und auch das KI-Modell wird festgehalten. Auch wenn also die KI die Arbeit erledigt hat, ist immer klar, wer verantwortlich ist. Siehe [KI-Transparenz](../infrastructure/ai-transparency.mdx). ## Änderungen durch ein verbundenes System [#änderungen-durch-ein-verbundenes-system] Ein verbundenes System - ein ERP, ein Data Warehouse, ein nächtlicher Job - schreibt über die Automation API, mit Zugangsdaten, die ein Admin dafür ausgestellt hat. Solche Änderungen werden wie alle anderen aufgezeichnet, mit zwei Unterschieden: * Der **Akteur** ist die **Integration selbst**, unter dem Namen, den sie bei der Erstellung der Zugangsdaten bekommen hat, und die **Akteur-Art** lautet **Integration**. Es saß niemand an der Tastatur, also wird keine Person als Urheberin angezeigt. Der Name bleibt auch dann erhalten, wenn die Zugangsdaten widerrufen oder gelöscht werden. * Wer die Zugangsdaten **ausgestellt** hat, ist eine Frage zu den Zugangsdaten und nicht zur Änderung; beantwortet wird sie auf dem Tab **API-Zugangsdaten** der Seite Zugriff. Jedes Ereignis zu Zugangsdaten - erstellt, Secret erneuert, Zugriff geändert, widerrufen - verlinkt direkt dorthin und ist dem Admin zugeschrieben, der es ausgeführt hat. Was ein verbundenes System ist und wie ein Admin seine Zugangsdaten ausstellt, steht auf der Seite [Zugangsdaten](../integrations/api/credentials.mdx). ## Wie die Historie aufbewahrt wird [#wie-die-historie-aufbewahrt-wird] Die Prüfhistorie wird bei **jeder Änderung** erfasst und in der App Audit Analytics verfügbar gehalten, und zwar **ohne Zeithorizont**: ältere Historie bleibt lesbar, egal wie alt sie wird. # Verbundene Systeme (/docs/de/administration/connected-systems) Ein **verbundenes System** ist eine andere Software, die eigenständig mit Ihrem Workspace arbeitet: ein ERP, ein Data Warehouse, ein nächtlicher Job. Es meldet sich mit eigenen Zugangsdaten an statt mit dem Konto einer Person, schreibt Daten in einen Datensatz, startet einen Lauf und holt sich die Ergebnisse nach seinem eigenen Zeitplan. Es sitzt niemand an der Tastatur, und es ist kein Browser beteiligt - genau darum geht es. Die Arbeit passiert um drei Uhr nachts, ob jemand da ist oder nicht. ## Was ein verbundenes System tun kann [#was-ein-verbundenes-system-tun-kann] Ein verbundenes System arbeitet über die **Automation API**, und was es dort darf, entscheidet allein der Zugriff, den Sie seinen Zugangsdaten geben: * **Den Workspace lesen** - die Apps auflisten, die Datensätze, in die es schreiben darf, und die Ausgaben, die es lesen darf. * **Daten schreiben** - Zeilen in einen Datensatz einer analytischen App importieren, als Datei, die es hochlädt und der Workspace einliest. * **Eine Pipeline ausführen** - einen Lauf starten und ihn bis zum Ende abfragen. * **Ergebnisse lesen** - einen Export einer Ausgabe erzeugen und die Datei herunterladen. Mehr gibt es nicht. Welchen Zugriff Sie auch erteilen: Ein verbundenes System kann keine Apps anlegen oder löschen, keine Pipeline ändern, keine Visualisierung bearbeiten, niemanden einladen und keine Workspace-Einstellungen ändern. Das bleibt bei den Menschen im Workspace. ## Wo die Zugangsdaten liegen [#wo-die-zugangsdaten-liegen] Öffnen Sie die Seite **Zugriff** und dort den Tab **API-Zugangsdaten**. Zugangsdaten sind wie ein Mitglied ein Prinzipal des Workspace und werden deshalb neben den Mitgliedern verwaltet: Wer und was hier arbeiten darf, steht auf einer Seite. Der Tab ist für Personen, die die API-Zugangsdaten des Workspace verwalten dürfen, und das sind heute die Admins: Einem Mitglied wird er gar nicht angeboten, und jede Anfrage dahinter wird ohne diese Berechtigung abgelehnt. Jede Zugangsdaten sind eine Zeile in der Tabelle des Tabs: ihr Name, ihr Schlüssel, ihre Zugriffsoptionen, wann sie zuletzt verwendet wurden und wann sie ablaufen oder als Nächstes bestätigt werden müssen. Das Zahnrad einer Zeile öffnet die Details der Zugangsdaten, und das Menü der Zeile bietet **Bestätigen**, **Zugriff bearbeiten**, **Secret erneuern** und **Widerrufen**. Den Schlüssel kopieren Sie mit der Schaltfläche daneben. Die Details haben drei Tabs: **Details**, **Zugriff** und **Aktivität**. **Zugriff** listet jede Berechtigung, die ihre Zugriffsoptionen geben - App-Konfiguration lesen, Daten lesen, Dateien importieren, Pipelines ausführen, Daten herunterladen, veröffentlichen - und welche Option sie jeweils gibt, genauso wie der Zugriff eines Mitglieds gezeigt wird. Zugangsdaten haben keine Rolle: Sie wirken in jedem gemeinsamen Bereich und jeder App darin, auch in später angelegten Apps, und nie im persönlichen Bereich einer Person. Widerrufene Zugangsdaten können nichts mehr. Zugangsdaten gehören dem **Workspace**, nicht einer Person - sie funktionieren weiter, wenn die erstellende Person den Workspace verlässt. Die Liste nennt weiterhin die Person, die sie ausgestellt hat, und markiert die Zugangsdaten mit **Ehemaliges Mitglied**, sobald diese Person nicht mehr im Workspace ist. So haben Sie einen Anlass zu prüfen, ob die Integration noch gewollt ist. [Tour der API-Zugangsdaten starten](https://app.ovidens.com/access?tab=credentials\&tour=integrations), um sich Zugangsdaten zeigen zu lassen, was das Erneuern und das Widerrufen jeweils kosten, und das Formular, das neue Zugangsdaten ausstellt. Die Tour legt nie Zugangsdaten an - sie hält bei der Schaltfläche an und wartet auf Sie. ## Zugangsdaten ausstellen [#zugangsdaten-ausstellen] **Neue Zugangsdaten** fragt nach einem Namen, einer optionalen Beschreibung und dem Zugriff, den Sie erteilen. Benennen Sie sie nach dem System, für das sie sind ("ERP-Nachtimport" statt "Test"), denn dieser Name erscheint im Prüfprotokoll des Workspace, sobald die Integration etwas schreibt. Beim Erstellen entstehen zwei Zeichenketten: * eine **Client-ID**, die die Zugangsdaten identifiziert und gefahrlos angezeigt werden darf, und * ein **Client-Secret**, mit dem das System nachweist, dass es diese Zugangsdaten besitzt. **Das Secret wird genau einmal angezeigt.** Es steht in dem Dialog, der direkt nach dem Erstellen erscheint, und nichts kann es erneut anzeigen: Es ist nirgends so gespeichert, dass jemand es zurücklesen könnte, auch kein Admin. Geht es verloren, erneuern Sie das Secret - dabei wird ein neues ausgestellt. Der Dialog kopiert beide einzeln oder zusammen als Umgebungsvariablen `OVIDENS_CLIENT_ID` und `OVIDENS_CLIENT_SECRET`, und **Ich habe es gespeichert** schließt ihn und verwirft das Secret von der Seite. Geben Sie beides an die Person weiter, die die Integration baut. Alles Weitere - wie sich das System damit anmeldet, welche Endpunkte es aufruft, wie die Datei aussehen muss - ist die Entwicklerseite der Arbeit und steht im maschinenlesbaren Vertrag der API selbst, nicht in dieser Anleitung. ## Was die Zugriffsoptionen bedeuten [#was-die-zugriffsoptionen-bedeuten] Jede Option erteilt genau eine Art von Arbeit und sonst nichts. Neben der Beschriftung zeigt die Auswahl außerdem den technischen Namen, den die Option in der API trägt - nach diesem Namen fragt die Person, die die Integration baut: | Zugriff | Was das System damit darf | | --------------------------------------- | ------------------------------------------------------------------------ | | **Workspace lesen** | Apps, Knoten und deren Konfiguration auflisten. | | **Daten schreiben** | Zeilen eines Datensatzes importieren und ändern. | | **Indikatorwerte schreiben** | Erhobene Indikatorwerte eintragen und leeren. | | **Veröffentlichte Pipelines ausführen** | Die veröffentlichte Konfiguration einer App ausführen. | | **Entwürfe ausführen** | Unveröffentlichte Konfiguration ausführen, die noch niemand geprüft hat. | | **Ergebnisse lesen** | Ergebnisse eines Durchlaufs abfragen und exportieren. | | **Veröffentlichen** | Ändern, was jede Person in diesem Workspace sieht. | Zwei Kennzeichen in der Auswahl sagen Ihnen, wie diese Liste zu lesen ist: * **Erweitert** markiert eine Option, die über das Lesen und das Ausführen der **veröffentlichten** Konfiguration hinausgeht - etwa einen Entwurf auszuführen, den noch niemand geprüft hat, oder zu veröffentlichen und damit zu ändern, was alle im Workspace sehen. Eine erweiterte Option ist eine bewusste Einzelentscheidung für eine bestimmte Integration und nichts, was neue Zugangsdaten einfach mitbringen. Erteilen Sie sie nur, wenn das System sie wirklich braucht. * **Noch ohne Wirkung** markiert eine Berechtigung, die derzeit nirgends geprüft wird. Sie gehört zum Vokabular, lässt sich also erteilen, speichern und anzeigen, aber sie zu erteilen ändert nichts, was ein Admin beobachten könnte. Das Kennzeichen ist die ehrliche Antwort auf die Frage, was eine Option heute tatsächlich bewirkt, und es verschwindet, sobald die Option zu wirken beginnt. **Zugriff bearbeiten** ändert die Berechtigungen bestehender Zugangsdaten. Es muss nichts neu ausgestellt werden: Die Änderung gilt ab der nächsten Anfrage dieser Zugangsdaten, und ein Entzug wirkt auch auf ein bereits ausgestelltes Token. ## Das Secret erneuern [#das-secret-erneuern] **Secret erneuern** stellt ein zweites Secret aus, ohne dass etwas ausfällt. Das heute verwendete Secret funktioniert neben dem neuen weiter, Sie können das neue also in Ruhe ausrollen statt unter Zeitdruck. Mehr als zwei Secrets gibt es nie, deshalb zieht erst die **nächste** Erneuerung das ältere zurück - zweimal zu erneuern ist also das bewusste "Das alte Secret gilt nicht mehr", und der Dialog sagt das vor dem Bestätigen. Erneuern Sie, wenn ein Secret nach außen gelangt sein könnte, wenn die Person geht, die die Integration eingerichtet hat, oder in dem Rhythmus, den Ihre eigene Richtlinie vorgibt. Die Details zeigen, wann das aktuelle Secret erstellt wurde und wann das vorherige zuletzt verwendet wurde - so sehen Sie das alte Secret verstummen, bevor Sie es zurückziehen. ## Erkennen, ob es funktioniert [#erkennen-ob-es-funktioniert] Zwei Stellen geben Auskunft, jede auf eine andere Frage: * **Die Tabelle der Zugangsdaten** sagt, ob die Zugangsdaten überhaupt verwendet werden. **Zuletzt verwendet** ist der Zeitpunkt der letzten Anmeldung und steht auf "Nie verwendet", bis der erste erfolgreiche Aufruf der Integration eingegangen ist - das ist deshalb das Erste, was man bei einer neuen, stummen Integration prüft. * **Das Prüfprotokoll** sagt, was das System tatsächlich getan hat. Alles, was ein verbundenes System tut, wird wie jede andere Änderung aufgezeichnet: Öffnen Sie die App **Audit Analytics**, gehen Sie auf **All actions** (Alle Aktionen) und filtern Sie die **Akteur-Art** auf **Integration**. Der **Akteur** einer maschinellen Schreiboperation ist die Integration selbst, unter dem Namen ihrer Zugangsdaten - nicht der Admin, der die Zugangsdaten ausgestellt hat, denn er hat die Maschine autorisiert, die Aktion aber nicht ausgeführt. Wie man das Protokoll liest, steht auf der Seite [Prüfprotokoll](./audit-trail.mdx). Die Ereignisse zu den Zugangsdaten selbst - erstellt, Secret erneuert, Zugriff geändert, widerrufen - stehen im selben Protokoll und sind dem Admin zugeschrieben, der sie ausgeführt hat, denn dort hat tatsächlich eine Person gehandelt. ## Widerrufen [#widerrufen] **Widerrufen** setzt Zugangsdaten sofort außer Kraft, auch bereits ausgestellte Token: Die nächste Anfrage wird abgelehnt. Das lässt sich nicht rückgängig machen - den Zugriff wiederherzustellen heißt, neue Zugangsdaten auszustellen und dem System das neue Secret zu geben. Widerrufene Zugangsdaten bleiben in der Liste, markiert mit **Widerrufen** und mit nichts mehr zu ändern, nur ihr Schlüssel lässt sich noch kopieren, und sie behalten ihren Namen im Prüfprotokoll - so bleibt lesbar, was sie getan haben, solange sie gültig waren. # Administration (/docs/de/administration) Die Administration ist der Ort, an dem Sie Ihren **Workspace** verwalten: wer darin ist, eine Aufzeichnung dessen, was passiert ist, und wie viel Ihres Tarifs Sie nutzen. Das meiste davon ist Aufgabe eines **Admins**; jeder im Workspace kann an den Apps arbeiten. ## Ihr Workspace [#ihr-workspace] Ein **Workspace** ist das gemeinsame Zuhause für alles, was Ihr Team in Ovidens tut: seine Apps, Daten und Mitglieder. Jeder, den Sie einladen, kann dieselben Apps sehen und daran arbeiten, sodass ein Workspace in der Regel einem Team oder einem Unternehmen entspricht. Bei der ersten Anmeldung richten Sie Ihren Workspace ein: Ovidens begrüßt Sie mit "Benennen Sie Ihren Workspace, um die Einrichtung abzuschließen und ihn zu öffnen.", fragt nach einem **Organisationsnamen** (dem Namen, den alle in Ihrer Organisation sehen) und lässt Sie optional **Rechnungsdetails hinzufügen** - eine Postanschrift für Ihre Rechnungen, die Sie überspringen und später ergänzen können. **Workspace erstellen** schließt die Einrichtung ab und bringt Sie direkt hinein. Wenn Sie mehreren Workspaces angehören, wechseln Sie zwischen ihnen über den Workspace-Umschalter oben in der linken Navigation; jeder hält seine Apps, Daten und Mitglieder vollständig getrennt. Wenn Sie angemeldet zu [app.ovidens.com](https://app.ovidens.com) zurückkehren, öffnet Ovidens Ihre Apps automatisch. Bei mehreren Workspaces öffnet es den zuletzt ausgewählten Workspace, sofern Sie ihm weiterhin angehören; andernfalls wählen Sie einen aus. Ist Ihre Sitzung beendet, melden Sie sich erneut an, um fortzufahren. Eine noch offene Einrichtung des Workspace schließen Sie zuerst ab. Der Workspace-Name ist nicht endgültig. Ein Admin kann ihn ändern und auch Symbol und Farbe wählen, die in der Seitenleiste daneben erscheinen: auf dem Tab **Allgemein** der Seite **System** unter **Identität**. ## Mitglieder und Rollen [#mitglieder-und-rollen] Die Seite **Zugriff** listet auf ihrem Tab **Mitglieder** alle im Workspace auf. Jedes Mitglied hat eine **Rolle**: * Ein **Mitglied** kann mit den Apps arbeiten, die seine Rollen öffnen (siehe **Zugriff** und **Standardzugriff für neue Mitglieder** unten). * Ein **Admin** kann alles, was ein Mitglied kann, und verwaltet zusätzlich, wer im Workspace ist. Die Mitgliederliste sehen alle. **Benutzer einladen**, die Liste **Ausstehende Einladungen** und **Entfernen** sind für alle, die Mitglieder verwalten dürfen: einen Admin oder ein Mitglied, dem die **Mitgliederverwaltung** übertragen wurde. Zu entscheiden, was jemand tun darf - die Spalte **Workspace-Rolle**, **Zugriff verwalten** und jemanden als Admin einladen -, bleibt Admins vorbehalten. Ein Mitglied ohne beides sieht die Liste und sonst nichts. Die Liste ist eine Tabelle, die Sie nach Name oder E-Mail durchsuchen und nach Workspace-Rolle filtern können. Neben jeder Person stehen ihre **Workspace-Rolle** und, für Admins, die Rollen, die sie **In Bereichen** hat. Für einen Admin öffnen der Name und das Zahnrad in der Zeile (**Zugriff verwalten**) einen Dialog mit dem **Zugriff** der Person und **Was die Person darf**. Das Menü der Zeile bietet **KI-Berechtigungen**, **Aktivitätsprotokoll** und **Aus dem Workspace entfernen**, jeweils nur denen, die es nutzen dürfen; dürfen Sie nur eines davon nutzen, ist es stattdessen eine Schaltfläche in der Zeile. Eine E-Mail kopieren Sie mit der Schaltfläche daneben. Um jemanden einzuladen, öffnen Sie **Zugriff**, wählen **Benutzer einladen**, geben die E-Mail-Adresse ein, wählen die Rolle und senden die Einladung. Wenn Sie Mitglieder verwalten, ohne Admin zu sein, bietet der Dialog keine Rollenauswahl: Alle, die Sie einladen, treten als Mitglied bei. Die Person erscheint unter **Ausstehende Einladungen**, bis sie annimmt, und wandert dann zu **Mitglieder**. Aus dieser Liste können Sie mit **Einladungslink kopieren** die Einladung selbst verschicken oder eine noch nicht angenommene mit **Einladung zurückziehen** zurücknehmen. Ein neues Mitglied beginnt ohne Zugriff: Es hat seinen eigenen persönlichen Bereich und sonst nichts, bis ihm jemand eine Rolle gibt - es sei denn, Ihr Workspace legt einen **Standardzugriff für neue Mitglieder** fest (unten). Das Einladungsformular sagt das ebenfalls. Wer als Admin eingeladen wurde, ist von Anfang an Admin. Ein Admin ändert die **Workspace-Rolle** einer Person in ihrer Zeile: Die Änderung wird sofort gespeichert, mit **Rückgängig** in der Meldung, die sie bestätigt. Jemanden zum Admin zu machen oder einem Admin die Rechte zu entziehen, fragt vorher nach. Wem die Admin-Rechte entzogen werden, bleibt als Mitglied im Workspace und behält seine eigene Arbeit. Noch nicht angenommene Einladungen als Admin, die die Person verschickt hat, werden dabei storniert, und eine Einladung als Admin lässt sich nur annehmen, solange die einladende Person noch Admin ist. Ein Admin kann erst aus dem Workspace entfernt werden, wenn ihm die Admin-Rechte entzogen wurden. **Aus dem Workspace entfernen** fragt immer nach. ### Ein Mitglied mit eigenen Apps entfernen [#ein-mitglied-mit-eigenen-apps-entfernen] Jedes Mitglied hat einen persönlichen Bereich, den nur es selbst sieht. Darin erstellt, veröffentlicht, führt und archiviert es seine eigenen Apps, schreibt eigene SQL-Abfragen darauf und führt sie aus und kann seine Ausführungen schützen und löschen und Seiten der Wissensdatenbank aus dem Papierkorb endgültig löschen. Eine Abfrage dort liest nur die App, in der sie läuft, nie eine andere App. Niemand sonst kann einen persönlichen Bereich öffnen, auch kein Admin. Wenn Sie jemanden entfernen, in dessen persönlichem Bereich Apps liegen, nennt Ihnen die Bestätigung **wie viele** es sind - nie welche, denn außer der Person selbst sieht sie niemand - und fragt, was mit ihnen geschehen soll, bevor **Entfernen** möglich ist: * **In ... verschieben** verschiebt alle Apps der Person in einen Team-Bereich, wo alle mit Zugriff auf diesen Bereich damit arbeiten können. Angeboten werden nur die Team-Bereiche, in denen Sie Apps erstellen dürfen. * **Archivieren** archiviert alle Apps an Ort und Stelle. Niemand sieht sie, sie zählen nicht mehr gegen Ihr App-Limit, und sie werden endgültig gelöscht, sobald die Aufbewahrungsfrist für archivierte Apps abgelaufen ist. Kommt die Person vorher zurück, findet sie die Apps in ihrem eigenen Archiv und kann sie wiederherstellen. Ist der persönliche Bereich leer, fragt die Bestätigung nichts weiter. Beim Entfernen eines Mitglieds werden außerdem alle KI-Tools getrennt, die die Person mit diesem Workspace verbunden hatte; kommt sie zurück, verbindet sie sie neu. ### Zugriff: eine Rolle für den gesamten Workspace, einen Bereich oder eine App [#zugriff-eine-rolle-für-den-gesamten-workspace-einen-bereich-oder-eine-app] **Zugriff verwalten** in der Zeile eines Mitglieds (nur für Admins) öffnet dessen **Zugriff**: jede Rolle, die diese Person hat, und wo sie gilt - im gesamten Workspace, in einem Bereich oder in einer einzelnen App -, je eine Zeile, in der sich die Rolle direkt ändern lässt. Hier geben Sie jemandem eine engere Rolle als Admin oder eine Rolle für weniger als den gesamten Workspace: zum Beispiel **App-Ersteller** für den Bereich eines Teams, damit die Person dessen Apps konfigurieren und veröffentlichen kann und keine anderen, oder **App-Ersteller** für eine einzelne App, damit sie genau diese App ändern kann. Um eine Rolle zu vergeben, wählen Sie unter **Zugriff hinzufügen** den Bereich oder die App (oder den Workspace) und dann die Rolle. Angeboten wird nur, was Sie dort vergeben dürfen: Persönliche Bereiche und die App Audit Analytics werden nie angeboten, archivierte Apps ebenso wenig. Eine Rolle, die nichts hinzufügen würde - weil die Person dort über eine andere Rolle schon so viel hat -, wird nicht angeboten, und eine Zeile, die eine andere Rolle bereits abdeckt, ist abgeblendet und nennt diese Rolle. Die Rollen sind: * **Admin** - alles, auch die Entscheidung, was alle anderen tun dürfen. * **App-Ersteller** - Apps anlegen, konfigurieren, veröffentlichen und archivieren (auch mit Vera) und mit ihren Daten arbeiten. * **Analyst** - die Konfiguration und die Daten einer App lesen, sie ausführen und ihre Daten herunterladen. * **Mitwirkender** - die Konfiguration und die Daten einer App lesen, Daten erfassen (von Hand oder durch den Import einer Datei) und herunterladen. Was ein neues Mitglied beim Beitritt erhält, ist der Standardzugriff des Workspace für neue Mitglieder (unten). * **Betrachter** - das veröffentlichte Dashboard einer Analyse-App, ihre Wissensbasis und ihre Daten lesen, ohne ihre Konfiguration, Entwürfe oder ihren Verlauf, und ohne die Daten herunterzuladen oder KI darauf auszuführen. * **Mitgliederverwaltung** - Mitglieder einladen und entfernen (nur für den gesamten Workspace). * **Berichtsverantwortlicher** - die Erhebung einer Indikator-App durchführen: ihren Berichtszeitplan, Erhebungsrunden und die gefragten Personen, die Beurteilung der Antworten sowie das Einreichen, Freigeben und erneute Öffnen von Berichtszeiträumen (auch mit Vera). Die Rolle liest die Apps, die sie abdeckt, samt Konfiguration und Daten, und ändert beides nicht. Vergeben Sie sie für einen Bereich oder eine App, etwa an eine Nachhaltigkeitsverantwortliche, die dort das Berichtswesen führt und sonst nichts administriert. Zahlen erfasst sie allein nicht: Wer auch Zahlen erfasst, erhält daneben **Mitwirkender**. Ein **App-Ersteller** führt keine Erhebung durch, sodass die Person, die eine App gestaltet, nicht diejenige ist, die das darin Erhobene freigibt - es sei denn, sie erhält auch diese Rolle. Um eine Rolle zu entziehen, wählen Sie **Entfernen** daneben. Jede Änderung wird sofort gespeichert, mit **Rückgängig** in der Meldung, die sie bestätigt. Wird eine Änderung abgelehnt - etwa weil sie den letzten Admin entfernen würde -, nennt die Meldung den Grund, und nichts ändert sich. Ihren eigenen Zugriff sehen Sie hier, können ihn aber nicht ändern. **Was die Person darf**, die zweite Ansicht des Dialogs, zeigt, was die Person tatsächlich tun darf und warum: für den gesamten Workspace, für jeden Bereich und jede App, für die sie eine Rolle hat, und für ihren eigenen persönlichen Bereich jede Berechtigung, die sie dort hat, jeweils mit der Rolle, die sie gibt („App-Ersteller für Finance“). Ein Bereich oder eine App führt auch auf, was ihre Rollen für den gesamten Workspace erlauben, sodass jeder Ort für sich vollständig ist. Ihr persönlicher Bereich zeigt nur, dass sie dort immer arbeiten kann - nie, was darin liegt. ### Mein Zugriff [#my-access] Jedes Mitglied sieht auf dem Tab **Mein Zugriff** der Seite **Zugriff**, was es selbst tun darf. Es ist dieselbe Ansicht wie **Was die Person darf** im Dialog eines Admins, nur über Sie: für den gesamten Workspace, für jeden Bereich und jede App, für die Sie eine Rolle haben, und für Ihren eigenen persönlichen Bereich jede Berechtigung, die Sie dort haben, und die Rolle, die sie gibt. Ihr persönlicher Bereich wird als Ihrer aufgeführt. Brauchen Sie mehr, bitten Sie einen Admin darum: Admins vergeben Rollen über **Zugriff verwalten** in Ihrer Zeile. Eine Rolle für einen Bereich oder eine App entscheidet, was die Person dort TUN darf: Konfigurieren, Veröffentlichen, Daten erfassen, Herunterladen und was Vera und verbundene KI in ihrem Namen tun dürfen, werden App für App entschieden. Sie entscheidet auch, welche Apps die Person überhaupt sieht. In jeder App-Liste und in der Suche findet jede Person nur die Apps, die eine ihrer Rollen abdeckt - eine Rolle für den ganzen Workspace deckt jede App ab, eine Rolle für einen Bereich die Apps darin -, und eine App, die keine ihrer Rollen abdeckt, verhält sich für sie, als gäbe es sie nicht, auch für Vera und verbundene KI, die in ihrem Namen handeln. Die Rolle **Mitgliederverwaltung** allein öffnet keine App. Was eine Person von einer Analyse-App lesen darf, die sie öffnen kann, hängt von ihrer Rolle ab. Eine Rolle, die die Konfiguration der App liest - **Mitwirkender**, **Analyst**, **App-Ersteller** und **Admin** -, öffnet den Editor (schreibgeschützt, wo sie nicht konfigurieren darf) und liest den Entwurf, die Konfiguration und das SQL jedes Knotens, den Versionsverlauf und die Skills. Ein **Betrachter** liest die App so, wie sie zuletzt **veröffentlicht** wurde: ihre Dashboard-Seiten, wobei jedes SQL-Widget seine Zeilen zeigt, aber nie die Abfrage dahinter, ihre Wissensbasis und die Daten dahinter. Ein Betrachter sieht nicht, dass an einem Entwurf gearbeitet wird, bekommt kein **Editor öffnen** angeboten, und der Link in den Editor führt ihn stattdessen zum Dashboard. Eine App, die nie veröffentlicht wurde, zeigt einem Betrachter ihren leeren Zustand. Eine **Indikator-App** wird genauso gelesen. Ein Betrachter liest ihre Indikatoren, Gruppen, Dimensionen und Prüfungen so, wie sie zuletzt **veröffentlicht** wurden - jeden Indikator vollständig, mit seiner Anleitung, seiner Einheit, seiner Rahmenwerk-Zuordnung und seinen Regeln, und einen berechneten Indikator mit seiner Berechnung - und ihre erfassten Werte genau so wie alle anderen, zusammen mit ihren Berichtszeiträumen und den Erhebungsrunden, in denen er selbst angefragt ist. Die Berichtseinheiten in diesen Runden, in seinen eigenen Aufgaben und in der Tabelle **Erhebungsfortschritt** der App samt ihren Begleittabellen tragen die zuletzt veröffentlichten Namen, und eine Einheit, die nur im Entwurf hinzugefügt wurde, erscheint für ihn gar nicht. Ein Betrachter sieht nicht den Entwurf, an dem gearbeitet wird, den Versionsverlauf, Veras Arbeit oder die Prüfwerkzeuge des Editors, bekommt kein **Editor öffnen** angeboten, und der Link in den Editor führt ihn stattdessen zur App. Eine Indikator-App, die nie veröffentlicht wurde, zeigt einem Betrachter, dass sie noch nicht veröffentlicht wurde. Eine **Erhebung-App** wird genauso gelesen. Ein Betrachter liest das Formular so, wie es zuletzt **veröffentlicht** wurde - jede Frage genau so, wie sie den Befragten gestellt wurde - und jede eingegangene Antwort, zusammen mit den Dateien, die angehängt wurden. Die Seite **Informationen** der Erhebung, ihre Zahlen und ihre Erfassungsregeln liest jeder gleich. Ein Betrachter sieht keine Fragen, die für die nächste Runde entworfen werden, keinen Versionsverlauf, keinen Freigabelink und keine Prüfwerkzeuge des Formular-Editors, bekommt kein **Editor öffnen** angeboten, und der Link in den Editor führt ihn stattdessen zur Erhebung. Die Antworten als CSV herunterzuladen gehört nicht zur Rolle Betrachter. Eine Erhebung, die nie veröffentlicht wurde, zeigt einem Betrachter, dass ihre Antworten erscheinen, sobald jemand, der die Erhebung verwaltet, sie veröffentlicht. Folgt jemand einem Link zu einer App, die er nicht öffnen kann, sieht er **App nicht gefunden** mit der Zeile „Diese App existiert nicht, oder Sie haben keinen Zugriff darauf.“ und den Namen der Admins, die ihm Zugriff geben können - bis zu drei, dann „und N weitere“, oder die Person, die den Link geteilt hat, wenn es keinen anderen Admin gibt. Die Seite sagt nie, welcher der beiden Fälle vorliegt, und verrät so nicht, ob die App existiert. Um die Person hineinzulassen, geben Sie ihr eine Rolle für die App, ihren Bereich oder den ganzen Workspace. Der Workspace behält immer mindestens einen Admin. Dem letzten Admin die Rechte zu entziehen, wird deshalb abgelehnt, bevor sich etwas ändert: Machen Sie zuerst jemand anderen zum Admin. Ihre eigene Workspace-Rolle und **Aus dem Workspace entfernen** sind in Ihrer eigenen Zeile nicht verfügbar - Ihre Rolle ändert ein anderer Admin. Im Menü eines Mitglieds legt **KI-Berechtigungen** fest, was die KI dieser Person in diesem Workspace höchstens tun darf (siehe [MCP](../integrations/mcp.mdx)), und **Aktivitätsprotokoll** öffnet die Historie dieses Mitglieds in der App Audit Analytics. ### Rollen und Bereiche [#roles-and-spaces] **Rollen** auf der Seite **Zugriff** ist für alle: Der Tab listet jede Berechtigung, nach Gebiet gruppiert, mit einer Spalte je Rolle, die zeigt, ob sie sie erlaubt, und nennt unter einer Berechtigung, wenn sie nur für den gesamten Workspace oder einen Bereich vergeben werden kann. Klicken Sie auf Rollennamen in der Kopfzeile, um diese Rollen zu vergleichen (**Rolle hinzufügen** holt eine zurück, die Sie nicht mehr sehen), und **Nur Unterschiede**, um zu sehen, worin sie sich unterscheiden. Rollen lassen sich nicht ändern. Eine Rolle für einen Bereich oder eine App erlaubt nur, was dort gelten kann - **Admin** für einen Bereich lässt zum Beispiel niemanden Mitglieder verwalten. **Bereiche** ist eine Tabelle mit dem **Workspace** und jedem Teambereich, der Standardbereich zuerst: wer dort eine Rolle hat, wie viele Apps darin liegen und was sie belegen. Eine Rolle für den gesamten Workspace gilt auch in jedem Teambereich und steht deshalb einmal oben statt unter jedem Bereich. Persönliche Bereiche werden nie aufgelistet, weil nur ihre Eigentümer sie öffnen können: Der Tab nennt nur, wie viele Mitglieder einen haben und wie viele Apps darin liegen. Der Tab zeigt außerdem, wie viel Speicher der Workspace im Verhältnis zu seiner Grenze belegt und was die Apps jedes gemeinsamen Bereichs belegen. Persönliche Bereiche zählen gegen dieselbe Grenze, deshalb nennt der Tab, was die persönlichen Bereiche der Mitglieder zusammen belegen - nie, was der Bereich einer einzelnen Person belegt, und erst, wenn mindestens drei persönliche Bereiche von Kolleginnen und Kollegen Daten enthalten; bis dahin zählt diese Zahl zu den Dateien, die zu keiner App gehören. Was zu keiner App gehört (Originaldateien des Prüfprotokolls, Anhänge und andere Dateien des Workspace), steht als eigene Zahl da. Die Zahlen je Bereich werden einmal täglich gemessen, und der Tab nennt den Tag. Er zeigt außerdem, wie viele gemeinsame Bereiche der Workspace im Verhältnis zu seiner Höchstzahl hat, zum Beispiel "3 von 10 Teambereichen belegt". Admins verwalten die gemeinsamen Bereiche hier. Ein Klick auf einen Teambereich öffnet **Bereichseinstellungen** - dieselben Bereichseinstellungen wie auf der Seite **Apps** - mit seinem **Zugriff** und unter **Details** seinem Namen, seinem Symbol und **Löschen**: * **Neuer Bereich** legt einen an. Geben Sie ihm einen Namen mit höchstens 80 Zeichen, den kein anderer gemeinsamer Bereich schon trägt (Groß- und Kleinschreibung zählt nicht als Unterschied). Hat der Workspace seine Höchstzahl erreicht, ist die Schaltfläche nicht verfügbar und sagt warum; das Ovidens-Team kann die Grenze anheben. Der Standardbereich zählt mit; persönliche Bereiche zählen nie. * Den **Namen** unter **Details** zu ändern, benennt einen Bereich um, auch den des Standardbereichs. Die für den Bereich vergebenen Rollen und die Apps darin bleiben unberührt. * **Löschen** unter **Details** entfernt nach Ihrer Bestätigung einen gemeinsamen Bereich, aber nur einen leeren: Liegen darin noch Apps (auch archivierte), für ihn vergebene Rollen oder Vorgaben für neue Mitglieder, wird das Löschen abgelehnt und nennt, wie viele davon jeweils übrig sind. Verschieben Sie zuerst seine Apps mit **In einen anderen Bereich verschieben** auf der Seite **Apps** in einen anderen Bereich - dort sehen Sie, wer Zugriff gewinnt und verliert, bevor etwas verschoben wird - und entfernen Sie seine Rollen und Vorgaben hier auf der Seite **Zugriff**. Der Standardbereich kann nicht gelöscht werden. Jede dieser Änderungen wird im Aktivitätsprotokoll festgehalten. Ein Mitglied, das Mitglieder verwaltet, ohne Admin zu sein, sieht auch den Tab **Bereiche**: Er listet die gemeinsamen Bereiche, die es öffnen kann - aber nicht, wer darin welche Rolle hat; das bleibt bei den Admins. Alle anderen sehen **Mitglieder**, **Mein Zugriff** und **Rollen**, und ein Link zu **Bereiche** sagt ihnen, dass ihn nur sehen kann, wer Mitglieder verwaltet oder festlegt, was sie dürfen. ### API-Zugangsdaten [#api-credentials] Ein verbundenes System - ein nächtlicher Import, ein Skript, ein Planer - meldet sich mit **API-Zugangsdaten** an statt mit dem Konto einer Person. Zugangsdaten liegen auf der Seite **Zugriff** neben den Mitgliedern, auf dem Tab **API-Zugangsdaten**, den nur Admins sehen. Alles rund um Zugangsdaten geschieht dort: sie anlegen und ihr Secret kopieren (es wird einmal angezeigt), sehen, was sie dürfen, ihr Secret erneuern, ihren Zugriff ändern und sie widerrufen. Der Tab ist eine Tabelle - Name, Schlüssel und Zugriffsoptionen jeder Zugangsdaten, wann sie zuletzt genutzt wurden und wann sie ablaufen oder als Nächstes bestätigt werden müssen -, und das Zahnrad einer Zeile öffnet die Details. Ihr Tab **Zugriff** listet jede Berechtigung, die ihre Zugriffsoptionen geben, und welche Option sie jeweils gibt, genauso wie bei einem Mitglied. Zugangsdaten haben keine Rolle; sie wirken deshalb in jedem gemeinsamen Bereich und jeder App darin, auch in später angelegten Apps, und nie im persönlichen Bereich einer Person. Die Anleitung steht unter [Verbundene Systeme](./connected-systems.mdx). ### Standardzugriff für neue Mitglieder [#standardzugriff-für-neue-mitglieder] Alle, die dem Workspace ab jetzt beitreten - indem sie eine Einladung mit einem bereits vorhandenen Konto annehmen, sich über eine Einladung registrieren oder vom Ovidens-Team hinzugefügt werden -, erhalten die Rollen, die unten auf dem Tab **Mitglieder** der Seite **Zugriff** unter **Standardzugriff für neue Mitglieder** aufgeführt sind, dazu ihren eigenen persönlichen Bereich. Wer als Admin eingeladen wurde, ist zusätzlich Admin; jede andere Einladung gibt nichts über die Vorgaben hinaus. Admins sehen diesen Abschnitt und ändern ihn dort; alle anderen sehen ihn nicht. Jeder Workspace beginnt ohne Standardzugriff: Ein neues Mitglied hat keine Rolle, bis ihm jemand eine gibt, und arbeitet bis dahin nur in seinem eigenen persönlichen Bereich. Wer bereits Mitglied war, behält jede Rolle, die er hat. Ein Satz über der Liste sagt, was neue Mitglieder erhalten. Um eine Vorgabe hinzuzufügen, wählen Sie rechts in der Überschrift **Vorgabe hinzufügen**, im Dialog den Workspace, einen Bereich oder eine App (sie lassen sich durchsuchen) und dann die Rolle, und wählen **Vorgabe hinzufügen**: Sie wird hinzugefügt, mit **Rückgängig** in der Meldung, die das bestätigt. So lässt zum Beispiel **Betrachter** für den Bereich eines Teams jede neue Person die Dashboards dieses Teams lesen und sonst nichts. Angeboten werden nur Rollen, die Sie vergeben dürfen, eine dort bereits gesetzte Rolle wird nicht erneut angeboten, und **Admin** und **Mitgliederverwaltung** für den gesamten Workspace werden nie angeboten: Eine Vorgabe kann niemanden zum Admin machen oder ihm die Verwaltung von Mitgliedern übertragen. Ein Workspace kann bis zu 20 Vorgaben haben. Um eine nicht mehr zu vergeben, wählen Sie **Entfernen** daneben; auch das bietet **Rückgängig** an. Wird eine Änderung abgelehnt, nennt die Meldung den Grund, und nichts ändert sich. Eine Änderung der Vorgaben ändert nie die Rollen von jemandem, der bereits beigetreten ist - dafür nutzen Sie **Zugriff verwalten** in seiner Zeile. Die Vorgaben werden gelesen, wenn eine Person beitritt, nicht wenn sie eingeladen wurde; eine noch offene Einladung gibt also die Vorgaben, wie sie beim Annehmen gelten. Ein Mitglied, dessen Rollen noch keine App öffnen - zum Beispiel in einem Workspace ohne Standardzugriff -, sieht auf der App-Seite „Noch keine Apps mit Ihnen geteilt“ mit den Namen der Admins, die es um Zugriff bitten kann, statt eines leeren Workspace. Konnte seine Liste der Apps nicht geladen werden, sagt die Seite das und bietet **Erneut versuchen** an. Wenn Sie es lieber gezeigt als erklärt bekommen, führt Sie die Seite **Zugriff** selbst durch ihren Tab Mitglieder: [Tour der Seite Zugriff starten](https://app.ovidens.com/access?tour=users). Eine Tour zeigt nur; sie ändert nie etwas. ## Aktivitätsprotokoll [#aktivitätsprotokoll] Ihr Workspace zeichnet bei jeder Änderung automatisch auf, wer was wann geändert hat: Apps erstellt, Daten bearbeitet, Pipelines ausgeführt, Teammitglieder eingeladen. Admins lesen diese Aufzeichnung in der App **Audit Analytics** und erreichen sie überall im Produkt über die Schaltfläche **Prüfprotokolle anzeigen**. Die Seite [Audits](../administration/audit-trail.mdx) ist die vollständige Anleitung. Welches KI-Modell Vera antreibt, ist eine Entscheidung pro Workspace, die ein Workspace-Admin auf dem Tab **Allgemein** der Seite **System** trifft - siehe [Das KI-Modell des Workspace auswählen](../infrastructure/ai-transparency.mdx#das-ki-modell-des-workspace-auswählen). ## Die Seite System [#usage] Die Seite **System** hat diese Tabs. API-Zugangsdaten gehören nicht dazu: Sie liegen auf dem Tab **API-Zugangsdaten** der Seite **Zugriff** (siehe [API-Zugangsdaten](#api-credentials)). * **Nutzung** nennt die **Compute-Stufe** und die **Instanz** Ihres Workspace, die die Werte darunter maßgeblich bestimmen, und zeigt die Obergrenzen mit einem live aktuellen Wert daneben: Apps, Benutzer, Audit-Log-Einträge, gespeicherte Daten, monatliche Compute-Credits, monatliche KI-Credits und Teambereiche. Darunter ein Diagramm der Nutzung über die Zeit, umschaltbar zwischen **KI-Credits**, **Compute-Credits** und **Speicher**, nach Stunde, Tag, Woche oder Monat. Admins - wer die Aktivitätsaufzeichnungen des Workspace lesen darf - sehen am Fuß des Tabs außerdem **Daten-Downloads**: was Mitglieder dieses Workspace heruntergeladen haben - wer, aus welcher App und welchem Knoten, ob es die aktuellen Daten, eine Version oder ein Lauf waren, wie viele Zeilen und wie viel - mit den Summen der letzten 30 Tage darüber. Ein Download, der schmaler war als sein Knoten, ist als **gefiltert** oder mit der Anzahl der genommenen Spalten markiert, damit eine kleine Datei nie mit dem gesamten Datensatz verwechselt wird. Ein Download aus einer App, die Sie nicht öffnen können - einer im persönlichen Bereich einer Kollegin oder eines Kollegen -, wird weder aufgeführt noch in den Summen gezählt. Nachweise werden 90 Tage aufbewahrt, die Dateien selbst deutlich kürzer. Es ist dieselbe Frage, die die Seite [Zugangsdaten](../integrations/api/credentials.mdx) für ein automatisiertes System beantwortet, hier über Personen gestellt. * **Limits** zeigt die festen Obergrenzen pro App, pro Version und pro Visualisierung - wie viele Knoten, Wissensdatenbank-Elemente, Widgets, Kennzahlen, Dimensionen oder Skills jeweils erlaubt sind - sowie wie viele Durchlaufdaten pro App aufbewahrt werden und wie lange eine archivierte App erhalten bleibt, bevor sie endgültig gelöscht wird. * **Allgemein** enthält die **Identität** Ihres Workspace (Name, Symbol und Farbe) und seine **KI-Konfiguration** (das Modell, das Vera antreibt). Ein Admin bearbeitet beides und speichert es gemeinsam mit einem **Speichern**; alle anderen, auch ein Mitglied mit Mitgliederverwaltung, sehen es schreibgeschützt. [Tour der Seite System starten](https://app.ovidens.com/system?tour=system), um sich jeden Tab auf der Seite selbst zeigen zu lassen. ### Wenn Sie ein Limit erreichen [#wenn-sie-ein-limit-erreichen] Zwei der Zähler können die Arbeit stoppen, und beide sagen das deutlich, statt stillschweigend zu versagen: * **KI-Credits** setzen sich monatlich zurück. Am monatlichen Limit stoppen die KI-Funktionen mit einer Meldung, die das Limit des Workspace nennt und auf den Support verweist; der Rest des Workspace arbeitet normal weiter. * **Compute-Credits** setzen sich ebenfalls monatlich zurück und bezahlen das Ausführen von Pipelines und das Lesen von Daten. Am Limit erscheint "Das monatliche Compute-Credit-Limit für diesen Workspace wurde erreicht.", bis der nächste Monat beginnt oder ein Admin ein höheres Budget vereinbart. Andere Limits, etwa die Anzahl der Apps, Mitglieder oder gemeinsamen Bereiche, verweigern schlicht die Aktion, die sie überschreiten würde, und nennen das erreichte Limit. # Mit Vera erstellen (/docs/de/analytical-apps/build-with-vera) **Vera** ist die in Ovidens integrierte KI-Assistentin. Beschreiben Sie in normaler Sprache, was Sie erstellen, ändern oder verstehen möchten. Vera wählt die verfügbaren Werkzeuge für Ihre Anfrage aus den Bereichen **Datenpipeline**, **Visualisierung** und **Wissensdatenbank**. Sie müssen vor dem Start keine Wissensdatenbank vorbereiten. ## So starten Sie [#so-starten-sie] 1. Öffnen Sie Ihre App, klicken Sie auf **Editor öffnen** und öffnen Sie **Vera**. 2. Senden Sie eine Anfrage, etwa „Importiere diese Bestelldatei, berechne den monatlichen Umsatz und zeige ihn als Liniendiagramm“. Hängen Sie die Datei an. 3. Beantworten Sie Rückfragen zu fehlenden Angaben. Vera zeigt während der Arbeit ihren Fortschritt und antwortet zum Abschluss kurz. Kann sie einen Schritt nicht ausführen, erklärt sie die Einschränkung und bereits erledigte Arbeit. **Details** öffnet Token-Verbrauch, einen gemeinsamen Schrittverlauf und Zeitpunkte, ohne Eingabe und Antwort zu wiederholen. Der Gesprächsverlauf zeigt Beginn, letzte Öffnung und App-Kontext der ersten Anfrage. Die Titelsuche umfasst das gespeicherte Archiv. Das kompakte Menü für Selbstständigkeit neben der Anhang-Schaltfläche im Nachrichtenfeld bietet Stufen 0 bis 9; Stufe 4 ist der ausgewogene Standard. Dokumentationslinks öffnen einen neuen Tab. Bei gemeinsamen App-Änderungen pausiert die Visualisierungsbearbeitung bis zum Neuladen; veraltete Speicherungen werden abgewiesen. Pipeline-Ergebnisse bleiben über die Pipeline- und Verlauf-Bedienelemente der App verfügbar. ## Setzen Sie das Gespräch fort [#setzen-sie-das-gespräch-fort] Schreiben Sie im selben Bereich weiter: „Verwende das Bestelldatum für den Monat“, „Mache dieses Diagramm blau“ oder „Erkläre die Umsatzberechnung“. Vera nutzt Ihr jüngstes Gespräch, um Bezüge auf frühere Anfragen und Antworten zu verstehen. Ihre Gespräche gehören Ihnen innerhalb dieses Workspace. Wählen Sie über die Gesprächsauswahl einen früheren Verlauf aus oder starten Sie mit **Neues Gespräch** einen eigenen benannten Verlauf. Sie können ihn später umbenennen. Der vollständige Verlauf wird gespeichert; **Frühere Nachrichten laden** zeigt ältere Beiträge. Andere Personen haben eigene Gespräche. Vera öffnet Ihr letztes Gespräch automatisch nur in derselben App und innerhalb von 6 Stunden nach Ihrer letzten Interaktion wieder. Andernfalls erscheint ein leerer Chat, der mit der ersten Nachricht gespeichert wird. **Gesprächsverlauf** öffnet eine mittig angeordnete Tabelle mit Titelsuche und Seiten-Bedienelementen. Ältere Gespräche oder einen Verlauf aus einer anderen App können Sie dort manuell öffnen. Jede Nachricht zeigt ihre Ausgangs-App; die aktuelle Seite liefert den Kontext Ihrer nächsten Anfrage. Vera kann in den Apps arbeiten, die Ihre Berechtigungen erlauben. **Diese Anfrage stoppen** beendet die laufende Aufgabe; ein Gesprächswechsel allein lässt sie auf dem Server weiterlaufen. ## Geben Sie Vera die nötigen Angaben [#geben-sie-vera-die-nötigen-angaben] * **Beschreiben Sie Ziel und Geschäftsregeln.** Geben Sie beispielsweise an, ob Umsatz Steuern enthält und welches Datum den Berichtsmonat bestimmt. * **Hängen Sie hilfreiche Dateien an.** Bitten Sie Vera, ein Dokument zu lesen oder zusammenzufassen, oder beschreiben Sie, welche Daten importiert werden sollen. Das Lesen einer Datei lässt die App unverändert. Bei Unklarheit fragt Vera nach. * **Nutzen Sie die Wissensdatenbank bei Bedarf.** Vorhandene Dokumentation kann Kontext liefern. Sie können Vera bitten, sie zu erstellen oder zu aktualisieren. Eine Antwort im Gespräch wird nicht automatisch als Geschäftsdokumentation gespeichert. * **Fragen Sie nach Ovidens.** Vera kann in der Benutzerdokumentation nachsehen und auf passende Anleitungen verlinken, etwa zum Datenimport oder Veröffentlichen. Vera stellt gezielte Rückfragen, wenn wichtige Angaben fehlen. Klare Anfragen kann sie direkt ausführen. Sie liest nicht für jede Nachricht alle Dokumente, baut nicht stets die ganze Pipeline neu und fügt nicht automatisch Beispieldaten hinzu. ## Wer Vera nutzen kann [#wer-vera-nutzen-kann] Gespräche mit Vera setzen KI-Zugriff auf die App voraus. Sie können Fragen stellen, ohne die Konfiguration der App ändern zu dürfen. Jedes Bearbeitungswerkzeug braucht weiterhin seine eigenen Berechtigungen. Vera kann nur ändern, was Sie selbst ändern dürfen und was Ihre freigegebenen KI-Werkzeuge erlauben. Siehe [Rollen](../administration/index.mdx). Sie beantworten Fragen in Ihrem eigenen Gespräch. Anfragen und Antworten anderer Personen gehören nicht zu Ihrem Chat. ## Änderungen und Veröffentlichung [#änderungen-und-veröffentlichung] Vera erstellt oder verwendet den Entwurf der App, wenn eine angeforderte Änderung einen Entwurf braucht. Fragen nach Erklärungen oder Hilfe erzeugen keinen Entwurf. Ein Pipeline-Lauf zur Bearbeitung ist eine Vorschau; Betrachter sehen weiterhin die veröffentlichte App. Prüfen Sie Änderungen vor der Veröffentlichung. Klicken Sie auf **Veröffentlichen** oder bitten Sie Vera ausdrücklich darum, sofern dieses Werkzeug für Sie freigegeben ist. Ohne eine solche Anfrage bleiben Veras Änderungen im Entwurf. **Verwerfen** entfernt alle Änderungen dieses Entwurfs, einschließlich währenddessen hochgeladener Daten. Siehe [Entwurf und Veröffentlichung](../overview/apps.mdx#entwurf-und-veroeffentlichung). Ein [KI-Widget](./skills.mdx) ist eine eigene, schreibgeschützte Nutzung von Vera. Seine Ausführung liefert eine Antwort, ohne Daten oder Konfiguration zu ändern oder die App in den Entwurfsmodus zu versetzen. [Mit Vera interagieren](../tips/interact-with-vera.mdx) erklärt Ergebnisse, Wiederaufnahme und KI-Verbrauch. # App exportieren und importieren (/docs/de/analytical-apps/export-import) Eine Analyse-App laesst sich als einzelne **Bundle-Datei** herunterladen und in einen anderen Workspace importieren. Nutzen Sie das, um eine App zwischen Workspaces zu verschieben, einer Kollegin ein fertiges Setup zu uebergeben oder eine Offline-Kopie einer bewaehrten Konfiguration aufzubewahren. Das Bundle enthaelt die **Konfiguration** der App, nicht ihre Daten. Diese Unterscheidung ist das Wichtigste, bevor Sie damit arbeiten, deshalb steht sie unten ausdruecklich. ## Exportieren [#exportieren] Oeffnen Sie die App und waehlen Sie **App exportieren** ("Diese App als portables Bundle herunterladen"). Ein Dialog fragt vor dem Schreiben nach dem Dateinamen, vorausgefuellt mit der technischen ID der App und dem heutigen Datum. Den Namen koennen Sie aendern; die Endung `.app-bundle.json` liegt fest, sodass sich auch ein umbenanntes Bundle importieren laesst. Der Export nimmt immer eine **veroeffentlichte Version**. Wurde die App nie veroeffentlicht, gibt es noch nichts zu exportieren - veroeffentlichen Sie sie zuerst. Aenderungen in einem offenen Entwurf sind nicht enthalten; Sie erhalten den zuletzt veroeffentlichten Stand, sodass ein Bundle immer eine in sich stimmige, vollstaendige Konfiguration ist und nie eine halbfertige Bearbeitung. Der Export erfordert die Rolle **App-Ersteller** oder **Admin** für die App. ## Was im Bundle mitwandert [#was-im-bundle-mitwandert] * **Die App selbst** - Name, Beschreibung, Symbol und technische ID. * **Die gesamte Pipeline** - jeder Knoten mit seiner vollstaendigen Konfiguration: Datensatzknoten mit ihren Spaltendefinitionen, SQL-Transformationen mit ihren Abfragen, Indikatordaten-Bruecken und Analytik-App-Importe. * **Die Visualisierungs-Leinwand** - die Seiten und alle Widgets darauf. * **Die Wissensdatenbank** - der Ordnerbaum und der Inhalt jeder Seite. * **Die [Skills](./skills.mdx) der App** - Name, Beschreibung, deklarierte Eingaben, ob der Skill ueber MCP angeboten wird, und der vollstaendige Anweisungstext. ## Was nicht [#was-nicht] * **Ihre Daten.** Keine Datensatzzeilen, keine hochgeladenen Dateien, keine Indikatorwerte. Die importierte App kommt mit fertiger Struktur und leeren Eingaben an, bereit dafuer, dass Sie die Daten auf der neuen Seite hochladen oder verbinden. * **Die Historie.** Versionen, Pipeline-Laeufe und das Pruefprotokoll bleiben bei der urspruenglichen App. Die importierte App beginnt eine eigene, neue Historie. * **Workspace-Spezifisches.** Mitglieder, Berechtigungen und KI-Berechtigungen gehoeren zum Workspace, nicht zur App. ## Importieren [#importieren] Waehlen Sie auf der Seite **Apps** oben rechts die Import-Schaltflaeche (**App-Datei importieren**) oder die in der Kopfzeile des Bereichs, in den die App kommen soll (oder **App importieren** auf dem Willkommensbildschirm eines Workspace ohne Apps), und dann eine Bundle-Datei; von oben rechts fragt Ovidens, in welchen Bereich die App kommt, wenn Sie in mehr als einen importieren duerfen. Die App wird in diesem Bereich mit der Konfiguration aus dem Bundle angelegt, und Sie landen direkt darin, bereit zum Hinzufuegen der Daten. Ein paar Dinge, die Sie erwarten sollten: * Die App behaelt die **technische ID** aus dem Bundle. Existiert im Workspace bereits eine App mit dieser ID, wird der Import abgelehnt, statt irgendetwas zu ueberschreiben - benennen Sie die vorhandene App um oder entfernen Sie sie zuerst. * Der Import zaehlt gegen das **App-Limit** Ihres Workspace, genau wie eine von Hand angelegte App. * Eine Datei, die kein gueltiges Bundle ist, wird mit einer klaren Meldung abgelehnt, und es wird nichts angelegt. * Jeder Importknoten in der Datei (Analytik-App-Daten oder Indikatordaten) muss eine App dieses Workspace nennen, deren Daten Sie lesen koennen, und keine in einem persoenlichen Bereich. Trifft das auf einen nicht zu, wird der Import abgelehnt, unter Nennung dieser Knoten, und es wird nichts angelegt: Entfernen Sie sie aus der Datei, oder fragen Sie zuerst Zugriff auf die Apps an, aus denen sie lesen. Siehe [wer eine App aendern kann, die aus einer anderen App importiert](./pipeline/analytic-data.mdx#wer-eine-app-ändern-kann-die-aus-einer-anderen-app-importiert). Der Import erfordert die Rolle **App-Ersteller** oder **Admin** für den Workspace oder für den Team-Bereich, in dem neue Apps abgelegt werden. ## Was sich nicht exportieren laesst [#was-sich-nicht-exportieren-laesst] Die App **Audit Analytics** laesst sich nicht exportieren. Sie wird vom System verwaltet, ihre Quellen sind fuer das Pruefarchiv des Workspace reserviert, und jeder Workspace hat bereits eine - portabel ist daran nichts. Siehe [Audits](../administration/audit-trail.mdx). ## Hinweis zum Wechsel zwischen Workspaces [#hinweis-zum-wechsel-zwischen-workspaces] Da die Daten nicht mitwandern, ist das Verschieben einer App ein Zwei-Schritt-Vorgang: Bundle importieren, um die Struktur zu erhalten, und die Daten anschliessend auf der neuen Seite laden wie bei einer neuen App - Dateien in die Datensatzknoten hochladen oder die Indikatordaten- und Analytik-App-Knoten auf die Apps richten, aus denen sie in diesem Workspace lesen sollen. Diese Verbindungen verweisen **ueber die technische ID** auf andere Apps: Liest die importierte App aus einer anderen App, muss diese hier existieren und fuer Sie lesbar sein, bevor der Import angenommen wird: importieren Sie sie zuerst, unter derselben technischen ID. # Wissensdatenbank (/docs/de/analytical-apps/knowledge-base) Die **Wissensdatenbank** ist ein Ort innerhalb jeder App, um Notizen, Dokumente und Kontext zu Ihren Daten aufzubewahren. Hier halten Sie die Dinge fest, die für Sie offensichtlich sind, für andere (oder etwas anderes) jedoch nicht: was eine Spalte bedeutet, woher die Daten stammen oder welche Regeln Ihr Unternehmen befolgt. ## Warum sie wichtig ist [#warum-sie-wichtig-ist] Die Wissensdatenbank erfüllt einen doppelten Zweck. * Für **Menschen** ist sie eine Dokumentation, die die App begleitet, sodass ein Teammitglied, das sie später öffnet, die Daten versteht, ohne nachfragen zu müssen. * Für **Vera** liefert sie hilfreichen Hintergrund, wenn ihre Definitionen für Ihre Anfrage relevant sind. Sie können Ihre Ziele direkt im Chat erklären und Vera bitten, die Dokumentation zusammen mit der Pipeline und Visualisierung zu erstellen oder zu aktualisieren. Vorher Notizen zu schreiben ist optional. ## Wo Sie sie finden [#wo-sie-sie-finden] Öffnen Sie Ihre App, klicken Sie auf **Editor öffnen** und öffnen Sie den Bereich **Wissensdatenbank** (oder drücken Sie K). Links hat der Bereich vier Tabs: * **Seiten** - die Notizen selbst, in Ordnern. * **Favoriten** - die Seiten, die Sie markiert haben. * **Skills** - die wiederverwendbaren KI-Aufgaben der App. Siehe [Skills](./skills.mdx). * **Papierkorb** - ein Papierkorb-Symbol mit dem, was Sie gelöscht haben. ## Seiten schreiben [#seiten-schreiben] Sie organisieren die Wissensdatenbank mit **Ordnern** und **Seiten**, ähnlich wie Ordner auf Ihrem Computer. **Neue Seite** und **Neuer Ordner** legen sie auf oberster Ebene an, und das Menü "Weitere Aktionen" an einem Ordner legt eine Seite oder einen Ordner darin an. Über dasselbe Menü benennen Sie alles um, und Sie ziehen Seiten zwischen Ordnern, wenn Ihre App wächst. Ein Suchfeld über der Liste filtert die Seiten nach Namen. Der Seiteneditor unterstützt Markdown: Überschriften, Listen, Tabellen, Code-Blöcke und mehr. Er stellt außerdem **Mermaid-Diagramme** dar, sodass ein Ablaufdiagramm oder eine kleine Architekturskizze bei der Notiz liegt, die sie erklärt. Mit **Volle Breite umschalten** bekommt eine breite Tabelle mehr Platz. Sie müssen nichts speichern. Seiten sichern sich beim Schreiben selbst; eine kleine Anzeige zeigt **Wird gespeichert…** und dann **Gespeichert**. Markieren Sie eine Seite mit **Zu Favoriten hinzufügen**, um sie an den Tab **Favoriten** zu heften - das lohnt sich für die zwei oder drei Seiten, die alle ständig öffnen. **Aus Favoriten entfernen** löst sie wieder. ## Dokumente importieren [#dokumente-importieren] Man beginnt selten mit einem leeren Blatt. **Importieren** bringt vorhandenes Material herein, in zwei Formen: * **Markdown oder Text** - MD-, Markdown-, TXT- und CSV-Dateien, direkt als Seiten. * **Dokument** - PDF-, Word-, PowerPoint- und Excel-Dateien. Dokumente werden in Markdown umgewandelt, damit sie durchsuchbar, bearbeitbar und für Vera lesbar sind, und das Original bleibt genau so zum Download verfügbar, wie es hereinkam. Sie können mehrere Dateien auf einmal ablegen; der Dialog zeigt den Fortschritt ("3 von 8") und meldet am Ende, wie viele importiert wurden, mit **Erneut versuchen** für fehlgeschlagene. Lassen Sie den Dialog offen, bis der Import fertig ist. ## Löschen und wiederherstellen [#löschen-und-wiederherstellen] Das Löschen einer Seite oder eines Ordners zerstört nichts: **In den Papierkorb** legt sie samt allem darin in den Tab **Papierkorb**. Von dort holt **Wiederherstellen** sie an ihren Platz zurück, und **Endgültig löschen** entfernt sie unwiderruflich. Schreiben, in den Papierkorb legen und wiederherstellen können Mitglieder mit der Rolle **App-Ersteller** oder **Admin** für die App; **Endgültig löschen** bleibt Administratoren vorbehalten. Alle anderen lesen die Wissensdatenbank ohne die Bearbeitungselemente und ohne den Tab **Papierkorb**. # Übersicht über Analyse-Apps (/docs/de/analytical-apps/overview) Eine **Analyse-App** nimmt Ihre Rohdaten, formt sie zu den Ergebnissen, die für Sie wichtig sind, und stellt diese Ergebnisse in einer **Visualisierung** dar, in der Ihr Team arbeitet. Sie ist die richtige Art von App, wenn Sie Tabellenkalkulationen oder Dateien bereinigen, kombinieren und in Tabellen und Diagramme verwandeln möchten. ## Die drei Bestandteile einer Analyse-App [#die-drei-bestandteile-einer-analyse-app] Jede Analyse-App besteht aus drei Bestandteilen, von denen jeder einen eigenen Bereich in der App hat. * Die **[Datenpipeline](./pipeline/overview.mdx)** ist der Ort, an dem Sie Ihre Daten formen: Sie führen sie ein und transformieren sie dann Schritt für Schritt. * Die **[Visualisierung](./visualisation.mdx)** ist das, worin Ihre Teammitglieder arbeiten, aufgebaut aus den Ergebnissen der Pipeline: Tabellen, Diagramme, Filter und Eingaben. * Die **[Wissensdatenbank](./knowledge-base.mdx)** ist ein Ort für Notizen und Kontext zu Ihren Daten. Sie hilft Ihnen und liefert Vera den Hintergrund, den es benötigt, um eine bessere App zu erstellen. In ihrem Tab **Skills** hinterlegen Sie [wiederverwendbare KI-Fragen](./skills.mdx) für die App. ## Zuerst mit Vera erstellen [#zuerst-mit-vera-erstellen] Der einfachste Weg, eine Analyse-App zu erstellen, ist, **Vera**, den KI-Assistenten, zu fragen. Beschreiben Sie in einfacher Sprache, was Sie möchten, zum Beispiel "importiere diese Verkaufsdatei, summiere den Umsatz nach Region und stelle ihn als Diagramm dar", und Vera richtet die Pipeline, die Visualisierung und die Wissensdatenbank für Sie ein. Es sind keine SQL- oder technischen Kenntnisse erforderlich. Siehe [Mit Vera erstellen](./build-with-vera.mdx). Der Rest dieses Abschnitts erläutert jeden Bestandteil, sodass Sie überprüfen können, was Vera erstellt, oder die einzelnen Teile bei Bedarf von Hand erstellen und anpassen können. ## Der Aufbauzyklus [#der-aufbauzyklus] Der manuelle Aufbau einer Analyse-App folgt einem einfachen Zyklus: 1. **Daten einbringen**, indem Sie einen Datensatz hinzufügen und eine Datei importieren. 2. **Transformieren Sie sie** mit Schritten, die Ihre Daten bereinigen und kombinieren. 3. **Führen Sie** die Pipeline **aus**, sodass jeder Schritt verarbeitet wird und frische Ergebnisse erzeugt. 4. **Bauen Sie die Visualisierung** auf Basis dieser Ergebnisse mit Widgets wie Tabellen und Diagrammen auf. Sie wiederholen diesen Zyklus, wenn sich Ihre Anforderungen ändern: Passen Sie einen Schritt an, führen Sie erneut aus, und die Visualisierung wird aktualisiert. ## Der Editor, Ausführen und Veröffentlichen [#der-editor-ausführen-und-veröffentlichen] Sie bauen im **Editor**: Öffnen Sie die App und klicken Sie oben rechts auf **Editor öffnen** oder drücken Sie E. Die drei Bestandteile oben sind seine Tabs. Um die App so zu sehen, wie Ihre Teammitglieder sie sehen, gehen Sie über den Pfeil ganz links in der Editorleiste **Zurück zur Ansicht** oder drücken erneut E. Alles, was Sie ändern, gehört zu Ihrem privaten Entwurf, bis Sie es freigeben: * **Ausführen** verarbeitet die Pipeline und aktualisiert die Ergebnisse. Das ist eine private Vorschau - Ihre Teammitglieder sehen weiterhin die zuletzt veröffentlichten Ergebnisse. * **Veröffentlichen** gibt den Entwurf frei, sodass alle Ihre Änderungen sehen, und hält die App als Version fest, zu der Sie später zurückkehren können. **Veröffentlichen und ausführen** erledigt beides in einem Schritt. Siehe [Entwurf und Veröffentlichung](../overview/apps.mdx#entwurf-und-veroeffentlichung) für den gesamten Zyklus, einschließlich Verwerfen eines Entwurfs und Wiederherstellen einer älteren Version. Beides betrifft auch Daten, nicht nur die Konfiguration: Beim Verwerfen eines Entwurfs gehen auch die Zeilen verloren, die Sie bei offenem Entwurf hochgeladen haben, und beim Wiederherstellen tritt der Datenstand jener Version an die Stelle dessen, was seither erfasst wurde. Die Konfiguration der App ändern und sie veröffentlichen können Mitglieder mit der Rolle **App-Ersteller** oder **Admin** für die App. Alle anderen, die den Editor öffnen, sehen die Pipeline, die Wissensdatenbank, Skills und die Visualisierung schreibgeschützt, ohne **Veröffentlichen**, **Verwerfen** oder **Wiederherstellen**, und können weiterhin Daten in die Tabellen der App eintragen. Siehe [Wer baut und veröffentlicht](../overview/apps.mdx#wer-baut-und-veroeffentlicht). # Analyse-App-Daten (/docs/de/analytical-apps/pipeline/analytic-data) Ein **Analyse-App-Daten**-Knoten importiert das Ergebnis eines Knotens aus einer **anderen** Ihrer Analyse-Apps in diese Pipeline. So können mehrere Apps auf einem gemeinsamen, bereits veröffentlichten Ergebnis aufbauen, anstatt dieselben Daten zu kopieren oder dieselben Schritte in jeder App neu aufzubauen. Ein häufiges Muster ist eine gemeinsame "Referenzdaten"-App, deren Ausgabe mehrere Berichts-Apps wiederverwenden, oder eine aufwendige Datenaufbereitungs-App, deren Ergebnis eine Handvoll kleinerer Apps speist. Anstatt diese Arbeit zu duplizieren, importiert jede nachgelagerte App das Ergebnis mit einem Analyse-App-Daten-Knoten. ## Wählen Sie, was importiert werden soll [#wählen-sie-was-importiert-werden-soll] Wenn Sie einen Analyse-App-Daten-Knoten hinzufügen, teilen Sie ihm mit, was er einbringen soll: * die **Quell-Analyse-App**, aus der gelesen werden soll (eine der anderen Analyse-Apps in Ihrem Workspace), * den **Knoten** innerhalb dieser App, dessen Ergebnis Sie möchten, * optional, **welche Spalten** in welcher Reihenfolge importiert werden sollen. Lassen Sie dies leer, um jede Spalte genau so zu importieren, wie sie ist. Die Liste der Quell-Apps bietet nur die Apps an, aus denen Sie importieren dürfen - Apps, deren Daten Sie lesen können, und eine App in einem persönlichen Bereich nur für eine App in demselben Bereich - und markiert jede App, die noch kein veröffentlichtes Ergebnis hat, sodass Sie nur eine App auswählen können, die zum Importieren bereit ist. ## Wie die Daten erfasst werden [#wie-die-daten-erfasst-werden] Die Daten werden als **Momentaufnahme** der letzten erfolgreichen **veröffentlichten** Ausführung der Quell-App importiert, aufgenommen in dem Moment, in dem diese Pipeline ausgeführt wird. Daraus folgen zwei Dinge: * Es wird nur ein **veröffentlichtes** Ergebnis importiert. Eine private Vorschau-Ausführung in der Quell-App wird nie importiert, sodass nachgelagerte Apps immer nur auf Ergebnissen aufbauen, die ihre Eigentümer bewusst veröffentlicht haben. * Der Import ist eine zeitpunktbezogene Kopie. Wenn die Quell-App neuere Ergebnisse veröffentlicht, ändert sich diese App nicht von selbst. **Führen Sie diese Pipeline erneut aus**, um den Import mit den neueren Werten zu aktualisieren. Der importierte Knoten ist **schreibgeschützt**: Er zeigt das importierte Ergebnis als Tabelle an, und Sie bearbeiten die Daten in der Quell-App, nicht hier. Von diesem Knoten fließen die importierten Spalten in die danach verbundenen Knoten, sodass Sie das gemeinsame Ergebnis mit dem Rest Ihrer Daten vermischen können. ## Wer eine App ändern kann, die aus einer anderen App importiert [#wer-eine-app-ändern-kann-die-aus-einer-anderen-app-importiert] Ein Analyse-App-Daten-Knoten bringt die Daten einer anderen App in diese, deshalb wird er bei jeder Änderung an der Konfiguration dieser App geprüft: * Einen Importknoten hinzufügen oder auf eine andere App richten können Sie nur für eine App, deren Daten Sie lesen können. Eine App im persönlichen Bereich einer Person lässt sich nur in eine App in demselben persönlichen Bereich importieren, und die App Audit Analytics lässt sich nie importieren. * Ein Import kann deshalb Kolleginnen und Kollegen aussperren. Bevor Sie einen Importknoten speichern, der auf eine neue App zeigt, nennt der Knoten, wie viele Personen diese App danach nicht mehr ändern könnten, zum Beispiel "3 Personen werden diese App nicht mehr ändern können". Namen nennt er nie. * Sobald diese App aus einer anderen App importiert, steht jede Änderung an ihr - an Pipeline, Wissensbasis, Skills, Visualisierung und Einstellungen, und das Veröffentlichen - nur Personen offen, die jede App lesen können, aus der sie importiert. Alle anderen erfahren, welcher Importknoten sie aufhält, und können Zugriff auf die App anfragen, aus der er importiert. * Die App reparieren können sie trotzdem selbst, Importknoten für Importknoten: Einen solchen Knoten zu löschen oder auf eine App zu richten, die sie lesen können, ist immer erlaubt. Sonst nichts - nicht einmal, andere Spalten derselben App auszuwählen. Dasselbe gilt, wenn die App, aus der ein Importknoten liest, gelöscht wurde. Ein Hinweis oben im Editor nennt die Importknoten, die sie aufhalten, damit sie wissen, welche sie ändern müssen. * Eine frühere Version wiederherzustellen oder einen Entwurf zu verwerfen ist nur erlaubt, wenn die Version, die dadurch aktuell wird, aus keiner App importiert, die sie nicht lesen können; eine Version, die das tut, bleibt außer Reichweite, auch wenn sie aus weniger solchen Apps importiert. * Die Pipeline ausführen, Daten erfassen, exportieren und archivieren ist davon nicht betroffen. Jede Person, die die Daten dieser App lesen kann, sieht auch, was sie importiert, selbst ohne Zugriff auf die Quell-App - und immer nur das **veröffentlichte** Ergebnis der Quell-App, nie ihre Entwürfe. Bedenken Sie das, bevor Sie aus einer App importieren, deren Leserkreis kleiner ist als der dieser App. # Datensätze (/docs/de/analytical-apps/pipeline/datasets) Ein **Datensatz**-Knoten ist eine Eingabetabelle, die Sie mit Ihren eigenen Daten füllen. Hier gelangen Ihre Informationen in die Pipeline, entweder durch den Import einer Datei oder durch die manuelle Eingabe. Alles Nachgelagerte baut auf dem auf, was Ihre Datensätze enthalten. ## Spalten und ihre Typen definieren [#spalten-und-ihre-typen-definieren] Ein Datensatz ist in **Spalten** gegliedert, und jede Spalte hat einen **Datentyp**, der festlegt, welche Art von Wert in sie gehört. Die Wahl des richtigen Typs hält Ihre Daten sauber: Zahlen bleiben Zahlen, Datumsangaben zeigen eine Datumsauswahl, und ein Dropdown lässt nur die von Ihnen festgelegten Optionen zu. Zu den verfügbaren Typen gehören: * **Text** und **Langtext** für Wörter und freie Notizen, * **Ganzzahl** und **Dezimalzahl** für ganze und gebrochene Zahlen, * **Währung** für Geldbeträge und **Prozent** für Prozentsätze, * **Boolean** für ein einfaches Ja/Nein, * **Datum**, **Uhrzeit**, **Zeitstempel** und **Jahr** für Zeitpunkte, * **Telefon** und **E-Mail** für diese speziellen Arten von Text, * **Dropdown**, um eine Option aus einer Liste auszuwählen, und **Mehrfachauswahl**, um mehrere auszuwählen, * **Anhang** für eine oder mehrere hochgeladene Dateien. Geben Sie jeder Spalte einen klaren Namen und den Typ, der zu den Werten passt, die sie enthalten wird. ## Eine CSV- oder Excel-Datei importieren [#eine-csv--oder-excel-datei-importieren] Der schnellste Weg, einen Datensatz zu füllen, ist der Import einer Tabellenkalkulation. Ein Assistent führt Sie in sechs Schritten hindurch: **Datei auswählen**, **Tabellenblatt wählen**, **Einleseoptionen**, **Spalten zuordnen**, **Vorschau & Prüfung** und **Bestätigen**. Sie können jederzeit **Zurück** gehen, ohne Ihre Einstellungen zu verlieren. Der Import einer Datei steht allen zur Verfügung, die die Daten der App bearbeiten dürfen - denselben Personen, die seine Zeilen [von Hand bearbeiten](#daten-von-hand-bearbeiten) können. Wer die App nur ansehen darf, kann nicht importieren. ### Datei auswählen [#datei-auswählen] Ziehen Sie eine CSV- oder Excel-Datei auf den Assistenten oder klicken Sie, um eine auszuwählen. Trennzeichen und Kodierung werden automatisch erkannt. Wenn Sie bei null anfangen, liefert **Vorlage** eine Datei, die die Spalten dieses Datensatzes bereits als Überschriften trägt. Wählen Sie im Dialog CSV oder Excel, benennen Sie die Datei und füllen Sie sie aus - laden Sie sie hoch, dann bleibt beim Zuordnen nichts mehr zu raten. Ist die Datei zu groß oder hat sie mehr Zeilen, als ein Datensatz aufnehmen darf, sagt der Assistent das sofort und nennt das Limit, sodass Sie die Datei teilen können, statt auf einen fehlgeschlagenen Import zu warten. Sehr große Dateien werden genau so importiert, wie sie eingelesen wurden, statt in der Vorschau Zelle für Zelle bearbeitbar zu sein. Eine einzelne Zeile darf bis zu 2.000.000 Zeichen enthalten. Eine längere Zeile stoppt den Import mit einer Meldung, die ihre Zeilennummer nennt, und es wird nichts importiert. Kürzen Sie dann die längsten Werte dieser Zeile oder verteilen Sie sie auf mehrere Zeilen. ### Tabellenblatt wählen [#tabellenblatt-wählen] Eine Excel-Arbeitsmappe hat meist mehrere Tabellenblätter, wählen Sie also das mit Ihren Daten. Jedes wird mit seiner Größe aufgeführt, zum Beispiel "1.204 Zeilen · 12 Sp.". Bei einer CSV entfällt dieser Schritt. ### Einleseoptionen [#einleseoptionen] In diesem Schritt behandeln Sie eine Datei, die nicht perfekt sauber ist. Alles hier hat eine sinnvolle Vorbelegung, und die Vorschau aktualisiert sich beim Ändern. * **Trennzeichen** - das Zeichen, das Spalten trennt (Komma, Semikolon, Tab). * **Kodierung** - der Zeichensatz der Datei. Windows-1252 für ältere Excel-Exporte mit fehlerhaften Umlauten. * **Kopfzeile** - welche Zeile die Spaltennamen enthält, oder **Keine Kopfzeile**, wenn die Datei direkt mit Daten beginnt. * **Führende Zeilen überspringen** und **Letzte Zeilen überspringen** - einen Titelblock oder Notizen am Anfang und Summen oder Fußnoten am Ende ignorieren. * **Leer-/Null-Werte** - Werte, die als leer gelten sollen, zum Beispiel `N/A` oder `-`. Tippen und mit Enter hinzufügen. * **Leerzeichen um Werte entfernen** - überflüssige Leerzeichen aus jedem Wert entfernen. ### Spalten zuordnen [#spalten-zuordnen] Bringen Sie jede Spalte Ihres Datensatzes mit einer Spalte aus Ihrer Datei in Übereinstimmung. Der Assistent ordnet zu, was er anhand der Namen erkennt, und sagt dazu, wie sicher er ist: exakte Übereinstimmung, Namensübereinstimmung oder ungefähre Übereinstimmung. Was Sie nicht möchten, setzen Sie auf "leer lassen". Zwei Abkürzungen helfen bei einer sperrigen Datei: * **Mit KI vorschlagen** bittet Vera, die Zuordnung aus den Spaltennamen abzuleiten. Jeder Vorschlag ist mit "KI-Vorschlag · bitte prüfen" markiert, Sie bestätigen also, statt blind zu vertrauen. Der Assistent sagt klar, was Ihren Workspace verlässt: "Spaltennamen und einige Beispielwerte werden zur Zuordnung an unseren KI-Anbieter gesendet." * **Vorherige Zuordnung übernehmen** wendet die Zuordnung des letzten Imports in diesen Datensatz an - genau richtig für eine Datei, die Sie jeden Monat neu einspielen. Der Assistent warnt Sie, wenn eine Pflichtspalte nicht zugeordnet ist oder wenn Spalten Ihrer Datei ungenutzt bleiben. ### Vorschau & Prüfung [#vorschau--prüfung] Jetzt prüft der Assistent jeden Wert gegen den Typ seiner Spalte und zeigt die Summen: wie viele Zeilen gültig sind, wie viele Fehler haben und wie viele Warnungen tragen. Warnungen betreffen Dinge, die einen zweiten Blick verdienen statt echter Fehler, etwa eine vollständig leere Spalte, eine Spalte mit nur einem einzigen Wert oder doppelte Zeilen. Klicken Sie auf einen Befund, um **zur Zelle** dahinter **zu springen** und sie in der Vorschau zu korrigieren. Die Vorschau zeigt die ersten 500 Zeilen und sagt das auch; geprüft und importiert werden weiterhin alle Zeilen. Ein Schalter entscheidet über schlechte Zeilen: * **Ungültige Zeilen überspringen** an: die ungültigen Zeilen bleiben draußen, die gültigen werden importiert. * Aus: alle Zeilen müssen gültig sein, und der Import wird abgelehnt, wenn eine Zeile Fehler enthält. Wird ein Import abgelehnt, listet **Fehlerbericht herunterladen** genau auf, welche Zeilen gescheitert sind - auch solche jenseits der Vorschau. Ein sehr langer Wert wird im Bericht gekürzt und endet auf "...", damit der Bericht eine Größe behält, die eine Tabellenkalkulation öffnen kann; die Zeilennummer führt Sie zur vollständigen Zeile in Ihrer Datei. **Duplikate erkennen anhand** ist optional: Wählen Sie eine oder mehrere Spalten als Schlüssel, und eingehende Zeilen, deren Schlüssel bereits existiert, werden übersprungen. ### Bestätigen [#bestätigen] Zum Schluss wählen Sie den **Import-Modus**: **Ersetzen** überschreibt alle vorhandenen Daten, **Anhängen** fügt diese Zeilen zu den vorhandenen hinzu (nur angeboten, wenn es etwas zum Anhängen gibt). Nach der Bestätigung meldet Ovidens, was passiert ist, zum Beispiel "1.180 Zeilen importiert · 24 übersprungen". Eine Datei braucht mindestens eine Datenzeile. Eine Datei, die nur eine Kopfzeile oder gar nichts enthält, wird in beiden Modi mit der Meldung "Die Datei enthält keine Datenzeilen, es gibt also nichts zu importieren" abgelehnt, und der Datensatz behält seine Zeilen. Eine leere Datei ist meist ein Versehen - ein Export ohne Treffer oder eine zu früh gespeicherte Datei -, deshalb versteht Ovidens sie nicht als Anweisung, den Datensatz zu leeren. Dasselbe gilt bei aktiviertem **Ungültige Zeilen überspringen**, wenn jede Zeile der Datei ungültig ist: Der Import wird mit einer Meldung abgelehnt, die nennt, wie viele Zeilen ungültig waren, statt keine zu importieren. Um einen Datensatz bewusst zu leeren, löschen Sie seine Zeilen in der Datentabelle. Während eine Pipeline-Ausführung läuft, ist kein Import möglich; der Assistent sagt das und wartet. ## Daten von Hand bearbeiten [#daten-von-hand-bearbeiten] Sie müssen nicht alles importieren. Sie können Werte auch direkt in den Datensatz eingeben und Zellen an Ort und Stelle bearbeiten, was bei kleinen Tabellen oder schnellen Korrekturen praktisch ist. Sie können für eine Spalte auch grundlegende Regeln festlegen, etwa sie als Pflichtfeld zu markieren oder ihre Werte als eindeutig zu verlangen, sodass Ihre Daten bei der Eingabe konsistent bleiben. Das gilt schon für die erste Zeile. Ein neuer Datensatz, der noch nie Daten erhalten hat, zeigt eine leere Tabelle mit dem Hinweis "Noch keine Daten. Fügen Sie die erste Zeile manuell hinzu oder laden Sie über Importieren eine Datei." und der Schaltfläche **Erste Zeile hinzufügen**. Ein Klick darauf öffnet die Bearbeitung mit der bereits angelegten ersten Zeile; tippen Sie hinein und **Speichern** Sie. Danach fügt die Leiste **+ Neue Zeile** am unteren Rand des Rasters weitere Zeilen hinzu. Ein Dateiimport ist vorher nicht nötig, und der Import einer Datei bleibt jederzeit über dieselbe Werkzeugleiste verfügbar. Wer nicht importieren kann, sieht den Hinweis ohne den Import: "Noch keine Daten. Fügen Sie die erste Zeile manuell hinzu." Es gibt drei Möglichkeiten, eine Zeile hinzuzufügen. Die Zeile **+ Neue Zeile** am unteren Rand der Tabelle und das Hinzufügen-Steuerelement, das links neben jeder Zeile angezeigt wird, während Sie bearbeiten, legen die neue Zeile beide sofort an, ohne dass zuerst etwas eingegeben werden muss. **Zeile** in der Werkzeugleiste öffnet stattdessen ein Formular, das eine Zeile erst hinzufügt, sobald Sie mindestens ein Feld ausgefüllt haben - wird es leer abgeschickt, wird nichts hinzugefügt. Wie auch immer Sie eine Zeile hinzufügen, sie landet am Ende der Tabelle - ein Datensatz behält seine Zeilen in der Reihenfolge, in der sie angelegt wurden, sodass eine neue Zeile immer angehängt und nie zwischen zwei bestehenden eingefügt wird. Die Zeile zählt als ungesicherte Änderung, bis Sie **Speichern** wählen. Voraussetzung dafür ist lediglich mindestens eine Spalte. Ein Datensatz ohne definierte Spalten zeigt stattdessen den Import-Ablagebereich, denn der Import einer Datei definiert auch die Spalten. ### Dateien in einer Anhang-Spalte [#attachments] Eine **Anhang**-Spalte enthält Dateien - eine oder mehrere pro Zeile. Während Sie bearbeiten, fahren Sie mit der Maus über eine Zelle der Spalte und wählen **Datei hinzufügen** (in einer leeren Zelle) oder die Büroklammer (in einer Zelle, die bereits Dateien enthält). Im Dialog, der sich öffnet, klicken Sie, um Dateien auszuwählen, oder ziehen sie auf den Ablagebereich, und bestätigen dann mit der Hochladen-Schaltfläche, die nennt, wie viele Dateien sie sendet. Jede Datei darf bis zu 10 MB groß sein; eine größere Datei wird im Dialog mit einem entsprechenden Hinweis abgelehnt, und alle anderen Änderungen bleiben erhalten. Eine hochgeladene Datei gehört erst zur Zeile, wenn Sie **Speichern**. Wählen Sie stattdessen **Abbrechen**, wird sie zusammen mit den übrigen ungesicherten Änderungen verworfen. Ein Klick auf das Dateisymbol in einer Zelle zeigt alle ihre Dateien mit Namen und Größe. Dort können Sie eine Datei herunterladen (Sie werden zuerst nach dem Dateinamen gefragt), ein Bild vergrößert ansehen, weitere Dateien anhängen oder eine entfernen. Um eine Datei zu ersetzen, entfernen Sie sie, fügen die neue hinzu und wählen **Speichern**. Wer die Daten der App lesen kann, kann ihre Dateien öffnen und herunterladen. Hinzufügen und entfernen können sie nur Administratoren des Arbeitsbereichs. Dieselben Zellen mit denselben Regeln erscheinen in einem bearbeitbaren [Tabellen-Widget](../visualisation.mdx#table-attachments) auf dem Dashboard der App. Eine Datei bleibt herunterladbar, wenn die Konfiguration der App bearbeitet oder eine neue Version veröffentlicht wird. Wird eine Datei aus einer Zelle entfernt oder nach dem Hochladen abgebrochen, wird die gespeicherte Kopie nicht gelöscht: Sie bleibt erhalten, zählt weiterhin zum Speicher Ihres Arbeitsbereichs und wird derzeit nicht automatisch bereinigt. ## Die Daten herunterladen [#die-daten-herunterladen] **Exportieren** in der Tabellen-Symbolleiste lädt herunter, was der Datensatz enthält. Vor jedem Download werden Sie nach einem Dateinamen gefragt, damit in Ihrem Download-Ordner etwas Wiedererkennbares landet und keine technische Kennung. Ein Download bringt die Zeilen aus dem Workspace heraus, deshalb ist er eine eigene Berechtigung, getrennt vom Lesen. Sie gehört zu den Rollen **Mitwirkender**, **Analyst**, **App-Ersteller** und **Admin**. Wer eine App nur ansehen darf, kann ihre Tabellen lesen, bekommt aber kein **Exportieren** angeboten, weder hier noch an einer Tabelle oder einem Pivot in der Visualisierung oder unter den Ergebnissen einer SQL-Abfrage, und ein Bericht bietet kein **PDF exportieren** an, weil das PDF die Tabellen des Berichts vollständig enthält. **Die Datei entspricht dem, was Sie sehen.** Wenn Sie die Tabelle gefiltert oder durchsucht oder Spalten ausgeblendet haben, fragt der Dialog, was die Datei enthalten soll - und jede Antwort nennt ihre eigene Zahl: * **Zeilen** erscheint, sobald ein Filter oder eine Suche die Tabelle einschränkt. **Aktueller Filter** enthält genau die Zeilen am Bildschirm, **Alle Zeilen** den gesamten Datensatz. Vorausgewählt ist, was Sie sehen - ein Bestätigen ohne weitere Änderung lädt also genau Ihre Ansicht herunter. * **Spalten** erscheint, sobald Sie mindestens eine ausgeblendet haben. **Sichtbar** enthält die Spalten am Bildschirm in Ihrer Reihenfolge, **Alle** jede Spalte des Datensatzes. Die Zeile unter dem Dateinamen beschreibt immer Ihre aktuelle Auswahl - die Zahl dort ist also die Zeilenzahl der Datei. **Alle Zeilen** kann bei einem sehr großen Datensatz zudem die Kapazität einer Tabellenkalkulation überschreiten; Excel ist dann ausgegraut, solange diese Auswahl aktiv ist. Eine Ansicht lässt sich noch nicht einzeln herunterladen: Ist **nur fehlerhafte Zeilen** eingeschaltet, wird **Aktueller Filter** ausgegraut angeboten und der Download nimmt alle Zeilen. Schalten Sie die Ansicht aus, um die Treffer eines Spaltenfilters zu laden. Der Name ist bereits ausgefüllt, aus dem Namen des Knotens und dem heutigen Datum - ein wöchentlicher Export desselben Datensatzes überschreibt damit nicht mehr den der Vorwoche. Ändern Sie ihn, wenn Sie etwas anderes möchten. Der Dateityp steht neben dem Feld und ist nicht Teil des Namens, sodass beide nie widersprüchlich sein können. Wählen Sie **CSV** oder **Excel**: * **CSV** ist das Austauschformat. Es ist reiner Text, jeder Wert wird als Text geschrieben, und es lässt sich über denselben Assistenten wieder importieren. * **Excel** überträgt die Typen. Ein Datum kommt als Datum an, eine Zahl als Zahl und eine Ja/Nein-Spalte als TRUE oder FALSE, sodass Sie eine Spalte sortieren und summieren können, ohne sie neu einzutippen. Eine Prozentspalte zeigt ihr `%` und eine Währungsspalte ihr Symbol, genau wie am Bildschirm. Datumsangaben werden als `2026-08-29` geschrieben und bedeuten so in jeder Region dasselbe. Ein Tabellenblatt fasst rund eine Million Zeilen. Hat der Datensatz mehr, wird Excel ausgegraut mit der Zeilenzahl angeboten, und CSV übernimmt. Die Ergebnisse eines Laufs und die festgeschriebenen Daten einer veröffentlichten Version laden Sie genauso herunter, über dasselbe Bedienelement, und ihre Namen sagen, welches von beiden Sie geholt haben. # Indikatordaten (/docs/de/analytical-apps/pipeline/indicator-data) Ein **Indikatordaten**-Knoten zieht Kennzahlen aus einer Ihrer Indikator-Apps in diese Pipeline. Er ist die Brücke zwischen den beiden App-Arten: Er ermöglicht es einer Analyse-App, die Zahlen zu lesen, die Sie in einer Indikator-App gesammelt haben, und sie mit anderen Daten zu kombinieren. Wenn Sie in einer Indikator-App Kennzahlen wie Kraftstoffverbrauch oder Strom erfassen, ist dieser Knoten der Weg, diese Zahlen in eine Analyse-App neben, zum Beispiel, Ihren Finanzdaten zu bringen. ## Wählen Sie, was eingebracht werden soll [#wählen-sie-was-eingebracht-werden-soll] Wenn Sie einen Indikatordaten-Knoten hinzufügen, teilen Sie ihm mit, was er abrufen soll: * die **Quell-Indikator-App**, aus der gelesen werden soll - die Liste bietet nur die Indikator-Apps an, aus denen Sie importieren dürfen: Apps, deren Daten Sie lesen können, und eine App in einem persönlichen Bereich nur für eine App in demselben Bereich, * welche **Indikatoren** (Kennzahlen) Sie möchten, * den **Periodenbereich** und den **Periodentyp**, zum Beispiel monatliche Werte über eine Spanne von Monaten oder Jahren, * optionale **Dimensionsfilter**, um die Kennzahlen auf bestimmte Aufschlüsselungen einzugrenzen, etwa eine Region oder einen Standort (siehe [Dimensionen](../../indicator-apps/dimensions/other-dimensions.mdx)), * und ob **Dimensionsbezeichnungen einschließen** aktiv sein soll. Ist **Dimensionsbezeichnungen einschließen** aus, kommt jede Aufschlüsselung als kurze technische ID an, zum Beispiel `de_south`. Ist es an, fügt der Knoten daneben eine zweite Spalte mit dem lesbaren Namen des Elements hinzu, zum Beispiel "Süd". Schalten Sie es ein, wenn die Zahlen in einer Tabelle oder einem Diagramm landen, das Menschen lesen: die Bezeichnung gehört auf die Achse oder in die Spaltenüberschrift, während die ID das Verlässliche zum Verknüpfen und Filtern bleibt. ## Wie die Werte erfasst werden [#wie-die-werte-erfasst-werden] Die Kennzahlen werden als **Momentaufnahme** erfasst, wenn die Pipeline ausgeführt wird. Mit anderen Worten: Der Knoten hält die Indikatorwerte so fest, wie sie zum Zeitpunkt der Ausführung sind, sodass der Rest Ihrer Pipeline mit einem stabilen Satz von Zahlen arbeitet. Führen Sie die Pipeline erneut aus, wann immer Sie diese Momentaufnahme mit den neuesten Werten aktualisieren möchten. Von dort fließen die Indikatorwerte in die Knoten, die nach diesem verbunden sind, sodass Sie erfasste Kennzahlen mit dem Rest Ihrer Daten vermischen können. ## Wer eine App ändern kann, die aus einer anderen App importiert [#wer-eine-app-ändern-kann-die-aus-einer-anderen-app-importiert] Ein Indikatordaten-Knoten bringt die Daten einer anderen App in diese, deshalb wird er bei jeder Änderung an der Konfiguration dieser App geprüft: * Einen Importknoten hinzufügen oder auf eine andere App richten können Sie nur für eine App, deren Daten Sie lesen können. Eine App im persönlichen Bereich einer Person lässt sich nur in eine App in demselben persönlichen Bereich importieren, und die App Audit Analytics lässt sich nie importieren. * Ein Import kann deshalb Kolleginnen und Kollegen aussperren. Bevor Sie einen Importknoten speichern, der auf eine neue App zeigt, nennt der Knoten, wie viele Personen diese App danach nicht mehr ändern könnten, zum Beispiel "3 Personen werden diese App nicht mehr ändern können". Namen nennt er nie. * Sobald diese App aus einer anderen App importiert, steht jede Änderung an ihr - an Pipeline, Wissensbasis, Skills, Visualisierung und Einstellungen, und das Veröffentlichen - nur Personen offen, die jede App lesen können, aus der sie importiert. Alle anderen erfahren, welcher Importknoten sie aufhält, und können Zugriff auf die App anfragen, aus der er importiert. * Die App reparieren können sie trotzdem selbst, Importknoten für Importknoten: Einen solchen Knoten zu löschen oder auf eine App zu richten, die sie lesen können, ist immer erlaubt. Sonst nichts - nicht einmal, andere Spalten derselben App auszuwählen. Dasselbe gilt, wenn die App, aus der ein Importknoten liest, gelöscht wurde. Ein Hinweis oben im Editor nennt die Importknoten, die sie aufhalten, damit sie wissen, welche sie ändern müssen. * Eine frühere Version wiederherzustellen oder einen Entwurf zu verwerfen ist nur erlaubt, wenn die Version, die dadurch aktuell wird, aus keiner App importiert, die sie nicht lesen können; eine Version, die das tut, bleibt außer Reichweite, auch wenn sie aus weniger solchen Apps importiert. * Die Pipeline ausführen, Daten erfassen, exportieren und archivieren ist davon nicht betroffen. Jede Person, die die Daten dieser App lesen kann, sieht auch, was sie importiert, selbst ohne Zugriff auf die Indikator-App - und immer nur deren **veröffentlichte** Struktur mit den erfassten Werten, nie ihre Entwürfe. Bedenken Sie das, bevor Sie aus einer App importieren, deren Leserkreis kleiner ist als der dieser App. # Pipeline-Übersicht (/docs/de/analytical-apps/pipeline/overview) Die **Datenpipeline** ist die Abfolge von Schritten, die Ihre Rohdaten aufnehmen und zu den Ergebnissen formen, die Ihre Visualisierung verwendet. Stellen Sie sie sich wie ein Fließband vor: Am einen Ende kommen Daten herein, jeder Schritt erledigt ein wenig Arbeit, und am anderen Ende kommen fertige Ergebnisse heraus. ## Knoten und Verbindungen [#knoten-und-verbindungen] Jeder Schritt in der Pipeline ist ein **Knoten**. Sie verbinden Knoten so, dass einer den nächsten speist, und die Arbeitsfläche zeichnet diese Verbindungen als Linien, sodass Sie den Datenfluss auf einen Blick erkennen. Ein Knoten früh im Ablauf bringt Daten ein; ein späterer Knoten nimmt diese Daten und transformiert sie. Sie bauen die Pipeline im Editor auf dem Tab **Datenpipeline** auf, indem Sie Knoten hinzufügen und Verbindungen zwischen ihnen zeichnen. Eine Pipeline hat drei Stufen: **Eingaben** - ein Datensatz und eine Indikator-Brücke - speisen einen **Verbraucher**, eine **SQL**-Transformation (derzeit der einzige Transformationstyp), deren Ergebnis die **Ausgabe** ist. Diese Ausgabe wird auf der separaten Registerkarte **Visualisierung** als Tabelle, Diagramm, Pivot oder SQL-Editor dargestellt. ## Die vier Knotentypen [#die-vier-knotentypen] Es gibt vier Arten von Knoten, jede mit einer eigenen Seite. * **[Datensätze](./datasets.mdx)** bringen Daten ein. Ein Datensatz ist eine Eingabetabelle, die Sie mit Ihren eigenen Daten füllen, in der Regel durch den Import einer Datei. * **[SQL-Knoten](./sql-nodes.mdx)** transformieren Daten. Sie nehmen die Ausgabe früherer Knoten und formen sie um. * **[Indikatordaten](./indicator-data.mdx)**-Knoten ziehen Kennzahlen aus einer Ihrer Indikator-Apps, sodass Sie diese Kennzahlen mit anderen Daten kombinieren können. * **[Analyse-App-Daten](./analytic-data.mdx)**-Knoten importieren das Ergebnis eines Knotens aus einer anderen Ihrer Analyse-Apps, sodass Sie auf einem gemeinsamen, bereits veröffentlichten Ergebnis aufbauen können, ohne es zu kopieren. ## Die Pipeline ausführen und die Ergebnisse lesen [#die-pipeline-ausführen-und-die-ergebnisse-lesen] Das Hinzufügen und Verbinden von Knoten beschreibt, was geschehen soll, aber es wird nichts verarbeitet, bis Sie es ausführen. Klicken Sie auf **Ausführen**, um jeden Schritt der Reihe nach zu verarbeiten. Die Ausführung erfolgt im Hintergrund, und der Status aktualisiert sich von selbst, wenn sie abgeschlossen ist, sodass Sie nicht warten oder aktualisieren müssen. Wenn die Ausführung abgeschlossen ist, klicken Sie auf einen beliebigen Knoten, um die von ihm erzeugten Daten anzuzeigen. Jeder Knoten zeigt an, ob er erfolgreich war, und Ihre Visualisierungs-Widgets zeigen die frischen Ergebnisse. Wenn ein Schritt fehlschlägt, können Sie ihn öffnen, um den Fehler zu lesen, ihn zu beheben und erneut auszuführen. ## Ausführungsverlauf und was aufbewahrt wird [#ausführungsverlauf-und-was-aufbewahrt-wird] Jede Ausführung steht im Verlauf der App, unter **Verlauf** (der Uhr-Schaltfläche in der Editorleiste). Über **Gesamten Verlauf anzeigen** erreichen Sie die vollständige Liste mit Startzeit, Status, Dauer, Anzahl der Knoten und der Größe dessen, was die Ausführung erzeugt hat. Ein Klick auf eine Zeile zeigt die Pipeline genau so, wie diese Ausführung sie hinterlassen hat. Die *Liste* behält jede Ausführung. Die *Daten*, die eine Ausführung erzeugt hat, bleiben nicht ewig, denn sie sind das Größte, was eine App speichert. Ovidens bewahrt die Ergebnisse der neuesten Ausführungen auf (drei, sofern Ihr Workspace nicht anders eingestellt ist), behält immer die letzte erfolgreiche Ausführung und gibt den Rest still frei. Die ältere Ausführung steht weiterhin im Verlauf, sie hat nur keine Daten mehr zum Öffnen. Administratoren können das für jede einzelne Ausführung übersteuern: * **Daten dieses Durchlaufs schützen** heftet eine Ausführung fest, sodass die Bereinigung sie nie anfasst. Eine geschützte Ausführung ist in der Liste mit **Geschützt** markiert. **Schutz für diesen Durchlauf aufheben** gibt sie wieder frei und sagt klar, was das bedeutet: die Daten werden "beim nächsten Aufräumlauf zur automatischen Löschung freigegeben". * **Diesen Durchlauf löschen** gibt die Daten dieser Ausführung sofort frei und entfernt sie aus dem Verlauf. Das lässt sich nicht rückgängig machen und wird für die letzte erfolgreiche Ausführung nicht angeboten. Die Seite **System** Ihres Workspace führt beide Limits unter **Limits** auf: "Aufbewahrte Durchlaufdaten pro App" und "Geschützte Durchläufe pro App". Siehe [Verwaltung](../../administration.mdx). ## Wenn Ergebnisse nicht mehr aktuell sind [#wenn-ergebnisse-nicht-mehr-aktuell-sind] Wenn Sie nach einer Ausführung Daten ändern, sagt Ovidens das, statt still veraltete Zahlen zu zeigen. Im Editor erscheint ein Hinweis: "Die Daten haben sich seit dem letzten Lauf geändert. Führen Sie den Lauf erneut aus, um die Ergebnisse zu aktualisieren." samt einer Schaltfläche **Erneut ausführen**. Einzelne Widgets tragen dieselbe Aussage als kleine Markierung **Nicht aktuell**. Wer die veröffentlichte App nutzt, sieht den entsprechenden Hinweis: Jemand, der die App erstellt, etwa ein Workspace-Administrator, muss sie neu berechnen und veröffentlichen. ## Lassen Sie Vera es für Sie erstellen [#lassen-sie-vera-es-für-sie-erstellen] Sie müssen eine Pipeline nicht von Hand zusammenstellen. Beschreiben Sie die Daten und das gewünschte Ergebnis, und **Vera** kann die gesamte Pipeline für Sie erstellen. Siehe [Mit Vera erstellen](../build-with-vera.mdx). # SQL-Knoten (/docs/de/analytical-apps/pipeline/sql-nodes) Ein **SQL**-Knoten ist ein Schritt, der Ihre Daten transformiert. Er liest die Ausgabe früherer Knoten und formt sie zu etwas Neuem um, zum Beispiel indem er zwei Tabellen kombiniert, Zeilen filtert oder eine Summe berechnet. SQL ist die Standardsprache für die Arbeit mit Daten, und ein SQL-Knoten verwendet eine Abfrage, die aus den einspeisenden Knoten liest. ## Sie müssen kein SQL beherrschen [#sie-müssen-kein-sql-beherrschen] Das ist der wichtige Teil: Sie müssen SQL nicht selbst schreiben. Beschreiben Sie die gewünschte Transformation in einfacher Sprache, zum Beispiel „behalte nur die Bestellungen dieses Jahres und summiere den Umsatz pro Kunde", und **Vera** schreibt die Abfrage für Sie. Vera ist der No-Code-Weg; diese Seite erläutert den Knoten für den Fall, dass Sie lesen oder anpassen möchten, was Vera erzeugt hat. Siehe [Mit Vera erstellen](../build-with-vera.mdx). ## Arbeiten in einem SQL-Knoten [#arbeiten-in-einem-sql-knoten] Wenn Sie doch direkt mit der Abfrage arbeiten möchten: 1. Öffnen Sie im Editor den SQL-Knoten. 2. Schreiben oder passen Sie die Abfrage an. Sie liest aus den Knoten, die mit diesem verbunden sind. 3. Führen Sie die Abfrage aus, um ihre Ausgabe und die von ihr erzeugten Spalten zu sehen. Die Ergebnisse eines SQL-Knotens speisen alle nachgeschalteten Knoten sowie Ihre Visualisierungs-Widgets. Da nachgelagerte Schritte von der Ausgabe dieses Knotens abhängen, verfügen sie erst dann über frische Daten, wenn die Pipeline erfolgreich ausgeführt wurde. Wenn die Abfrage noch nicht ausgeführt wurde, haben Knoten, die auf sie angewiesen sind, keine Daten zum Arbeiten, bis Sie auf **Ausführen** klicken. Eine Abfrage in einem SQL-Knoten auszuführen ist eine eigene Berechtigung, getrennt vom Lesen der App. Sie gehört zu den Rollen **Mitwirkender**, **Analyst**, **App-Ersteller** und **Admin** für den gesamten Workspace - eine Rolle für einen einzelnen Bereich oder eine App gibt sie nicht -, und in Ihrem eigenen persönlichen Bereich haben Sie sie immer. Ohne sie zeigt der Knoten seine Abfrage ohne Schaltfläche **Ausführen** und mit einem kurzen Hinweis, warum. Konnte die App nicht prüfen, ob Sie sie haben, sagt der Hinweis das stattdessen; laden Sie die Seite neu, um es erneut zu versuchen. ### Was die Schaltfläche Ausführen lesen kann [#was-die-schaltflaeche-ausfuehren-lesen-kann] Eine Abfrage, die Sie im Editor ausführen, liest **nur die eigenen Knoten dieser App**, direkt über ihre ID benannt - dieselbe Regel, die auch der SQL-Editor und das SQL-Widget befolgen. Eine Abfrage darf einen Knoten benennen oder eine `WITH`-Klausel, die sie selbst definiert, und sonst nichts. Ein [Indikatordaten](./indicator-data.mdx)- oder [Analyse-App-Daten](./analytic-data.mdx)-Knoten kann gelesen werden, sobald die Pipeline gelaufen ist: Die Abfrage liest die Kopie seiner Zeilen, die dieser Lauf angelegt hat. Bevor die App einen Lauf hat, gibt es diese Kopie nicht, und der Name wird abgelehnt wie jeder andere Name, hinter dem nichts steht. Eine lokale Entwicklungsumgebung bietet diese beiden Knotentypen einer Abfrage nie an und lehnt ihre Namen ab; lesen Sie einen solchen Knoten dort über einen SQL-Knoten, der aus ihm liest. Folgendes wird abgelehnt, bevor etwas ausgeführt wird, mit einer Meldung, was zu ändern ist: * ein Name mit Präfix, etwa `other_app.orders` oder `main.orders` - schreiben Sie `orders`; * ein Name, der kein Knoten dieser App ist, etwa die Tabelle einer anderen App, eine Systemtabelle wie `duckdb_tables` oder ein Dateipfad; * ein Knoten, dessen ID zugleich der Name einer Katalogtabelle der Datenbank ist, etwa `duckdb_tables`, `pg_class` oder `sqlite_master` - benennen Sie den Knoten um, um ihn zu lesen; * eine Tabellenfunktion in einer `FROM`-Klausel, etwa `read_parquet(...)`, `range(...)` oder `unnest(...)` - `unnest(...)` in der `SELECT`-Liste ist erlaubt; * `DESCRIBE`, `SHOW` oder `SUMMARIZE` als Unterabfrage, `PIVOT` und `UNPIVOT`, das Lesen einer früheren Version mit `AT (VERSION => ...)`, eine `WITH ... AS MATERIALIZED`-Klausel und der interne Zustand eines Aggregats mit `EXPORT_STATE`; * eine Funktion, die Einstellungen, Sitzungsangaben oder den Datenbankkatalog liest, als Text übergebenes SQL ausführt oder etwas verändert - zum Beispiel `current_setting(...)`, `current_database()`, `json_serialize_plan(...)` oder `nextval(...)`. Die üblichen Funktionen für Text, Zahlen, Datumswerte, Listen und JSON, Aggregate und Fensterfunktionen funktionieren alle. Eine Funktion, die hinter einem Wert steht, wie in `name.upper()`, wird ebenfalls abgelehnt - schreiben Sie `upper(name)`; * ein Typ, der zu einem Schema gehört, etwa `CAST(x AS other_app.mood)`, oder der kein eingebauter DuckDB-Typ ist; * eine Zahl, die zu groß ist, als dass die Datenbank sie darstellen könnte, etwa `1e400` - die Abfrage kann nicht geprüft werden und wird daher nicht ausgeführt. Die SQL-Schlüsselwörter, die ohne Klammern geschrieben werden - `current_user`, `current_schema`, `current_date` und ähnliche - funktionieren weiterhin, auch als Spaltennamen. Sie melden nur die eigene Verbindung dieser Abfrage, nie eine andere App. Ein Pipeline-Lauf und die Live-Vorschau eines Knotens wenden dieselbe Regel auf seine gespeicherte Abfrage an. Eine Abfrage mit einer dieser Formen lässt sich speichern, doch beim Lauf der Pipeline schlägt dieser Knoten mit derselben Meldung fehl, und die Knoten, die aus ihm lesen, werden übersprungen. Schreiben Sie sie ohne diese Formen um - etwa mit einem bedingten `sum(CASE WHEN ...)` statt eines `PIVOT` oder mit `SELECT unnest(range(10)) AS n` statt `FROM range(10)`. Um Daten einer anderen App zu verwenden, fügen Sie einen [Analyse-App-Daten](./analytic-data.mdx)-Knoten hinzu und lesen diesen Knoten über seine ID. ### Wie viel eine Ergebnisseite enthalten kann [#wie-viel-eine-ergebnisseite-enthalten-kann] Die Ergebnisse einer Abfrage werden Seite für Seite angezeigt, zusammen mit der Anzahl aller Zeilen, die die Abfrage geliefert hat. Eine Seite darf höchstens **4 MB** Daten und höchstens **262.144 Werte** enthalten. Ein Wert wird so gemessen, wie er als Text angezeigt würde; ein langer Text, eine große Liste oder ein in einen anderen Wert verschachtelter Wert zählt also vollständig. Und jedes Element einer Liste, jedes Feld eines Structs sowie jeder Schlüssel und jeder Wert einer Map zählt als eigener Wert. Eine Abfrage, deren Seite über einer der beiden Grenzen liegt, wird mit einer entsprechenden Meldung abgelehnt. Nichts wird gekürzt: Sie sehen nie einen Teil eines Werts oder nur einige der Zeilen, dargestellt, als wäre es das ganze Ergebnis. So bringen Sie die Seite unter die Grenzen: * wählen Sie weniger oder kürzere Spalten aus - für einen langen Text liefert `left(text, 1000)` seine ersten 1.000 Zeichen und `length(text)` seine Länge, und `len(liste)` die Länge einer langen Liste; * filtern Sie mit einer `WHERE`-Klausel auf weniger Zeilen. Einige Spaltentypen können überhaupt nicht als Abfrageergebnis angezeigt werden: eine `UNION`, eine `VARIANT` und ein `ARRAY` fester Größe, allein oder in einer Liste, einem Struct oder einer Map. Eine Abfrage, die eine solche Spalte liefert, wird mit einer Meldung abgelehnt, die die Spalte nennt. Wandeln Sie sie in der Abfrage um - `CAST(... AS VARCHAR)` zeigt ihren Wert als Text, und `CAST(... AS INTEGER[])` macht aus einem Array fester Größe eine Liste. Die Grenzen gelten überall, wo die Ergebnisse einer Abfrage sofort angezeigt werden: bei der Schaltfläche **Ausführen**, im SQL-Editor, in einem SQL-Widget, bei den KI-Werkzeugen, die eine App abfragen, und in der unten beschriebenen Vorschau eines SQL-Knotens. Sie begrenzen, was eine Seite enthält, nicht, wie viele Zeilen eine Abfrage liefern darf. ## Sehen, was eine Abfrage liefert, bevor Sie ausführen [#sehen-was-eine-abfrage-liefert-bevor-sie-ausführen] Sie müssen die Pipeline nicht ausführen, um zu sehen, ob eine Abfrage stimmt. Öffnen Sie den Tab **Daten** eines SQL-Knotens, wenn es keine Ausführung zu zeigen gibt, und er zeigt eine Vorschau der AKTUELLEN Abfrage des Knotens über den AKTUELLEN Inhalt der Datasets der App - sofort. Ein Streifen über den Zeilen sagt das ausdrücklich, denn dies sind keine Ausführungsergebnisse: niemand sonst sieht sie, Ihre Visualisierung verwendet sie nicht, und sie ändern sich, sobald Sie die Abfrage oder die Daten bearbeiten. Das macht die gewöhnliche Schleife kurz. Abfrage bearbeiten, Zeilen ansehen, korrigieren, wieder ansehen - und erst auf **Ausführen** klicken, wenn es stimmt. Tippen Sie einen Wert in die Tabelle eines Datasets, spiegelt die nächste Vorschau ihn bereits wider. Einiges dazu sollten Sie wissen: * **Sie ist für Personen, die die App bearbeiten dürfen.** Wer nur liest, sieht wie bisher die Ausführungsergebnisse. * **Sie zeigt die erste Seite, in der Reihenfolge der Abfrage selbst.** Sortieren, Filtern und das Suchfeld werden hier nicht angeboten - eine Vorschau, die eine Sortierung stillschweigend ignoriert, wäre schlechter als eine, die sie gar nicht anbietet. Führen Sie die Pipeline aus, um die vollständige Tabelle zu erhalten. * **Die Spalten stammen aus dem, was die Abfrage tatsächlich liefert**, nicht aus der Liste der Ausgabespalten - eine gerade umgeschriebene Abfrage zeigt ihre neuen Spalten also sofort. * **Wenn die Abfrage nicht laufen kann, sagt sie warum.** Die Meldung nennt die Relation, die sich nicht auflösen ließ; das ist meist ein Tippfehler oder ein fehlender Eintrag in `manifest.yml` (siehe unten). ### Was eine Vorschau lesen kann [#was-eine-vorschau-lesen-kann] Eine Vorschau bindet **jeden Dataset-Knoten der App**, genau wie eine Ausführung - eine Abfrage, die ein Dataset liest, funktioniert also unabhängig davon, ob `manifest.yml` es erwähnt. Die **SQL-Knoten**, die sie bindet, sind die, die Ihre `manifest.yml` deklariert, und deren Eingaben wiederum. Eine Abfrage, die die Ausgabe eines ANDEREN SQL-Knotens liest, ohne diesen Knoten in `manifest.yml` aufzuführen, lässt sich daher nicht in der Vorschau anzeigen, und die Meldung nennt ihn. Nehmen Sie ihn in `input_tables` auf, und die Vorschau funktioniert - was ohnehin sinnvoll ist, denn dieser Eintrag zeichnet auch den Pfeil auf der Arbeitsfläche und sagt der Pipeline, in welcher Reihenfolge die beiden Schritte laufen. Zwei Arten von Eingaben lassen sich überhaupt nicht in der Vorschau anzeigen, und die Meldung sagt das, statt wie eine kaputte Abfrage auszusehen: ein [Analytics-Daten-Import](./analytic-data.mdx) und eine [Indikatordaten](./indicator-data.mdx)-Brücke. Ihre Zeilen werden beim Ausführen der Pipeline geholt, es gibt also nichts Aktuelles zu lesen. Führen Sie die Pipeline aus, um einen Knoten zu sehen, der von einer solchen Eingabe abhängt. ## `manifest.yml` muss zur Abfrage passen [#manifestyml-muss-zur-abfrage-passen] In `manifest.yml` deklariert ein SQL-Knoten, welche anderen Knoten er liest. Diese Liste ist keine Formalität: Sie ist das Einzige, was die Pfeile auf der Arbeitsfläche zeichnet, und das Einzige, was einer Ausführung sagt, welcher Schritt vor diesem fertig sein muss. Eine Abfrage, die einen Knoten liest, den die Liste nicht nennt, läuft zwar - aber das Bild, das die App von sich selbst hat, ist dann falsch, und zwei Schritte, die in einer bestimmten Reihenfolge laufen müssen, dürfen in der falschen laufen. Beim Speichern eines SQL-Knotens werden die beiden daher gegeneinander geprüft. Stimmen sie nicht überein, wird das Speichern abgelehnt, und die Meldung nennt den Knoten und die Tabelle: * **Die Abfrage liest einen Knoten, den `input_tables` nicht auflistet.** Nehmen Sie ihn auf. * **`input_tables` nennt einen Knoten, den die Abfrage nie liest.** Entfernen Sie ihn, oder lesen Sie ihn in der Abfrage. * **`input_tables` nennt etwas, das kein Knoten dieser App ist.** Meist ein Tippfehler in der ID. Ein Knoten, der keinen anderen Knoten liest, braucht keine Liste; eine leere `manifest.yml` ist für ihn richtig. Wenn Sie Apps über Vera oder einen KI-Connector bauen, ist das bereits erledigt: Die Werkzeuge verlangen die Liste und kennen dieselbe Regel, sodass die Pfeile von der ersten Version an da sind und nicht erst, nachdem Sie danach fragen. ## Die Zeilen-ID eines Datasets lesen [#die-zeilen-id-eines-datasets-lesen] Die Tabelle jedes Dataset-Knotens führt hinter Ihren eigenen Spalten drei weitere: | Spalte | Bedeutung | | ------------- | ------------------------------------------------------------------------ | | `_row_id` | Die stabile ID der Zeile - dieselbe, die auch das Datenraster verwendet. | | `_created_at` | Wann die Zeile angelegt wurde. | | `_updated_at` | Wann die Zeile zuletzt geändert wurde. | Sie stehen NACH jeder vom Dataset deklarierten Spalte, in der von Ihnen deklarierten Reihenfolge, und sie können niemals mit einer Ihrer eigenen Spalten kollidieren: ein von Ihnen vergebener Spaltenname beginnt immer mit einem Buchstaben. `SELECT * FROM orders` liefert sie neben Ihren Spalten mit. Zu Ausgabespalten des Knotens werden sie aber nicht - die Liste **Ausgabespalten** eines SQL-Knotens übergeht sie, eine Abfrage mit `_row_id` speichert und läuft also genau wie eine ohne. Die Zeilen-ID erlaubt es, eine Zeile mit einer Aussage ÜBER diese Zeile zu verbinden - am nützlichsten mit den [Validierungsergebnissen](./validation.mdx). Der Validierungsknoten heißt normalerweise `validation_errors`; in einer App, in der einer Ihrer eigenen Knoten diesen Namen bereits hält, erhält er stattdessen eine nummerierte Variante - verwenden Sie also den Namen, den Ihre App tatsächlich anzeigt: ```sql title="orders_with_findings.sql" SELECT o.*, e.validation_error FROM orders o LEFT JOIN validation_errors e ON e.dataset_technical_id = 'orders' AND e.row_id = o._row_id ``` Sie stehen überall zur Verfügung, wo Sie SQL gegen die App schreiben - im Skript eines SQL-Knotens und im Ad-hoc-Abfragefeld -, dieselbe Abfrage funktioniert also an beiden Stellen. Ein Dataset ohne Zeilen besitzt die Spalten trotzdem, eine Abfrage darauf liefert also keine Zeilen statt zu scheitern. Zeilen, die vor der Einführung der Zeilen-ID hochgeladen wurden, lesen sich als leer: eine erfundene ID, die stabil aussieht, wäre schlechter als gar keine. ## Jede von der Abfrage gelieferte Spalte braucht einen brauchbaren Namen [#jede-von-der-abfrage-gelieferte-spalte-braucht-einen-brauchbaren-namen] Der Bezeichner einer Spalte stammt direkt aus der Abfrage, daher muss die Abfrage jeder Spalte einen Namen geben, der als Bezeichner taugt: Kleinbuchstaben, Ziffern und Unterstriche, beginnend mit einem Buchstaben, maximal 32 Zeichen. `order_id`, `revenue` und `region_2024` sind alle in Ordnung. Die meisten Abfragen erfüllen das bereits, denn eine ausgewählte Spalte behält ihren Namen. Was das nicht tut, ist ein Ausdruck ohne eigenen Namen. `SELECT count(*)` liefert eine Spalte, die wörtlich `count_star()` heißt, und `SELECT a + b` eine namens `(a + b)`. Vergeben Sie dafür einen Alias: ```sql SELECT count(*) AS order_count ``` Das Speichern eines Knotens, dessen Abfrage einen unbrauchbaren Namen liefert, wird abgelehnt, und die Meldung nennt die betroffene Spalte. Meist sehen Sie es schon früher: Sobald Sie die Abfrage ausführen, nennt eine Zeile über den Ergebnissen die Spalten, die einen Alias brauchen. Der Alias lohnt sich ohnehin, nicht nur wegen der Regel. Nachgelagerte Knoten und Ihre Visualisierungs-Widgets sprechen diese Spalte über den NAMEN an - `order_count` können Sie lesen und referenzieren, `count_star()` nicht. Ihn zu vergeben kostet nichts: Sie benennen damit die Spalte, die Sie ohnehin gemeint haben. # Validierungsergebnisse (/docs/de/analytical-apps/pipeline/validation) Wenn Sie an einem Dataset-Knoten Validierungen konfigurieren - „diese Spalte ist erforderlich", „diese beiden Spalten müssen zusammen eindeutig sein", „dieser Code muss in jenem anderen Dataset existieren" -, führt die Plattform sie bei jedem Pipeline-Lauf aus und veröffentlicht die Ergebnisse als zwei Knoten in Ihrer Pipeline (sie führt sie außerdem jedes Mal aus, wenn Sie die Daten ansehen - siehe [weiter unten auf dieser Seite](#im-datenraster)): | Knoten | Inhalt | | --------------------- | ----------------------------------------------------------------------------------- | | **Validation rules** | Eine Zeile pro konfigurierter Validierung, über alle Dataset-Knoten der App hinweg. | | **Validation errors** | Eine Zeile pro Dataset-Zeile, die eine Validierung verletzt. | Beide erscheinen auf der Canvas, haben einen Daten-Reiter, können von einem SQL-Knoten gelesen und von einem Visualisierungs-Widget dargestellt werden. Der Unterschied ist, dass die Plattform sie schreibt: Sie sind eine eigene Knotenart mit eigenem Symbol und werden beim Hinzufügen eines Knotens nicht angeboten. Sie öffnen sich schreibgeschützt und aktualisieren sich selbst, sobald Sie eine Validierung ändern - es gibt nichts zu pflegen und nichts kaputtzumachen. Beim Öffnen sehen Sie die von der Plattform geschriebene Abfrage und können sie mit **Ausführen** laufen lassen, genau wie bei einem SQL-Knoten. Sie erscheinen in dem Moment, in dem Ihre App ihre erste konfigurierte Validierung hat, und bleiben danach bestehen. Wenn Sie später jede Validierung entfernen, bleiben beide Knoten und liefern einfach keine Zeilen mehr, denn Ihre eigenen Abfragen lesen sie möglicherweise bereits. ## Wie die Knoten heißen [#wie-die-knoten-heißen] Normalerweise heißen sie `validation_rules` und `validation_errors`, und die Beispiele auf dieser Seite verwenden diese Namen. Garantiert sind sie allerdings nicht. Wenn einer Ihrer eigenen Knoten den Namen bereits hält, nimmt die Plattform den nächsten freien - `validation_rules_2`, `validation_errors_2` und so weiter -, statt Ihren Knoten anzutasten. Ihr vorhandener Knoten behält Namen und Inhalt; das erzeugte Paar steht einfach daneben. Wenn Sie eine Abfrage gegen diese Knoten schreiben, prüfen Sie den Namen zuerst auf der Canvas und verwenden Sie den, den Ihre App tatsächlich hat. ## Validation errors [#validation-errors] | Spalte | Bedeutung | | ---------------------- | ---------------------------------------------------------------------------- | | `dataset_technical_id` | Der Dataset-Knoten, zu dem die fehlerhafte Zeile gehört. | | `row_id` | Die stabile ID dieser Zeile. | | `column_technical_id` | Die Spalte, bei einer Regel pro Spalte. Leer bei einer Bedingungsbaum-Regel. | | `rule_id` | Verbindet mit `validation_rules.rule_id`. | | `check_type` | Die Prüfung, die fehlgeschlagen ist. | | `validation_error` | Die Meldung, die den Fehler beschreibt. | `row_id` ist der eigentliche Zweck der Tabelle. Es ist dieselbe stabile Zeilen-ID, die auch das Datenraster verwendet, und die Tabelle jedes Dataset-Knotens führt sie als `_row_id` (siehe [SQL-Knoten](./sql-nodes.mdx#die-zeilen-id-eines-datasets-lesen)), sodass Sie einen Befund mit der zugehörigen Zeile verbinden können - mit dem Namen, den Ihre App dem Knoten tatsächlich gegeben hat, falls es nicht der übliche ist: ```sql title="orders_with_findings.sql" SELECT o.*, e.validation_error FROM orders o LEFT JOIN validation_errors e ON e.dataset_technical_id = 'orders' AND e.row_id = o._row_id ``` Eine Zeile ohne passenden Befund ist eine vertrauenswürdige Zeile. Eine Zeile mit mehreren verletzten Regeln erscheint einmal pro Regel, sodass eine Zeile mit zwei Verstößen zwei Zeilen ergibt, unterschieden durch `rule_id`. ### Einen SQL-Knoten auf die Befunde stützen [#einen-sql-knoten-auf-die-befunde-stützen] Am nützlichsten bauen Sie daraus einen Knoten, der nur die Zeilen führt, zu denen es keinen Befund gibt - alles Nachgelagerte arbeitet dann auf bereits geprüften Daten: ```sql title="trusted_orders.sql" SELECT o.* FROM orders o WHERE NOT EXISTS ( SELECT 1 FROM validation_errors e WHERE e.dataset_technical_id = 'orders' AND e.row_id = o._row_id ) ``` **Deklarieren Sie `validation_errors` als Eingabetabelle**, neben dem Dataset: ```yaml input_tables: - orders - validation_errors ``` Die Validierungsknoten laufen immer zuerst, vor jedem Knoten, den Sie selbst schreiben. Eine Abfrage, die sie liest, sieht also stets die Befunde desselben Laufs - unabhängig davon, ob Sie sie deklarieren. Die Deklaration bringt den Pfeil auf der Arbeitsfläche: ohne sie liefert der Knoten weiterhin das richtige Ergebnis, aber im Diagramm ist nicht zu sehen, dass er von den Validierungsergebnissen abhängt - und wer Ihre Pipeline als Nächstes liest, erkennt den Zusammenhang nicht. Um auf eine bestimmte Prüfung statt auf jeden Befund zu stützen, ergänzen Sie `AND e.check_type = 'required'` (oder verbinden Sie `validation_rules` und filtern dort). ## Im Datenraster [#im-datenraster] Sie müssen den Knoten nicht abfragen, um zu sehen, was fehlgeschlagen ist. Öffnen Sie den Tab **Daten** eines Dataset-Knotens: Dort sind die fehlerhaften Zellen markiert, mit der Begründung beim Überfahren mit der Maus - eine kräftige Markierung auf der Zelle, die eine Regel pro Spalte benannt hat, eine schwächere über allen Zellen einer Zeile, die ein Bedingungsbaum zurückgewiesen hat. Beim Überfahren einer markierten Zelle erscheint die Begründung und nur sie - der übliche Hinweis, der den Zellinhalt wiederholt, tritt dafür zurück. **Die Markierungen kommen zusammen mit den Zeilen.** Das Raster fordert eine Seite mit Daten an und erhält in derselben Antwort die Befunde zu genau diesen Zeilen - geprüft an den Zeilen, die es Ihnen gleich zeigt. Es gibt keinen Lauf zu nennen und kein „geprüft am" zu berücksichtigen, denn Zeilen und Markierungen stammen aus ein und derselben Leseoperation. Daraus folgt: * **Nichts ist veraltet, und nichts wird entfernt.** Speichern Sie eine Änderung im Raster oder schließen Sie einen Import ab, werden die betroffenen Zeilen beim nächsten Lesen erneut geprüft: Eine behobene Markierung ist weg, eine soeben verletzte Regel erscheint, und alle anderen Zeilen behalten ihre eigenen Markierungen. Sie müssen dafür die Pipeline nicht ausführen. * **Eine Zeile, die jemand anderes hinzugefügt oder geändert hat**, wird beim nächsten Lesen genauso geprüft. * **Jede fehlerhafte Zeile vor Ihnen ist markiert**, bei jeder Datasetgröße. Die Befunde einer Zeile reisen mit der Zeile, ein Block ist also markiert, sobald er erscheint - es gibt keinen festen ersten Ausschnitt, keinen halb markierten Bildschirm und nichts, was verspätet nachkommt. * **Die Zeile über dem Raster beschreibt das ganze Dataset**, nicht die Seite: entweder keine Befunde oder wie viele Zeilen etwas verletzen. Diese Anzahl ändert sich nicht, wenn Sie blättern, suchen oder filtern. Neben der Anzahl steht **Nur fehlerhafte Zeilen anzeigen**: Damit schränken Sie das Raster auf die Zeilen ein, die mindestens eine Prüfung verletzen - und wieder zurück. Ein Dataset ohne eigene Validierungen zeigt gar keine Zeile, auch wenn andere Datasets derselben App viele haben. **Auch die Datenansicht eines Laufs und die einer Version sind markiert**, jeweils mit ihrer eigenen Antwort. Ein Lauf zeigt genau das, was dieser Lauf gefunden hat. Eine Version wird mit den Validierungen geprüft, wie sie bei ihrer Veröffentlichung konfiguriert waren, und mit den Daten von damals - eine historische Ansicht wird also nie nach den heutigen Regeln neu beurteilt. Wenn die Prüfungen überhaupt nicht laufen können - etwa weil die Plattform eine der Tabellen, die eine Regel liest, nicht auflösen kann -, sagt die Zeile, dass die Validierungen nicht geprüft werden konnten, und es wird nichts markiert. **Ihre Daten sind davon nicht betroffen**: Jede Zeile ist da, in der richtigen Reihenfolge, mit der richtigen Gesamtzahl. Eine fehlgeschlagene Validierung kostet Sie die Markierungen, nie die Daten. ## Auf einer Visualisierungsseite [#auf-einer-visualisierungsseite] Dieselben Markierungen erscheinen in einem **Tabellen-Widget**, das auf einem Dataset beruht. So sehen die Personen, die die App lesen statt sie zu bauen, was die Validierung gefunden hat, ohne die Pipeline zu öffnen. Die Begründung erscheint auch dort beim Überfahren mit der Maus, und die Markierungen sind an genau den Zeilen geprüft, die die Tabelle zeigt. Drei Unterschiede zum Tab **Daten**, alle bewusst: * **Die Zeile über der Tabelle erscheint nur, wenn es etwas zu warnen gibt**: dass Befunde vorliegen oder dass die Prüfungen nicht laufen konnten. Ein sauberes Dataset wird einfach ohne Streifen gezeigt, damit aus einer Seite voller Tabellen keine Seite voller Hinweise wird. * **Eine gruppierte oder zusammengefasste Tabelle wird nicht markiert.** Ihre Zeilen sind Summen über viele Ursprungszeilen, nicht die Zeilen, die eine Regel benannt hat. Ebenso wenig eine Tabelle über einer SQL-Transformation: Ein Befund gehört zu einer Dataset-Zeile, und die Ausgabezeilen einer Transformation sind keine Dataset-Zeilen. * **Es gibt kein „Nur fehlerhafte Zeilen anzeigen".** Das ist ein Werkzeug der Autorinnen und Autoren und bleibt im Tab **Daten**; eine Seite gestaltet, wer sie gebaut hat. ### Solange ein Entwurf offen ist, wird die veröffentlichte App so geprüft, wie sie veröffentlicht wurde [#markierungen-und-entwuerfe] Ein offener Entwurf lässt jede Dataset-Tabelle der veröffentlichten App die **veröffentlichte Momentaufnahme** der Daten lesen statt der aktuellen Zeilen, damit eine halbfertige Änderung von Ihnen nicht zu den Lesenden durchsickert (siehe [Entwurf und Veröffentlichung](../../overview/apps.mdx#entwurf-und-veroeffentlichung)). Die Markierungen folgen dem genau: Die Tabelle wird mit den Validierungen der Version geprüft, die sie zeigt, und über der Momentaufnahme, die sie zeigt. Ihr Team sieht also, was die veröffentlichte App über die veröffentlichten Daten sagt, während der Tab **Daten** im Editor weiterhin die aktuellen Zeilen gegen die Regeln prüft, die Sie gerade bearbeiten. Keine der beiden Ansichten übernimmt die Antwort der anderen, und keine muss auf einen Lauf warten. ## Validation rules [#validation-rules] | Spalte | Bedeutung | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `rule_id` | Die Identität der Regel innerhalb dieses Laufs - `col:..` bei einer Regel pro Spalte, wobei `n` ihre Position in der Prüfungsliste dieser Spalte ist, und `tree:.` bei einem Bedingungsbaum, wobei `n` ihre Position in der Liste dieses Datasets ist. | | `dataset_technical_id` | Der Dataset-Knoten, an dem die Regel konfiguriert ist. | | `column_technical_id` | Die Spalte, bei einer Regel pro Spalte. Leer bei einer Bedingungsbaum-Regel. | | `rule_kind` | `column`, `complex` oder `overflow`. | | `check_type` | Der Operator bei einer Regel pro Spalte, oder die oberste Gruppe eines Bedingungsbaums. | | `parameter` | Der Wert, gegen den geprüft wird, sofern die Prüfung einen benötigt. | | `error_message` | Die Meldung, die die Regel bei Verletzung ausgibt. | | `compilable` | Ob die Regel tatsächlich ausgeführt wird. | | `skip_reason` | Leer, wenn die Regel läuft, sonst der Grund, warum nicht. | Diese Tabelle ist keine Dekoration: **Validation errors** liest `check_type` und `error_message` aus ihr. Wenn Sie also die Meldung einer Bedingungsbaum-Regel ändern, ändert sich damit, was Ihre Befunde sagen, ohne dass sich sonst etwas ändert. ## Was die einzelnen Prüfungen bedeuten [#was-die-einzelnen-prüfungen-bedeuten] Für alle Prüfungen gilt eine gemeinsame Regel, die vor der Tabelle zu lesen ist: > **Ein leerer Wert besteht jede Prüfung außer `Pflichtfeld`.** Ein leerer Wert ist eine leere Zelle oder eine, die nur Leerzeichen enthält. Ohne diese Regel würde eine optionale Spalte mit einer Musterprüfung jede leere Zelle als Fehler melden, was nicht bedeutet, was „optional" bedeutet. Wenn leere Werte für eine Spalte wichtig sind, fügen Sie `Pflichtfeld` neben der anderen Prüfung hinzu. | Prüfung | Verletzt, wenn | | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | Pflichtfeld | Die Zelle leer ist. Was „leer" je Spaltentyp bedeutet, steht in der Tabelle weiter unten. Dies ist die einzige Prüfung, die ein leerer Wert verletzt. | | Eindeutig | Eine andere Zeile denselben Wert hält. Jede Kopie eines Duplikats wird gemeldet, nicht nur die späteren. Leere Zellen sind nie Duplikate voneinander. | | Gleich | Der Wert nicht gleich dem angegebenen ist. | | Größer als | Der Wert nicht größer als der angegebene ist. | | Kleiner als | Der Wert nicht kleiner als der angegebene ist. | | Größer gleich | Der Wert unter dem angegebenen liegt. | | Kleiner gleich | Der Wert über dem angegebenen liegt. | | Entspricht Muster (`like`) | Der Text dem Muster nicht entspricht. `%` steht für eine beliebige Zeichenfolge, `_` für ein Zeichen. | | Entspricht Muster (Groß-/Kleinschreibung egal) (`ilike`) | Dasselbe, ohne Beachtung von Groß- und Kleinschreibung. | | Beginnt mit | Der Text nicht mit dem angegebenen Wert beginnt. | | Endet mit | Der Text nicht mit dem angegebenen Wert endet. | | Entspricht Ausdruck (`regex`) | Der Text dem regulären Ausdruck nicht entspricht. | Zwei Folgerungen ergeben sich daraus, beide beabsichtigt: * **Ein Wert, der sich nicht vergleichen lässt, verletzt einen Vergleich.** Trägt eine Spalte mit dem Wert `abc` die Prüfung „größer als 0", wird diese Zeile gemeldet. Der Wert lässt sich nicht vergleichen, und ein nicht vergleichbarer Wert ist nicht größer als null. * **Eine Prüfung, deren Antwort unbekannt ist, zählt als Verletzung.** Die Plattform liest „unbekannt" nie als „in Ordnung". ### Welche Prüfungen eine Spalte anbietet [#welche-prüfungen-eine-spalte-anbietet] Welche Prüfungen eine Spalte anbietet, folgt daraus, was diese Spalte enthält - ein Datum wird also mit einem Datum verglichen und ein Ja/Nein mit einem Ja/Nein: | Spaltentyp | Pflichtfeld | Eindeutig | Gleich | Größer / kleiner | Die fünf Textmuster | | --------------- | ----------- | --------- | ------ | ---------------- | ------------------- | | Text | ja | ja | nein | nein | ja | | Langtext | ja | ja | nein | nein | ja | | Telefon | ja | ja | nein | nein | ja | | E-Mail | ja | ja | nein | nein | ja | | Ganzzahl | ja | ja | ja | ja | nein | | Dezimalzahl | ja | ja | ja | ja | nein | | Prozent | ja | ja | ja | ja | nein | | Währung | ja | ja | ja | ja | nein | | Jahr | ja | ja | ja | ja | nein | | Datum | ja | ja | ja | ja | nein | | Uhrzeit | ja | ja | ja | ja | nein | | Zeitstempel | ja | ja | ja | ja | nein | | Boolean | ja | nein | ja | nein | nein | | Dropdown | ja | ja | ja | nein | nein | | Mehrfachauswahl | ja | nein | nein | nein | nein | | Anhang | ja | nein | nein | nein | nein | Die Regel hinter der Tabelle: **Pflichtfeld** wird überall angeboten; **Eindeutig** überall dort, wo eine Zelle einen Wert hält, der eine Zeile identifizieren könnte (also nicht bei Boolean mit seinen zwei Werten, nicht bei Mehrfachauswahl und nicht bei Anhang); **Gleich** überall dort, wo es einen exakten Vergleichswert gibt; die geordneten Vergleiche überall dort, wo Werte eine Ordnung haben; und die fünf Textmuster bei den vier freien Texttypen. Bei einer Textspalte IST **Entspricht Muster** ohne `%` bereits ein exakter Vergleich - deshalb wird Gleich dort nicht als zweite Schreibweise derselben Prüfung angeboten. **Eine früher konfigurierte Prüfung wird nie entfernt.** Wenn Sie den Typ einer Spalte nach dem Konfigurieren ihrer Prüfungen ändern, bleiben die Prüfungen, laufen weiter und werden weiterhin im Editor angezeigt - sie werden lediglich nicht mehr zum Hinzufügen angeboten. ### Was als Wert einzutragen ist [#was-als-wert-einzutragen-ist] Der Vergleichswert muss ein Wert sein, den die Spalte selbst halten könnte: | Spaltentyp | Das eintragen | Beispiel | | --------------------------------------- | ----------------------------- | --------------------- | | Ganzzahl, Dezimalzahl, Prozent, Währung | eine Zahl | `1500`, `-3.5` | | Jahr | vier Ziffern | `2026` | | Datum | `YYYY-MM-DD` | `2026-01-01` | | Uhrzeit | `HH:mm` oder `HH:mm:ss` | `12:30` | | Zeitstempel | `YYYY-MM-DDTHH:mm:ss` | `2026-01-01T08:00:00` | | Boolean | aus den zwei Optionen gewählt | `Wahr` | | Dropdown | der exakte Text der Option | `EU` | | Text, Langtext, Telefon, E-Mail | beliebiger Text (das Muster) | `A%` | Ein Wert außerhalb dieses Formats führt dazu, dass die Regel **übersprungen** statt geraten wird: Sie erscheint in **Validation rules** mit `compilable` false und `skip_reason` `operand_type_mismatch`, und alle anderen Regeln laufen weiter. Der Editor markiert das Feld schon beim Tippen, Sie sehen es also in der Regel vor dem Speichern. Zwei Details, die man kennen sollte: * Der Wert wird normalisiert gespeichert, und genau das meldet **Validation rules** in `parameter` zurück: `12:30` wird zu `12:30:00`, `2026-01-01T08:00:00` wird zu `2026-01-01 08:00:00`, und `JA` wird zu `true`. * Dezimalzahl, Prozent und Währung werden als Gleitkommazahlen verglichen. Ein Wert mit mehr Präzision, als sich damit darstellen lässt, ist möglicherweise nicht exakt gleich - **Gleich** auf einer Geldspalte ist deshalb nur für runde Beträge zu empfehlen. ### Was „Pflichtfeld" je Spaltentyp bedeutet [#was-pflichtfeld-je-spaltentyp-bedeutet] Pflichtfeld ist eine einzige Prüfung - „die Zelle ist nicht leer" - und was leer bedeutet, folgt daraus, was die Spalte speichert: | Spaltentyp | Pflichtfeld ist verletzt, wenn | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Mehrfachauswahl | Keine Option ausgewählt ist. Eine Mehrfachauswahl-Zelle speichert ihre Optionen als einen Text, ohne Auswahl also gar nichts - „mindestens eine Option ausgewählt" ist hier genau die Bedeutung von Pflichtfeld. | | Boolean | Die Zelle leer ist. `false` ist ein WERT und ist erfüllt: Um das Häkchen zu erzwingen, nutzen Sie **Gleich** `Wahr`. | | Anhang | Die Zelle keinen Anhang enthält. | | Dropdown | Keine Option gewählt ist. | | Alles andere | Die Zelle leer ist oder nur Leerzeichen enthält. | Eine Zelle, die in einer Anhang-Spalte den Text `[]` enthält, gilt NICHT als leer. Die Leer-Regel ist eine Regel für jeden Spaltentyp, und eine eigene für Anhänge wäre eine zweite Regel, die man mit ihr im Gleichklang halten müsste. ### Bedingungsbäume [#bedingungsbäume] Eine komplexe Validierung kombiniert Prüfungen zu einem Baum; die Gruppen bedeuten: | Gruppe | Verletzt, wenn | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | AND | Eine der untergeordneten Prüfungen verletzt ist. | | OR | Jede untergeordnete Prüfung verletzt ist. | | NOT | Die untergeordnete Prüfung erfüllt ist. | | UNIQUE | Eine andere Zeile dieselbe Kombination der aufgeführten Spalten hält. Eine Zeile, in der IRGENDEINE dieser Spalten leer ist, ist ausgenommen - ein Teilschlüssel ist kein Schlüssel. | | REFERENCE | Der Wert der Zeile in der Spalte des Ziel-Datasets nicht existiert. Ein leerer Quellwert ist ausgenommen. Der Vergleich ignoriert umgebende Leerzeichen, `" 1"` passt also zu `"1"`. | Eine Prüfung innerhalb eines Baums verhält sich genau wie die Variante pro Spalte, einschließlich der Leer-Regel - mit einer Folge, die man kennen sollte: > `NOT (Spalte entspricht "x%")` **wird bei einer leeren Spalte verletzt**, weil die > innere Prüfung bei einem leeren Wert erfüllt ist und `NOT` sie umkehrt. Wenn das nicht gewünscht ist, setzen Sie die Prüfung in ein `AND` neben `Pflichtfeld`. ## Wenn eine Regel nicht laufen kann [#wenn-eine-regel-nicht-laufen-kann] Eine Regel, die die Plattform nicht in eine korrekte Prüfung übersetzen kann, wird nie als fehlerhafte Prüfung ausgeführt. Sie wird übersprungen, und das Überspringen ist sichtbar: Die Regel bleibt eine Zeile in **Validation rules**, mit `compilable` false und einem `skip_reason`. Alle anderen Regeln laufen weiter, und der Pipeline-Lauf selbst ist nicht betroffen. Finden Sie sie mit einem SQL-Knoten, einem Visualisierungs-Widget oder einer Ad-hoc-Abfrage: ```sql title="skipped_rules.sql" SELECT rule_id, dataset_technical_id, column_technical_id, check_type, skip_reason FROM validation_rules WHERE NOT compilable ``` | `skip_reason` | Was zu tun ist | | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `operand_missing` | Die Prüfung braucht einen Vergleichswert, es wurde keiner angegeben. Tragen Sie ihn nach. | | `operand_not_numeric` | Eine Prüfung auf einer Zahlenspalte erhielt etwas, das keine Zahl ist. Korrigieren Sie den Wert. | | `operand_type_mismatch` | Der Wert ist keiner, den der Typ der Spalte halten könnte - ein Datum, das nicht `YYYY-MM-DD` ist, ein Jahr ohne vier Ziffern, eine Uhrzeit außerhalb von 24 Stunden, ein Ja/Nein, das keines ist. Siehe „Was als Wert einzutragen ist" weiter oben. | | `column_not_found` | Die Regel nennt eine Spalte, die das Dataset nicht mehr hat. Richten Sie sie auf eine vorhandene Spalte aus oder entfernen Sie sie. | | `reference_target_not_found` | Das Dataset, auf das die Referenz zeigt, existiert nicht mehr. | | `reference_target_not_dataset` | Eine Referenz muss auf einen Dataset-Knoten zeigen, nicht auf einen SQL-Knoten oder einen Import. | | `reference_column_not_found` | Die Referenz nennt eine Spalte, die das Ziel-Dataset nicht hat. | | `tree_incomplete` | Eine Gruppe hat keine Kinder, ein `NOT` hat mehr als eines, ein `UNIQUE` führt keine Spalten auf, oder ein `REFERENCE` bildet keine Spalten ab. Vervollständigen Sie die Regel. | | `rule_budget_exceeded` | Siehe unten. | ### Die Overflow-Zeile [#die-overflow-zeile] Es gibt eine Obergrenze dafür, wie viele Validierungen die erzeugten Knoten einer App abbilden können. Eine App, die sie überschreitet, erhält in **Validation rules** eine zusätzliche Zeile mit der `rule_id` `overflow`, deren `parameter` angibt, wie viele konfigurierte Validierungen nicht abgebildet sind, und deren Meldung das erklärt. Die nicht abgebildeten Validierungen fehlen schlicht; Ihr Dataset wird trotzdem normal gespeichert. Wenn Sie das sehen, besteht die Lösung darin, weniger und aussagekräftigere Validierungen zu führen - oder die Datasets auf mehrere Apps aufzuteilen. ## Was dies nicht tut [#was-dies-nicht-tut] * **Eine verletzte Validierung blockiert nie einen Schreibvorgang.** Validierung ist beobachtend: Ein Lauf meldet, was nicht stimmt, er weist Ihre Daten nie zurück. Sie entscheiden, was zu tun ist. * **Es gibt keine Warnstufe.** Jede konfigurierte Validierung ist ein Fehler, wenn sie verletzt wird. * **Die gespeicherten Meldungen sind auf Englisch.** Sie gehören zu einer gemeinsamen Tabelle, die im Moment ihrer Erstellung keine Lesersprache kennt. Die strukturierten Spalten daneben (`check_type`, `parameter`, `column_technical_id`) sind das, woraus eine übersetzte Ansicht rendert - und genau das tut das Datenraster: Seine Zellbegründungen sind für jede Prüfung pro Spalte in Ihrer Sprache und bei einem Bedingungsbaum die Meldung, die Sie selbst geschrieben haben. # Skills & das KI-Widget (/docs/de/analytical-apps/skills) Ein **Skill** ist eine wiederverwendbare, app-spezifische KI-Aufgabe, die Sie einmal erstellen und anderen als Aktion mit einem Klick anbieten. Das Musterbeispiel ist eine Emissionsfaktor-App: Ein Nutzer gibt eine Materialbeschreibung ein, und der Skill durchläuft die Faktor-Taxonomie und liefert den passendsten Faktor mit Wert und Quelle. Andere Apps haben andere Skills. Skills sind eigenständige Bestandteile Ihrer App, verwaltet auf dem Tab **Skills** des Bereichs [Wissensdatenbank](./knowledge-base.mdx) und gemeinsam mit allem anderen in der App versioniert, als Entwurf bearbeitet und veröffentlicht. Eine Definition erreicht zwei Zielgruppen: * **Endnutzer in der App** führen einen Skill über ein **KI-Widget** auf der Visualisierungs-Leinwand aus und lesen die Antwort direkt. * **Externe KI-Clients** (Ihre eigene, mit Ihrem Workspace verbundene KI) können denselben Skill als **MCP-Prompt** aufrufen. ## Woraus ein Skill besteht [#woraus-ein-skill-besteht] Ein Skill trägt zusätzlich zu seinem Markdown-Inhalt etwas strukturierte Metadaten: * **Name** - der Anzeigename, der als Widget-Titel und in Auswahllisten erscheint. * **Technische ID** - eine stabile ID in Kleinbuchstaben (zum Beispiel `find_emission_factor`). Sie ist nach dem Erstellen fest, da sowohl das Widget als auch der MCP-Prompt den Skill darüber referenzieren. Der Anzeigename bleibt änderbar; um die technische ID selbst zu ändern, löschen Sie den Skill und erstellen einen neuen. * **Beschreibung** - eine einzeilige Zusammenfassung, die unter dem Widget und externen KI-Clients angezeigt wird. * **Eingaben** - die Fragen, die der Skill dem Nutzer stellt. Jede Eingabe hat eine Beschriftung (was der Nutzer sieht), einen Argumentschlüssel (eine ID in Kleinbuchstaben), einen optionalen Hilfetext und eine Erforderlich-Markierung. Ein Skill kann keine Eingaben haben (nur eine Ausführen-Schaltfläche) oder mehrere. * **Als MCP-Prompt verfügbar machen** - standardmäßig aktiviert; deaktivieren Sie es, um einen Skill nur in der App zu halten. * **Anweisungen** - der Inhalt, in klarer Sprache verfasst, im selben Rich-Text-Editor wie Wissensdatenbank-Seiten und als Markdown gespeichert. Das ist die Aufgabe, die Sie der KI geben ("Sie helfen dem Nutzer, ein Material einem Emissionsfaktor zuzuordnen. Filtern Sie nach Bereich, dann nach Aktivität, und geben Sie den einen besten Treffer mit Faktorwert und Quelle zurück."). ## Einen Skill erstellen [#einen-skill-erstellen] Öffnen Sie Ihre analytische App im Editor, öffnen Sie den Bereich **Wissensdatenbank** und wechseln Sie auf dessen Tab **Skills**. Die linke Leiste listet die Skills der App; rechts befindet sich der Editor. 1. Klicken Sie auf die Hinzufügen-Schaltfläche, um einen neuen Skill zu beginnen. 2. Geben Sie einen **Namen** ein. Die technische ID wird aus dem Namen vorgeschlagen; Sie können sie beim Erstellen anpassen, danach ist sie fest. 3. Schreiben Sie eine kurze **Beschreibung**. 4. Fügen Sie die benötigten **Eingaben** hinzu, je eine Zeile. Markieren Sie die Pflichteingaben als erforderlich. 5. Entscheiden Sie, ob der Skill **als MCP-Prompt verfügbar** sein soll. 6. Schreiben Sie die **Anweisungen** im Inhaltseditor. 7. Speichern Sie. Skills sind Teil Ihrer App-Konfiguration und werden daher auf einem Entwurf bearbeitet. Sie werden für Endnutzer und externe KI-Clients verfügbar, sobald Sie die App **veröffentlichen**. Wenn Sie im Verlauf der App eine frühere Version anheften, zeigt der Tab Skills die Skills dieser Version schreibgeschützt. Siehe [Entwurf und Veröffentlichung](../overview/apps.mdx#entwurf-und-veroeffentlichung). ## Ein KI-Widget hinzufügen [#ein-ki-widget-hinzufügen] Ein KI-Widget lässt Endnutzer einen Skill aus einer Visualisierung heraus ausführen. 1. Öffnen Sie den Bereich **Visualisierung** und fügen Sie aus der Palette ein **KI**-Widget hinzu. 2. Wählen Sie in den Widget-Einstellungen den **Skill**, den es ausführen soll. Der Widget-Titel entspricht standardmäßig dem Namen des Skills; Sie können ihn überschreiben. 3. Veröffentlichen Sie die App, damit Ihre Betrachter das Widget sehen. Auf der veröffentlichten Visualisierung zeigt das Widget ein Feld pro Skill-Eingabe und eine **Ausführen**-Schaltfläche. Eine kurze Zeile unter der Schaltfläche nennt dem Betrachter, wie lange ein Lauf dauern kann: Ein Skill ist auf **etwa 30 Schritte und etwa 150 Sekunden begrenzt**, sodass er stets zügig abschließt, statt sich festzulaufen. Die Ausführung des Widgets ist **schreibgeschützt**. Vera liest bestehende Konfiguration und fragt veröffentlichte Daten ab. Sie kann keine Daten bearbeiten, Pipeline oder Wissensdatenbank ändern, veröffentlichen oder einen Entwurf erstellen, auch wenn die Person Bearbeitungsrechte hat. Eine Skill-Vorschau liest einen bestehenden Entwurf, ohne ihn zu ändern. Erfordert die Aufgabe eine Änderung, erklärt Vera kurz, dass das Widget diese nicht vornehmen kann. Widget-Antworten sind vom persönlichen Gespräch im Vera-Bereich des Editors getrennt. ### Was der Betrachter während des Laufs sieht [#was-der-betrachter-während-des-laufs-sieht] Während der Skill läuft, bleibt das Widget transparent. Es zeigt: * die **aktuelle Aktion**, die Vera ausführt (zum Beispiel „Daten abfragen" oder „Spalten des Knotens prüfen"), * einen **hochzählenden Timer** ab dem Start und * eine **Live-Schrittspur** - die Abfolge der bisher ausgeführten Aktionen, einschließlich der SQL-Abfragen, die Vera auf Ihren Daten ausführt. So sieht ein Betrachter auf einen Blick, was gerade passiert und wie weit der Lauf ungefähr fortgeschritten ist. ### Das Ergebnis [#das-ergebnis] Ist der Lauf abgeschlossen, führt das Widget mit **der Antwort** an, formatiert dargestellt. Unter der Antwort blendet ein einklappbarer Abschnitt **„Wie Vera darauf gekommen ist"** die vollständige Schrittspur sowie eine dezente Zeile mit den verbrauchten KI-Credits ein, sodass ein Betrachter nachvollziehen kann, wie die Antwort zustande kam. Das Widget macht jedes Ergebnis unterscheidbar: * **Antwort bereit** - die Antwort wird gezeigt, die Schrittspur „Wie Vera darauf gekommen ist" ist verfügbar und eine Aktion **Erneut ausführen** steht bereit. * **Keine Antwort** - der Lauf wurde abgeschlossen, ohne dass Vera ein Ergebnis gefunden hat. Das Widget sagt dies klar und bietet **Erneut versuchen**. Es weicht nie auf themenfremden Text aus. * **Schrittlimit erreicht** - der Lauf hat sein Schritt- oder Zeitlimit erreicht, bevor er fertig war. Das Widget zeigt das Teilergebnis, weist darauf hin, dass es abgeschnitten wurde, und bietet **Erneut versuchen** (stellen Sie eine gezieltere Anfrage). * **Fehler** - etwas ist schiefgelaufen; die Meldung wird mit **Erneut versuchen** angezeigt. Benötigt der Assistent weitere Angaben, stellt er direkt im Widget eine Rückfrage, die die Person beantwortet, die den Lauf gestartet hat, um fortzufahren. Das Ausführen eines Skills nutzt KI, daher benötigt die ausführende Person KI-Zugriff auf die App. Jede Rolle außer **Betrachter** hat ihn. Wer die Rolle **Betrachter** hat, sieht die Eingaben des Widgets deaktiviert und einen Hinweis, dass zum Ausführen KI-Zugriff nötig ist, ohne Schaltfläche **Ausführen**. Konnte der Zugriff nicht geprüft werden (etwa bei einem Verbindungsproblem), sagt der Hinweis genau das. Eine Administratorin oder ein Administrator kann die KI einer Person außerdem in ihren **KI-Berechtigungen** abschalten; das Widget erklärt dann, dass KI für sie nicht aktiviert ist. ## Skills als MCP-Prompts [#skills-als-mcp-prompts] Jeder veröffentlichte Skill, bei dem **Als MCP-Prompt verfügbar machen** aktiviert ist, steht Ihrer eigenen KI über die MCP-Verbindung Ihres Workspace als Prompt mit dem Namen `{app_technical_id}.{skill_technical_id}` zur Verfügung (zum Beispiel `emission_factor_hub.find_emission_factor`). Die Eingaben des Skills werden zu den Argumenten des Prompts. Ihr KI-Client ruft den Prompt ab, füllt die Argumente aus und führt ihn gegen die Werkzeuge aus, die Ihr Workspace bereitstellt. Skills, die sich noch im Entwurf befinden oder bei denen die Option deaktiviert ist, werden nicht als Prompts angeboten. Die Anweisungen eines Skills sind Inhalte Ihres eigenen Workspace, daher gelten für die Prompts dieselben KI-Berechtigungen wie für alles andere: Die Verbindung benötigt **Skills auflisten**, um sie zu sehen, und **Skill abrufen**, um einen davon zu laden. Beide gehören zu den üblichen Leseberechtigungen. Meldet eine verbundene KI keine Prompts, obwohl Sie veröffentlichte Skills mit aktivierter Option haben, fehlen dieser Verbindung genau diese beiden - eine Workspace-Administratorin oder ein Workspace-Administrator kann sie in den **KI-Berechtigungen** dieser Person ergänzen, und die Prompts erscheinen bei der nächsten Anfrage, ohne die Verbindung neu aufzubauen. ## Eingebaute Prompts [#eingebaute-prompts] Über Ihre eigenen Skills hinaus bietet die MCP-Verbindung Ihres Workspace auch fünf vorgefertigte Prompts für gängige Aufgaben, verfügbar in jedem Workspace: `create-node` (einen Pipeline-Knoten im Entwurf einer App anlegen), `run-pipeline` (die Pipeline einer App ausführen und das Ergebnis melden), `query-data` (eine Datenfrage zu den Daten einer App mit SQL beantworten), `collect-indicator-values` (fehlende Indikatorwerte für eine offene Berichtsperiode Schritt für Schritt erfassen) und `report-status` (den Erfassungsstand einer Berichtsperiode für eine Indikator-App zusammenfassen). Jeder fragt nach allem, was er benötigt, etwa nach der App, falls Sie es nicht schon angegeben haben. Jeder dieser Prompts benötigt eigene KI-Berechtigungen, um zu erscheinen, genau wie ein Werkzeug: Ihr KI-Client sieht nur die, die Ihre wirksamen Berechtigungen abdecken, unabhängig davon, ob Sie eigene Skills veröffentlicht haben. Siehe [Eigene KI verbinden](../integrations/mcp.mdx) dazu, wie Sie einen externen KI-Client mit Ihrem Workspace verbinden. # Visualisierung (/docs/de/analytical-apps/visualisation) Die **Visualisierung** ist der Ort, an dem Ihr Team mit einer fertigen Analyse-App arbeitet. Sie ist aus **Widgets** auf einem Raster aufgebaut, Tabellen, Diagramme, Text und Eingaben, die die Ergebnisse Ihrer Pipeline in etwas verwandeln, das Menschen tatsächlich nutzen können. Sie ist mehr als ein Bericht zum Lesen: Aus der Visualisierung heraus kann Ihr Team die App ausführen, die neuesten Ausführungsergebnisse sehen, die Daten filtern, Werte in eine Tabelle eintippen und herunterladen, was benötigt wird. Eine App kann mehr als eine Seite mit Widgets enthalten, sodass eine einzelne App mehrere verwandte Themen abdecken kann. ## Wie die Visualisierung aufgebaut wird [#wie-die-visualisierung-aufgebaut-wird] Sie bauen sie im Editor auf dem Tab **Visualisierung** auf. Fügen Sie Widgets hinzu, ordnen Sie sie durch Ziehen und Größenanpassung auf dem Raster an und geben Sie jedem Daten-Widget eine **Datenquelle**: den Pipeline-Schritt (Knoten genannt), dessen Ergebnisse es zeigen soll. Ein Tabellen-, Diagramm-, Pivot- oder Kennzahl-Widget kann jeden Knoten lesen, der Daten enthält, ebenso ein Parameter, dessen Dropdown-Optionen aus einem Knoten geladen werden: einen [Datensatz](./pipeline/datasets.mdx), einen [SQL-Knoten](./pipeline/sql-nodes.mdx) und die beiden Knoten, die Daten von anderswo importieren, [Indikatordaten](./pipeline/indicator-data.mdx) aus einer Ihrer Indikator-Apps und [Analyse-App-Daten](./pipeline/analytic-data.mdx) aus einer anderen Analyse-App. Importierte Werte können Sie direkt zeigen, ohne einen SQL-Knoten dazwischen. Ein importierter Knoten erhält seine Spalten aus der letzten erfolgreichen Ausführung der Pipeline. Führen Sie die Pipeline deshalb einmal aus, nachdem Sie einen hinzugefügt haben: Bis dahin fordern die Spalteneinstellungen des Widgets Sie auf, sie zuerst auszuführen. Damit Daten erscheinen, muss die Pipeline mindestens einmal ausgeführt worden sein. Wenn ein Widget Sie auffordert, die Pipeline auszuführen, wechseln Sie zur **Datenpipeline**, klicken auf **Ausführen**, und das Widget füllt sich mit frischen Ergebnissen. Siehe die [Pipeline-Übersicht](./pipeline/overview.mdx) dazu, wie das Ausführen funktioniert. Während Sie arbeiten, lebt all das in Ihrem privaten Entwurf. Klicken Sie auf **Veröffentlichen**, um das neue Layout an Ihr Team zu übergeben; bis dahin sehen alle anderen weiterhin das bisherige. Siehe [Entwurf und Veröffentlichung](../overview/apps.mdx#entwurf-und-veroeffentlichung). ## Wenn die Visualisierung nichts zeigt [#empty-visualisation] Eine App, die Ihr Team öffnet, zeigt die Ergebnisse der neuesten **veröffentlichten** Ausführung. Gibt es keine solche Ausführung, bleiben die Widgets leer, und ein Hinweis oben auf der Seite sagt, welche von drei Voraussetzungen fehlt und was sie erfüllt: | Der Hinweis sagt | Was er bedeutet | Was ihn erfüllt | | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | Noch nicht veröffentlicht | Die App hat keine veröffentlichte Version, Ihr Team hat also nichts zu sehen. Eine Ausführung in diesem Zustand führt nur Ihren Entwurf aus. | Die App im Editor **veröffentlichen** (ein Mitglied mit der Rolle **App-Ersteller** oder **Admin** für die App). | | Noch nicht berechnet | Die App ist veröffentlicht, aber ihre Pipeline wurde noch nie ausgeführt. | Auf der Visualisierung **Ausführen** klicken oder die App in der Daten-Pipeline ausführen. | | Noch hat keine Ausführung Ergebnisse erzeugt | Die Pipeline wurde ausgeführt, aber keine Ausführung hat Ergebnisse für Ihr Team erzeugt. Eine Vorschau-Ausführung des Entwurfs hält ihre Ergebnisse im Editor privat; eine fehlgeschlagene oder noch laufende Ausführung hat noch keine. | Auf der Visualisierung **Ausführen** klicken - das führt die veröffentlichte Version aus. | Gibt es keinen Hinweis und ist ein Widget trotzdem leer, IST die App befüllt, und die Frage betrifft die Daten selbst: Der Filter oder die Abfrage hat keine Zeilen gefunden. Die Schaltfläche **Ausführen** erscheint nur für Personen, die Apps ausführen dürfen. Alle anderen sehen denselben Hinweis, der mit "Bitten Sie einen Workspace-Administrator ..." endet, weil die Ausführung für sie abgelehnt würde. ## Wenn ein Widget eine Spalte nennt, die es nicht mehr gibt [#missing-column] Ein Diagramm oder ein Pivot, die ihre Daten gruppieren, nennen die Spalten, nach denen sie gruppieren, die Spalten, die sie zusammenfassen, und wie sie jede zusammenfassen. Ist eine dieser Spalten nicht mehr in der Datenquelle des Widgets - weil sie in der Pipeline umbenannt oder entfernt wurde, oder weil Vera oder ein Import das Widget mit einer Spalte angelegt hat, die es nie gab -, oder nennt eine Zusammenfassung eine andere Funktion als sum, count, avg, min oder max, rät das Widget nicht. Statt Daten zeigt es einen kurzen Hinweis: | Wo | Was Sie sehen | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Pivot | "Die Gruppierung dieses Widgets lässt sich nicht lesen: Sie nennt eine Spalte, die es in seiner Datenquelle nicht gibt, eine Aggregation ohne Spalte oder eine andere Funktion als sum, count, avg, min oder max. Sie lässt sich in den Einstellungen des Widgets korrigieren.", im Raster des Pivots. | | Diagramm | "Die Gruppierung dieses Diagramms lässt sich nicht lesen: Sie verwendet eine Spalte, die es in seiner Datenquelle nicht gibt, oder eine andere Funktion als sum, count, avg, min oder max. Sie lässt sich in den Einstellungen des Diagramms korrigieren.", anstelle der Balken oder Segmente. | | Bericht | Ein Pivot-Block behält seine Spaltenüberschriften, und die Zeile darunter lautet "Die Gruppierung lässt sich nicht lesen: Sie nennt eine Spalte, die der Datenquelle fehlt, oder eine Funktion, die sich nicht anwenden lässt", wo sonst die Zeilenzahl steht. Ein Diagramm-Block zeigt den Hinweis des Diagramms. | Es gibt kein **Erneut versuchen**: Eine neue Anfrage liefert dieselbe Antwort, bis das Widget geändert wird. Zum Beheben öffnen Sie im Editor die Einstellungen des Widgets. Die Spalte, die es nicht mehr gibt, erscheint dort als leere Auswahl: Wählen Sie an dieser Stelle eine Spalte der Datenquelle, oder entfernen Sie die Zeile mit ihrem **X**. Das Widget liest seine Daten neu und zeichnet sich, sobald die Antwort da ist. Wer die veröffentlichte App liest, sieht den Hinweis, bis Sie die korrigierte Fassung [veröffentlichen](../overview/apps.mdx#entwurf-und-veroeffentlichung). ## Wenn ein Widget eine nicht mehr unterstützte Funktion verwendet [#unsupported-feature] Verwenden die gespeicherten Einstellungen eines Widgets eine Funktion, die Ovidens nicht mehr unterstützt, zeigt es an seiner Stelle den Hinweis "Dieses Widget verwendet eine Funktion, die nicht mehr unterstützt wird". Die anderen Widgets auf der Seite funktionieren weiter. Derselbe Hinweis erscheint in jedem Bericht und Ausdruck, der dieses Widget platziert. Vera und verbundene KI-Clients erhalten den Hinweis als Notiz, ohne Daten für dieses Widget. Ein nicht unterstütztes Widget hindert Sie nicht daran, den Rest der Visualisierung zu bearbeiten: Die Editoren speichern Ihre übrigen Änderungen weiter und lassen dieses Widget genau so, wie es gespeichert war. Um den Hinweis zu entfernen, löschen Sie das Widget oder ersetzen Sie es durch ein neues. Hinterlässt eine Änderung ein Widget mit einer Einstellung, die sein Typ nicht akzeptiert, nennt ein dauerhafter Hinweis das Widget und seine Seite, und Änderungen werden erst gespeichert, wenn Sie es korrigieren oder entfernen. Jedes Widget akzeptiert nur die Einstellungen seines eigenen Typs. Übergibt Vera, ein verbundener KI-Client oder ein App-Import eine ungültige Einstellung, lehnt Ovidens das Speichern ab und nennt das Widget und das Feld. Wenn Sie im Editor einen anderen Typ wählen, beginnt das Widget mit den Einstellungen dieses Typs. ## Widget-Einstellungen für KI-Clients [#widget-settings] Das Werkzeug-Schema beschreibt neun erstellbare Widget-Typen, ausgewählt über `type`. Gemeinsam sind ihnen `id`, `type`, `title`, `description`, `descriptionAsTooltip`, `width` und `height`. Breiten sind `full`, `half`, `third` oder `quarter`; Höhen sind die Stufen 1 bis 4 oder `auto`. Jeder Typ ergänzt nur diese Felder: | Typ | Einstellungen | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `table` | `nodeId`, `fixedFilters`, `parameterOptOut`, `visibleColumns`, `pinnedColumns`, `linkColumns`, `columnWidths`, `editable`, `contextSources` | | `chart` | `nodeId`, `fixedFilters`, `parameterOptOut`, `chartType`, `xColumn`, `chartGrouped`, `chartStacked`, `chartOrientation`, `chartLabelAngle`, `chartCategoryOrder`, `chartShowLegend`, `chartCategoryLabels`, `chartAggregates` | | `pivot` | `nodeId`, `fixedFilters`, `parameterOptOut`, `tableGroupBys`, `tableAggregates`, `columnWidths` | | `stat` | `nodeId`, `fixedFilters`, `parameterOptOut`, `statColumn`, `statAggregation`, `statFormat`, `statComparisonFilters` | | `parameter` | `params` | | `sql` | `nodeId`, `textContent` | | `text` | `textContent`, `textAlign` | | `ai` | `skillTechnicalId` | | `knowledge_base` | `kbItemKey` | Auch verschachtelte Einträge haben eigene Schemas. Filter verwenden `id`, `column`, `operator` und `value`; Zusammenfassungen verwenden `id`, `column`, `function` und optional `label` (bei Diagrammen auch `color`). Zusammenfassungsfunktionen sind `sum`, `count`, `avg`, `min` und `max`. Eine Tabelle hat keine Gruppierungseinstellungen: Verwenden Sie einen Pivot, um Zeilen zusammenzufassen. Das Werkzeug-Schema nennt die optionalen Felder. Ausgelassene Einstellungen eines vorhandenen Widgets bleiben erhalten; ein neues Widget beginnt mit den Vorgaben seines Typs. Ein nicht unterstütztes Widget ist ein schreibgeschützter Lesezustand außerhalb des Werkzeug-Schemas. Ein Schreibzugriff, der es unverändert lässt - der es genau mit `id` und `type` zurücksendet oder es in einer Bearbeitung nicht nennt -, behält es genau so, wie es gespeichert ist, und blockiert daher keine Änderung an einem anderen Widget. Wird es mit allen gespeicherten Einstellungen zurückgesendet, wird der Schreibzugriff abgelehnt. Ein Schreibzugriff, der es ändert, ohne es gültig zu machen, wird abgelehnt und nennt das Widget und seine Einstellungen: Entfernen Sie es, oder ändern Sie seinen `type`, um es neu aufzubauen. Beim Wechsel des Typs bleiben Titel, Beschreibung und Größe erhalten und, wenn der neue Typ Daten liest, auch Knoten, feste Filter und der Parameter-Ausschluss; alle anderen Einstellungen beginnen mit den Vorgaben des neuen Typs. Ein KI-Schreibzugriff ist für das, was er hinzufügt oder ändert, in der Länge begrenzt: Eine neue Seiten- oder Widget-ID hat höchstens 64 Zeichen, ein Widget-Titel 200, ein Seitenname 120 und eine Widget-Beschreibung 2.000. Ein unverändert zurückgesendeter Wert ist nicht begrenzt, sodass ein längerer Name aus dem Editor erhalten bleibt. Der Inhalt eines Text-Widgets ist nicht begrenzt. ## Die Widgets [#die-widgets] Jedes Widget wird im Editor hinzugefügt, auf dem Raster positioniert und (außer Text) für seine Daten auf einen Pipeline-Knoten ausgerichtet. ### Text [#text] Das **Text**-Widget platziert formatierten Text auf der Seite: Überschriften, kurze Notizen und Anweisungen. Verwenden Sie es, um der Seite Struktur zu geben und zu erklären, was die Betrachtenden vor sich haben, zum Beispiel einen Titel, einen Hinweis, woher die Daten stammen, oder wie ein Filter zu verwenden ist. Es zeigt keine Daten, also benötigt es keine Datenquelle. ### Tabelle [#table] Das **Tabellen**-Widget zeigt Ihre Daten als Zeilen und Spalten, direkt aus einem Knoten. Sie wählen, welche Spalten erscheinen. Um Zeilen nach Gruppen zusammenzufassen, verwenden Sie einen [Pivot](#pivot). Eine Tabelle funktioniert auf eine von zwei Arten: * **Schreibgeschützte** Tabellen zeigen Daten an und können nicht geändert werden, die übliche Wahl für einen Bericht. * **Dateneingabe**-Tabellen lassen Menschen Werte direkt aus der App in die Tabelle eintippen. Änderungen werden gespeichert, wenn die Pipeline ausgeführt wird. So wird eine Visualisierung zu einem Ort, an dem man Daten eingibt, nicht nur liest. Nur eine Dateneingabe-Tabelle bietet **Importieren**, um eine Datei hineinzuladen, und im Editor trägt ihre Kopfzeile die Markierung **Bearbeitbar**, sodass sie sich auf einen Blick von einer schreibgeschützten Tabelle unterscheidet. Eine Tabelle mit festen Filtern ist immer schreibgeschützt; in ihren Einstellungen steht statt des Schalters **Bearbeitbar** ein kurzer Hinweis auf den Grund. Wer feste Filter hinzufügt, schaltet **Bearbeitbar** damit aus, und es bleibt aus, wenn sie später wieder entfernt werden. Soll die Tabelle wieder Eingaben annehmen, schalten Sie es erneut ein. Wer in der App Daten erfassen darf - mit **Mitwirkender**, **App-Ersteller** oder **Admin** -, kann die Zeilen einer Dateneingabe-Tabelle bearbeiten: einen Wert ändern, eine Zeile hinzufügen oder eine löschen. Jede Änderung wird im Prüfpfad unter dem Namen der Person festgehalten, die sie vorgenommen hat. **Importieren**, also eine Datei in die Tabelle laden, bleibt der Workspace-Administration vorbehalten; andere Mitglieder sehen es nicht. Tabellen berücksichtigen Ihre [Parameter](#parameters): Ist ein Regionsfilter auf "Nord" gesetzt, zeigt die Tabelle nur Nord-Zeilen, und ein kleines Label zeigt an, welcher Filter aktiv ist. Eine Tabelle behält die Zeilen, die sie enthält, im Blick. Bei automatischer Höhe wächst sie mit ihren Zeilen, und während jemand sie bearbeitet (**Bearbeiten**), wächst sie etwas weiter, um Platz für die Schaltflächen **Speichern** und **Abbrechen** und den Streifen **Neue Zeile** zu schaffen. So bleiben in einer Tabelle, die auf die ein oder zwei zu korrigierenden Zeilen gefiltert ist, genau diese Zeilen sichtbar. Bei fester Höhe behält die Karte ihre Größe; ist sie für die Tabelle zu klein, scrollt die Tabelle innerhalb der Karte unter dem Titel des Widgets, sodass jede Zeile, die Seitensteuerung sowie **Speichern** und **Abbrechen** erreichbar bleiben. Bei automatischer Höhe ist eine Tabelle nie höher als das Fenster, in dem sie gelesen wird. Sie zeigt so viele ganze Zeilen, wie unter ihren Titel passen, zusammen mit der Seitensteuerung und der Schaltfläche **Alle anzeigen**, und immer mindestens fünf Zeilen, sodass in einem sehr kleinen Fenster stattdessen die Seite scrollt. Ein [Pivot](#pivot) mit automatischer Höhe verhält sich genauso. Wenn Sie das Fenster vergrößern oder verkleinern oder den Browser zoomen, folgt die Tabelle und zeigt mehr oder weniger Zeilen, ohne etwas neu zu laden. **Alle anzeigen** zeigt bis zu 100 Zeilen auf einmal, und die Seite scrollt, um sie zu erreichen. Lassen sich die Daten einer Tabelle nicht laden, sagt die Tabelle das an ihrer eigenen Stelle, dort, wo sonst Spaltennamen und Zeilen stehen, mit der Schaltfläche **Erneut versuchen**, die die Daten noch einmal anfordert. Die Meldung bleibt stehen, solange der neue Versuch läuft; danach erscheinen die Zeilen, oder die Meldung erscheint erneut, wenn die Daten weiterhin nicht geladen werden können. Suche, Sortierung und Filter bleiben verfügbar, denn jede Änderung daran fordert die Daten neu an. **Bearbeiten** und **+ Zeile** sind nicht verfügbar, bis die Daten geladen sind, und bereits eingegebene, noch nicht gespeicherte Werte bleiben erhalten. Eine Tabelle lässt sich direkt aus dem Widget herunterladen. Sie wählen CSV oder Excel - die Excel-Datei behält die Spaltentypen sowie die Prozent- und Währungsformatierung, die die Tabelle am Bildschirm zeigt, und ist damit zum Sortieren und Summieren bereit. Siehe [Die Daten herunterladen](pipeline/datasets.mdx#die-daten-herunterladen). Welche Zeilen die Datei enthält, hängt davon ab, wo die Tabelle steht. Eine Tabelle in der App [Audit Analytics](../administration/audit-trail.mdx) oder auf der Seite **Ergebnisse** einer Erhebung lädt die Zeilen herunter, die sie zeigt: Ihre Filter (auch die, die ein Link beim Öffnen der Seite bereits gesetzt hat, etwa auf eine bestimmte Ressource), die [Parameter](#parameters) der Seite und die Suche wirken sich alle aus - eine Tabelle, die 312 Zeilen zeigt, lädt also 312 Zeilen herunter. Die Datei enthält jede Spalte der Datenquelle der Tabelle, nicht nur die am Bildschirm sichtbaren, und ihre Zeilen stehen in der Standardreihenfolge der Tabelle, unabhängig davon, wie Sie die Tabelle sortiert haben. Eine Tabelle in jeder anderen App lädt ihre gesamte Datenquelle herunter, egal was am Bildschirm gefiltert ist. Eine Tabellenkalkulation fasst rund eine Million Zeilen; für eine größere Tabelle wird Excel mit einer entsprechenden Meldung abgelehnt - wählen Sie dafür CSV. #### Dateien in einer Tabelle [#table-attachments] Eine Tabelle über einem Datensatz mit einer **Anhang**-Spalte zeigt die Dateien jeder Zeile genau so wie die Datenansicht des Datensatzes selbst. Ein Klick auf das Dateisymbol in einer Zelle zeigt die Dateien mit Namen und Größe; dort können Sie eine Datei herunterladen (Sie werden zuerst nach dem Dateinamen gefragt) oder ein Bild vergrößert ansehen. Herunterladen funktioniert in jeder Tabelle, auch in schreibgeschützten, für alle, die die App lesen können. In einer Dateneingabe-Tabelle können Administratoren während der Bearbeitung (**Bearbeiten**) außerdem Dateien hinzufügen und entfernen: Wählen Sie **Datei hinzufügen** in einer leeren Zelle oder die Büroklammer in einer gefüllten, und wählen Sie dann Dateien aus oder ziehen Sie sie auf den Hochladen-Dialog. Jede Datei darf bis zu 10 MB groß sein; eine größere wird im Dialog abgelehnt, und alle anderen Änderungen bleiben erhalten. Um eine Datei zu ersetzen, entfernen Sie sie und fügen die neue hinzu. Eine Datei wird hochgeladen, sobald Sie den Dialog bestätigen, gehört aber erst zur Zeile, wenn Sie **Speichern** wählen; **Abbrechen** verwirft sie zusammen mit den übrigen ungesicherten Änderungen. Andere Leser der App können Dateien herunterladen, aber nicht hinzufügen oder entfernen. Was mit einer entfernten Datei geschieht, steht unter [Dateien in einer Anhang-Spalte](pipeline/datasets.mdx#attachments). #### Kontextspalten [#context-columns] **Kontextspalten** stellen Angaben aus anderen Datensätzen neben jede Zeile, sodass alle, die eine Tabelle korrigieren, das Nötige sehen, ohne sie zu verlassen. Eine Tabelle über einem Datensatz, schreibgeschützt oder zur Dateneingabe, kann insgesamt bis zu 10 davon tragen; sie erscheinen nach ihren eigenen Spalten, mit einem Schloss in der Kopfzeile, das den Datensatz nennt, aus dem jede stammt. Kontextspalten stammen aus einem **Kontext aus einem Datensatz**: Sie wählen einmal einen Datensatz derselben App und, wie seine Zeilen den Zeilen dieser Tabelle zugeordnet werden, und wählen dann so viele seiner Spalten, wie Sie brauchen. Alle Spalten eines Kontexts lesen dieselben passenden Zeilen. Jede Spalte ist eines von beiden: * **Wert** zeigt eine Spalte dieses Datensatzes aus der ersten passenden Zeile: zum Beispiel zu jeder Bestellung die Produktkategorie aus Ihrem Produkt-Datensatz. * Eine **Funktion** zählt oder summiert alle passenden Zeilen: zum Beispiel, wie viele Kundinnen und Kunden ein Produkt gekauft haben, oder den Umsatz all seiner Bestellungen. Die Funktionen sind **Anzahl**, **Anzahl eindeutiger Werte**, **Summe**, **Durchschnitt**, **Minimum** und **Maximum**, und diese Liste ist vollständig: Eine Berechnung darüber hinaus, etwa ein Verhältnis oder eine Zählung nur mancher Zeilen, gehört in einen SQL-Knoten. Ein Kontext kann beides mischen: aus Ihrem Bestell-Datensatz die erste Kundin, die Zahl der Kundinnen und Kunden und den Umsatz, alle über das Produkt zugeordnet. Zum Hinzufügen öffnen Sie die Einstellungen der Tabelle und finden **Kontextspalten** in der Gruppe Daten, unter den Spalten. Klicken Sie auf **Kontext aus einem Datensatz hinzufügen**. Wählen Sie **Aus Datensatz**, danach unter **Zeilen zuordnen, wo**, welche Spalte dieser Tabelle einer Spalte jenes Datensatzes entspricht (**Weitere Spalte zuordnen** fügt ein zweites oder drittes Paar hinzu). Wählen Sie dann für jede Spalte ihre **Spalte** und ihre **Funktion** - **Wert** oder eine der Funktionen - und geben Sie ihr einen **Namen**; **Spalte hinzufügen** fügt eine weitere Spalte aus demselben Datensatz hinzu. Der Editor bietet nur die Funktionen an, für die der Datensatz eine Spalte hat, und je Funktion nur die Spalten, die sie verwenden kann. Wird eine Spalte umbenannt oder ihre Funktion geändert, bleibt sie dieselbe Spalte. Wechseln Sie einen Kontext später zu einem anderen Datensatz, bleiben die Spalten, die auch dieser Datensatz hat - über dieselbe Spalte oder eine gleichnamige gefunden -, und die übrigen entfallen; wird die letzte Spalte eines Kontexts entfernt, entfällt der Kontext. Es ist jeweils ein Kontext zur Bearbeitung geöffnet. **Wie Werte zugeordnet werden.** Ein Dropdown wird nach der Beschriftung verglichen, die Sie sehen. Eine Zahl und ein Text werden als Zahlen verglichen, sodass `1` zu `"1"`, `"1.0"` und `"1.00"` passt, während ein Text, der keine Zahl ist, zu nichts passt - auch eine Zahl mit Dezimalkomma wie `1,5` nicht. Ein Datum und ein Zeitstempel werden nach dem Kalendertag verglichen, sodass das Datum 5. Januar 2024 zu jedem Zeitstempel dieses Tages passt. Ein Text und ein Datum oder ein Zeitstempel werden als dieses Datum oder dieser Zeitstempel verglichen: `2024-01-05` passt zum Datum 5. Januar 2024 und unter Zeitstempeln nur zu Mitternacht dieses Tages. Zwei Zeitstempel müssen genau übereinstimmen. Zeilen des anderen Datensatzes, deren Zuordnungswert leer ist oder sich nicht als der verglichene Typ lesen lässt, bleiben außen vor. **Was eine Spalte zeigt.** Eine Zeile ohne Treffer zeigt bei einer Zählung 0 und bleibt sonst leer. Eine Zeile, deren eigener Zuordnungswert leer ist, bleibt in jeder Spalte leer, auch bei Zählungen, denn "keine Kundin" heißt nicht "keine Bestellungen". **Anzahl** ohne Spalte zählt die passenden Zeilen, mit einer Spalte deren nicht leere Werte. **Durchschnitt** ist der Mittelwert der nicht leeren Werte. **Minimum** und **Maximum** eines Textes vergleichen nach Zeichencode, sodass `Zebra` vor `apple` kommt, und ein Dropdown nach seiner Beschriftung, nicht nach der Reihenfolge seiner Optionen. Eine Summe behält den Typ ihrer Spalte: Die Summe einer Währungsspalte zeigt diese Währung, die Summe einer Prozentspalte ein Prozent und die Summe ganzer Zahlen eine ganze Zahl; ein Durchschnitt behält eine Währung oder ein Prozent und ist sonst eine Dezimalzahl. Ein Wert, Minimum und Maximum einer Währungs- oder Prozentspalte zeigen ebenfalls deren Format. **Was abgelehnt wird.** **Summe** und **Durchschnitt** nehmen eine Zahlen-, Währungs- oder Prozentspalte, aber kein Jahr: Die Summe von Jahren ist keine Zahl, die jemand liest. Eine Mehrfachauswahl-Spalte enthält mehrere Optionen in einer Zelle, daher kann sie keine Zuordnungsspalte sein, und **Anzahl eindeutiger Werte**, **Minimum** und **Maximum** können sie nicht lesen; **Anzahl** und **Wert** können es. Kontextspalten lassen sich wie jede Spalte sortieren, filtern und durchsuchen, werden aber nie bearbeitet: Sie gehören weder zu **Bearbeiten** noch zum Formular für eine neue Zeile oder zum Detailfenster, und der Textfilter schlägt für sie keine Werte vor. Sie werden immer angezeigt, denn Ausblenden und Anheften gelten für die eigenen Spalten der Tabelle. Ihre Werte stammen aus denselben Daten wie die Zeilen - im Editor aus Ihrem Entwurf, für Ihr Team aus der veröffentlichten Fassung - und folgen einer Änderung beim nächsten Laden der Tabelle, ohne dass die Pipeline ausgeführt wird. Ein Download enthält nur die eigenen Spalten der Tabelle. Kommt ein Zuordnungswert im Datensatz eines Kontexts mehrfach vor, zeigt eine Spalte mit **Wert** den Wert der ersten Zeile, und ein gelbes **!** in ihrer Kopfzeile sagt, wie viele Werte sich wiederholen; eine Funktion liest alle passenden Zeilen und zeigt keine Warnung. Eine Spalte, die sich nicht lesen lässt - weil ihr Datensatz oder ihre Spalte gelöscht wurde oder sie etwas oben Abgelehntes verlangt -, bleibt leer, und ein rotes **!** in der Kopfzeile nennt den Grund, während der Rest der Tabelle wie gewohnt lädt. Auch [Vera](../tips/interact-with-vera.mdx) kann Kontextspalten anlegen; eine, die etwas nicht Vorhandenes nennt oder etwas oben Abgelehntes verlangt, wird mit Begründung abgelehnt. ### Diagramm [#diagramm] Das **Diagramm**-Widget zeichnet Ihre Daten als **Balken**-, **Linien**-, **Flächen**-, **Streu**-, **Kreis**- oder **Ring**diagramm, sodass ein Trend oder eine Rangfolge auf einen Blick erkennbar ist. Balken vergleichen Werte über Kategorien, Linien und Flächen zeigen Veränderungen über die Zeit, Kreise und Ringe zeigen, wie sich ein Ganzes in Teile aufteilt, und ein Streudiagramm stellt zwei Zahlenspalten gegeneinander dar, um zu zeigen, wie sich eine Größe mit einer anderen bewegt. Ein Diagramm benötigt eine Datenquelle plus die Spalte, die jeden Punkt oder Balken beschriftet, die Spalte, die die Werte liefert, und optional mehr als eine Reihe zum Vergleich. Sie können **nach einer Kategorie gruppieren**, und das Diagramm zeichnet sich neu, um zu Ihren [Parametern](#parameters) zu passen. Die horizontale Achse eines Streudiagramms und jede seiner Reihen müssen **Zahlen**spalten sein: Über einer Text- oder Datumsspalte gibt es nichts zu zeichnen, deshalb sagt das Diagramm das, statt eine leere Fläche zu zeigen. Ein Streudiagramm kann mehrere Reihen tragen, jede an ihren eigenen Werten gezeichnet. Ein Diagramm, das mit einem Typ gespeichert wurde, den Ovidens nicht zeichnet, etwa von einem anderen Werkzeug, nennt diesen Typ statt einer Zeichnung, bis in seinen Einstellungen ein Diagrammtyp gewählt wird. Ein Kreis- oder Ringdiagramm oder ein gruppiertes Balken-, Linien- oder Flächendiagramm, dessen Beschriftungs- oder Wertespalte nicht mehr in seiner Datenquelle ist, zeigt statt einer Zeichnung einen Hinweis; behoben wird das in den Einstellungen des Diagramms. Siehe [Wenn ein Widget eine Spalte nennt, die es nicht mehr gibt](#missing-column). ### Pivot [#pivot] Das **Pivot**-Widget fasst Zeilen zu Gruppen zusammen und zeigt für jede Gruppe eine Zusammenfassung. Eingerichtet wird es in den Einstellungen des Pivots: eine oder mehrere **Gruppieren-nach**-Spalten (etwa Region) und eine oder mehrere Zusammenfassungsspalten, jede eine Summe, Anzahl, ein Durchschnitt, Minimum oder Maximum einer Spalte (etwa die Summe des Umsatzes). Eine Jahresspalte bietet nur Anzahl, Minimum und Maximum an, weil eine Summe oder ein Durchschnitt von Jahren keine Zahl ist, die jemand liest. Wer die Seite liest, sieht das Ergebnis, eine Zeile je Gruppe. Verwenden Sie eine [Tabelle](#table), wenn Menschen die Details brauchen, und einen Pivot, wenn sie die Summen brauchen. Ein neuer Pivot beginnt mit einer Voreinstellung, damit er sofort etwas zeigt: Er gruppiert nach der ersten Spalte, die keine Zahl ist, und summiert die erste Zahlenspalte, die kein Jahr ist. Die Voreinstellung füllt nur einen Teil, den Sie leer gelassen haben. Eine Spalte, die Sie gewählt haben, wird immer so verwendet, wie Sie sie gewählt haben, und nie stillschweigend durch eine andere ersetzt. Ist sie nicht mehr in der Datenquelle, zeigt der Pivot statt des Rasters einen Hinweis; behoben wird das in den Einstellungen des Pivots. Siehe [Wenn ein Widget eine Spalte nennt, die es nicht mehr gibt](#missing-column). **Formate.** Ein Pivot zeigt seine Zahlen so, wie die Datenquelle sie festlegt. Die Summe einer Währungsspalte erscheint in dieser Währung mit ihren Nachkommastellen, die Summe einer Prozentspalte mit ihren Nachkommastellen und einem %-Zeichen. Eine Anzahl ist immer eine ganze Zahl, weil sie Zeilen zählt. Ein Durchschnitt ganzer Zahlen oder Dezimalzahlen wird auf zwei Nachkommastellen gerundet. Das früheste oder späteste Datum einer Datumsspalte erscheint als Datum, und ein Jahr als Jahr (2023, nie 2.023). Eine Gruppierung nach einer Auswahlspalte zeigt den Namen und die Farbe der Option. **Jede Gruppe, Seite für Seite.** Ein Pivot zeigt jede Gruppe seiner Daten, eine Seite nach der anderen, und seine Seitenleiste sagt, wie viele Gruppen es sind. **Suchen, filtern und sortieren.** Das Suchfeld findet Gruppen über die Werte der Gruppieren-nach-Spalten, und bei einer Auswahlspalte über die Namen der Optionen, die Sie sehen. Ein Filter auf einer Gruppieren-nach-Spalte schränkt die Zeilen ein, bevor zusammengefasst wird, sodass die Summen dem Filter folgen. Ein Filter auf einer Zusammenfassungsspalte behält die Gruppen, deren Zusammenfassung ihn erfüllt, etwa die Regionen mit mehr als 10.000 Umsatz. Das Sortieren nach einer Zusammenfassungsspalte ordnet die Gruppen nach ihrer Zusammenfassung. Die **Festen Filter** des Pivots (in der Gruppe Daten seiner Einstellungen) und die [Parameter](#parameters) der Seite schränken die Zeilen ebenfalls ein, bevor zusammengefasst wird. **Vollbild und Download.** Öffnen Sie einen Pivot über seine Kopfzeile im Vollbild (im Editor über die Schaltflächen über der Kachel); Ihre Suche bleibt dabei erhalten. Der Download enthält jede Gruppe, die der Pivot unter seiner aktuellen Suche, seinen Filtern und seiner Sortierung zeigt, unter denselben Spaltenüberschriften. Zahlen werden als reine Zahlen geschrieben, damit eine Tabellenkalkulation mit ihnen rechnen kann; eine Excel-Datei behält das Format einer Währungs- oder Prozentspalte, und ein Datum wird als Datum geschrieben (2024-01-01). Kann ein Pivot seine Daten nicht lesen, etwa weil die Abfrage zu lange gedauert hat, sagt er das anstelle des Rasters und bietet **Erneut versuchen** an. Ein Berichtsblock zeigt einen Pivot genau wie die Kachel, Formate, Jahre und Optionsnamen eingeschlossen. ### Eine Zusammenfassungsspalte benennen [#aggregate-labels] Jede Zusammenfassungsspalte eines Pivots kann eine eigene Überschrift haben. Öffnen Sie in den Einstellungen des Widgets die Stift-Schaltfläche neben der Zusammenfassung und tragen Sie die Überschrift unter **Spaltenüberschrift** ein, etwa "Umsatz in EUR". Die Überschrift steht über dieser Spalte auf der Seite, im Vollbild, in Berichten und in einer heruntergeladenen Datei; das kleine Kennzeichen der Zusammenfassung (SUM, AVG, ...) bleibt daneben. Lassen Sie das Feld leer oder wählen Sie **Zurücksetzen**, und die Spalte trägt wieder den Namen ihrer Spalte in der Datenquelle. Ein Bericht und ein Download haben kein Kennzeichen, deshalb trägt eine unbenannte Zusammenfassungsspalte dort den Namen der Spalte und ihre Zusammenfassung, etwa "Betrag (sum)" neben "Betrag (max)", und ein Filter darauf liest sich über der Seite genauso. ### Parameter [#parameters] Ein **Parameter**-Widget fügt Eingabesteuerungen hinzu, ein Dropdown, ein Textfeld, ein Zahlenfeld, eine Datumsauswahl oder einen Bereich, die es Menschen erlauben, die Seite zu filtern oder anzupassen, ohne etwas zu ändern, das Sie aufgebaut haben. Statt eine Seite pro Region zu bauen, fügen Sie einen Regionsparameter hinzu: Menschen wählen eine Region, und jede Tabelle und jedes Diagramm aktualisiert sich entsprechend. Eine Seite bedient alle. Verbinden Sie einen Parameter mit der **Spalte**, nach der er filtert, und geben Sie die Optionen eines Dropdowns entweder als feste Liste an oder laden Sie sie live aus einem Pipeline-Knoten. Ein Parameter filtert jedes Widget, das die passende Spalte verwendet, sodass eine Steuerung die ganze Seite antreiben kann. Ein Widget, das immer alles zeigen soll (zum Beispiel eine Gesamtsumme), kann so eingestellt werden, dass es Parameter ignoriert. ### SQL [#sql] Das **SQL**-Widget platziert ein Abfragefeld auf der Seite, sodass jemand, der mit SQL vertraut ist, Ad-hoc-Fragen an die Pipeline-Ausgabe stellen kann. Wählen Sie einen Knoten als Quelle, und es füllt eine Startabfrage aus, die Sie anpassen. Eine Abfrage kann einen Datensatz oder einen SQL-Knoten nennen, aber keinen importierten Knoten (Indikatordaten oder Analyse-App-Daten): Um importierte Daten abzufragen, fügen Sie in der Pipeline nach dem Import einen SQL-Knoten hinzu und fragen diesen ab. Wenn Sie mit SQL nicht vertraut sind, benötigen Sie dieses Widget nicht: Beschreiben Sie Ihre Frage stattdessen [Vera](../tips/interact-with-vera.mdx), oder verwenden Sie die anderen Widgets, die die meisten Bedürfnisse ohne eine einzige Abfrage abdecken. Eine Abfrage liest hier **nur die eigenen Knoten dieser App**, direkt über ihre ID benannt: Ein Name mit Präfix, die Tabelle einer anderen App, eine Systemtabelle, ein Dateipfad oder eine Tabellenfunktion in `FROM` wird mit einer Meldung abgelehnt, was zu ändern ist, ebenso eine Funktion, die Einstellungen oder den Katalog liest, als Text übergebenes SQL ausführt oder etwas verändert. Die vollständige Regel steht unter [Was die Schaltfläche Ausführen lesen kann](./pipeline/sql-nodes.mdx#was-die-schaltflaeche-ausfuehren-lesen-kann). Eine eigene Abfrage zu schreiben und auszuführen ist eine eigene Berechtigung, getrennt vom Lesen der App. Sie gehört zu den Rollen **Mitwirkender**, **Analyst**, **App-Ersteller** und **Admin** für den gesamten Workspace - eine Rolle für einen einzelnen Bereich oder eine App gibt sie nicht -, und in Ihrem eigenen persönlichen Bereich haben Sie sie immer. Wer eine App nur ansehen darf, sieht bei jedem SQL-Widget dessen Ergebnis - die Zeilen der im Widget gespeicherten Abfrage - ohne Abfragefeld: Er liest, was der Autor der Seite geschrieben hat, und kann es nicht ändern. Wer im Editor die App ändern, aber keine Abfragen ausführen darf, bearbeitet die gespeicherte Abfrage ohne Schaltfläche **Ausführen**, und ein kurzer Hinweis sagt, warum. ### KI [#ki] Das **KI**-Widget führt einen kuratierten KI-[Skill](./skills.mdx) aus und zeigt die Antwort direkt auf der Seite. Seine einzige Einstellung ist, welchen Skill es ausführt; das Widget übernimmt den Namen des Skills als Titel. Fragt der Skill nach etwas, zum Beispiel nach einer Region oder einem Zeitraum, zeigt das Widget pro Frage ein beschriftetes Feld an; die lesende Person füllt sie aus und klickt auf **Ausführen**. Während der Ausführung sehen Sie, woran Vera gerade arbeitet, wie lange es schon läuft und wie weit es gehen darf. Am Ende erhalten Sie die Antwort samt einer aufklappbaren Erklärung, wie Vera darauf gekommen ist. Eine Datenquelle braucht es nicht: der Skill weiß bereits, welche Daten er ansehen muss. Wer keinen KI-Zugriff hat, sieht statt der Schaltfläche Ausführen einen Hinweis darauf. ### Wissensdatenbank [#knowledge-base-widget] Das **Wissensdatenbank**-Widget bettet eine einzelne Seite aus der [Wissensdatenbank](./knowledge-base.mdx) der App in die Visualisierung ein, schreibgeschützt dargestellt. Verwenden Sie es, um Erläuterungen direkt neben den Zahlen zu halten: was die Daten bedeuten, wie die App gemeint ist oder welche Regeln hinter einer Zahl stehen, sodass Menschen die Seite nicht verlassen müssen, um es nachzuschlagen. Es zeigt keine Pipeline-Daten, also benötigt es keine Datenquelle; Sie wählen einfach, welche Seite es anzeigt. # Mit Vera erstellen (/docs/de/indicator-apps/build-with-vera) **Vera** ist die in Ovidens integrierte KI-Assistentin. Beschreiben Sie, was Sie in Ihrer Kennzahlen-App erstellen, ändern oder verstehen möchten. Vera verwendet dafür die für Sie verfügbaren Werkzeuge. Eine vorbereitete Wissensdatenbank oder eine feste Einrichtungsfolge ist nicht erforderlich. ## So starten Sie [#so-starten-sie] 1. Öffnen Sie Ihre Kennzahlen-App, klicken Sie auf **Editor öffnen** und öffnen Sie **Vera**. 2. Senden Sie eine Anfrage, etwa „Erfasse monatlichen Stromverbrauch je Standort und berechne die prozentuale Änderung gegenüber dem Vorjahresmonat“. 3. Beantworten Sie gezielte Rückfragen zu fehlenden Kennzahlen, Einheiten oder Regeln. Vera kann je nach Anfrage mit [Kennzahlen](./indicators/input.mdx), [Dimensionen](./dimensions/other-dimensions.mdx), [Gruppen](./groups.mdx) und [Berechnungen](./indicators/formula.mdx) arbeiten. Sie zeigt ihren Fortschritt und antwortet zum Abschluss kurz zum Ergebnis. Kann sie etwas nicht ausführen, erklärt sie die Einschränkung und bereits erledigte Arbeit. ## Setzen Sie Ihr Gespräch fort [#setzen-sie-ihr-gespräch-fort] Stellen Sie Anschlussfragen wie „Füge einen weiteren Standort hinzu“, „Verwende kWh als Einheit“ oder „Erkläre diese Berechnung“. Ihre jüngsten Anfragen, Antworten auf Veras Rückfragen und Veras Antworten bilden den Kontext der nächsten Nachricht. Nach dem Schließen des Bereichs oder Neuladen erscheinen das Gespräch und noch offene Fragen wieder. Ihre Gespräche gehören Ihnen innerhalb dieses Workspace. Wählen Sie über die Gesprächsauswahl einen früheren Verlauf aus oder starten Sie mit **Neues Gespräch** einen eigenen benannten Verlauf. Sie können ihn später umbenennen. Der vollständige Verlauf wird gespeichert; **Frühere Nachrichten laden** zeigt ältere Beiträge. Andere Personen haben eigene Gespräche. Vera öffnet Ihr letztes Gespräch automatisch nur in derselben App und innerhalb von 6 Stunden nach Ihrer letzten Interaktion wieder. Andernfalls erscheint ein leerer Chat, der mit der ersten Nachricht gespeichert wird. **Gesprächsverlauf** öffnet eine mittig angeordnete Tabelle mit Titelsuche und Seiten-Bedienelementen. Ältere Gespräche oder einen Verlauf aus einer anderen App können Sie dort manuell öffnen. Jede Nachricht zeigt ihre Ausgangs-App; die aktuelle Seite liefert den Kontext Ihrer nächsten Anfrage. Vera kann in den Apps arbeiten, die Ihre Berechtigungen erlauben. **Diese Anfrage stoppen** beendet die laufende Aufgabe; ein Gesprächswechsel allein lässt sie auf dem Server weiterlaufen. ## Geben Sie Vera hilfreichen Kontext [#geben-sie-vera-hilfreichen-kontext] * Nennen Sie Kennzahlen, Einheiten und Erfassungsrhythmus. * Beschreiben Sie Aufteilungen, etwa nach Region oder Standort. * Erläutern Sie Berechnungsregeln und den Umgang mit fehlenden Werten. * Fragen Sie bei Bedarf nach Ovidens. Vera kann in der Benutzerdokumentation nachsehen und auf die passenden Anleitungen verlinken. Sie können Ihre Regeln direkt im Chat erklären. Ist eine wichtige Entscheidung unklar, fragt Vera nach, bevor sie diese für Sie trifft. ## Wer Vera nutzen kann [#wer-vera-nutzen-kann] Gespräche mit Vera setzen KI-Zugriff auf die App voraus. Sie können Fragen stellen, ohne die Konfiguration der App ändern zu dürfen. Jedes Bearbeitungswerkzeug braucht weiterhin seine eigenen Berechtigungen. Vera kann nur ändern, was Sie selbst ändern dürfen und was Ihre freigegebenen KI-Werkzeuge erlauben. Siehe [Rollen](../administration/index.mdx). Sie beantworten Fragen in Ihrem eigenen Gespräch. Anfragen und Antworten anderer Personen gehören nicht zu Ihrem Chat. ## Änderungen und Veröffentlichung [#änderungen-und-veröffentlichung] Eine angeforderte Strukturänderung erstellt oder verwendet den Entwurf der App. Fragen oder Hilfegesuche erzeugen keinen Entwurf. Prüfen Sie das Ergebnis und klicken Sie auf **Veröffentlichen**, oder bitten Sie Vera ausdrücklich darum, sofern dieses Werkzeug für Sie verfügbar ist. Andernfalls bleiben ihre Änderungen im Entwurf. **Verwerfen** entfernt alles, was in diesem Entwurf geändert wurde, einschließlich währenddessen erfasster Werte. Siehe [Entwurf und Veröffentlichung](../overview/apps.mdx#entwurf-und-veroeffentlichung). [Mit Vera interagieren](../tips/interact-with-vera.mdx) erklärt Ergebnisse, Wiederaufnahme und KI-Verbrauch. # Konsistenzprüfungen (/docs/de/indicator-apps/checks) Eine [Validierungsregel](./indicators/input.mdx) sichert einen einzelnen Wert ab: diese Zahl ist nie negativ, jene höchstens 100. Eine **Konsistenzprüfung** sichert eine Beziehung *zwischen* Datenpunkten: erneuerbare plus nicht erneuerbare Energie ergibt die Gesamtenergie, umgeleiteter Abfall überschreitet nie den erzeugten Abfall, ein Anteil bleibt unter 100 %. Prüfungen machen aus "jede Zelle ist gefüllt" ein "der Bericht ist in sich stimmig", und eine verletzte Prüfung kann die Freigabe eines Berichtszeitraums blockieren. ## Wo sie liegen [#wo-sie-liegen] Prüfungen gehören zur Definition der App und liegen daher im Editor. Öffnen Sie Ihre Indikator-App, klicken Sie auf **Editor öffnen** und gehen Sie auf den Tab **Prüfungen**. Sie sehen die vertraute Zwei-Bereiche-Ansicht: links eine durchsuchbare Liste der Prüfungen, rechts die ausgewählte Prüfung. Das Löschen liegt auf der Zeile in der Liste. Wie alles andere im Editor liegt eine neue oder geänderte Prüfung in Ihrem privaten Entwurf, bis Sie **veröffentlichen**. Siehe [Entwurf und Veröffentlichung](../overview/apps.mdx#entwurf-und-veroeffentlichung). ## Mit einem gängigen Muster starten [#mit-einem-gängigen-muster-starten] Eine neue Prüfung bietet **Mit einem gängigen Muster starten** mit drei fertigen Formen an: * **Aufschlüsselung ergibt die Summe** - Komponenten ergeben zusammen einen Gesamtwert. * **Teil bleibt im Ganzen** - ein Teil darf das Ganze nicht überschreiten. * **Anteil bleibt unter 100 %** - ein Verhältnis oder Prozentsatz bleibt im Rahmen. Ein Klick füllt das Formular für Sie aus; Sie ersetzen dann die Datenpunkte durch Ihre eigenen. Nichts ist festgezurrt, eine Vorlage ist ein Ausgangspunkt, keine Einschränkung. ## Eine Prüfung schreiben [#eine-prüfung-schreiben] Eine Prüfung ist ein Vergleich aus linker Seite, Operator und rechter Seite, und jede Seite wird **aus Bausteinen aufgebaut** statt getippt - denselben Bausteinen, aus denen ein [Formel-Indikator](./indicators/formula.mdx) entsteht. So gibt es nur eines zu lernen und keine Sprache zu behalten: * **Name** - eine kurze Bezeichnung, damit sie in der Liste leicht wiederzuerkennen ist, zum Beispiel "Summen stimmen überein". Eine technische ID wird beim Tippen aus dem Namen erzeugt und bleibt nach dem Anlegen fest. * **Linke Seite** und **Rechte Seite** - jeweils eine Berechnung über Ihre Datenpunkte. Für eine Prüfung, dass erneuerbare und nicht erneuerbare Energie sich aufsummieren, ist die linke Seite ein Baustein **Rechnung**, der zwei Bausteine **Referenz** addiert, und die rechte Seite eine einzelne Referenz auf den Gesamtenergieverbrauch. * **Operator** - `=`, `!=`, `<`, `>`, `<=` oder `>=`. * **Toleranz** - nur für `=` und `!=` angeboten. Ein kleiner Spielraum, damit eine Rundungsdifferenz von ein paar Nachkommastellen eine Gleichheit nicht verletzt. * **Meldung bei Fehlschlag** - der Satz, den eine erfassende Person liest, wenn die Prüfung verletzt wird, zum Beispiel "Erneuerbare Energie darf den Gesamtenergieverbrauch nicht überschreiten." Ovidens bietet eine **Vorgeschlagene Meldung** aus dem Vergleich an; **Übernehmen** setzt sie ein. * **Schweregrad** - **Blockierend** ("eine verletzte Zelle blockiert das Freigeben & Sperren für diesen Zeitraum") oder **Warnung** ("zeigt eine Warnung; die Freigabe bleibt möglich"). Während Sie bauen, stellt Ovidens **dieselbe Frage wie das Speichern** und zeigt eine Zeile: entweder ein **Liest sich als:**, das die Prüfung in Worten wiedergibt - so bestätigen Sie, dass sie das Gemeinte trifft - oder den Grund, warum sie nicht ausgewertet werden kann, mit dem Datenpunkt, der Erfassungsfrequenz oder der Aufschlüsselung, die nicht zusammenpasst. Daneben sagt eine Zeile, wie oft ausgewertet wird, zum Beispiel "geprüft je Standort, je Jahr". ## Die Ergebnisse lesen [#die-ergebnisse-lesen] Unter dem Formular sitzt die Tabelle **Auswertung pro Scheibe**: die Prüfung auf Ihre echten Daten angewandt, eine Zeile je Zeitraum und Aufschlüsselungsscheibe, mit dem Ergebnis und, bei einem numerischen Vergleich, der Differenz (Δ) gegenüber der Toleranz. Vier Chips filtern sie: **Alle**, **Verletzt**, **Unbekannt** und **Erfüllt**, jeweils mit ihrer Anzahl. **Unbekannt** ist kein Problem: es bedeutet, dass mindestens einer der referenzierten Datenpunkte noch keinen Wert hat, es also nichts zu vergleichen gibt. Diese Zeilen benennen, was fehlt, und eine unbekannte Prüfung blockiert und warnt nicht. Eine verletzte Zeile bietet **In Dateneingabe korrigieren** an und bringt Sie direkt zu genau dem Datenpunkt und Zeitraum dahinter - von "diese Zahlen passen nicht zusammen" mit einem Klick zur Zelle, die korrigiert werden muss. ## Was eine Prüfung mit Ihrem Bericht macht [#was-eine-prüfung-mit-ihrem-bericht-macht] * Eine verletzte **blockierende** Prüfung verhindert die Freigabe des Berichtszeitraums. **Freigeben und sperren** bleibt deaktiviert, und der Tab **Status und Freigabe** des Zeitraums listet auf, was blockiert - nach Datenpunkt und nach Ihrer Prüfmeldung. * Eine verletzte **Warn**-Prüfung wird gemeldet, blockiert die Freigabe aber nie. * In beiden Fällen sehen die Erfassenden Ihre Meldung schon während der Eingabe, sodass genau die Person davon erfährt, die die Unstimmigkeit beheben kann. Den Freigabevorgang selbst beschreibt [Berichtszeitraum prüfen und freigeben](./collection-overview.mdx#sign-off). # Erfassungsübersicht (/docs/de/indicator-apps/collection-overview) Die **Erfassungsübersicht** beantwortet auf einen Blick eine Frage: Wie läuft die Datenerfassung? Öffnen Sie eine Indikator-App, und Sie landen zuerst genau hier, auf dem Tab **Übersicht**, neben **Fragebogen** und **Mitwirkende**. Sie zeigt Vollständigkeit, wer beiträgt und wo die Lücken sind, an einem Ort. Sie wird für Sie aus den Daten erstellt, die Sie erfassen, und es gibt nichts zu konfigurieren. Das Zeitfenster ergibt sich aus dem **Berichtszeitraum**, den Sie im Kopfbereich wählen und den sich alle drei Tabs teilen; die App leitet ihre Zeiträume aus ihrem Berichtsintervall ab, es gibt also immer einen zur Auswahl. Darüber hinaus haben Sie nur einen **Gruppenfilter** und eine Schaltfläche **Aktualisieren** sowie an der Heatmap selbst eine Steuerung **Ansicht**, die festlegt, wie fein die Spalten sind. ## Die Kennzahlen im Überblick [#die-kennzahlen-im-überblick] Am oberen Rand sitzen drei Zusammenfassungskarten: * **Gesamtvollständigkeit** - der Anteil der erwarteten Werte, die tatsächlich erfasst wurden, mit den dahinterliegenden Zahlen der befüllten und erwarteten Zellen. * **Aktive Mitwirkende** - wie viele Personen Daten eingegeben haben, sodass Sie sehen können, dass die Erfassung eine gemeinsame Anstrengung ist und nicht auf einer Person allein ruht. * Eine kombinierte Karte für den Handlungsbedarf: wie viele Indikatoren **Überfällig** sind, wie viele **Indikatoren in Gefahr** sind (unter 50 % vollständig) und wann der **Letzte Eintrag** erfolgt ist. ## Die Lücken finden [#die-lücken-finden] Unter den Kennzahlen im Überblick zeigen mehrere Panels, wo die Erfassung Aufmerksamkeit benötigt: * **Vollständigkeits-Heatmap** - ein Raster aus Indikatoren gegen Perioden, eingefärbt von gut abgedeckt bis leer. Filtern Sie es nach Gruppe, suchen Sie nach Namen, klappen Sie Gruppen auf oder zu, ändern Sie mit **Ansicht** die Spaltenauflösung und öffnen Sie es im **Vollbild** für eine umfangreiche App. * **Abdeckung nach Dimension** - für Indikatoren, die nach einer [Dimension](./dimensions/other-dimensions.mdx) aufgeschlüsselt sind, Balken, die zeigen, wie vollständig jedes Mitglied ist, das schlechteste zuerst, sodass eine zurückliegende Region oder ein Standort auffällt. * **Gefährdet & überfällig** - eine kurze Tabelle der am wenigsten vollständigen Indikatoren, mit der Angabe, wie viele ihrer erwarteten Werte vorliegen. * **Eingabe-Trend** - ein Diagramm darüber, wie viele Werte Tag für Tag eingegeben wurden, sodass Sie sehen können, ob die Erfassung Schritt hält. ## Berichtszeiträume [#reporting-periods] Ein **Berichtszeitraum** benennt den Zeitraum, über den Sie berichten - zum Beispiel "Berichtsjahr 2025" - damit der Erfassungsaufwand auf ein klar definiertes Fenster begrenzt ist. Sie legen Zeiträume nie von Hand an: Die App kennt ihr **Berichtsintervall** (beim Anlegen der App gewählt - Kalenderjahr, Quartal, Monat oder ein eigener Startmonat mit Länge) und leitet die Zeitachse der Zeiträume daraus ab, in die Vergangenheit wie in die Zukunft. Jeder Zeitraum ist da, sobald die App existiert. Ein abgeleiteter Zeitraum beginnt als einfacher Kalendereintrag. Er wird in dem Moment zu einem **Datensatz**, in dem Sie mit ihm arbeiten - ihn umbenennen, zur Prüfung einreichen, freigeben oder eine [Erhebungsrunde](./collection-rounds.mdx) daran binden - und behält Name und Fenster von da an. Umbenennen geht bei jedem offenen Zeitraum; das Fenster selbst kommt immer aus dem Intervall und lässt sich weder verschieben noch in der Länge ändern. **Eine Intervall-Änderung** wird ab einem von Ihnen gewählten Monat wirksam, nach vorn: Zeiträume davor behalten ihre Fenster, die Zeitachse danach folgt dem neuen Rhythmus. Schneidet die neue Grenze einen alten Zeitraum ab, wird der verkürzte Rest einfach ein eigener (kürzerer) Zeitraum. **Die Erfassung wird nie durch einen fehlenden Zeitraum blockiert.** Sie können Daten für jedes Datum in der Erfassungsfrequenz des Datenpunkts eingeben - Vergangenheit wie Zukunft - ohne vorher etwas einzurichten. Eine Zelle sperrt nur ein Zeitraum, der **zur Prüfung eingereicht oder freigegeben** wurde: Jeder Bucket, der ein solches Fenster berührt, ist schreibgeschützt, bis der Zeitraum wieder geöffnet wird. **Berichtszeiträume werden im Editor verwaltet.** Klicken Sie auf **Editor öffnen** und öffnen Sie dann den Tab **Berichtszeiträume** (das Kalendersymbol, der letzte der Editor-Tabs). Die Karte **Berichtsintervall** über der Liste zeigt die Intervall-Historie und bietet Admins und Berichtsverantwortlichen **Intervall ändern ab…** sowie das Entfernen der letzten Änderung an. Darunter zeigt die Liste jeden Zeitraum - abgeleitete mit einem dezenten Hinweis "abgeleitet", bearbeitete mit ihrem Status-Badge - und der Detailbereich hat zwei Tabs: **Konfiguration** (die Bezeichnung) und **Status und Freigabe** (der Freigabevorgang weiter unten). Das Löschen wird in der Zeile eines bearbeiteten Zeitraums angeboten, solange er offen und ohne Erhebungsrunde ist; danach wird sein Fenster wieder ein einfacher abgeleiteter Zeitraum, und die in diesem Fenster erfassten Werte bleiben erhalten. Der gewählte Zeitraum gilt app-weit: Was Sie im Kopfbereich auswählen, zeigen alle Tabs, und die Auswahl steht in der Seitenadresse, sodass ein geteilter Link denselben Zeitraum öffnet. ### Der Vorjahreswert, und ihn übernehmen [#der-vorjahreswert-und-ihn-übernehmen] Ovidens stellt die Zahl des Vorjahres genau dorthin, wo Sie sie brauchen: an die Zelle selbst. Überall, wo Sie einen Wert erfassen, erscheint ein kleines Uhrsymbol; fahren Sie darüber, und Sie sehen den Wert genau dieser Zelle in den vorangehenden Kalenderjahren, das jüngste zuerst, bis zu drei Jahre zurück. Aufgeführt werden nur Jahre, die tatsächlich einen Wert enthalten, und ohne Historie erscheint das Symbol gar nicht. Es liest sich für eine einzelne Zahl genauso wie für jede Zelle einer Aufschlüsselung. Ist eine Zahl gegenüber dem Vorjahr unverändert, können Sie sie **übernehmen**. Jede leere Zelle in einem offenen Berichtszeitraum bietet die Aktion **Aus dem Vorjahr übernehmen** an, die den Wert des vorherigen Fensters überträgt und die Zelle als übernommen markiert. Die Markierung hält die Herkunft für Prüfende sichtbar, und der übernommene Wert wird nach denselben Regeln geprüft wie ein selbst erfasster Wert. In einem freigegebenen und gesperrten Zeitraum wird die Aktion nicht angeboten. ## Berichtszeitraum prüfen und freigeben [#sign-off] Ist die Erfassung eines Zeitraums abgeschlossen, schließen Sie ihn mit **Freigeben und sperren**: einem gesperrten, versionierten Nachweis dessen, was Sie berichtet haben. Öffnen Sie im Editor den Tab [Berichtszeiträume](#reporting-periods), wählen Sie den Zeitraum und nutzen Sie dessen Tab **Status und Freigabe**. Er benennt in einem Satz, was Freigeben und sperren bewirkt (es friert die Daten des Zeitraums ein und veröffentlicht eine unveränderliche Version), zeigt den Status des Zeitraums mit einer verständlichen Erklärung und listet auf, was noch blockiert. * **Zur Prüfung einreichen** (optional) übergibt den Zeitraum von der Erfassung an die Prüfung. Solange ein Zeitraum in Prüfung ist, sind seine Werte für alle gesperrt, auch für Administratorinnen und Administratoren, und nur ein Admin oder eine berichtsverantwortliche Person dieser App kann ihn wieder öffnen - die prüfende Person sieht also genau das, was freigegeben wird. Weil die Übergabe das gesamte Zeitfenster einfriert, steht sie denen offen, die hier erfassen oder verantworten, und nicht dem ganzen Arbeitsbereich: Administratorinnen und Administratoren, Berichtsverantwortlichen dieser App sowie Personen mit einer aktiven Zuordnung in einer offenen oder abgeschlossenen [Erhebungsrunde](./collection-rounds.mdx) dieser App. Wer nichts davon ist, hat hier auch keine Zahl erfasst; die Meldung nennt dann beide Wege: einen Admin oder eine berichtsverantwortliche Person um die Übergabe bitten, oder sich einer Erhebungsrunde dieser App zuordnen lassen. Sie dürfen einreichen, während eine Erhebungsrunde noch offen ist - der Bereich weist darauf hin und darauf, dass sich die Zahlen bis zu deren Abschluss noch ändern können. * **Freigeben und sperren** friert den Zeitraum ein. ZWEI Dinge steuern das. Erstens darf keine an den Zeitraum gebundene Erhebungsrunde mehr **offen** sein: Die Freigabe friert die Daten ein, eine Freigabe mitten in der Erhebung würde den Bericht also einfrieren, während noch geantwortet wird. Der Bereich nennt die offenen Runden, und die Bestätigung bleibt deaktiviert, bis Sie sie schließen. Zweitens muss die Bereitschaft grün sein: jeder anwendbare Datenpunkt ist gefüllt und jede blockierende [Konsistenzprüfung](./checks.mdx) besteht. Bis dahin ist die Schaltfläche deaktiviert und der Bereich listet auf, was noch blockiert, nach Datenpunkt und nach Prüfmeldung. Freigeben dürfen Admins und Berichtsverantwortliche dieser App; ein kleines Team kann direkt aus dem offenen Zustand freigeben, ohne separaten Prüfschritt. * **Haben Sie die Zahlen des Zeitraums zuletzt bearbeitet, sagt der Freigabedialog das**, bevor Sie bestätigen. Sie dürfen trotzdem freigeben: Der Hinweis ist da, weil eine Prüfung der Berichterstattung fragt, ob die Person, die die Zahlen erklärt, sie auch erfasst hat, und das Aktivitätsprotokoll hält bei der Freigabe fest, ob die freigebende Person die Zahlen zuletzt bearbeitet hat. Eine Freigabe über Vera oder eine verbundene KI hält dasselbe fest. Wenn Ihre Organisation das Erfassen und das Freigeben von Zahlen trennt, bitten Sie eine andere Person um die Freigabe. * **Eine offene Änderungsanforderung blockiert die Freigabe nie.** Hat eine prüfende Person zu einzelnen Antworten eine Änderung angefordert, nennt der Bereich die Anzahl und sagt ausdrücklich, dass dies nicht blockiert: Diese Zahlen sind weiterhin bearbeitbar und der Zeitraum ist weiterhin freigebbar. Der Hinweis ist da, damit Sie bewusst entscheiden, nicht damit die Plattform für Sie entscheidet. Diese drei Zeilen sehen nur die Personen, die die Erhebung der App verantworten - Admins und ihre Berichtsverantwortlichen; wer als erfassende Person einen Zeitraum zur Prüfung einreicht, erhält also keinen Hinweis auf eine offene Runde - die harte Sperre liegt auf Freigeben und sperren, das ohnehin nur sie ausführen können. Ein Zeitraum, dessen Runden gelöscht wurden, behält seine Änderungsanforderungen, hat aber keine Runde mehr, an der sie gezählt werden könnten; dort fehlt die Zeile. * Das Freigeben hält eine unveränderliche **Version** der gesamten App fest, sodass der Bericht stets genau so wiederhergestellt werden kann, wie er freigegeben wurde. Im Freigabedialog können Sie optional **Erklärung hinzufügen** ausklappen, um festzuhalten, wer den Bericht erklärt, deren Rolle und, je nach Berichtsrahmen, Grundlage und Modul-Option - jedes Feld inline erläutert. Eine Erklärung ist nicht erforderlich und kann bei einer späteren Freigabe ergänzt werden, sodass eine Freigabe mit einem Klick auf einer bereiten Periode gültig ist. Der freigegebene Zustand zeigt, wer wann freigegeben hat, einen Link zu dieser Version und die Erklärung, sofern vorhanden. Ein freigegebener Zeitraum ist **gesperrt**: seine Zellen werden im Raster schreibgeschützt und ein Banner benennt den Status, sodass niemand eine Zahl nach der Erklärung unbemerkt ändern kann. Um etwas zu ändern, **entsperren und öffnen** Sie den Zeitraum erneut. Wieder öffnen dürfen Admins und Berichtsverantwortliche dieser App, hinter einer Bestätigung, denn es entsperrt den Zeitraum zur Bearbeitung und legt die Freigabe beiseite; die freigegebene Version bleibt in der Historie, und eine erneute Freigabe erzeugt eine frische Version und einen frischen Freigabenachweis. Jedes Einreichen, jede Freigabe und jedes erneute Öffnen wird im Prüfprotokoll mit der handelnden Person festgehalten. ## Konsistenzprüfungen [#consistency-checks] Während eine [Validierungsregel](./indicators/input.mdx) einen einzelnen Wert absichert, sichert eine **Konsistenzprüfung** eine Beziehung zwischen Datenpunkten, zum Beispiel, dass sich eine erneuerbare und eine nicht erneuerbare Energiezahl zur Gesamtsumme addieren. Eine verletzte Prüfung kann **Freigeben und sperren** für den Zeitraum blockieren; Prüfungen machen also aus "alle Zellen gefüllt" ein "der Bericht ist in sich stimmig". Wie Sie Prüfungen schreiben und lesen, steht unter [Konsistenzprüfungen](./checks.mdx). ## Mitwirkende [#mitwirkende] **Mitwirkende** ist ein eigener Tab, neben Übersicht und Fragebogen. Er listet für denselben Berichtszeitraum auf, wer Daten eingegeben hat und wie aktiv die Person war. Es ist der schnellste Weg zu sehen, wem man danken oder wen man anstupsen sollte, wenn eine Erfassungsfrist näher rückt. # Erhebungsrunden (/docs/de/indicator-apps/collection-rounds) Eine **Erhebungsrunde** ist die Art, wie eine App ihre Zahlen erfragt. Statt die Zahlen einer App vom ganzen Arbeitsbereich zu erfragen, fragt eine Erhebungsrunde NAMENTLICH BEKANNTE PERSONEN nach den Zahlen der Einheit, für die sie verantwortlich sind - eine Tochtergesellschaft, ein Standort, eine Abteilung - und lässt Administratoren oder Berichtsverantwortliche jede Antwort genehmigen, ablehnen oder mit einer Begründung an dieselbe Person zurückgeben. Vier Begriffe tragen die gesamte Funktion, und nichts anderes bedeutet dasselbe: | Begriff | Was es ist | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Berichtseinheit** | Das *Von-wem* einer Zahl, so wie ein Berichtszeitraum das *Wann* ist. Ein Mitglied der EINEN Dimension, nach der DIE APP erhebt. | | **Ansprechpartner** | Eine Person, benannt per E-Mail-Adresse, die IN EINER RUNDE für eine Berichtseinheit verantwortlich ist. Wen Sie GEFRAGT haben. | | **Erhebungsrunde** | Ein Berichtszeitraum, die Datenpunkte, die sie abfragt, und die Personen, die sie fragt. Sie wählt keine Dimension - die App hat eine, und jede Runde dieser App fragt danach. | | **Beurteilung** | Ihr Urteil über die Antwort einer Einheit zu einem Datenpunkt. | Weil der RUNDE ihr Zeitraum, ihre Datenpunkte und ihre Personen gehören, können Sie die Runde des nächsten Jahres einrichten, während die diesjährige noch läuft, und dabei umentscheiden, wer sie beantwortet, ohne die diesjährige anzufassen. Das Einzige, was eine Runde NICHT wählt, ist die Dimension, aus der ihre Einheiten stammen: Die App hat eine, jede Runde fragt danach, und sobald eine Runde der App den Entwurf verlassen hat, lässt sie sich nicht mehr anderswohin verschieben. Eine Runde im Entwurf hält sie gar nicht - solange niemand gefragt wurde, können Sie es sich noch anders überlegen. Runden werden im **Editor** der App eingerichtet, in einem Bereich namens **Erhebung**, und auf dem Tab **Erhebung** der App beurteilt. BEANTWORTET werden sie ganz woanders: unter **Aufgaben**, einer Seite oberster Ebene außerhalb jeder App. **Das Einrichten einer Runde und das Beurteilen sind Administratoren und Berichtsverantwortlichen vorbehalten.** **Berichtsverantwortlicher** ist eine Rolle, die ein Admin für den gesamten Workspace, einen Bereich oder eine App vergibt (siehe [Mitglieder und Rollen](../administration/index.mdx#mitglieder-und-rollen)): Sie führt dort Runden durch, beurteilt Antworten und gibt Berichtszeiträume frei, und nirgends sonst. Der Bereich Erhebung des Editors zeigt die E-Mail-Adresse aller für eine Einheit verantwortlichen Personen, deshalb wird er nur denen angeboten, die die Runden der App verwalten - und der Tab Erhebung der App, auf dem Antworten beurteilt werden, aus demselben Grund ebenfalls. Antworten darf jede gefragte Person, und sie braucht dafür weder den Editor noch die App: Was von ihr erwartet wird, erscheint unter **Aufgaben** (siehe [Schritt 5](#schritt-5-sie-beantworten)). ## Schritt 1: die Berichtseinheiten-Dimension der App wählen [#schritt-1-die-berichtseinheiten-dimension-der-app-wählen] Eine Berichtseinheit ist keine neue Liste, die Sie tippen. Sie ist ein **Dimensionsmitglied**: Sie wählen, welche Dimension der App die Berichtseinheiten enthält, und ihre Mitglieder werden zu diesen Einheiten. Erheben Sie je Standort, ist die Dimension `Standort` es; erheben Sie je Tochtergesellschaft, jene. **Die App hat genau eine, und jede Erhebungsrunde dieser App erfasst danach.** Öffnen Sie den Editor, gehen Sie zu **Struktur -> Dimensionen**, öffnen Sie die gewünschte Dimension und schalten Sie **Berichtseinheiten-Dimension** ein. Schalten Sie sie bei einer anderen Dimension ein, wechselt sie dorthin - eine Dimensionsliste zeigt nie zwei davon. Zwei Dinge folgen daraus, und das zweite sollten Sie vorher wissen: * Die App muss **veröffentlicht** sein, damit eine Runde den Entwurf verlassen kann, denn die Erhebungsrunde läuft gegen die veröffentlichte Struktur. Legen Sie die Dimension fest, veröffentlichen Sie, öffnen Sie danach die Runde. * Sobald eine Erhebungsrunde dieser App läuft oder abgeschlossen ist, lässt sich die Dimension nicht mehr **verschieben**. Jede Antwort und jede Prüfentscheidung benennt eines ihrer Mitglieder; ein Wechsel würde sie ins Leere laufen lassen. Sie wieder auf die Dimension zu setzen, die die veröffentlichte Struktur trägt, bleibt erlaubt - das ist die Reparatur, wenn ein Entwurf davon abgewichen ist. Um nach einer anderen Achse zu erheben, setzen Sie zuerst die Prüfentscheidungen dieser Erhebungsrunden zurück und versetzen Sie sie dann in den Entwurf; oder verwenden Sie eine eigene App. ## Schritt 2: die Runde definieren [#schritt-2-die-runde-definieren] Wechseln Sie zu **Erhebung** und legen Sie eine Runde an. Sie trägt: * einen **Namen** und eine technische ID, * eine optionale **Beschreibung** - was diese Runde abfragt und von wem, * genau einen **Berichtszeitraum**, der das Fenster ist, für das sie erhebt, und * die **Datenpunkte**, die sie abfragt. Die Berichtseinheiten-Dimension der App wird hier zur Information angezeigt; sie ist nichts, was die Runde auswählt. Die Wahl der Datenpunkte IST die Definition dessen, was die Runde abfragt. Drei Arten von Datenpunkt können nicht abgefragt werden; jede wird ausgegraut und mit ihrer eigenen Begründung aufgeführt statt ausgeblendet: * einer, den Sie noch nicht wählen können, weil die App **keine Berichtseinheiten-Dimension** hat. Legen Sie diese zuerst fest - sie entscheidet, was überhaupt abgefragt werden kann. * einer, der **berechnet** statt beantwortet wird. Die App ermittelt ihn aus anderen Datenpunkten, es gibt also keine Antwort, die jemand geben könnte. Ändern Sie ihn auf der Seite Kennzahlen in einen erhobenen Datentyp, wenn er in einer Runde sein soll. * einer, der **nicht nach der Berichtseinheiten-Dimension der App erhoben** wird. Er hat eine einzige unternehmensweite Antwort und damit keine Einheit, gegen die er beurteilt werden kann. Weisen Sie ihm diese Dimension auf der Seite Kennzahlen zu. Enthält eine Runde bereits einen solchen Datenpunkt - weil er sich geändert hat, nachdem Sie ihn ausgewählt haben - können Sie ihn abwählen, solange die Runde ein Entwurf ist. Solange eine Runde zu einem Berichtszeitraum gehört, kann dieser Zeitraum nicht gelöscht werden: Ihn zu löschen würde die Runde und jedes zu ihren Antworten erfasste Prüfergebnis mitnehmen. Löschen Sie zuerst die Runden oder behalten Sie den Zeitraum. ## Schritt 3: festlegen, wer verantwortlich ist [#schritt-3-festlegen-wer-verantwortlich-ist] Wechseln Sie auf den Tab **Personen** der Runde. Er zeigt die Mitglieder der Berichtseinheiten-Dimension der App, eingerückt wie sie verschachtelt sind - eine Region über ihren Standorten. Personen zugeordnet werden können nur die Einheiten auf der Ebene, auf der die App erhebt; der Tab nennt diese Ebene. Die Einheiten darüber werden zur Orientierung angezeigt und sind nicht auswählbar. Wählen Sie eine Einheit und dann die verantwortlichen Personen aus. Die Auswahl lässt sich nach Name und Adresse durchsuchen und bietet nur Personen an, die dieser Einheit noch nicht zugeordnet sind. **Eine Zuordnung gibt der Person keinen Zugriff auf die App.** Sie legt fest, für welche Einheiten jemand antwortet; ob die Person überhaupt antworten darf, hängt an ihrem Zugriff auf die App. Eine Person sieht die Aufgabe und kann antworten, wenn sie die Daten der App bearbeiten darf - die Rolle **Mitwirkender** oder mehr, auf der App, auf ihrem Bereich oder auf dem ganzen Workspace. Wer ohne diesen Zugriff zugeordnet ist, sieht für diese Runde keine Aufgabe, kann sie nicht öffnen und kann weder antworten, kommentieren, sie als erledigt markieren noch den Zeitraum zur Prüfung übergeben. Geben Sie den Zugriff zuerst oder zusätzlich unter **Zugriff**. Damit das nicht unbemerkt bleibt, zeigt die Runde es an: Auf dem Tab **Personen** und in der Liste **Aufgaben** unter **Erhebung** trägt eine Person, die zugeordnet ist, aber die Daten der App nicht bearbeiten darf, den Hinweis **Kann nicht antworten**; fährt man darüber, steht dort, was ihr fehlt. Der Hinweis verschwindet, sobald sie diesen Zugriff hat. **Personen übernehmen aus** auf dem Tab Definition holt die Personen einer anderen Runde in einem Zug in diese, wobei bereits vorhandene übersprungen werden - die Runde des nächsten Jahres einzurichten bedeutet also nicht, vierzig Adressen erneut zu tippen. Angeboten wird das, solange die Runde ein Entwurf ist. Das Ergebnis benennt jede Art von Übersprungenem, damit nichts stillschweigend zurückbleibt: * Wer in dieser Erhebungsrunde **zurückgezogen** ist, bleibt es - die Übernahme stellt nie Schreibzugriff wieder her -, und Sie können diese Personen unter Personen wiederherstellen, wenn sie erneut befragt werden sollen. * Eine Einheit, die **keine Berichtseinheit dieser App mehr** ist, bleibt zurück. Das passiert, wenn sich die Erhebungsebene der App seit der Runde verschoben hat, aus der Sie übernehmen. * Eine Adresse, die der Arbeitsbereich **niemandem mehr zuordnen kann**, bleibt zurück: niemand hier verwendet sie noch, oder zwei Personen tun es, oder die Einladung, aus der sie entstand, wurde von jemandem angenommen, der den Arbeitsbereich seither verlassen hat, oder sie ist abgelaufen, bevor jemand angenommen hat. Die Übernahme prüft genau das, was auch die Auswahl prüft: Eine Adresse, die der Arbeitsbereich keiner Person zuordnen kann, wird durch eine Übernahme so wenig für eine Einheit verantwortlich wie durch eine Zuordnung von Hand. Soll weiterhin jemand für diese Einheit antworten, laden Sie die Person ein oder wählen Sie sie unter Personen aus. **Sie können ein Mitglied dieses Arbeitsbereichs auswählen oder jemanden, den Sie eingeladen haben und der noch nicht angenommen hat** - und für die Übernahme gilt genau dieselbe Regel. Eine offene Einladung erscheint in derselben Liste, gekennzeichnet mit **Eingeladen**. Ist die gesuchte Person weder das eine noch das andere, laden Sie sie auf der Seite **Zugriff** ein - die Auswahl verlinkt dorthin - und ordnen Sie sie zu, sobald die Einladung besteht. Ein Freitextfeld für Adressen gibt es nicht: Sie wählen immer eine Person aus, die der Arbeitsbereich bereits kennt. Eine Adresse, die hier niemand hat, kann damit nicht verantwortlich gemacht werden. **Eine eingeladene Person zuzuordnen funktioniert und beginnt zu greifen, sobald sie annimmt.** Bis dahin hängt die Zuordnung an ihrer Adresse und gewährt nichts - die Person kann sich noch nicht anmelden. Sobald sie die Einladung annimmt und zum ersten Mal antwortet, gehört die Zuordnung ihr als Person. Ziehen Sie die Einladung stattdessen ZURÜCK, bleibt die Zuordnung an der Einheit, gewährt nichts und wird als **Nicht im Arbeitsbereich** angezeigt; entfernen Sie sie oder laden Sie die Person erneut ein. Ein Mitglied zuzuordnen benennt die PERSON und nicht den Text ihrer Adresse; die Adresse ist danach nur noch eine Beschriftung: Wechselt die Person ihre E-Mail-Adresse, behält sie ihre Einheiten, und wird ihre alte Adresse später einer neuen Kollegin gegeben, erbt diese nichts. Entfernen Sie eine Zuordnung, wenn jemand geht. Jede Adresse zeigt, wo sie steht: * **Mitglied** - die Person ist in diesem Arbeitsbereich und kann sich anmelden. * **Eingeladen** - sie wurde eingeladen und hat noch nicht angenommen. * **Nicht im Arbeitsbereich** - niemand hier hält diese Adresse. So gekennzeichnet sein kann eine Zeile nur, wenn sie entstand, bevor Verantwortung durch Auswahl eines Mitglieds vergeben wurde, oder wenn die Person seither aus dem Arbeitsbereich entfernt wurde. Eine solche Zeile gewährt nichts: Entfernen Sie sie und wählen Sie die Person erneut aus, sobald sie Mitglied ist. Teilen sich zwei Mitglieder des Arbeitsbereichs dieselbe Adresse, wird die Zuordnung für beide abgelehnt und nennt die Adresse: Die Zuordnung hängt an dieser Adresse, und die sagt nicht, wen Sie meinen. Geben Sie den Beteiligten je eine eigene Adresse und wählen Sie danach die gemeinte Person aus. Eine bereits angenommene Einladung, deren Adresse kein aktuelles Mitglied mehr verwendet, wird ebenfalls abgelehnt - die Person hat den Arbeitsbereich womöglich verlassen oder die Adresse in ihrem Konto geändert. Laden Sie sie erneut ein oder wählen Sie stattdessen ein aktuelles Mitglied. **Zuweisen können Sie nur eine BERICHTSEINHEIT** - ein aktives Element der festgelegten Dimension der App, auf der Ebene, auf der die App erhebt. Welche das sind, zeigt die Leiste: Alles andere wird als Struktur dargestellt und lässt sich gar nicht erst auswählen. Ein übergeordnetes Element, eines darunter oder ein deaktiviertes ist also nie etwas, für das Sie eine Person auswählen und danach eine Ablehnung erhalten. Die Zahl einer Region wird nicht neben ihren Standorten eingetragen, sondern von der Pipeline aus ihnen aggregiert - antworten tut also, wer einen Standort hält. Wer für drei Standorte verantwortlich ist, wird allen dreien zugewiesen. Eine Einheit ohne zugewiesene Person ist in keiner Runde enthalten. Das ist Absicht: Eine erhobene Einheit hat immer jemanden, den man fragen kann. **Jemanden aus einer bereits laufenden Runde zu entfernen ZIEHT DIE PERSON ZURÜCK, statt sie zu löschen.** Die Runde hat sie gefragt, und ihre Antworten und jede Beurteilung dazu überdauern die Zuordnung - die Zeile bleibt also durchgestrichen und als **Zurückgezogen** gekennzeichnet stehen, und die Person kann schlicht nicht mehr antworten und sieht die Runde nicht mehr. Holen Sie sie mit dem Wiederherstellen-Bedienelement neben der Zeile zurück, oder indem Sie dieselbe Adresse erneut hinzufügen. In einer Runde, die noch ein Entwurf ist, entfernt dasselbe Bedienelement die Zeile ganz, denn ein Entwurf hat niemanden gefragt. ## Schritt 4: sie öffnen [#schritt-4-sie-öffnen] Eine Erhebungsrunde hat drei Zustände, und die Schaltflächen sagen, was jeder Wechsel bewirkt. | Zustand | Was er bedeutet | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Entwurf** | Es wurde noch niemand etwas gefragt. **Für eine Runde im Entwurf sieht niemand eine Aufgabe**, gleich wie viele Personen Sie ihr schon zugeordnet haben - Sie können sie also besetzen, es sich anders überlegen und neu besetzen, ohne dass jemand davon erfährt. Ein Entwurf ändert keine Zugriffsrechte und trägt keine Beurteilungen, Sie können ihn also frei bearbeiten. | | **Offen** | Die Runde läuft. Die Fragen einer Einheit können jetzt nur noch von den für sie verantwortlichen Personen beantwortet werden. | | **Geschlossen** | Die Runde ist vorbei. Alle von ihr regierten Antworten sind schreibgeschützt, für alle, auch für Administratoren. | Sobald eine Erhebungsrunde den Entwurf verlässt, ist ihre Definition fest - sie ist das, worauf sich jede erfasste Beurteilung bezieht. Eine gelaufene Runde wird durch **Schließen** beendet, nie durch Löschen, damit keine erfasste Beurteilung verloren geht. Eine offene Erhebungsrunde können Sie nur zurück in den Entwurf setzen, solange keine Antwort eine Beurteilung trägt. ### Zwei Runden dürfen nicht gleichzeitig dieselbe Frage stellen [#zwei-runden-dürfen-nicht-gleichzeitig-dieselbe-frage-stellen] Eine Runde zu öffnen wird abgelehnt, wenn eine andere, bereits offene oder geschlossene Runde einen derselben Datenpunkte über einen **überlappenden** Berichtszeitraum abfragt. Die Meldung nennt jene Runde, den ersten gemeinsamen Datenpunkt und wie viele sie sich teilen, und sie nennt den Ausweg: die Datenpunktliste dieser Runde verkleinern, solange sie noch ein Entwurf ist, oder die andere Runde zurück in den Entwurf setzen. Eine Runde, die Sie ERNEUT ÖFFNEN, lässt sich nicht mehr bearbeiten - dort bleibt Ihnen nur der zweite Weg. Der Grund: Über eine Zahl muss GENAU EINE Runde entscheiden, wer sie beantworten darf und ob die Antwort eingefroren ist. Zwei Runden über einer Antwort bedeuten zwei Urteile und keine Möglichkeit zu sagen, welches gilt. Runden über denselben Datenpunkten in VERSCHIEDENEN Berichtszeiträumen sind in Ordnung - das ist der gewöhnliche Fall von Jahr zu Jahr -, ebenso Runden über verschiedenen Datenpunkten im selben Zeitraum. ### Veröffentlichen Sie die App, bevor eine Runde den Entwurf verlässt [#veröffentlichen-sie-die-app-bevor-eine-runde-den-entwurf-verlässt] Sie definieren eine Runde gegen das, was Sie im Editor sehen. BEANTWORTET wird sie gegen die **veröffentlichte** Struktur der App - dieselbe Struktur, gegen die jede Zahl erfasst wird. Eine Runde, die offen oder geschlossen ist, während beides auseinanderfällt, kann niemand beantworten. Der Wechsel aus dem Entwurf wird deshalb abgelehnt, und die Meldung nennt den Fall: * die App hat **gar keine Berichtseinheiten-Dimension** - legen Sie zuerst eine fest (siehe [Schritt 1](#schritt-1-die-berichtseinheiten-dimension-der-app-wählen)), * ihre Berichtseinheiten-Dimension ist **nicht in der veröffentlichten Struktur**, * der Entwurf legt eine **andere** Berichtseinheiten-Dimension fest als die veröffentlichte Struktur, * eine Einheit, der Sie jemanden zugewiesen haben, ist noch nicht in der veröffentlichten Struktur, oder * eine Einheit, der Sie jemanden zugewiesen haben, steht in der veröffentlichten Struktur, ist aber **keine Berichtseinheit dieser App** - sie liegt auf einer anderen Ebene, oder sie liegt auf der Erhebungsebene und wurde deaktiviert. Die Meldung nennt die Einheit, die Dimension und die Ebene. Ziehen Sie diese Person unter **Personen** zurück, ordnen Sie ihr die Einheiten der Erhebungsebene zu, für die sie wirklich antwortet, oder aktivieren Sie die Einheit wieder. Wer für eine Einheit OBERHALB der Erhebungsebene zuständig ist, würde für jede Einheit darunter antworten, auch für solche, die ihr niemand zugewiesen hat - deshalb wird das abgelehnt und nicht durchgelassen. Setzen Sie die Berichtseinheiten-Dimension im Entwurf auf die veröffentlichte zurück. Oder veröffentlichen Sie die App - solange noch keine Runde der App offen oder geschlossen ist und der Entwurf eine eigene Dimension festlegt - und holen Sie die Runde danach erneut aus dem Entwurf. Die Prüfung läuft immer dann, wenn eine Runde den **Entwurf verlässt**, und erneut, wenn eine geschlossene Runde **wieder geöffnet** wird, denn eine geschlossene Runde bestimmt über ihre Antworten genauso wie eine offene. Eine bereits offene Runde zu schließen wird nicht geprüft: Sie erhebt schon, das Schließen beginnt also nichts Neues, und eine Runde nicht mehr STOPPEN zu können wäre die schlechtere Antwort. Die Schaltflächen hier bringen eine Entwurfsrunde nach **Offen** und nicht weiter; den Entwurf verlassen heißt hier also: sie öffnen. Die Regel ist trotzdem über die Zustände formuliert, denn die API und die KI-Werkzeuge können eine Entwurfsrunde auch direkt nach **Geschlossen** bringen, und dieselbe Prüfung deckt diesen Wechsel ab. Solange eine Runde offen oder geschlossen ist, wird aus demselben Grund abgelehnt, eine Version zu veröffentlichen oder wiederherzustellen, die die Berichtseinheiten-Dimension der App wechseln oder eine Einheit unter der dafür verantwortlichen Person wegbewegen würde: Die bereits erfassten Antworten und Beurteilungen benennen diese Einheiten. Eine **neue** Einheit hinzuzufügen ist in Ordnung, ebenso jede andere Strukturänderung. Eine laufende Runde friert die App nicht ein. Die Berichtseinheiten-Dimension der App zu löschen, wird abgelehnt, solange die App IRGENDEINE Runde hat, auch einen Entwurf. Löschen Sie zuerst diese Runden und danach die Dimension - und eine Runde, die den Entwurf verlassen hat, müssen Sie erst in den Entwurf zurückversetzen, bevor sie sich überhaupt löschen lässt. Die vollständige Reihenfolge lautet also: Prüfentscheidungen zurücksetzen, in den Entwurf versetzen, löschen. (Um die Dimension zu verschieben, brauchen Sie weniger: Die Runde in den Entwurf zurückzuversetzen genügt.) Eine Dimension, die die App nicht dafür festgelegt hat, lässt sich wie immer löschen. Das Öffnen einer Runde verschickt keine E-Mail. Die für eine Einheit verantwortlichen Personen sehen die Runde, sobald sie die App das nächste Mal öffnen - sagen Sie ihnen also auf dem üblichen Weg Bescheid. ## Schritt 5: sie beantworten [#schritt-5-sie-beantworten] Ein Ansprechpartner öffnet **Aufgaben** in der linken Navigation. Er betritt die fragende App nie, und Zugriff auf den Editor braucht er gar nicht: Eine Zahl zu liefern ist kein Grund, in die App einer anderen Person geführt zu werden. Der Eintrag Aufgaben erscheint erst, wenn tatsächlich etwas von jemandem erwartet wird, und trägt die Anzahl der offenen Runden. Die Seite zeigt EINE Zeile je **Aufgabe** - eine Runde, eine Person, wie viele Berichtseinheiten diese Person auch hält. Eine Standortleiterin mit drei Werken sieht EINE Zeile und öffnet EINEN Fragebogen für alle drei, statt drei zu verfolgen. Jede Zeile nennt die Runde, die fragende App, das Fälligkeitsdatum und den Fortschritt in ZELLEN - "6 von 8 Zellen ausgefüllt" - damit wer für einen von zwei Standorten geantwortet hat, als halb fertig gelesen wird und nicht als fast fertig. Ein Klick auf die Zeile öffnet **den Fragebogen** für die ganze Aufgabe, mit einer Spalte je Einheit bei jeder Frage. Der Weg zurück ist dieselbe Seite; nichts an der Aufgabe führt in die App. Sonst ändert sich am Fragebogen nichts - es ist dasselbe geführte Formular wie unter [Eine Erhebungsrunde beantworten](./questionnaire.mdx) beschrieben - er ist nun aber eingegrenzt auf die Fragen, die die Runde **der eigenen Einheit** stellt (siehe "Warum eine Einheit manche Fragen bekommt und andere nicht" weiter unten), und auf die Zahlen dieser Einheit. Der Berichtszeitraum folgt der Runde, denn eine Erhebungsrunde erhebt für genau einen - auf diesem Bildschirm gibt es also gar keine Zeitraumauswahl. **Eine Aufgabe erscheint, sobald die Runde GEÖFFNET wird, und nicht früher.** Jemanden einer Runde im Entwurf zuzuordnen sagt dieser Person nichts; erst das Öffnen fragt sie, und in diesem Moment erhalten alle Zugeordneten ihre Aufgabe. Das Schließen der Runde nimmt sie nicht wieder weg - die Aufgabe bleibt lesbar, damit Antworten, Beurteilungen und die Diskussion dazu weiterhin nachgelesen werden können; es macht die Antworten nur unveränderbar. Eine Runde, aus der jemand **zurückgezogen** wurde, erscheint dieser Person nicht mehr. Bittet eine prüfende Person um eine Änderung, steht die Begründung an der Antwort, und Sie können in der Diskussion daneben antworten - siehe [Schritt 6](#schritt-6-die-antworten-beurteilen). Vera oder einen angebundenen KI-Assistenten zu bitten, eine Runde auszufüllen, funktioniert genauso und ist genauso begrenzt: Der Assistent reicht genau so weit wie **Sie** und keinen Schritt weiter - Ihre eigenen Einheiten, wenn Sie verantwortlich sind, jede Einheit, wenn Sie die Runden der App verwalten. Eine verantwortliche Person zuzuweisen und eine Beurteilung zu erfassen kann ein Assistent gar nicht: Das Erste würde erweitern, wer Daten erfassen darf, das Zweite hält das Urteil einer namentlich genannten Person fest. ## Schritt 6: die Antworten beurteilen [#schritt-6-die-antworten-beurteilen] Ein Admin oder eine berichtsverantwortliche Person öffnet denselben Tab **Erhebung**. Die Tabelle ist Ihre Arbeitsliste: eine Zeile je Runde und Einheit, die Sie durchsuchen, nach jeder Spalte sortieren, blättern und exportieren können wie jede andere Tabelle in der Anwendung. | Spalte | Was sie nennt | | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Erhebungsrunde** / **Status** / **Berichtszeitraum** | Zu welcher Runde die Zeile gehört, ob sie läuft, und der Zeitraum, für den erhoben wird | | **Einheit** | Die Berichtseinheit, in der Reihenfolge der Berichtseinheiten-Dimension der App - dieselbe Reihenfolge wie der Tab Personen der Runde, damit eine Einheit dort steht, wo Sie sie erwarten | | **Verantwortlich** | Wer die Einheit HEUTE hält, eine Adresse je Zeile, damit jede lesbar ist (ab sieben die ersten fünf plus eine Zeile "+N weitere", deren Tooltip den Rest trägt). Steht dort niemand, liest die Zelle "Für diese Einheit ist heute niemand verantwortlich." Nur Administratoren sehen diese Spalte | | **Teilnahme** | Wo die Einheit in der Runde steht - siehe die Kennzeichen unten | | **Beantwortet** | **N von M beantwortet** mit einem Balken: wie viele der Fragen, die diese Einheit bekommt, bereits mindestens eine Zahl innerhalb des Berichtszeitraums der Runde tragen. Fahren Sie über die Zelle für die Abdeckung - wie viele der Fragen der Runde diese Einheit überhaupt erreichen. Kann der Fortschritt gerade nicht berechnet werden, sagt die Zelle das, statt null zu behaupten | | **Genehmigt** / **Zu ändern** / **Offen** | Wie ihre Antworten beurteilt wurden und wie viele noch warten | | **Prüfen** | Der Klemmbrett-Knopf in der letzten Spalte - immer sichtbar, und der eine Weg, den Fragebogen dieser Einheit für diese Runde zu öffnen | Das Suchfeld trifft den Namen der Runde, den Namen der Einheit und die Adressen der verantwortlichen Personen. In einem großen Baum tippen Sie also einen Standortnamen oder die Adresse einer Kollegin und landen direkt auf deren Zeile. Sortieren ist Ihre Sache; solange Sie nicht sortieren, stehen die Runden neueste zuerst, mit den Einheiten jeder Runde in der Reihenfolge der Berichtseinheiten-Dimension der App. | Kennzeichen | Was es bedeutet | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Befragt** | Für diese Einheit ist in dieser Runde jetzt jemand verantwortlich | | **Zurückgezogen** | Die Runde hat die Einheit befragt, und alle dafür verantwortlichen Personen sind seither zurückgezogen worden. Ihre Antworten und die Beurteilungen dazu bleiben lesbar, und Sie können eine Beurteilung weiterhin wieder öffnen | | **Nicht befragt** | Die Runde hält für diese Einheit überhaupt niemanden, und sie steht nur deshalb in der Liste, weil zu einer ihrer Antworten ein Prüfergebnis erfasst wurde - **eine Freigabe bleibt also immer auffindbar und, falls sie falsch war, zurücksetzbar**. Keine Antwort bleibt durch eine Beurteilung gesperrt, die niemand mehr erreichen kann | Wer HEUTE für eine Einheit verantwortlich ist, ist eine andere Frage als das Kennzeichen: Eine **zurückgezogene** Einheit trägt ein Kennzeichen und keine Adresse. ### Über eine einzelne Antwort sprechen [#über-eine-einzelne-antwort-sprechen] Zu jeder Antwort gehört eine eigene **Diskussion** - ein Strang je Frage und Berichtseinheit, geöffnet direkt an der Antwort im Fragebogen und auf dem Beurteilungsbildschirm. Dort wird eine Änderungsbitte beantwortet: Die Begründung der prüfenden Person steht an der Antwort, und wer die Einheit hält, antwortet dort statt per E-Mail - so stehen die Frage und die Antwort darauf neben der Zahl, um die es geht. * **Schreiben darf, wer diese Einheit hält, und wer die Runden der App verwaltet.** Sonst sieht niemand den Strang. * **Es wird nur angehängt.** Eine Nachricht kann von niemandem bearbeitet oder gelöscht werden - genau das macht den Strang als Nachweis des Vereinbarten brauchbar. * **Er bleibt offen, auch wenn die Antwort es nicht mehr ist.** Über eine genehmigte Antwort und eine geschlossene Runde lässt sich weiterhin sprechen - eine eingefrorene Zahl ist genau die Art, zu der man Fragen hat. * **Nichts hängt davon ab.** Ein Strang blockiert keine Freigabe, ändert keine Beurteilung und sperrt keine Zelle; er ist das Gespräch, nicht die Entscheidung. ### Warum eine Einheit manche Fragen bekommt und andere nicht [#warum-eine-einheit-manche-fragen-bekommt-und-andere-nicht] Sie stellen das nicht ein, und es gibt nichts abzugleichen. Jeder Datenpunkt wird auf genau einer **Ebene** der Berichtseinheiten-Dimension der App erhoben - manche auf Standort, manche auf Region - und daraus folgt die Regel: > Eine Einheit wird zu einem Datenpunkt befragt, wenn mindestens ein Mitglied der > Erhebungsebene dieses Datenpunkts auf oder unterhalb dieser Einheit liegt. Gelesen an einem Baum Region -> Standort: | Die Einheit | Ein Datenpunkt auf Standortebene | Ein Datenpunkt auf Regionsebene | | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | **Europa** (eine Region mit Hamburg und Rotterdam darunter) | befragt - der Fragebogen gibt ihr eine Spalte je Standort, und die verantwortliche Person antwortet für beide | befragt - eine Antwort, für Europa selbst | | **Hamburg** (ein Standort) | befragt - eine Antwort, für Hamburg | **nicht befragt** - Hamburg ist keine Region, und unter Hamburg liegt keine | | **Iberien** (eine Region, unter der noch kein Standort liegt) | **nicht befragt** - darunter liegt nichts, wofür geantwortet werden könnte | befragt | Eine Frage, die eine Einheit nicht bekommt, wird ihr schlicht nicht gezeigt und zählt nicht gegen ihre Vollständigkeit: Eine Einheit, die 1 von 2 Fragen bekommt, erreicht 100 %, indem sie diese eine beantwortet - und die Spalte **Beantwortet** der Arbeitsliste zählt gegen dieselbe befragte Menge, ihr Nenner ist also das, was die Einheit tatsächlich beantworten kann. Eine Einheit, die **keine** davon bekommt, steht weiterhin in der Arbeitsliste und liest ein gedämpftes `0 von M Fragen` mit dem Grund in der Zeile darunter, an der Stelle des Anteilsbalkens - meist, weil ihr Teil des Baums kein Mitglied auf der Erhebungsebene der Runde hat. Ergänzen Sie die fehlenden Einheiten, oder erheben Sie den Datenpunkt auf einer Ebene, die die Einheit erreicht, dann erscheinen die Fragen. Ein deaktiviertes Mitglied ist aus der Erhebungsrunde genommen: Eine Region, deren einziger Standort deaktiviert ist, wird zu dieser Standortfrage nicht mehr befragt. Öffnen Sie eine Einheit, landen Sie in **genau dem Fragebogen, den diese Person ausgefüllt hat** - dieselben Fragen in derselben Reihenfolge, jede mit ihrer Anleitung, ihrer Einheit, ihrem eigenen Raster und den erfassten Zahlen. Es gibt keine gesonderte Werteliste zum Beurteilen, denn eine flach gelistete Zahl zu beurteilen heißt, etwas anderes zu beurteilen als das, was beantwortet wurde. Während der Beurteilung ist der Fragebogen schreibgeschützt. Das ist Absicht: eine Zahl zu korrigieren und die eigene Korrektur dann unter fremdem Namen zu genehmigen ist keine Beurteilung. Muss eine Zahl wirklich korrigiert werden, verlassen Sie die Beurteilung und bearbeiten sie wie sonst auch. Haken Sie beliebig viele Antworten an und geben Sie ein Urteil: | Urteil | Was es bewirkt | | --------------------- | --------------------------------------------------------------------------------------------------------------- | | **Genehmigen** | Nimmt die Zahl an und friert ihre Zellen ein, für alle einschließlich Administratoren. | | **Änderung erbitten** | Gibt die Antwort mit Ihrer Begründung zurück und öffnet sie wieder, damit dieselbe Person sie korrigieren kann. | | **Wieder öffnen** | Entfernt die Beurteilung, sodass die Antwort wieder offen ist und nichts dagegen erfasst bleibt. | Es gibt zwei Urteile und kein drittes für "nicht angenommen": Das zu sagen und die Zahl zugleich einzufrieren, würde eine abgelehnte Zahl unveränderbar im Bericht stehen lassen. **Änderung erbitten** sagt dasselbe, trägt die Begründung und lässt die Antwort offen - so endet die Runde mit einer korrigierten Zahl oder einer ehrlichen Lücke. Eine erbetene Änderung muss eine **Begründung** tragen. Sie ist keine Notiz an Sie selbst: Die antwortende Person liest sie neben der Frage, die sie korrigieren soll. Das Urteil gilt in einem Zug für jede angehakte Antwort, und jede Antwort trägt es danach neben der Frage. Für die antwortende Person ist eine genehmigte Antwort schreibgeschützt, unter einem Kennzeichen, das nennt, wer wann beurteilt hat; eine zurückgegebene Antwort ist wieder bearbeitbar, unter Ihrer Begründung. Eine Beurteilung können nur Admins und Berichtsverantwortliche der App erfassen. Wer sonst einen Beurteilungslink öffnet, erhält schlicht den eigenen Fragebogen mit den eigenen Antworten. ## Wo Sie nachlesen, wer was genehmigt hat [#wo-sie-nachlesen-wer-was-genehmigt-hat] Das an einer Antwort angezeigte Urteil ist das **derzeit** geltende. Brauchen Sie den Nachweis - wer eine Zahl wann genehmigt hat und ob sie zuvor jemand wieder geöffnet hat -, öffnen Sie aus Ihrer App-Liste die App **Audit Analytics** und dort den Reiter **Indicator Data Changes**. Unter der Tabelle der Wertänderungen steht eine zweite Tabelle, **Answer Review Decisions**. Sie führt eine Zeile je Antwort, die ein Urteil benannt hat: Wer die fünf Antworten einer Einheit in einem Zug genehmigt, hinterlässt fünf Zeilen mit derselben Audit-Id. Jede Zeile nennt den Berichtszeitraum, die Berichtseinheit, den Datenpunkt, das Urteil und die beurteilende Person - unter dem Namen, den sie damals trug, nicht unter dem heutigen. Ein wieder geöffnetes Urteil wird wie jede andere Entscheidung erfasst; filtern Sie die Tabelle also auf eine Einheit und einen Datenpunkt, lesen Sie den gesamten Verlauf dieser Antwort, das Neueste zuerst. Der Link in einer Zeile öffnet den Reiter **Daten** der App, auf dem die beurteilten Zahlen liegen. Zweierlei zeigt sie bewusst nicht. Die **Begründung**, die Sie bei einer Ablehnung oder einer erbetenen Änderung gegeben haben, steht nicht darin - eine Begründung ist an die antwortende Person gerichtet und bleibt neben der Frage, an der sie gelesen wird. Und nichts darin wird je einem Assistenten zugeschrieben: Ein Urteil zu erfassen kann nur ein Mensch, die KI-Spalte bleibt also immer leer. Die App Audit Analytics sehen nur Administratorinnen und Administratoren des Arbeitsbereichs. Fehlt sie in Ihrem Arbeitsbereich noch, kann sie eine administrierende Person aus der App-Liste heraus anlegen. ## Beurteilungen per SQL lesen [#beurteilungen-per-sql-lesen] Urteile sind Daten, nicht nur Kennzeichen auf einem Bildschirm, und zwei Tabellen tragen sie für alles, was SQL spricht. **In der App** führt die Pipeline eine Tabelle **Cell reviews**: eine Zeile je erfasster Zahl mit dem aktuell geltenden Urteil - genehmigt, Änderung erbeten oder keines - samt Begründung, entscheidender Person, Zeitpunkt und zugehörigem Berichtszeitraum. Ein Urteil trifft genau die EIGENE Berichtseinheit der Zahl, ohne Weg nach oben oder unten im Baum, und deckt jede weitere Aufgliederung derselben Frage innerhalb des Zeitraums ab - genau wie der Fragebogen es zeigt. Die Tabelle ist immer aktuell und existiert in jeder Indikator-App, ob eine Runde läuft oder nicht. **Im Data Lake Ihres Arbeitsbereichs** werden dieselben Urteile in eine Tabelle namens `ireview_app` neben den Zahlen der App gespiegelt, aufgefrischt innerhalb von etwa fünf Minuten nach einer Entscheidung - ein BI-Werkzeug oder ein Export, der den Lake liest, kann "was berichtet wurde" also mit "was genehmigt wurde" verbinden, ohne die App zu berühren. Der Spiegel ist eine Kopie zum Lesen: Was ein Urteil ERLAUBT, entscheidet immer der eigene Bestand der Plattform, nie der Lake. ## Erhebungsfortschritt messen [#erhebungsfortschritt-messen] Auch der Fortschritt ist Daten. Die Pipeline jeder Indikator-App führt eine Tabelle **Erhebungsfortschritt**: eine Zeile je Runde, Berichtseinheit und Frage, mit der Auskunft, ob die Einheit sie gestellt bekam, ob mindestens eine Zahl innerhalb des Berichtszeitraums der Runde sie beantwortet, wie viele Zellen diese Antwort umfasst, und welche Beurteilung dazu erfasst ist - daneben Name, Status und Berichtszeitraum der Runde sowie die Anzahl der verantwortlichen Personen der Einheit. Eine Einheit, der nichts gestellt wird, erscheint trotzdem, als einzelne Zeile ohne Frage, damit niemand von einem Dashboard verschwindet. Die Tabelle ist immer aktuell, existiert in jeder Indikator-App, und eine App ohne Erhebungsrunde liest schlicht eine leere Tabelle. Drei Begleittabellen tragen die Definitionen, aus denen sie zusammengesetzt wird - die Runden, die Einheiten jeder Runde und die Fragen jeder Einheit - sodass Ihr eigenes SQL an beiden Enden ansetzen kann. **Für ein Diagramm in einer Analyse-App** fügen Sie deren Pipeline einen Knoten **Indikatordaten** hinzu und stellen dessen **Inhalt** auf **Erhebungsfortschritt**: Der Knoten importiert die ganze Fortschrittstabelle der gewählten Indikator-App, und ein Lauf nimmt sie wie jede andere Datenquelle als Schnappschuss auf - erneut ausführen heißt auffrischen. Eingrenzen oder umformen lässt sie sich mit einem gewöhnlichen SQL-Schritt, dann eine Tabelle oder ein Diagramm auf die Visualisierung: "Hamburg hat 12 von 15 beantwortet" ist einen Filter und eine Zählung entfernt. Fortschritt ist bewusst eine Sache von Zahlen: Keine dieser Tabellen trägt den Namen oder die Adresse einer verantwortlichen oder beurteilenden Person. Wer verantwortlich ist, bleibt auf den Verwaltungsoberflächen; wer entschieden hat, bleibt im Prüfprotokoll. ## Was eine Erhebungsrunde vertraulich hält und was nicht [#was-eine-erhebungsrunde-vertraulich-hält-und-was-nicht] Das gehört genau gesagt, weil man leicht mehr annimmt. * Das **Schreiben** ist eingegrenzt. Solange eine Runde offen ist, kann ein Ansprechpartner nur Zahlen für die eigenen Einheiten erfassen, und ein Mitglied des Arbeitsbereichs ohne Zuweisung kann von den erhobenen Zahlen dieser App gar keine erfassen. * **Die Ansichten, die die Zahlen Zelle für Zelle auflisten, sind genauso eingegrenzt**: der Fragebogen, der Tab **Daten** der App und das Datenraster des Editors. In einer App mit laufender Runde sieht dort, wer ihre Runden nicht verwaltet, die Zahlen der eigenen Einheiten und sonst keine - und der Tab Daten SAGT das, statt ein leeres Raster zu zeigen: Er nennt die Einheiten, die er Ihnen zeigt, oder erklärt, dass Sie für keine verantwortlich sind, oder dass der gewählte Datenpunkt eine einzige unternehmensweite Antwort hat, die gar nicht auf eine Einheit eingegrenzt ist. Der festgehaltene Stand einer **früheren Version** ist nicht eingegrenzt. * **Summen und Aggregate bleiben offen.** Wer die App öffnen kann, sieht weiterhin die Übersicht, die Vollständigkeitsansichten, den Aktivitätsverlauf und jede Summe und jedes Aggregat - genau wie vor Ihrer Erhebungsrunde, auch wenn darin Einheiten zusammengezählt werden, für die diese Person nicht verantwortlich ist. * **Einzelne Zellen dagegen nicht.** Überall dort, wo die App eine einzelne Zelle statt einer Summe benennt - der Vorjahreswert hinter einer Frage, die von einer Prüfregel oder einer Konsistenzprüfung beanstandeten Zellen samt der Zahlen, an denen sie gescheitert sind, und die von einer Regel als nicht zutreffend markierten Zellen -, sieht eine Person, die die Runden der App nicht verwaltet, nur die Zellen ihrer eigenen Einheiten. Die Anzahlen daneben bleiben vollständig, sodass erkennbar bleibt, dass es mehr davon gibt als angezeigt werden. Eine Erhebungsrunde entscheidet, wer GEFRAGT wird und wer SCHREIBEN darf, und sie hält die Einzelzahlen einer Einheit von einer anderen fern. Die Summen der App verbirgt sie nicht vor dem übrigen Arbeitsbereich. # Daten (/docs/de/indicator-apps/data) Der Tab **Daten** zeigt einen Datenpunkt nach dem anderen, als **Datensatz** und nicht als Formular: was der Datenpunkt ist und welche Zahlen im Zeitverlauf für ihn erfasst wurden. Hier lesen Sie eine Zahl, prüfen ihre Herkunft oder korrigieren sie - ohne sich dafür durch einen Fragebogen zu arbeiten. Hier erfasst auch eine App, die **keine Erhebungsrunde** durchführt. Hat niemand Erhebungsrunden eingerichtet, ist dies der einzige Ort, an dem Zahlen eingegeben werden - und in einer solchen App können das nur Administratorinnen und Administratoren sowie Berichtsverantwortliche der App, die dort auch Daten erfassen dürfen, denn eine Zahl zu erfassen erfordert entweder die Verantwortung für die Erhebung der App oder eine Zuordnung, und eine Zuordnung kommt aus einer Runde. ## Sich zurechtfinden [#sich-zurechtfinden] **Links** steht die Liste der Datenpunkte der App, in ihren Gruppen, mit einem Suchfeld darüber. Es ist eine Leseliste: Hier gibt es nichts hinzuzufügen, zu ziehen oder umzusortieren. Ein Klick auf einen Datenpunkt öffnet rechts seinen Datensatz. **Rechts** stehen drei Dinge, in dieser Reihenfolge: * der **Name** des Datenpunkts und seine technische ID, * eine eingeklappte Aufklappung **Konfiguration** und * die **Zahlen**. Öffnen Sie **Konfiguration**, um zu lesen, wie der Datenpunkt definiert ist: sein Datentyp, seine Einheit, seine Erläuterung, nach welchen Dimensionen er aufgeschlüsselt ist, seine Validierungen und seine Anwendbarkeitsregel. Sie ist hier nur lesbar - geändert wird ein Datenpunkt im Editor -, und Zuklappen räumt sie wieder weg. Darunter steht dasselbe Raster, das auch der Tab Dateneingabe des Editors verwendet: eine Tabelle oder ein Diagramm, eine Zeitraumnavigation, Transponieren und die Aggregationssteuerung für eine Zahl. Ihre Ansicht steht in der Adresse, sodass ein Link, den Sie jemandem schicken, denselben Datenpunkt, dieselben Jahre und dieselbe Tabellen- oder Diagrammwahl öffnet, die Sie gerade sehen. ## Lesen ist offen, Schreiben nicht [#lesen-ist-offen-schreiben-nicht] **Wer die App öffnen kann, liest hier die Zahlen jeder Einheit**, ob die App eine Runde durchführt oder nicht - es gibt keinen Streifen, keine verborgene Spalte und keine eingegrenzte Tabelle. **Wer eine Zahl ÄNDERN darf, ist eine andere Frage**, und die Antwort lautet: eine Administratorin oder ein Administrator, eine berichtsverantwortliche Person der App, die dort auch Daten erfassen darf, oder wer eine Zuordnung hält, die diese Zahl abdeckt. Das ist enger als bei einer Datensatzzeile, die jede Person mit **Mitwirkender**, **App-Ersteller** oder **Admin** bearbeiten kann, denn eine Runde legt fest, wer für welche Berichtseinheit antwortet. ## Hier bearbeiten [#hier-bearbeiten] Sie können auf diesem Tab Zahlen eingeben und korrigieren, genau wie im Datenraster des Editors, unter denselben Regeln: * die Zahl muss in einen offenen [Berichtszeitraum](./collection-overview.mdx#reporting-periods) fallen, * Sie müssen Administratorin oder Administrator oder berichtsverantwortlich für die App sein oder eine Zuordnung halten, die diese Zahl abdeckt - also **Ihre eigenen** Einheiten, für die Datenpunkte, die die Runde abfragt, in ihrem Berichtszeitraum, * eine Zahl, die gar keine Berichtseinheit nennt - ein unternehmensweiter Datenpunkt - ist Administratorinnen und Administratoren und den Berichtsverantwortlichen der App vorbehalten, und * eine **freigegebene** Antwort ist für alle gesperrt, einschließlich Administratorinnen und Administratoren, bis eine prüfende Person sie wieder öffnet. Eine zur **Überarbeitung** zurückgegebene Antwort bleibt beschreibbar - genau darum wird sie zurückgegeben. Weist eine dieser Regeln eine Änderung zurück, sagt sie welche und was zu tun ist - und zwar an der Stelle, an der Sie die Änderung versucht haben. # Weitere Dimensionen (/docs/de/indicator-apps/dimensions/other-dimensions) Eine **Dimension** ist eine Möglichkeit, eine Metrik aufzuschlüsseln, damit Sie sie detaillierter verfolgen können. Neben der Zeit können Sie einem Indikator Dimensionen wie **Region** oder **Standort** geben. Ohne Dimension hält ein Indikator einen einzelnen Wert pro Periode. Mit einer Dimension hält er einen Wert pro Periode für jeden Teil der Aufschlüsselung, sodass Sie statt einer einzelnen unternehmensweiten Summe einen Wert pro Region erfassen. ## Mitglieder und Ebenen [#mitglieder-und-ebenen] Eine Dimension besteht aus **Mitgliedern** und, optional, **Ebenen**. * Ein **Mitglied** ist ein Eintrag in der Aufschlüsselung, zum Beispiel "Nord" oder "Süd". * Eine **Ebene** gruppiert Mitglieder zu einer Hierarchie, wobei jedes Mitglied unter einem übergeordneten Mitglied eine Ebene höher sitzt. Eine Hierarchie kann **bis zu fünf Ebenen tief** sein. Für eine Organisation könnten Sie eine Dimension Standort wie folgt aufbauen: ``` Land Deutschland Schweiz Region Süd Nord Zentral Standort München Stuttgart Berlin Zürich ``` Hier ist **Land** die oberste Ebene mit zwei Mitgliedern (Deutschland, Schweiz), **Region** sitzt darunter, und **Standort** ist die unterste Ebene, auf der Sie tatsächlich Daten eingeben. Sie bauen die Dimension auf, indem Sie ihre Ebenen von oben nach unten hinzufügen und dann die benannten Mitglieder in jeder. Das Erfassen ganz unten (pro Standort) lässt Ovidens die Zahlen automatisch nach oben (pro Land) aufrollen. ## Einem Indikator eine Dimension zuweisen [#einem-indikator-eine-dimension-zuweisen] Wenn Sie einem Indikator eine Dimension zuweisen, wird dieser Indikator **pro Mitglied erfasst**. In der Praxis bedeutet das, dass die Dateneingabe einen Wert für jedes Mitglied verlangt statt eines einzelnen Werts. Eine Dimension Region auf Ihrem Energie-Indikator bedeutet zum Beispiel, dass Sie den Energieverbrauch für Nord, Süd und so weiter jede Periode eingeben. Ein Indikator kann mehr als eine Dimension haben; in diesem Fall erfassen Sie einen Wert für jede Kombination, zum Beispiel jede Region in jeder Periode. ### Eine Ebene, und jede Dimension benannt [#eine-ebene-und-jede-dimension-benannt] Eine Zuweisung wählt auch **die Ebene, auf der der Indikator erfasst wird**. Werte werden auf dieser Ebene eingegeben und sonst nirgends: Summen für die darüberliegenden Ebenen werden aus dem berechnet, was Sie eingegeben haben, und nie ein zweites Mal gespeichert - genau das verhindert, dass eine Landessumme und die Standorte darunter auseinanderlaufen. Daraus folgen zwei Dinge, und die Plattform setzt beide durch, statt einen Wert zu speichern, den nichts lesen kann: * Ein Wert muss ein Mitglied **auf der Erfassungsebene** benennen. Ein Mitglied darüber oder darunter wird mit einer entsprechenden Meldung abgelehnt. * Ein Wert muss **für jede Dimension** ein Mitglied benennen, die dem Indikator zugewiesen ist. Auf einem dimensionierten Indikator gibt es keine Zelle "ohne Angabe". Die Tabelle der Dateneingabe zeigt genau diese Zellen - was Sie sehen, ist also das, was Sie ausfüllen können. Auch daraus folgen zwei Dinge: * **Eine Ebene ohne Mitglieder zeigt keine Spalten.** Wenn Sie eine Dimension `Land > Standort` zuweisen, während es nur Länder gibt, wählen Sie Land als Erfassungsebene - oder legen Sie die Standorte an. Die Tabelle sagt, welcher der beiden Fälle vorliegt, statt Spalten zu zeigen, in die nichts eingegeben werden kann. * **Ein Mitglied oberhalb der Erfassungsebene ist keine Zelle.** Ein Land ohne Standorte darunter erhält auf einem Indikator, der auf Standortebene erfasst, keine Spalte. Erfassen Sie auf Landesebene, oder legen Sie einen Standort an. Wenn Sie die Erfassungsebene ändern, nachdem Werte eingegeben wurden, bleiben die bereits gespeicherten Werte, wo sie sind, und werden nicht mehr gelesen. Die Liste **Verwaiste Daten** im Tab Pipeline benennt sie, und der Weg zurück führt über die Tabelle: Setzen Sie die Erfassungsebene auf die zurück, auf der sie eingegeben wurden, dann sind sie wieder Zellen. Danach können Sie sie löschen wie jeden anderen Wert - Zelle leeren und speichern - und anschließend die Ebene ändern. Eine Zelle, die die Tabelle nicht zeigt, lässt sich nicht über die Tabelle löschen; genau das macht sie verwaist. ## Die Berichtseinheiten-Dimension [#die-berichtseinheiten-dimension] Wenn Sie [Erhebungsrunden](../collection-rounds.mdx) durchführen, ist eine der Dimensionen einer App ihre **Berichtseinheiten-Dimension**: diejenige, deren Mitglieder die Einheiten sind, für die Sie benannte Personen antworten lassen. Es gibt genau eine je App, und jede Erhebungsrunde dieser App fragt danach - so ist die Verantwortung aller in einer einzigen Sprache ausgedrückt. Festgelegt wird sie im Editor unter **Struktur -> Dimensionen**: Öffnen Sie die Dimension und schalten Sie **Berichtseinheiten-Dimension** ein. Schalten Sie sie bei einer anderen Dimension ein, wechselt sie dorthin; eine Dimensionsliste zeigt nie zwei davon. Daraus folgen zwei Regeln: * **Die App muss veröffentlicht sein, damit eine Runde den Entwurf verlassen kann.** Die Erhebungsrunde läuft gegen die veröffentlichte Struktur: Dimension festlegen, veröffentlichen, danach die Runde öffnen. * **Sobald eine Erhebungsrunde läuft oder abgeschlossen ist, lässt sich die Dimension nicht mehr verschieben** - denn deren Antworten und Prüfentscheidungen benennen ihre Elemente. Sie wieder auf die Dimension zu setzen, die die veröffentlichte Struktur trägt, bleibt erlaubt; das ist die Reparatur, wenn ein Entwurf davon abgewichen ist. Eine Erhebungsrunde im Entwurf hält sie gar nicht. Um nach einer anderen Achse zu erheben, setzen Sie die Prüfentscheidungen dieser Erhebungsrunden zurück und versetzen Sie sie in den Entwurf, oder verwenden Sie eine eigene App. * **Das Löschen der Dimension wird schon früher abgelehnt**: solange die App IRGENDEINE Erhebungsrunde hat, auch einen Entwurf, denn die Personen einer Entwurfs-Erhebungsrunde sind bereits deren Elementen zugeordnet. Die beiden Regeln setzen bewusst unterschiedlich früh an - dass eine Erhebungsrunde den Entwurf verlässt, legt die WAHL fest; das bloße Bestehen einer Erhebungsrunde schützt die DIMENSION. ## Warum Hierarchien flach bleiben [#warum-hierarchien-flach-bleiben] Die Deckelung auf fünf Ebenen ist bewusst gewählt. Eine Hierarchie aus Land, Region und Standort ist leicht zu lesen und schnell einzugeben; tiefer zu gehen vervielfacht die Zellen, die eine erfassende Person füllen muss, und bringt selten zusätzliche Erkenntnis. Halten Sie Ihre flach und auf die Aufschlüsselungen konzentriert, über die Sie tatsächlich berichten. # Zeitdimension (/docs/de/indicator-apps/dimensions/time) Jeder Indikator in einer Indikator-App wird über die Zeit verfolgt, ein Wert pro **Periode**. Die **Zeitdimension** ist das, was eine Indikator-App zu einer Aufzeichnung darüber macht, wie sich eine Metrik verändert, statt zu einer einzelnen Momentaufnahme. Bevor Sie Werte erfassen, legen Sie fest, wie oft jeder Indikator gemessen wird. ## Das Erfassungsintervall wählen [#das-erfassungsintervall-wählen] Das **Erfassungsintervall** (auch Periodentyp genannt) legt fest, wie oft ein Indikator einen Wert erhält. Sie wählen eines von fünf: **Täglich**, **Wöchentlich**, **Monatlich**, **Vierteljährlich** oder **Jährlich**. Wählen Sie das Intervall, das dazu passt, wie die Messung in der realen Welt erfolgt. Ein Zähler, den Sie monatlich ablesen, sollte ein monatlicher Indikator sein; ein jährliches Ziel sollte jährlich sein. ## Wie Perioden funktionieren [#wie-perioden-funktionieren] Sobald ein Indikator ein Intervall hat, erfassen und betrachten Sie seine Werte Periode für Periode. Sie wechseln zwischen den Perioden, um für jede einen Wert einzugeben, indem Sie zum Beispiel die Zahl für Januar eintippen, dann die für Februar und so weiter. Wenn Sie die Daten ansehen, sehen Sie den Wert über die Perioden hinweg ausgebreitet, sodass der Verlauf über die Zeit leicht nachzuvollziehen ist. Sie können vorwärts und rückwärts durch die Perioden gehen und sich auf einen bestimmten Bereich davon konzentrieren. ## Wie Zeitraster zusammenhängen [#wie-zeitraster-zusammenhängen] Indikatoren, die in unterschiedlichen Intervallen gemessen werden, können dennoch miteinander in Beziehung stehen. Ein feineres Intervall kann sich zu einem gröberen aufsummieren: Zwölf monatliche Werte ergeben zum Beispiel eine Jahressumme. Ovidens versteht diese Beziehungen, sodass eine jährliche Metrik aus monatlichen Daten berechnet werden kann, ohne dass Sie die Rechnung von Hand durchführen. Sie werden das in Aktion sehen, wenn Sie eine [Berechnung](../indicators/formula.mdx) aufbauen, die Indikatoren kombiniert, die in unterschiedlichen Intervallen verfolgt werden. # Gruppen (/docs/de/indicator-apps/groups) Eine **Gruppe** ist ein optionales Label, das Ihre Indikatoren in Abschnitte gliedert. Wenn eine Indikator-App wächst, kann ihre Liste von Indikatoren lang werden; Gruppen erlauben es Ihnen, sie in übersichtliche Abschnitte wie „Energie", „Emissionen" und „Finanzen" zu sortieren, damit die Liste leicht zu überblicken bleibt. ## Was eine Gruppe bewirkt [#was-eine-gruppe-bewirkt] Bei einer Gruppe geht es vor allem um Übersichtlichkeit: sie entscheidet, wo ein Indikator in der Liste steht - das Äquivalent dazu, verwandte Elemente in Ordner zu legen, damit Sie sie schnell finden, übertragen auf die Indikator-App. Eine Gruppe kann außerdem eine [Anwendbarkeitsregel](./indicators/input.mdx) tragen, eine Bedingung über die Werte anderer Datenpunkte, die alle Datenpunkte der Gruppe auf einmal steuert, zum Beispiel damit ein ganzer Abschnitt nur unter einer gewählten Berichtsoption zutrifft. Ist die Gruppenregel für eine Periode falsch, werden alle ihre Datenpunkte als nicht anwendbar markiert. ## Eine Gruppe erstellen und Indikatoren verschieben [#eine-gruppe-erstellen-und-indikatoren-verschieben] So ordnen Sie Ihre Indikatoren: * erstellen Sie eine Gruppe und geben Sie ihr einen Namen, der den Abschnitt beschreibt, * verschieben Sie Indikatoren in die Gruppe, sodass sie darunter erscheinen, * verschieben Sie sie wieder heraus oder in eine andere Gruppe, wann immer Sie neu ordnen. Indikatoren, die Sie in keine Gruppe einordnen, funktionieren weiterhin genau gleich; sie erscheinen einfach für sich allein statt unter einer Abschnittsüberschrift. Sie können so viele oder so wenige Ihrer Indikatoren gruppieren, wie Sie möchten. # Formel-Indikator (/docs/de/indicator-apps/indicators/formula) Ein **Formel**-Indikator ist eine Metrik, die Ovidens für Sie aus anderen Indikatoren ermittelt, statt einer, die Sie von Hand eingeben. Sie bauen die Berechnung einmal auf - "Umsatz minus Kosten" - und Ovidens wertet sie automatisch für jede Periode und jedes Dimensionsmitglied aus. Wählen Sie am Indikator den Datentyp **Formel**, und an die Stelle der Erfassungseinstellungen tritt das Feld **Berechnung**. Eine Berechnung wird **einmal pro Koordinate** ausgewertet. Eine Koordinate ist eine einzelne **Periode** (zum Beispiel März 2026) plus **ein Mitglied pro Dimension**, wenn der Indikator Dimensionen hat (zum Beispiel Periode März 2026 mit Region "Nord"). Jede Referenz in der Berechnung wird an dieser Koordinate aufgelöst. Hat eine Eingabe dort keinen Wert, ist das Ergebnis **leer**, es sei denn, Sie hinterlegen einen Ersatzwert (siehe [Fehlende Eingaben](#fehlende-eingaben)). Neu bei Indikator-Apps? Beginnen Sie mit der [Übersicht](../overview.mdx), die Indikatoren, [Dimensionen und Hierarchien](../dimensions/other-dimensions.mdx) sowie [Perioden und Zeitgranularität](../dimensions/time.mdx) erklärt. ## Sie bauen eine Berechnung auf, Sie schreiben keine [#sie-bauen-eine-berechnung-auf-sie-schreiben-keine] Es gibt keine Formelsprache zu lernen. Eine Berechnung entsteht aus einem kleinen Satz von **Bausteinen**, jeder aus einem Menü gewählt: * **Referenz** - liest einen anderen Indikator. Nur dieser Baustein liest Daten. * **Zahl** - ein Wert, den Sie eingeben. * **Rechnung** - zwei Bausteine mit `+`, `-`, `*` oder `/` dazwischen. * **Betrag** - der Betrag eines Bausteins, ohne Vorzeichen. * **Gerundet** - ein Baustein, auf eine Anzahl Nachkommastellen gerundet. * **Ersatzwert** - ein Baustein plus ein zweiter, der einspringt, wenn der erste leer ist. * **Bedingung** - vergleicht zwei Bausteine und nimmt je nach Antwort einen von zwei weiteren. Jeder Baustein wird aus einer Auswahlliste gewählt, und jeder kann weitere Bausteine enthalten. Eine Berechnung wie "runde erneuerbare Energie geteilt durch Gesamtenergie, mal 100, auf eine Nachkommastelle" entsteht also durch Verschachtelung. Der Editor verlangt nirgends eine Eingabe außer einer Zahl, und es gibt nichts, das Sie falsch schreiben können. Unter den Bausteinen zeigt der Editor zwei Zeilen: was Ihre Berechnung in **Klartext** besagt, und dieselbe Berechnung in der Kurznotation von Ovidens. Die erste prüfen Sie gegen Ihre Absicht; die zweite ist das, was Fehlermeldungen zitieren. ## Vera baut sie für Sie [#vera-baut-sie-für-sie] Klicken Sie auf **Metrik beschreiben**, formulieren Sie die Berechnung in eigenen Worten - etwa "Umsatz minus Kosten" oder "Jahresvergleich des Umsatzwachstums" - und Vera baut sie auf. Zurück kommt eine echte Berechnung im Editor: Sie können sie aufklappen, einen Teil ändern und den Rest behalten. Ovidens prüft Veras Antwort, bevor sie bei Ihnen ankommt - ein Vorschlag, den Sie übernehmen, lässt sich also speichern. ## Daten lesen: der Baustein Referenz [#daten-lesen-der-baustein-referenz] Ein Baustein **Referenz** liest genau einen anderen Indikator. Sie wählen den Indikator aus einer Liste und dann, **wie** er gelesen wird - dafür sind die Referenzfunktionen da. Lesen können Sie einen erfassten [numerischen](./input.mdx#numeric) Indikator, einen erfassten [Ja/Nein](./input.mdx#boolean)-Indikator oder einen anderen Formel-Indikator. Eine Ja/Nein-Referenz ergibt eine Zahl: **Ja wird zu 1, Nein zu 0**, und ein fehlender Wert verhält sich wie eine fehlende Zahl. `SUM` über ein Ja/Nein zählt also, wie viele Perioden das Ziel erreicht haben, und `AVG` ist der Anteil. Ein [Auswahl](./input.mdx#select)-Indikator wird mit `SELECTED` verglichen, und bei einem [Text](./input.mdx#text)- oder [Datei](./input.mdx#file)-Indikator lässt sich nur fragen, ob er überhaupt beantwortet wurde - mit `HAS_VALUE`. Beides steht unten. ### Die Referenzfunktionen [#die-referenzfunktionen] | Funktion | Was sie liest | | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `VALUE` | den Wert an dieser Koordinate | | `SUM` `AVG` `MIN` `MAX` `FIRST` `LAST` `COUNT` | einen **feiner** erfassten Indikator, verdichtet auf die Periode dieses Indikators | | `ANCESTOR_SUM` `ANCESTOR_AVG` `ANCESTOR_MIN` `ANCESTOR_MAX` | eine Dimension, **N Ebenen über** dem aktuellen Mitglied verdichtet | | `LEVEL_SUM` `LEVEL_AVG` `LEVEL_MIN` `LEVEL_MAX` | eine Dimension, auf die **absolute Ebene N** verdichtet (1 ist die oberste) | | `PERIOD_AGO` | den Wert **N Perioden zurück** | | `YEAR_AGO` | den Wert genau **ein Jahr zurück** | | `DIM_SUM` | die **Summe über alle Mitglieder** einer Dimension | | `MEMBER` | den Wert **eines benannten Mitglieds** | | `SELECTED` | ob ein **Auswahl**-Indikator eine bestimmte Option enthält - 1 wenn ja, 0 bei einer anderen | | `HAS_VALUE` | ob ein Indikator **beliebigen Typs** beantwortet wurde - 1 oder 0, nie leer | | `MEMBER_COUNT` | **wie viele Mitglieder** eine Dimension auf einer Ebene hat (diese Funktion benennt eine Dimension, keinen Indikator) | Der Editor fragt nur die Argumente ab, die die gewählte Funktion tatsächlich nimmt. Wählen Sie `PERIOD_AGO`, fragt er nach der Anzahl Perioden; wählen Sie `MEMBER`, fragt er nach dem Mitglied; bei `VALUE` fragt er nichts weiter. Wechseln Sie die Funktion, folgen die Argumente - ein Argument, das die neue Funktion nicht nimmt, kann nicht zurückbleiben. **Ebenennummern** begrenzt die Dimension selbst. Eine Hierarchie ist höchstens fünf Ebenen tief, ein Vorfahrenabstand also höchstens 4 und eine absolute Ebene höchstens 5; `MEMBER_COUNT` kann keine Ebene jenseits der tatsächlichen Tiefe benennen. Der Editor bietet nur die vorhandenen Ebenen an. **Zeitverschiebungen.** Bei `PERIOD_AGO` ist die Anzahl eine ganze Zahl von Perioden, mindestens 1. `YEAR_AGO` landet auf der passenden Periode ein Jahr früher, mit zwei kleinen Anpassungen, damit es immer eine Vergleichsperiode gibt: eine Periode vom 29. Februar vergleicht sich mit dem 28. Februar des Vorjahres, und hat das Vorjahr keine 53\. Kalenderwoche, vergleicht sich eine 53.-Wochen-Periode mit dessen letzter Woche. **Eine Dimension benennen.** Wird ein Indikator über mehr als eine Dimension erfasst, muss eine Verdichtung wissen, über welche. Der Editor fragt danach - und wenn es nur eine Möglichkeit gibt, füllt er sie aus und sagt es Ihnen. **Ein Mitglied wählen.** `MEMBER` liest genau einen Ausschnitt. Das Mitglied muss ein **Erfassungsmitglied** sein, also eines auf der Ebene, auf der Werte gespeichert werden; ein übergeordnetes Mitglied ist eine Verdichtung, wofür die Funktionen `ANCESTOR_*` und `LEVEL_*` da sind. Ein Mitglied zu wählen klappt diese Dimension zu - ein Indikator ohne eigene Dimensionen kann so einen einzelnen Ausschnitt eines dimensionierten Indikators lesen. **Eine Auswahl lesen.** `SELECTED` fragt, ob ein Auswahl-Indikator **eine bestimmte Option** enthält - Sie wählen die Option aus der Liste dieses Indikators. Die Antwort ist 1, wenn die Zelle sie enthält, 0 bei einer anderen Option, und **nichts**, wenn die Zelle nicht beantwortet ist: eine unbeantwortete Frage ist unbekannt und nicht "nicht diese Option". Legen Sie einen **Ersatzwert** darum, wenn eine unbeantwortete Zelle als 0 zählen soll. Die Option wird über ihre eigene Identität gemerkt, nicht über ihre Position - ein **Umsortieren der Optionsliste ändert also nichts** an den Regeln, die sie benennen. Eine Option zu entfernen, die eine Regel benennt, wird abgelehnt, mit einer Meldung, die die Option nennt, statt still eine Regel zurückzulassen, die nichts mehr bedeutet. **Fragen, ob überhaupt geantwortet wurde.** `HAS_VALUE` funktioniert bei einem Indikator **beliebigen** Typs, auch [Text](./input.mdx#text) und [Datei](./input.mdx#file), und antwortet an jeder Zelle mit 1 oder 0 - nie leer, auch dort nicht, wo der Indikator gar keinen gespeicherten Wert hat. Damit wird die häufigste Fragebogenregel ausdrückbar: *wenn Sie mit Ja geantwortet haben, müssen Sie den Nachweis anhängen*. ## Fehlende Eingaben [#fehlende-eingaben] Eine fehlende Eingabe macht das **gesamte Ergebnis leer**, und der Baustein **Ersatzwert** ist der eine Weg, es anders zu sagen: Er nimmt einen Baustein und einen zweiten, der einspringt, wenn der erste leer bleibt. Um eine einzelne Referenz gelegt, setzt er eine Zahl an die Stelle des fehlenden Werts; um eine ganze Teilberechnung gelegt, deckt er auch das ab, was die Arithmetik selbst erzeugt - eine Division durch null, ein Ergebnis, das zu groß zum Darstellen ist. ## Ausgearbeitete Beispiele [#ausgearbeitete-beispiele] Jedes Beispiel unten sind die **Bausteine, die Sie zusammensetzen**, und darunter steht die Kurznotation, die Ovidens danach unter dem Editor ausgibt. Diese Zeile tippen Sie nie - sie liest Ihnen Ihre Arbeit zurück, und sie ist die Form, die eine Fehlermeldung zitiert. **Marge.** `revenue` und `costs` sind beide monatlich mit denselben Dimensionen, jedes ist also eine Referenz, gelesen als `VALUE`. * **Rechnung** mit `-` dazwischen * **Referenz** -> `revenue`, gelesen als `VALUE` * **Referenz** -> `costs`, gelesen als `VALUE` ``` ([revenue] - [costs]) ``` **Jahressumme aus Monatsdaten.** In einem jährlichen Indikator liest eine Zeitverdichtung die Monatsreihe. * **Referenz** -> `monthly_sales`, gelesen als `SUM` ``` SUM([monthly_sales]) ``` **Verhältnis mit Schutz vor Division durch null.** Ein Baustein **Bedingung** gibt 0 zurück, wenn das Ziel null ist. * **Bedingung** * *Wenn dies*: **Referenz** -> `target`, gelesen als `VALUE` * *Verglichen mit* `>`: **Zahl** -> 0 * *Dann nehmen*: **Rechnung** mit `/` zwischen `actual` und `target` * *Sonst nehmen*: **Zahl** -> 0 ``` IF([target] > 0; ([actual] / [target]); 0) ``` **Jahresvergleich.** Derselbe Indikator zweimal gelesen, einmal wie er steht und einmal ein Jahr zurück. * **Rechnung** mit `/` dazwischen * **Rechnung** mit `-`: **Referenz** -> `revenue` (`VALUE`) und **Referenz** -> `revenue` (`YEAR_AGO`) * **Referenz** -> `revenue`, gelesen als `YEAR_AGO` ``` (([revenue] - YEAR_AGO([revenue])) / YEAR_AGO([revenue])) ``` **Anteil an einer Summe.** `energy_consumption` wird über eine Aufschlüsselung `energy_source` erfasst. Der erneuerbare Anteil ist das Mitglied "renewable" geteilt durch die Summe über alle Mitglieder, in Prozent, auf eine Stelle gerundet. * **Gerundet** auf 1 Nachkommastelle * **Rechnung** mit `*` * **Rechnung** mit `/`: **Referenz** -> `energy_consumption`, gelesen als `MEMBER` von `renewable`, und **Referenz** -> `energy_consumption`, gelesen als `DIM_SUM` * **Zahl** -> 100 ``` ROUND(((MEMBER([energy_consumption]; renewable) / DIM_SUM([energy_consumption])) * 100); 1) ``` Bei 120 erneuerbar von 300 gesamt ergibt das 40. ## Granularitätsregeln [#granularitätsregeln] Die **Granularität** einer Berechnung ist ihre Zeitgranularität (der Periodentyp) und ihr Dimensionsumfang (welche Dimensionen, auf welcher Ebene). Referenzen müssen dazu passen, sonst verweigert Ovidens das Speichern und nennt die Referenz, die nicht passt. Die Zeithierarchie, von fein nach grob: ``` täglich < wöchentlich < monatlich < quartalsweise < jährlich ``` Die Regeln: * Eine einfache `VALUE`-Referenz muss auf **derselben** Zeitgranularität liegen wie der Indikator. * Ein **feinerer** Indikator muss mit einer Zeitverdichtung gelesen werden (`SUM`, `AVG`, `MIN`, `MAX`, `FIRST`, `LAST`, `COUNT`). * Ein **gröberer** Indikator wird gelesen wie er ist und an jeder feineren Koordinate wiederholt. Er wird **nicht** aufgeteilt - teilen Sie ihn selbst, `MEMBER_COUNT` ist genau dafür da. * Eine Referenz auf einer **feineren** Hierarchieebene einer gemeinsamen Dimension muss mit `ANCESTOR_*` oder `LEVEL_*` verdichtet werden. * Eine Referenz, die eine Dimension behält, über die dieser Indikator nicht erfasst wird, lässt sich nicht mit ihm vergleichen - Ovidens nennt die Dimension. ## Was Ovidens ablehnt, und wann [#was-ovidens-ablehnt-und-wann] **Beim Speichern.** Alles daran, wie eine Berechnung **passt**, wird beim Speichern entschieden, und jede Ablehnung benennt, was nicht gepasst hat: * eine Referenz auf einen Indikator, den diese App nicht führt, oder auf einen ohne numerischen Wert; * ein **Zyklus** - eine Berechnung, die sich direkt oder über andere selbst liest; * eine **Erfassungsfrequenz**, die nicht zusammengeht: eine feinere Referenz ohne Verdichtung oder eine Verdichtung über eine gröbere; * eine **Dimension**, die sich nicht ausrichten lässt: eine zusätzliche, über die der Indikator nicht erfasst wird, oder eine feinere Ebene ohne Verdichtung; * ein **Mitglied**, das es nicht gibt oder das ein übergeordnetes statt eines Erfassungsmitglieds ist; * eine **Ebene** jenseits der tatsächlichen Tiefe der Dimension; * eine schlicht **zu große** Berechnung - es gibt eine Obergrenze für die Anzahl Bausteine einer Berechnung und für die Anzahl ihrer Referenzen. Der Editor stellt beim Bauen dieselbe Frage wie das Speichern - eine Ablehnung erreicht Sie also als rote Zeile unter der Berechnung und nicht als fehlgeschlagenes Speichern. **Bei einem Formel-Indikator.** Eine Zelle hat eine Zahl oder keine. Es gibt keine Begründung pro Zelle, und das ist Absicht und kein Versäumnis: Ihre Berechnung ist EINE Anweisung über das gesamte Raster - eine leere Zelle ist also eine, deren Eingaben dort keinen Wert hatten oder deren Arithmetik dort nicht definiert war, und die Anweisung kann beides nicht unterscheiden, ohne ein zweites Mal ausgeführt zu werden. Eine Begründung bekommt, was **überhaupt nicht** laufen konnte. Dann hat keine Zelle des Rasters ein Ergebnis, die Begründung steht **einmal** über dem Raster, und jede Zelle zeigt **Kein Ergebnis** - so wird ein Satz über die Anfrage nicht in jede Zelle wiederholt, und eine Zelle ohne Ergebnis sieht nie aus wie eine Zelle mit null. ## Wo Ihre Berechnung tatsächlich läuft [#wo-ihre-berechnung-tatsächlich-läuft] Jede Berechnung wird zu **einer SQL-Anweisung**, die Ovidens erzeugt und mit Ihrer Konfiguration in Übereinstimmung hält. Sie können sie im Reiter [Pipeline](../pipeline.mdx) der App vollständig lesen und dort auch die Zeilen sehen, die sie erzeugt hat - das macht jede Zahl der App überprüfbar. Die Anweisung ist zum **Lesen** da, nicht zum Bearbeiten und nicht zum einzelnen Ausführen: Die Pipeline einer Indikator-App gehört Ovidens, sie wird bei jeder Änderung Ihrer Konfiguration neu geschrieben, und jeder Schritt läuft nur als Teil der Auswertung dieser App. Reichen die Bausteine für etwas nicht aus, lesen Sie die Daten mit einem Knoten [Indikatordaten](../../analytical-apps/pipeline/indicator-data.mdx) in eine **Analyse-App**. Eine Analyse-App hat einen echten SQL-Editor mit Vorschau und typisierten Ausgabespalten, und ihre Anweisungen gehören Ihnen. # Eingabe-Indikatoren (/docs/de/indicator-apps/indicators/input) **Eingabe-Indikatoren** sind die Kennzahlen, die Sie von Hand erfassen: ein Wert pro **Periode** und ein Wert pro Mitglied, wenn der Indikator [Dimensionen](../dimensions/other-dimensions.mdx) hat. Sie sind die Rohzahlen, auf denen Ihre Indikator-App aufbaut. Werte, die für Sie berechnet werden, stammen stattdessen aus einem [Formel](./formula.mdx)-Indikator. Es gibt fünf Eingabetypen, ausgewählt nach der Art des Werts, den Sie erfassen: * [**Numerisch**](#numeric) - eine Zahl. * [**Text**](#text) - ein Wort oder eine kurze Notiz. * [**Boolesch**](#boolean) - ein Ja/Nein. * [**Auswahl**](#select) - eine Option aus einer von Ihnen definierten Liste. * [**Datei**](#file) - ein hochgeladenes Dokument. ## Einstellungen, die jeder Eingabe-Indikator teilt [#einstellungen-die-jeder-eingabe-indikator-teilt] Unabhängig vom Typ prägen einige Einstellungen, wie sich ein Indikator verhält: * **Technische ID:** ein kurzer, stabiler Name, mit dem auf den Indikator verwiesen wird, zum Beispiel in einer [Formel](./formula.mdx). Er bleibt gleich, selbst wenn Sie den Anzeigenamen des Indikators umbenennen. * **Beschreibung:** eine einfache Erklärung, was die Kennzahl ist, damit die Erfassenden wissen, was sie eingeben sollen. * **Einheit:** für einen numerischen Indikator die Einheit, in der seine Werte gemessen werden, ausgewählt aus einer festen Liste (zum Beispiel MWh, tCO2e, %, EUR). Die Einheit wird als strukturierte Einstellung gespeichert, statt in den Namen geschrieben zu werden, und wird bei der Dateneingabe neben dem Wert angezeigt. Lassen Sie sie für eine einheitenlose Zahl unbelegt. * **Anleitung:** optionaler längerer Hilfetext, in einem Formatierungseditor geschrieben (Überschriften, Fett, Listen, Links). Hat ein Datenpunkt eine Anleitung, sehen die Erfassenden sie in einem **Anleitung**-Bereich über dem Datenraster, der standardmäßig geöffnet und einklappbar ist, sodass sie bei der Eingabe gelesen wird. Verwenden Sie ihn für standardgerechte Erklärungen, die zu lang für die Beschreibung sind. * **Erfassungsintervall:** wie oft ein Wert erfasst wird, täglich bis jährlich. Siehe [Zeitdimension](../dimensions/time.mdx). * **Erforderlich:** ob jede Periode ein Wert eingegeben werden muss. * **Bedingte Anwendbarkeit:** eine optionale Regel, die **pro Zelle** (pro Dimensions-Ausschnitt und Periode) anhand der Werte anderer Datenpunkte entscheidet, ob der Datenpunkt überhaupt zutrifft. Die Regel ist ein zweiseitiger Vergleich mit einer linken Seite, einem Operator (`= != < > <= >=`) und einer rechten Seite, jede Seite aus denselben [Bausteinen](./formula.mdx) aufgebaut wie ein Formel-Indikator - zum Beispiel links eine **Referenz** auf die Mitarbeiterzahl, Operator `>=`, rechts eine **Zahl** 50. Da sie pro Zelle ausgewertet wird, kann ein kleines Land befreit sein, während ein großes in derselben Periode gefordert ist (ein länderspezifischer Auslöser wird nie zu einer Summe verrechnet). Für eine Regel über einen **Ja/Nein**- oder **Auswahl**-Datenpunkt gibt es statt der Bausteine eine geführte Zeile: Ja/Nein-Schalter für den einen, die Bezeichnung der Option für den anderen - niemand muss also wissen, dass ein Ja eine 1 ist oder dass eine Option über ihre Position angesprochen wird. Ist sie für eine Zelle falsch, sehen die Erfassenden diese Zelle als "nicht anwendbar" mit Begründung markiert; ist ein referenzierter Wert noch nicht eingegeben, ist die Zelle anwendbarkeits-unbekannt, bleibt optional und wird nie stillschweigend ausgeblendet. Eine [Gruppe](../groups.mdx) kann dieselbe Art von Regel tragen (einmal pro Berichtszeitraum ausgewertet). Die Bereitschaft auf dem Tab [Berichtszeiträume](../collection-overview.mdx#reporting-periods) nutzt diese Regeln, um zu entscheiden, ob ein Berichtszeitraum vollständig ist: ein nicht anwendbarer Datenpunkt zählt nicht als fehlend. * **Wert-Einschränkungen und Wertregeln:** zwei verschiedene Dinge, und nur eines davon kann das Speichern eines Werts verhindern. **Wert-Einschränkungen** sind strukturell: ein numerischer Indikator kann ganze Zahlen verlangen, und ein Auswahl-Indikator kann die Eingabe auf eine Teilmenge seiner Optionen beschränken. Ein Wert, der diese Form nicht hat, wird bei der Eingabe abgelehnt. **Wertregeln** (nur für numerische Datenpunkte) sind zweiseitige Vergleiche aus denselben Bausteinen, und jede muss **diesen Datenpunkt** auf einer Seite benennen - die Referenzauswahl bietet ihn als **Dieser Datenpunkt** an, "höchstens Bürokapazität" sind also zwei Klicks. Ein-Klick-Vorlagen decken die häufigen Fälle nicht-negativ, Minimum, Maximum, 0-100 und Vorjahresvergleich ab. **Eine Wertregel verhindert das Speichern nie.** Ihre Zahl wird gespeichert, und die Zelle wird mit einem Warnsymbol markiert, dessen Tooltip die verletzte Regel nennt; korrigieren Sie die Zahl, und die Markierung verschwindet beim nächsten Laden. Das ist Absicht: eine Regel kann Ihren Wert mit einem vergleichen, den noch niemand eingegeben hat, oder mit der Summe über eine Aufschlüsselung, die Sie gerade erst zur Hälfte ausgefüllt haben - eine Regel, die Sie in diesem Moment nicht erfüllen können, darf Sie nicht Ihre Zahl kosten. Dieselben Markierungen werden auf dem Tab [Berichtszeiträume](../collection-overview.mdx#reporting-periods) zusammengefasst. Die Konsistenz zwischen zusammengehörigen Datenpunkten wird separat durch [Konsistenzprüfungen](../checks.mdx) behandelt, und nur eine **blockierende** Prüfung hält etwas auf - sie hält die Freigabe auf, nie eine Eingabe. * **Kommentar erforderlich:** ob die erfassende Person neben dem Wert eine Notiz hinzufügen muss, nützlich, wenn Zahlen einer Erklärung bedürfen. * **Framework-Zuordnung:** eine optionale Menge von Schlüssel-Wert-Einträgen, die den Datenpunkt einem externen Berichtsrahmen zuordnen (zum Beispiel einem Standard, einer Angabe, einem Absatz oder einem XBRL-Konzept). Fügen Sie Einträge direkt in der Konfiguration des Datenpunkts hinzu, bearbeiten und entfernen Sie sie; die gängigen Schlüssel werden bei der Eingabe vorgeschlagen. Die Zuordnung ist undurchsichtige Referenz-Metadaten, die mit dem Datenpunkt geführt werden - sie beeinflusst weder die Erfassung noch die Validierung. * **Archiviert:** blendet einen Indikator aus, den Sie nicht mehr erfassen, ohne seinen Verlauf zu löschen. ## Numerisch [#numeric] Ein **numerischer** Indikator hält eine Zahl. Er ist die häufigste Art und die Grundlage der meisten Nachverfolgung: verbrauchte Energie, erzielter Umsatz, verbrannte Liter Kraftstoff und so weiter. Numerische Werte sind reelle Zahlen, sodass sie in Berechnungen verwendet werden können. Zusammen mit [Formel](./formula.mdx)-Indikatoren sind sie die einzigen Eingaben, mit denen eine Formel rechnen kann. Wenn Sie möchten, dass eine Kennzahl in eine Berechnung einfließt, erfassen Sie sie als numerischen Indikator. ## Text [#text] Ein **Text**-Indikator erfasst einen Wert in Worten: eine kurze Notiz, einen Status oder eine Erklärung. Verwenden Sie ihn, wenn das, was Sie verfolgen, am besten in Worten festgehalten wird, zum Beispiel "im Plan" oder "verzögert", oder eine kurze Notiz darüber, was in dieser Periode geschah. Da sein Wert Worte statt einer Zahl ist, kann ein Text-Indikator nicht direkt in der [Formel](./formula.mdx)-Mathematik verwendet werden. Wenn Sie etwas Zählbares benötigen, modellieren Sie es stattdessen als [booleschen](#boolean), [Auswahl](#select)- oder [numerischen](#numeric) Indikator. ## Boolesch [#boolean] Ein **boolescher** Indikator erfasst ein einfaches Ja/Nein. Verwenden Sie ihn für Dinge, die entweder wahr oder falsch sind: ein erreichtes Ziel, ein erledigter Checklistenpunkt, ein bestandenes Audit. Jede Periode markiert die erfassende Person Ja oder Nein. Ein boolescher Wert kann direkt in einer [Formel](./formula.mdx) verwendet werden: **Ja wird zu 1 und Nein zu 0**. Das macht boolesche Werte in Berechnungen überraschend nützlich. Summiert man einen über eine Spanne von Perioden, zählt das, wie oft die Antwort Ja war; bildet man den Durchschnitt, ergibt das den Anteil der Perioden, die Ja waren. Eine Periode ohne Wert verhält sich wie jede andere fehlende Eingabe (eine Lücke, kein "Nein"), es sei denn, Ihre Formel liefert einen Standardwert. ## Auswahl [#select] Ein **Auswahl**-Indikator lässt die erfassende Person eine Option aus einer von Ihnen definierten Liste wählen, zum Beispiel "Niedrig", "Mittel", "Hoch". Sie legen die Optionen fest, wenn Sie ihn erstellen, und die erfassende Person kann nur aus dieser Liste wählen. Eine Auswahl hält die Daten konsistent, sodass "Hoch", "hoch" und "HOCH" nicht zu drei verschiedenen Antworten werden, und sie macht die Daten leicht gruppier- und filterbar. Bevorzugen Sie eine Auswahl gegenüber einem [Text](#text)-Indikator immer dann, wenn die Antwort aus einer bekannten Menge stammen soll, statt frei getippt zu werden. ## Datei [#file] Ein **Datei**-Indikator speichert ein hochgeladenes Dokument als Wert jeder Periode: ein PDF, ein Bild, eine Tabellenkalkulation oder jede Datei, die Sie neben Ihren Kennzahlen belegt haben müssen. Verwenden Sie ihn für Nachweise hinter einer gemeldeten Zahl, ein Zertifikat, das jede Periode eintrifft, oder ein Formular, das Sie aufbewahren müssen. Jede Periode lädt die erfassende Person die Datei dieser Periode hoch und kann sie ersetzen, wenn eine neuere Version eintrifft. Der jüngste Upload ist der, der gilt. ## Welche Typen eine Formel verwenden kann [#welche-typen-eine-formel-verwenden-kann] Nur [**numerische**](#numeric) und [**boolesche**](#boolean) Eingaben tragen einen Wert, mit dem eine [Formel](./formula.mdx) rechnen kann (ein boolescher Wert als 1 oder 0), neben anderen Formel-Indikatoren. [Text](#text)-, [Auswahl](#select)- und [Datei](#file)-Indikatoren haben keinen numerischen Wert und können nicht in einer Berechnung referenziert werden. # Übersicht über Indikator-Apps (/docs/de/indicator-apps/overview) Eine **Indikator-App** ist ein Ort, um **Metriken** im Zeitverlauf zu sammeln und zu verfolgen. Eine Metrik, die Ovidens als **Indikator** bezeichnet, ist etwas, das Sie immer wieder messen, zum Beispiel Kraftstoffverbrauch, Strom oder Umsatz. Während eine Analyse-App Daten umformt, die Sie bereits haben, ist eine Indikator-App darauf ausgelegt, eine Messung Periode für Periode zu erfassen und zu beobachten, wie sie sich verändert. ## Die Bausteine [#die-bausteine] Eine Indikator-App besteht aus drei Bausteinen, jeder mit einer eigenen Seite. * **[Indikatoren](./indicators/input.mdx)** sind die Metriken selbst, die Dinge, die Sie verfolgen. * **[Dimensionen](./dimensions/time.mdx)** sind die Arten, wie eine Metrik aufgeschlüsselt wird, zum Beispiel nach Region, nach Standort oder im Zeitverlauf. * **[Gruppen](./groups.mdx)** sind optionale Labels, die eine lange Liste von Indikatoren in Abschnitte gliedern. ## Erfasste Indikatoren und Formel-Indikatoren [#erfasste-indikatoren-und-formel-indikatoren] Indikatoren erhalten ihre Werte auf eine von zwei Arten. * **Erfasste** Indikatoren sind diejenigen, die Sie (oder ein Teammitglied) von Hand eingeben, Wert für Wert, Periode für Periode. Das sind die [Eingabe-Indikatoren](./indicators/input.mdx) in fünf Typen (numerisch, Text, boolesch, Auswahl, Datei). * **Formel**-Indikatoren werden für Sie berechnet. Ein **Formel**-Indikator ermittelt seinen Wert automatisch aus anderen Indikatoren, für jede Periode. So kann zum Beispiel die Marge aus Umsatz und Kosten berechnet werden, ohne dass jemand sie eintippt. Siehe [Formel](./indicators/formula.mdx). ## Wo was liegt [#wo-was-liegt] Eine Indikator-App hat zwei Seiten, und eine Schaltfläche bringt Sie von einer zur anderen. **Die App selbst** ist der Ort, an dem erfasst wird, und das, was alle öffnen. Sie hat vier Tabs: * **[Übersicht](./collection-overview.mdx)** - wie die Erfassung vorangeht: wie vollständig Ihre Daten sind, wer beiträgt und wo die Lücken sind. Sie wird für Sie aus den Daten erzeugt, die Sie erfassen, sodass es nichts einzurichten gibt. Ein **Berichtszeitraum**-Auswähler im Kopfbereich begrenzt sie. * **[Daten](./data.mdx)** - der Datensatz eines Datenpunkts: was er ist und seine Zahlen im Zeitverlauf. Hier lesen und korrigieren Sie eine Zahl, ohne ein Formular zu öffnen, und hier erfasst eine App, die keine Erhebungsrunde durchführt. * **[Erhebung](./collection-rounds.mdx)** - die Erhebungsrunden. Öffnen Sie eine Runde, um ihre Einheiten zu sehen, und eine Einheit, um ihren [Fragebogen](./questionnaire.mdx) zu beantworten oder zu prüfen. * **Mitwirkende** - wer Daten eingegeben hat und wie viel. Der Berichtszeitraum-Auswähler im Kopfbereich begrenzt auch diesen Tab. **Der Editor** ist der Ort, an dem die App definiert wird. Klicken Sie oben rechts auf **Editor öffnen** oder drücken Sie E. Den Editor bekommt jeder angeboten, dessen Rolle die Konfiguration der App liest; ein **Betrachter** liest die App so, wie sie zuletzt veröffentlicht wurde, und bekommt beides nicht angeboten. Er hat fünf Tabs: * **Indikatoren** - die Kennzahlen, sortiert in ihre [Gruppen](./groups.mdx). * **Dimensionen** - die Aufschlüsselungen. * **[Prüfungen](./checks.mdx)** - die Konsistenzregeln, die sichern, wie Ihre Zahlen zusammenpassen. * **Berichtszeiträume** - die Fenster, für die Sie erfassen, und der Ort, an dem ein abgeschlossener Zeitraum freigegeben und gesperrt wird. * **[Pipeline](./pipeline.mdx)** - die SQL-Pipeline, die Ovidens aus der Konfiguration dieser App erzeugt, Schritt für Schritt lesbar und herunterladbar, zusammen mit allen erfassten Werten, die nicht mehr zu der Struktur passen, gegen die sie erfasst wurden. Änderungen im Editor bleiben in einem privaten Entwurf, bis Sie sie **veröffentlichen**, genau wie in einer Analyse-App. Siehe [Entwurf und Veröffentlichung](../overview/apps.mdx#entwurf-und-veroeffentlichung). ## Zuerst mit Vera bauen [#zuerst-mit-vera-bauen] Der einfachste Weg, eine Indikator-App einzurichten, besteht darin, **Vera**, den KI-Assistenten, zu fragen. Beschreiben Sie die Metriken, die Sie verfolgen möchten, in einfacher Sprache, zum Beispiel "Verfolge unseren monatlichen Stromverbrauch pro Standort und berechne die Veränderung im Jahresvergleich", und Vera richtet die Indikatoren, Dimensionen, Gruppen und Berechnungen für Sie ein. Keine technischen Kenntnisse erforderlich. Siehe [Mit Vera bauen](./build-with-vera.mdx). Der Rest dieses Abschnitts erklärt jeden Baustein, damit Sie überprüfen können, was Vera baut, oder die einzelnen Teile selbst bauen und anpassen können. # Die generierte Pipeline (/docs/de/indicator-apps/pipeline) Jede Indikator-App trägt eine **Pipeline**, die Ovidens aus der Konfiguration der App erzeugt. Sie bauen sie nicht und Sie können sie nicht bearbeiten - sie wird neu erzeugt, sobald Sie die Struktur der App ändern - aber Sie können sie vollständig lesen, und genau darum geht es. Öffnen Sie den Editor und wählen Sie den Reiter **Pipeline** (oder drücken Sie P). ## Was Sie sehen [#was-sie-sehen] Die Pipeline hat drei Arten von Knoten. * **Relationen** sind Tabellen und keine Anweisungen. Jede trägt einen Teil der Konfiguration der App als Zeilen: ihre Indikatoren und deren Intervall, die Dimensionen, nach denen sie erfasst werden, jedes Element jeder Dimension, ihre Regeln und die dagegen erfassten Werte. * **Ihre Datenpunkte** sind je ein eigener Knoten - ein Schritt, benannt nach dem Datenpunkt, der genau die für ihn erfassten Zellen trägt. * **Engine-Schritte** sind einzelne SQL-Anweisungen über den Tabellen vor ihnen. Einige sind die Grundlagen der Plattform - der Periodenkalender, der Elementbaum der Dimensionen, die Erfassungselemente, die Befunde zu gestrandeten Daten. Die übrigen sind **je ein Schritt pro konfiguriertem Objekt**: einer je Berechnung eines Formel-Datenpunkts, einer je Satz Wertregeln eines Datenpunkts, einer je Konsistenzprüfung, einer je bedingter Gruppe. Jeder ist eine einzelne Anweisung, die Sie vollständig lesen können. Klicken Sie auf einen Knoten, um ihn zu lesen. ## Ihre Datenpunkte, je ein Schritt [#ihre-datenpunkte-je-ein-schritt] Jeder Datenpunkt, den Sie **erfassen** - eine Zahl, ein Text, ein Ja/Nein oder eine Einfachauswahl - hat einen eigenen Schritt. Öffnen Sie ihn, und Sie sehen die Zellen genau dieses Datenpunkts und sonst nichts: die Periode, für die sie erfasst wurden, je eine Spalte für jede Dimension, nach der Sie diesen Datenpunkt erfassen, und den Wert. **Jede Dimensionsspalte trägt den Namen Ihrer Dimension.** Darunter wird jede Zelle der App mit einer Adresse gespeichert, die sagt, zu welcher Dimension jedes ihrer Elemente gehört - `region` heißt also in der gespeicherten Zelle, in diesem Schritt und in allem, was Sie dagegen schreiben, dasselbe. Diese Schritte packen diese Adresse in je eine Spalte pro Dimension aus, in der Reihenfolge, in der Sie sie zugeordnet haben. Genau das macht auch das SQL erst lesenswert: eine Anweisung, die gegen `region` geschrieben ist, meint morgen noch `region`. **Ein Datenpunkt kann nach so vielen Dimensionen erfasst werden, wie Ihr Arbeitsbereich zulässt.** Darunter gibt es keine eigene Speichergrenze - die einzige Obergrenze ist die, die die Einstellungen Ihres Arbeitsbereichs nennen. Drei Spalten kommen von Ovidens und nicht von Ihnen; sie beginnen mit einem Unterstrich und können deshalb nie mit einer Ihrer eigenen Dimensionen kollidieren: `_period_type` (das Intervall), `_period_start` (welche Periode) und `_value` (der erfasste Wert selbst). Eine Dimension, die Sie `value` nennen, ist völlig zulässig und bekommt eine Spalte `value`, direkt neben `_value`. Zwei Arten von Datenpunkt haben hier keinen Schritt, jede aus eigenem Grund. Ein **Formel**-Datenpunkt hat überhaupt keine erfassten Zellen - seine Werte kommen aus seinem eigenen Engine-Schritt, der die von Ihnen konfigurierte Berechnung trägt. Der gespeicherte Wert eines **Datei**-Datenpunkts ist ein Verweis auf die hochgeladene Datei und kein Wert, den eine Ergebnistabelle tragen könnte; die Dateien selbst bleiben am Datenpunkt, wo Sie sie hochgeladen haben. Einen dieser Schritte zu lesen ist günstig: Er liest Ihre erfassten Zellen direkt, statt die ganze Engine auszuführen - das Öffnen der Daten eines Datenpunkts geht also auch in einer großen App schnell. **Jeder Knoten öffnet sich so, wie sich ein Knoten in einer Analyse-App öffnet** - dieselbe Ansicht, gleich gezeichnet, bis zum letzten Bedienelement: ein Reiter **Konfiguration** und ein Reiter **Daten**, der die Zeilen dieses Knotens in der gewohnten Tabelle zeigt. Nichts davon ist bearbeitbar - die Pipeline gehört Ovidens, nicht Ihnen - aber alles, was die Engine liest, und alles, was jeder ihrer Schritte erzeugt hat, liegt vor Ihnen. Zeilen werden beim Scrollen seitenweise gelesen, und Sie können sie sortieren, filtern und durchsuchen wie jede andere Tabelle; nichts wird weggelassen und nichts zusammengefasst. Unterschiedlich ist der Reiter Konfiguration. Eine **Relation** benennt, welchen Teil der App-Konfiguration sie trägt. Ein **Engine-Schritt** zeigt seine Anweisung **vollständig** - keine Zusammenfassung und keinen Ausschnitt - zusammen mit der Liste der Tabellen, die er liest, sodass Sie lesen können, was die Zeilen im Reiter daneben erzeugt hat. **Die Pipeline gehört Ovidens, ganz.** Kein Schritt lässt sich bearbeiten, übergeben oder übernehmen - weder in diesem Editor noch anderswo. Wenn sich mit den Bausteinen nicht ausdrücken lässt, was Sie brauchen, lesen Sie die Daten mit einem Knoten [Indikator-Daten](../analytical-apps/pipeline/indicator-data.mdx) in eine **Analyse-App**: Die hat einen echten SQL-Editor mit Vorschau und typisierten Ausgabespalten, und ihre Anweisungen gehören Ihnen. **Diese Pipeline ist kein Bild davon, wie Ihre App berechnet wird - sie ist es, was sie berechnet.** Jede Zahl, die Ihre App zeigt, kommt aus diesen Schritten: der Wert in der Zelle eines Formel-Datenpunkts, das Bestehen oder Scheitern einer Wertregel und einer Konsistenzprüfung, ob ein bedingter Datenpunkt für eine Periode gilt. Öffnen Sie den eigenen Schritt eines Formel-Datenpunkts, und Sie lesen genau die Zahlen, die das Erfassungsraster Ihnen zeigt, an denselben Koordinaten; öffnen Sie einen Schritt darüber, und Sie lesen, woraus sie berechnet wurden. Dahinter steht keine zweite, private Berechnung. **Ein Objekt, eine Anweisung.** Eine Berechnung wird nicht zur Laufzeit interpretiert - sie wird beim Speichern einmal in SQL übersetzt, und diese Anweisung ist es, die läuft. Der Schritt, den Sie lesen können, IST also Ihre Berechnung, und dazwischen steht keine Engine, die sie anders lesen könnte. Deshalb erreicht Sie eine Ablehnung auch **beim Speichern** und nicht als Befund, den Sie suchen müssen. Eine Referenz auf einen Datenpunkt, den die App nicht führt, eine Erfassungsfrequenz, die nicht zusammengeht, eine Aufschlüsselung, die sich nicht ausrichten lässt, eine Berechnung, die sich selbst liest: jede wird schon beim Erzeugen der Anweisung abgelehnt, mit der Angabe, was nicht passte. Nichts Unstimmiges wird je geschrieben - es gibt deshalb keinen Befundschritt zum Nachschlagen, denn die Ablehnung geschah, bevor es überhaupt einen Schritt gab. ## Warum "Ausführen" bei einem Engine-Schritt ausgegraut ist [#warum-ausführen-bei-einem-engine-schritt-ausgegraut-ist] Das SQL eines Schritts liegt in demselben Editor, den ein SQL-Knoten einer Analyse-App verwendet, mit derselben Schaltfläche **Ausführen** darunter - und bei einem Engine-Schritt ist sie bewusst abgeschaltet, mit der Begründung daneben. Ein Engine-Schritt ist keine Abfrage auf Tabellen, die irgendwo darauf warten, gelesen zu werden. Er läuft als ein Teil der Auswertung dieser App, über Relationen, die nur für die Dauer dieser Auswertung aufgebaut werden und danach verschwinden - für einen einzelnen Lauf gibt es also nichts zu lesen. Was diese Anweisung erzeugt hat, steht im Reiter **Daten** daneben, und das ist eine Seite des echten Ergebnisses und keine Wiederholung davon. An den Anweisungen selbst ist nichts Ovidens-spezifisch: Es ist gewöhnliches SQL für [DuckDB](https://duckdb.org). Markieren Sie den Text im Editor, um eine Kopie in einen eigenen Editor zu übernehmen und ihn dort in Ruhe zu lesen. ## Wenn der Reiter leer ist [#wenn-der-reiter-leer-ist] Die Pipeline einer App wird aus ihrer Struktur erzeugt, und eine App, die es schon vor der Pipeline gab, trägt noch keine. Jede gespeicherte Änderung an ihrer Struktur erzeugt sie: legen Sie einen Datenpunkt, eine Dimension, eine Gruppe oder eine Konsistenzprüfung an oder bearbeiten Sie eine, und der Reiter füllt sich. Jede seither angelegte App trägt ihre Pipeline von Anfang an. ## Verwaiste Daten [#verwaiste-daten] Ein Wert wird zusammen mit der Struktur gespeichert, gegen die er erfasst wurde: welcher Indikator, welche Dimensionselemente und welches Erfassungsintervall. Ändern Sie diese Struktur danach, wandern die bereits erfassten Werte nicht mit - sie bleiben zurück, außerhalb des Rasters, das der Indikator jetzt anfordert, und werden von nichts mehr gelesen. Der Reiter Pipeline meldet sie, mit dem Indikator und der Anzahl betroffener Zeilen. | Was dort steht | Was passiert ist | Was zu tun ist | | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | In einem Erfassungsintervall erfasst, das der Datenpunkt nicht mehr verwendet | Das Erfassungsintervall des Indikators wurde geändert, nachdem diese Werte eingegeben wurden | Das Intervall zurückstellen, um sie zu erreichen, oder sie löschen | | Gegen eine Dimension erfasst, die der Datenpunkt nicht mehr hat | Eine Dimension wurde dem Indikator entzogen, und diese Zeilen nennen weiterhin eines ihrer Elemente | Die Dimension erneut zuweisen oder die Zeilen löschen | | Erfasst, bevor eine Dimension hinzugefügt wurde | Eine Dimension wurde hinzugefügt, nachdem diese Werte eingegeben wurden, daher nennen sie dafür kein Element | Die Werte gegen die neue Dimension erneut erfassen oder sie löschen | | Gegen ein Element erfasst, das nicht mehr aktiv ist | Ein Dimensionselement wurde gelöscht oder deaktiviert, während Werte es noch nannten | Das Element wieder aktiv setzen oder die Zeilen löschen | | Gegen ein Element auf einer anderen Ebene der Hierarchie erfasst | Der Indikator erfasst die Dimension auf einer Ebene, und diese Zeilen nennen ein aktives Element darüber oder darunter - meist, weil die Erfassungsebene nach der Eingabe geändert wurde | Den Indikator wieder auf dieser Ebene erfassen oder die Zeilen löschen | Wenn es nichts zu melden gibt, sagt der Reiter das. Konnte die Prüfung nicht ausgeführt werden - die Plattform war ausgelastet, oder Ihr Arbeitsbereich hat sein Rechenkontingent für den Monat aufgebraucht - sagt er **das** stattdessen, statt ein sauberes Ergebnis über Daten zu melden, die er nicht gelesen hat. **Die Prüfung wird bei jedem Öffnen des Reiters Pipeline neu gestellt**, es gibt also keine Schaltfläche zum Aktualisieren. Solange der Reiter offen ist, ist nichts erreichbar, was die Antwort ändern könnte: Werte werden im Erfassungsraster eingegeben und die Struktur auf den anderen Reitern bearbeitet - hierher zurückzukommen ist also genau das, was eine frische Antwort holt. **Einen dieser Werte löschen.** Wo der Wert noch eine Zelle im Erfassungsraster hat - meist nach einer geänderten Erfassungsebene oder einem geänderten Element - leeren Sie die Zelle und speichern, genau wie bei jeder anderen Korrektur. Das geht auch dann, wenn das Eintragen eines NEUEN Werts an derselben Stelle abgelehnt wird: Löschen ist immer erlaubt, denn genau so handeln Sie auf diese Liste hin. Die Zelle liest sich danach als leer, statt aus dem Raster zu verschwinden. ## Was Rechenleistung braucht, und was ohne sie passiert [#was-rechenleistung-braucht-und-was-ohne-sie-passiert] Weil die Pipeline es ist, was die App berechnet, verbrauchen sowohl das Lesen als auch das Ändern das Rechenkontingent Ihres Arbeitsbereichs. Ein Arbeitsbereich, der es aufgebraucht hat (oder eine plattformweite Pause), sagt das - statt eine Antwort zu geben, die er nicht berechnet hat. * **Der Daten-Reiter eines Schritts** führt die Engine für diesen Schritt aus. Die Zeilen einer **Relation** zu lesen nicht - eine Relation ist Ihre eigene Konfiguration, ihre Spalten und Zeilen sind kostenlos. * **Die Struktur zu ändern** braucht **keine**. Die Pipeline wird allein aus Ihrer Konfiguration neu gebaut, und eine Berechnung wird geprüft, indem die Anweisung dafür erzeugt wird - beides geschieht in Ovidens selbst. Ein Datenpunkt, eine Dimension, eine Gruppe oder eine Prüfung lässt sich also auch dann speichern, wenn keine Rechenleistung verfügbar ist, samt einer Berechnung. Was währenddessen nicht geht, ist *die Ergebnisse zu lesen*. * **Einen Wert einzugeben** prüft die Regeln der App gegen Ihre Eingabe und braucht daher ebenfalls Rechenleistung. Das ist Absicht: einen Wert anzunehmen, den die Plattform nicht prüfen konnte, ist schlechter, als Sie zu bitten, später wiederzukommen. Die Ablehnung benennt sich immer selbst und sagt, was zu tun ist; nichts wird auf einer halben Antwort gespeichert oder angezeigt. ## Warum eine Intervalländerung abgelehnt werden kann [#warum-eine-intervalländerung-abgelehnt-werden-kann] Weil ein Wert das Intervall trägt, in dem er erfasst wurde, würde eine Änderung des **Erfassungsintervalls** eines Indikators jeden bereits erfassten Wert zurücklassen. Wenn Sie den Indikator bearbeiten, lehnt Ovidens diese Änderung ab, solange solche Werte vorhanden sind, und nennt Ihnen die Anzahl der betroffenen Zeilen. Der Weg nach vorn: diese Werte zuerst löschen und danach das Intervall ändern. Eine Option "trotzdem ändern" gibt es bewusst nicht - sie wäre eine Schaltfläche, deren einzige Wirkung stiller Datenverlust ist. **Für das Wiederherstellen einer früheren Version gilt diese Prüfung nicht.** Eine Wiederherstellung setzt die gesamte App zurück, Erfassungsintervalle eingeschlossen, und kann daher Werte zurücklassen, für die eine Bearbeitung abgelehnt worden wäre. Sie wird bewusst nicht blockiert - eine Rückkehr, die Sie nicht ausführen dürfen, ist ihrerseits eine Falle - stattdessen wird das Ergebnis gemeldet statt verhindert: prüfen Sie nach dem Wiederherstellen einer Version die Liste **Verwaiste Daten** weiter oben. # Eine Erhebungsrunde beantworten (/docs/de/indicator-apps/questionnaire) **Den Fragebogen öffnen Sie aus einer Erhebungsrunde heraus.** Wechseln Sie in der Indikator-App auf den Tab **Erhebung** und drücken Sie **Beantworten** auf der Zeile, die die Erhebungsrunde und die Berichtseinheit nennt, die Sie ausfüllen möchten - eine Zeile je Paar, sodass mehrere Einheiten mehrere Zeilen sind und kein zweites Menü. Das führt Sie in das geführte Formular: die Fragen der App, Abschnitt für Abschnitt, sodass Sie nie verstehen müssen, wie die App gebaut ist, um sie auszufüllen. Wie eine Erhebungsrunde eingerichtet wird und was diese Tabelle zeigt, steht unter [Erhebungsrunden](./collection-rounds.mdx). Der Fragebogen betrifft immer EINE Erhebungsrunde und die Berichtseinheiten, für die Sie gefragt wurden - beides kommt aus der Erhebungsrunde und Ihren eigenen Zuordnungen und nicht aus einer Auswahl auf diesem Bildschirm. **Die Erhebungsrunde legt den Berichtszeitraum fest**, deshalb gibt es hier keine Zeitraumauswahl, und sie grenzt die Fragen auf die ein, die Ihren Einheiten tatsächlich gestellt werden. Wer für zwei Standorte verantwortlich ist, beantwortet beide in einem Fragebogen, mit je einer Spalte. Wurden Sie noch um nichts gebeten, gibt es in der Navigation gar keinen Eintrag **Aufgaben**. **Eine Runde, in der Sie angefragt sind, erscheint erst, wenn Sie die Daten dieser App bearbeiten dürfen.** Die Zuordnung zu einer Einheit sagt, wofür Sie antworten, nicht, dass Sie die App öffnen dürfen. Wenn Sie eine Runde erwarten und sie nicht sehen, oder beim Öffnen gemeldet wird, die Runde gebe es nicht, bitten Sie eine Workspace-Administratorin oder einen Workspace-Administrator um Zugriff auf die App - zum Antworten braucht es die Rolle Mitwirkender oder mehr. Wenn Sie eine Zahl nur lesen oder korrigieren möchten - und Ihr Arbeitsbereich Ihnen die Berechtigung dafür gegeben hat -, ist der Tab **Daten** der andere Weg hinein: Er zeigt den Datensatz eines Datenpunkts, seine Konfiguration und seine Zahlen im Zeitverlauf, statt eines Formulars. Eine App ganz ohne Erhebungsrunde erfasst ausschließlich über diesen Tab. ## Sich zurechtfinden [#sich-zurechtfinden] Die Seite hat zwei Hälften. **Links** stehen Ihr Fortschritt und Ihre Landkarte: * eine Karte **Gesamtvollständigkeit** mit einem Fortschrittsring und der schlichten Angabe, wie viele Pflichtangaben erledigt sind, zum Beispiel "12 von 20 erforderlich erfasst", * ein Suchfeld über Abschnitte und Fragen und * eine Karte pro Abschnitt, jeweils mit eigener Vollständigkeit und, aufgeklappt, den Fragen als Liste. Ein Klick auf einen Abschnitt schaltet das Formular dorthin; ein Klick auf eine Frage scrollt direkt zu ihr. Mit **Abschnitte ausblenden** können Sie die ganze Leiste verbergen, wenn Sie das Formular über die volle Breite wollen; Ovidens merkt sich diese Wahl und auch, welche Abschnitte Sie eingeklappt gelassen haben. **Rechts** steht das Formular selbst: der aktuelle Abschnitt als Stapel von Fragezeilen, darüber **Abschnitt 3 von 8** und die Schaltflächen **Vorheriger Abschnitt** / **Nächster Abschnitt**, um sich durchzuarbeiten. ## Eine Fragezeile [#eine-fragezeile] Jede Zeile rahmt einen Datenpunkt: seine Nummer und seinen Namen, eine Markierung, wenn er erforderlich ist, seine Einheit und alle Hinweise, die die Autorin oder der Autor der App dazu geschrieben hat. Daneben steht sein Status: **Erforderlich**, **Erfasst**, **Optional**, **Nicht anwendbar** oder **Auslöser unbekannt**. Die letzten beiden sind wichtig zu kennen. Eine App kann eine Frage an eine Bedingung knüpfen, sodass sie nur gestellt wird, wenn etwas anderes zutrifft. Ist die Bedingung falsch, sehen Sie "Nicht anwendbar" mit der ausgeschriebenen Begründung, statt nach einer Zahl gefragt zu werden, die es nicht gibt. Hängt die Bedingung von einer Antwort ab, die noch niemand gegeben hat, bleibt die Frage offen und ist mit "Auslöser unbekannt" markiert, statt still ausgeblendet zu werden. Ein ganzer Abschnitt kann auf dieselbe Weise an eine Bedingung geknüpft sein. Das Eingabefeld passt zur Art des Datenpunkts: ein Zahlenfeld, ein Textfeld, ein Ja/Nein, eine Auswahlliste oder ein Datei-Upload. Werte werden laufend gespeichert, mit einem kleinen **Wird gespeichert…** und dann **Gespeichert** neben dem Feld; **Leeren** setzt einen Wert wieder zurück. ### Der Vorjahreswert [#der-vorjahreswert] Neben einem Wert sitzt eine kleine Uhr. Fahren Sie darüber, und Sie sehen, wie genau diese Zahl in den vorangehenden Kalenderjahren aussah, das jüngste zuerst, bis zu drei Jahre zurück. Sie erscheint nur, wenn es Historie zu zeigen gibt, und liest sich bei einer einzelnen Zahl genauso wie bei jeder Zelle einer Aufschlüsselung. ### Kommentare [#kommentare] **Kommentar hinzufügen** hängt eine Notiz an einen Wert: warum sich die Zahl bewegt hat, was ausgeklammert wurde, woher sie stammt. Manche Fragen verlangen einen Kommentar, und dann sagt die App das. Ein Wert mit Kommentar zeigt ihn an; **Kommentar bearbeiten** oder **Entfernen** ändern ihn später. ### Fragen mit einer Aufschlüsselung [#fragen-mit-einer-aufschlüsselung] Ist ein Datenpunkt nach einer [Dimension](./dimensions/other-dimensions.mdx) aufgeschlüsselt, zum Beispiel je Standort, enthält die Zeile statt eines einzelnen Feldes ein kleines Raster: eine Zeile je Element der Aufschlüsselung, mit einer **Summe**, damit Sie sehen, dass die Zahlen aufgehen. Das Raster zeigt Ihre eigene Berichtseinheit und alles darunter - und sonst nichts. Eine **Pflicht**-Aufschlüsselung gilt erst als vollständig, wenn jede einzelne Zelle eine Zahl enthält, in jedem Zeitraum des Berichtsfensters - eine als nicht zutreffend markierte Zelle ist die eine legitime Lücke. Eine optionale Aufschlüsselung ist lockerer: eine Zahl je Zeitraum genügt. Jede Zelle dieses Rasters nimmt einen eigenen Kommentar auf, und eine leere Zelle bietet **Aus dem Vorjahr übernehmen** an, was die Zahl des vorherigen Fensters überträgt und die Zelle so markiert, dass Prüfende die Herkunft sehen. Ist eine Aufschlüsselung zu groß, um inline bequem zu sein, bringt Sie **Aufschlüsselung bearbeiten** in das vollständige Datenraster des Editors für diesen Datenpunkt und Zeitraum, wo dieselben Werte mit mehr Platz bearbeitbar sind. ## Wenn eine Frage hier nicht beantwortet werden kann [#wenn-eine-frage-hier-nicht-beantwortet-werden-kann] Ein Datenpunkt wird in seiner eigenen Frequenz erfasst - monatlich, vierteljährlich, jährlich. Enthält der Berichtszeitraum der Erhebungsrunde keinen vollständigen Zeitraum dieser Frequenz, sagt die Frage das, statt eine Zahl anzunehmen, die nichts bedeuten würde. Ein Admin oder eine berichtsverantwortliche Person der App behebt das, indem ein abdeckender Berichtszeitraum angelegt wird. Eine **freigegebene** Antwort ist gesperrt, für alle einschließlich Administratorinnen und Administratoren: Der Hinweis darüber sagt, von wem und wann entschieden wurde, und erst das Wiederöffnen durch eine prüfende Person macht sie wieder beschreibbar. Eine **zur Überarbeitung zurückgegebene** Antwort ist das Gegenteil - sie ist wieder offen, mit der Begründung der prüfenden Person neben der Frage, damit Sie wissen, was zu korrigieren ist. # KI-Transparenz (/docs/de/infrastructure/ai-transparency) Diese Seite erklärt in einfachen Worten, wie KI in Ovidens funktioniert: was die KI ist, welche Modelle sie antreiben, welche Daten diese Modelle erreichen und welche Kontrollen dafür sorgen, dass ein Mensch die Verantwortung behält. Sie ist informativ und beschreibt das Produkt, wie es heute funktioniert; sie ist keine Rechtsberatung. ## Was die KI ist [#was-die-ki-ist] Ovidens hat eine KI namens **Vera**. Sie begegnet Ihnen an drei Stellen, doch es ist dieselbe Assistentin mit denselben Kontrollen dahinter: * Die **eingebaute Assistentin**, mit der Sie beim Bauen oder Abfragen einer App chatten. * **Skills**: wiederverwendbare KI-Aufgaben, die ein App-Autor speichert, sodass sie jeder ausführen kann. * Der **gehostete Connector**, eine Tür pro Workspace, über die Ihre eigene KI (zum Beispiel Claude) in Ihrem Workspace in Ihrem Namen arbeitet. Siehe [Eigene KI verbinden](../integrations/mcp.mdx). ## Modelle und Anbieter [#modelle-und-anbieter] Welches Modell Vera antreibt, ist eine **Entscheidung pro Workspace**. Ein Workspace-Admin wählt es aus; jedes Mitglied kann das aktive Modell sehen. Diese Modelle enthält der Katalog: | Modell | Anbieter | Wo Ihre Daten verarbeitet werden | | -------------------------------- | --------------------- | -------------------------------- | | **Claude Haiku 5.5** | Anthropic-API, direkt | Nicht auf die EU beschränkt | | **Claude Sonnet 5.5** (Standard) | Anthropic-API, direkt | Nicht auf die EU beschränkt | | **Claude Opus 5.5** | Anthropic-API, direkt | Nicht auf die EU beschränkt | Diese drei Modelle stehen zur Auswahl. Vera verwendet **ein konfiguriertes Modell pro Workspace für alle KI-Aufgaben**: Chat, KI-Widgets mit Skills, interne Hilfsaufgaben und Gesprächszusammenfassungen. Es gibt kein separates Hilfsmodell. Der **Standard** ist Claude Sonnet 5.5. Die API verwendet standardmäßig globales Inferenz-Routing. **Veras KI-Verarbeitung ist nicht auf die EU beschränkt**, auch wenn die Anwendung in Frankfurt gehostet wird. Siehe [Anthropics Angaben zum Datenstandort](https://platform.claude.com/docs/en/manage-claude/data-residency). Anthropic trainiert standardmäßig nicht mit Eingaben oder Ausgaben der kommerziellen API; siehe die [Datenrichtlinie für kommerzielle Produkte](https://privacy.claude.com/en/articles/7996868-is-my-data-used-for-model-training). Ovidens ist der **Anbieter** dieses KI-Systems und ein **Betreiber** von KI-Modellen mit allgemeinem Verwendungszweck. Ovidens trainiert keine eigenen Modelle; es nutzt die Modelle der Modellanbieter, wie sie über deren APIs bereitgestellt werden. ### Das KI-Modell des Workspace auswählen [#das-ki-modell-des-workspace-auswählen] Ein Workspace-Admin wählt das Modell auf dem Tab **Allgemein** der Seite **System** (die Workspace-Konfigurationsseite in der Seitenleiste) unter **KI-Konfiguration**: Die Karte „KI-Modell des Workspace" listet jedes auswählbare Modell mit einer kurzen Beschreibung und seinem Verarbeitungsort und zeigt, welches aktiv ist. Mitglieder sehen dieselbe Karte schreibgeschützt. Die Auswahl gilt für den gesamten Workspace - jeder Vera-Chat, jeder Einrichtungslauf und jeder Skill-Lauf nutzt das ausgewählte Modell, einschließlich interner KI-Aufgaben. Jede neue oder fortgesetzte Anfrage verwendet die aktuelle Workspace-Einstellung, auch beim Öffnen älterer Gespräche. Ein Gesprächsverlauf legt kein Modell dauerhaft fest. Das Prüfprotokoll hält für jede KI-Aktion das genutzte Modell fest, sodass ein Modellwechsel in der Historie des Workspace sichtbar ist. Siehe [Administration](../administration.mdx) zum Aktivitätsprotokoll. ## Verwendungszweck und Grenzen [#verwendungszweck-und-grenzen] Vera unterstützt beim **Bauen und Abfragen von Analytik**: beim Einrichten der Datenschritte, Indikatoren und der Visualisierung einer App und beim Beantworten von Fragen zu Ihren Daten. Ihre Ausgaben sind **KI-generiert und können ungenau sein**. Behandeln Sie sie als schnellen ersten Entwurf, nicht als endgültige Antwort. Wie der Hinweis in der App sagt: Vera ist eine KI-Assistentin, ihre Antworten sind KI-generiert und können ungenau sein, prüfen Sie sie daher bitte, bevor Sie darauf handeln. Die Verantwortung für das, was Sie veröffentlichen oder entscheiden, bleibt bei Ihnen. ## Menschliche Aufsicht und Kontrollen [#menschliche-aufsicht-und-kontrollen] Vera ist so gebaut, dass ein Mensch die Verantwortung behält: * **Änderungen folgen Ihrer Anfrage und Ihren Berechtigungen.** Angeforderte Änderungen verwenden den App-Entwurf. Vera veröffentlicht nur auf Ihre Anfrage und mit einem für Sie freigegebenen Veröffentlichungswerkzeug. KI-Widgets sind schreibgeschützt und ändern weder Daten noch erzeugen sie einen Entwurf. * **KI-Berechtigungen und Opt-outs pro Benutzer.** Ein Admin legt das Maximum dessen fest, was die KI eines Mitglieds je tun darf, und jedes Mitglied kann alles ausschalten, was seine KI nicht tun können soll. * **Externe KI ist standardmäßig gesperrt.** Der gehostete Connector ist für jedes Mitglied aus, bis ein Admin ihn pro Person und pro Verbindung freigibt. * **Ratenbegrenzungen pro Oberfläche** halten die KI-Nutzung begrenzt. * **Begrenzte Skill-Läufe.** Ein Skill-Lauf ist auf etwa 30 Schritte und rund 150 Sekunden begrenzt, sodass er stets in einem vorhersehbaren Rahmen endet. * **Ein Workspace-Prüfprotokoll** hält jede KI-Aktion mit KI-Herkunft pro Ressource fest: welche Oberfläche sie ausgeführt hat, welches Modell oder welcher Client genutzt wurde (das ausgewählte Modell des Workspace, pro Aktion) und welche Person die KI angewiesen hat. Zum Aktivitätsprotokoll siehe [Administration](../administration.mdx). ## Datenverarbeitung [#datenverarbeitung] Vera sendet Ihre Anfrage, relevanten Gesprächskontext, Anhänge und Werkzeugergebnisse an die Anthropic-API. Je nach Aufgabe können diese Ergebnisse App-Konfiguration, Wissensdatenbank-Inhalte und abgefragte Daten enthalten. Der vollständige gespeicherte Verlauf wird nicht bei jeder Anfrage gesendet; das Modell erhält einen begrenzten jüngeren Ausschnitt automatisch. Ältere Beiträge können Sie im Gesprächsverlauf lesen. Nennen Sie wichtige Angaben erneut, wenn sie außerhalb des jüngeren Modellkontexts liegen. Jeder Aufruf verwendet das konfigurierte Workspace-Modell über diese direkte API. Ihre Verarbeitung ist **nicht auf die EU beschränkt**. Ovidens prüft vor jeder Ressourcenoperation Ihre aktuellen Berechtigungen. Workspace-Gespräche bleiben für ihre jeweilige Person privat. Für Anthropics API gelten die [Datenrichtlinie für kommerzielle Produkte](https://privacy.claude.com/en/articles/7996868-is-my-data-used-for-model-training) und die [Datenstandort-Einstellungen](https://platform.claude.com/docs/en/manage-claude/data-residency). Eine externe, über MCP verbundene KI verwendet ihren eigenen Anbieter und dessen Bedingungen. Prüfen Sie diese vor dem Verbinden; sie sind unabhängig von Veras Modellauswahl. ## Transparenz-Kennzeichnungen [#transparenz-kennzeichnungen] Zwei Kennzeichnungen machen die KI-Nutzung sichtbar: * **Der Hinweis „Sie interagieren mit einer KI"**, der überall dort erscheint, wo Vera antwortet. * **Die KI-Herkunfts-Kennzeichnung im Prüfprotokoll**, die jede KI-getätigte Änderung mit der Oberfläche, dem Modell oder Client und der anweisenden Person markiert. ## Wie sich das auf den EU AI Act abbildet [#wie-sich-das-auf-den-eu-ai-act-abbildet] Für die hier beschriebenen KI-Funktionen behandelt Ovidens seine Pflichten als **Transparenzrisiko**-Pflichten nach **Artikel 50** des EU AI Act: Menschen wird mitgeteilt, wenn sie mit einem KI-System interagieren, und KI-generierte Aktionen werden protokolliert und sind zuordenbar. # Unterauftragsverarbeiter und KI-Daten (/docs/de/infrastructure/sub-processors) Diese Seite nennt die Dritten (Unterauftragsverarbeiter), die Ovidens zur Verarbeitung von Kundendaten für seine KI und sein Kern-Hosting einsetzt, und wo diese Verarbeitung stattfindet. Sie ist informativ und beschreibt den Dienst, wie er heute funktioniert; sie ist keine Rechtsberatung. Die folgende Liste umfasst die **KI- und Kern-Datenverarbeitung**. ## KI-Unterauftragsverarbeiter [#ki-unterauftragsverarbeiter] | Unterauftragsverarbeiter | Zweck | Verarbeitungsort | Training mit Ihren API-Daten? | | ------------------------------ | ---------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------- | | **Anthropic** (Claude-Modelle) | Alle Vera-Aufrufe mit dem konfigurierten Workspace-Modell über die direkte Anthropic-API | Nicht auf die EU beschränkt | Standardmäßig nein, gemäß Anthropics Richtlinie für kommerzielle Produkte | Ovidens trainiert keine Modelle mit Ihren Daten. Siehe [Anthropics Datenrichtlinie für kommerzielle Produkte](https://privacy.claude.com/en/articles/7996868-is-my-data-used-for-model-training) zum standardmäßigen Ausschluss des Trainings und zu Ausnahmen bei ausdrücklich geteiltem Feedback. Welches Claude-Modell Vera nutzt, wird pro Workspace ausgewählt. Siehe [Das KI-Modell des Workspace auswählen](./ai-transparency.mdx#das-ki-modell-des-workspace-auswählen). ## Kern-Hosting- und Daten-Unterauftragsverarbeiter [#kern-hosting--und-daten-unterauftragsverarbeiter] | Unterauftragsverarbeiter | Zweck | Region | | ----------------------------- | -------------------------------------------------------------------------------------- | -------------------------- | | **AWS** (Amazon Web Services) | Anwendungs-Hosting, Speicher und Datenbank | eu-central-1 (Frankfurt) | | **AWS Cognito** | Benutzeranmeldung und Authentifizierung | eu-central-1 (Frankfurt) | | **AWS SES** | Transaktions-E-Mails (Workspace-Einladungen) | eu-central-1 (Frankfurt) | | **MotherDuck** | Das verwaltete DuckDB-Data-Warehouse, das Ihre Analytikdaten speichert und verarbeitet | Auf AWS in der EU gehostet | ## Datenstandort und Aufbewahrung [#datenstandort-und-aufbewahrung] Das Kern-Anwendungs-Hosting, der Speicher und die Datenbank von Ovidens laufen auf AWS in **Frankfurt (eu-central-1)**. Analytikdaten werden von **MotherDuck** auf AWS in der EU gespeichert und verarbeitet. Für Daten, die an Veras Anthropic-API gesendet werden, gilt eine eigene Verarbeitungsgrenze: **Die KI-Verarbeitung ist nicht auf die EU beschränkt**. Das Inferenz-Routing der API ist standardmäßig global. Siehe [Anthropics Angaben zum Datenstandort](https://platform.claude.com/docs/en/manage-claude/data-residency) und [Veras Datenverarbeitung](./ai-transparency.mdx#datenverarbeitung). Ovidens' Aufbewahrung richtet sich nach Ihrem Vertrag. Für die Aufbewahrung beim Anbieter gelten dessen Bedingungen. Eine in der EU gehostete Anwendung begründet weder eine ausschließlich europäische KI-Verarbeitung noch eine Zusage, dass der Anbieter keine Daten aufbewahrt. ## Kein Training mit Ihren Daten [#kein-training-mit-ihren-daten] Ovidens trainiert keine Modelle mit Workspace-Daten. Anthropics kommerzielle API verwendet Eingaben oder Ausgaben standardmäßig nicht zum Training, gemäß der [Datenrichtlinie für kommerzielle Produkte](https://privacy.claude.com/en/articles/7996868-is-my-data-used-for-model-training). Ovidens prüft vor dem Senden von Ressourcendaten die Berechtigungen der jeweiligen Person und hält Workspace-Daten von anderen Workspaces getrennt. ## Auftragsverarbeitungsvertrag [#auftragsverarbeitungsvertrag] {/* TODO: Der Link zum Auftragsverarbeitungsvertrag (AVV) und dessen rechtliche Bedingungen müssen von der Rechtsberatung bereitgestellt werden. Hier keine AVV-Bedingungen formulieren. Diesen Platzhalter durch die von der Rechtsberatung freigegebene AVV-Referenz (ein verlinktes Dokument oder ein Kontaktweg) ersetzen, sobald verfügbar. */} Ein Auftragsverarbeitungsvertrag (AVV) ist für Kunden auf Anfrage verfügbar. Kontaktieren Sie uns unter [contact@ovidens.com](mailto:contact@ovidens.com), um ein Exemplar anzufordern. Die AVV-Bedingungen werden von unserer Rechtsberatung bereitgestellt; diese Seite gibt sie nicht wieder. *** Diese Seite ist eine sachliche, nach bestem Wissen erstellte Offenlegung und ist **informativ, keine Rechtsberatung**. Sie begründet keine vertragliche Bestimmung. # Zugangsdaten (/docs/de/integrations/api/credentials) Diese Seite richtet sich an die **Administration** des Workspace, die die Zugangsdaten ausstellt. Die Entwicklerseite ist [Automation API](./overview) - nur auf Englisch. Ein **verbundenes System** ist eine andere Software, die eigenständig mit Ihrem Workspace arbeitet: ein ERP, ein Data Warehouse, ein nächtlicher Job. Es meldet sich mit eigenen Zugangsdaten an statt mit dem Konto einer Person, schreibt Daten in einen Datensatz, startet einen Lauf und holt sich die Ergebnisse nach seinem eigenen Zeitplan. ## Was ein verbundenes System tun kann [#was-ein-verbundenes-system-tun-kann] Was es darf, entscheidet allein der Zugriff, den Sie seinen Zugangsdaten geben: **den Workspace lesen**, **Daten schreiben** in einen Datensatz einer analytischen App, **eine Pipeline ausführen** und **Ergebnisse lesen** als herunterladbaren Export. Mehr gibt es nicht. Welchen Zugriff Sie auch erteilen: Ein verbundenes System kann keine Apps anlegen oder löschen, keine Pipeline ändern, keine Visualisierung bearbeiten, niemanden einladen und keine Workspace-Einstellungen ändern. ## Zugangsdaten ausstellen [#zugangsdaten-ausstellen] Öffnen Sie die Seite **Zugriff** und dort den Tab **API-Zugangsdaten** neben den Mitgliedern. Der Tab ist **nur für Admins**: Einem Mitglied wird er gar nicht angeboten, und jede Anfrage dahinter wird für alle außer Admins abgelehnt. Zugangsdaten gehören dem **Workspace**, nicht einer Person - sie funktionieren weiter, wenn die erstellende Person den Workspace verlässt. Die Liste nennt weiterhin die Person, die sie ausgestellt hat, und markiert die Zugangsdaten mit **Ehemaliges Mitglied**, sobald diese Person nicht mehr da ist. So haben Sie einen Anlass zu prüfen, ob die Integration noch gewollt ist. **Neue Zugangsdaten** fragt nach einem Namen, einer optionalen Beschreibung, der Laufzeit der Zugangsdaten (30, 90, 180 oder 365 Tage; 365, sofern Sie nichts anderes wählen) und dem Zugriff, den Sie erteilen. Benennen Sie sie nach dem System, für das sie sind ("ERP-Nachtimport" statt "Test"): Dieser Name erscheint im Prüfprotokoll, sobald die Integration etwas schreibt. Beim Erstellen entstehen eine **Client-ID**, die die Zugangsdaten identifiziert und gefahrlos angezeigt werden darf, und ein **Client-Secret**, mit dem das System nachweist, dass es diese Zugangsdaten besitzt. **Das Secret wird genau einmal angezeigt**, in dem Dialog direkt nach dem Erstellen, und nichts kann es erneut anzeigen - es ist nirgends so gespeichert, dass jemand es zurücklesen könnte, auch kein Admin. Geht es verloren, erneuern Sie das Secret. Der Dialog kopiert beide einzeln oder zusammen als Umgebungsvariablen `OVIDENS_CLIENT_ID` und `OVIDENS_CLIENT_SECRET`, und **Ich habe es gespeichert** schließt ihn und verwirft das Secret von der Seite. Geben Sie beides an die Person weiter, die die Integration baut, zusammen mit einem Link: [Automation API](./overview) (auf Englisch). Alles Weitere - wie sich das System anmeldet, welche Endpunkte es aufruft, wie die Datei aussehen muss - steht dort. ## Was die Zugriffsoptionen bedeuten [#was-die-zugriffsoptionen-bedeuten] Jede Option erteilt genau eine Art von Arbeit und sonst nichts. Neben der Beschriftung zeigt die Auswahl den technischen Namen, den die Option in der API trägt - nach diesem Namen fragt die Person, die die Integration baut. | Zugriff | Was das System damit darf | | --------------------------------------- | ------------------------------------------------------------------------ | | **Workspace lesen** | Apps, Knoten und deren Konfiguration auflisten. | | **Daten schreiben** | Zeilen eines Datensatzes importieren und ändern. | | **Indikatorwerte schreiben** | Erhobene Indikatorwerte eintragen und leeren. | | **Veröffentlichte Pipelines ausführen** | Die veröffentlichte Konfiguration einer App ausführen. | | **Entwürfe ausführen** | Unveröffentlichte Konfiguration ausführen, die noch niemand geprüft hat. | | **Ergebnisse lesen** | Ergebnisse eines Durchlaufs abfragen und exportieren. | | **Veröffentlichen** | Ändern, was jede Person in diesem Workspace sieht. | Zwei Kennzeichen sagen Ihnen, wie diese Liste zu lesen ist: * **Erweitert** markiert eine Option, die über das Lesen und das Ausführen der **veröffentlichten** Konfiguration hinausgeht - einen ungeprüften Entwurf ausführen oder veröffentlichen und damit ändern, was alle sehen. Neue Zugangsdaten bringen so etwas nie mit; erteilen Sie es nur, wenn das System es wirklich braucht. * **Noch ohne Wirkung** markiert eine Berechtigung, die derzeit nirgends geprüft wird. Sie lässt sich erteilen, speichern und anzeigen, ändert aber nichts, was ein Admin beobachten könnte. Das Kennzeichen verschwindet, sobald die Option zu wirken beginnt. **Zugriff bearbeiten** ändert die Berechtigungen bestehender Zugangsdaten. Es muss nichts neu ausgestellt werden: Die Änderung gilt ab der nächsten Anfrage, und ein Entzug wirkt auch auf ein bereits ausgestelltes Token. ## Das Secret erneuern [#das-secret-erneuern] **Secret erneuern** stellt ein zweites Secret aus, ohne dass etwas ausfällt: Das heute verwendete funktioniert neben dem neuen weiter, Sie können das neue also in Ruhe ausrollen. Mehr als zwei Secrets gibt es nie, deshalb zieht erst die **nächste** Erneuerung das ältere zurück - zweimal zu erneuern ist also das bewusste "Das alte Secret gilt nicht mehr", und der Dialog sagt das vor dem Bestätigen. Erneuern Sie, wenn ein Secret nach außen gelangt sein könnte, wenn die Person geht, die die Integration eingerichtet hat, bevor die Zugangsdaten ablaufen, oder in dem Rhythmus, den Ihre Richtlinie vorgibt. Die Details zeigen, wann das aktuelle Secret erstellt wurde und wann das vorherige zuletzt verwendet wurde - so sehen Sie das alte Secret verstummen, bevor Sie es zurückziehen. Eine Erneuerung setzt außerdem ein **neues Ablaufdatum** - der Dialog fragt wie beim Erstellen nach der Laufzeit - und gilt als Bestätigung des Zugriffs (siehe unten). ## Ablauf und Bestätigung des Zugriffs [#ablauf-und-bestätigung-des-zugriffs] Alle Zugangsdaten **laufen ab**, spätestens 365 Tage nach dem Erstellen oder der letzten Erneuerung. Ab dem Ablaufdatum wird das verbundene System abgelehnt, und der einzige Weg zurück ist, das Secret zu **erneuern**, was ein neues Ablaufdatum setzt. Erneuern Sie vorher, fällt nichts aus. Alle **90 Tage** muss außerdem jemand, der Zugangsdaten verwaltet, sie **bestätigen**: also bestätigen, dass das System den Zugriff, den es hat, noch braucht. **Bestätigen** im Menü der Zugangsdaten listet ihren Zugriff. Sie können nur Zugriff behalten, den Sie selbst im gesamten Workspace haben; was Sie nicht haben, ist deshalb nicht angehakt und wird beim Bestätigen entfernt. Entfernen Sie auch den Haken bei allem anderen, was das System nicht mehr braucht. Hinzufügen können Sie auf diesem Weg nichts - das macht **Zugriff bearbeiten**. Bestätigen darf jede Person, die Zugangsdaten verwaltet, ob sie sie ausgestellt hat oder nicht - Zugangsdaten, deren ausstellende Person gegangen ist, bestätigt also, wer ihren Zugriff abdeckt, und sie behalten nur den Zugriff, den diese Person hat. Nichts an den Zugangsdaten hängt davon ab, dass die ausstellende Person noch Mitglied ist. Wird eine Bestätigung versäumt, funktionieren die Zugangsdaten noch **14 Tage** weiter, markiert mit **Bestätigung überfällig**. Danach sind sie **Gesperrt**: Das System wird abgelehnt, bis jemand sie bestätigt, was sie sofort wieder freigibt. Erstellen oder Erneuern gilt als Bestätigung. Die Tabelle der Zugangsdaten zeigt, wann sie ablaufen oder als Nächstes bestätigt werden müssen, und markiert sie **30 Tage** vor jedem der beiden Termine; die Details nennen außerdem, wer sie zuletzt bestätigt hat. Solange Zugangsdaten Aufmerksamkeit brauchen, sieht außerdem jede Person, die Zugangsdaten verwaltet, auf der Seite **Apps** einen Hinweis mit einem Link hierher. Eine E-Mail dazu gibt es nicht - achten Sie also auf diesen Hinweis. Ein verbundenes System erfährt, welcher der beiden Gründe es gestoppt hat - siehe die Fehlercodes in [Automation API](./overview) (auf Englisch) - und kann beide Termine vorab abfragen. ## Erkennen, ob es funktioniert [#erkennen-ob-es-funktioniert] Vier Stellen geben Auskunft, jede auf eine andere Frage: * Der Tab **Zugriff** in den Details der Zugangsdaten sagt, was die Zugangsdaten dürfen: jede Berechtigung, die ihre Zugriffsoptionen geben, und welche Option sie jeweils gibt. Sie haben keine Rolle und wirken deshalb in jedem gemeinsamen Bereich und jeder App darin, auch in später angelegten Apps, und nie im persönlichen Bereich einer Person. Widerrufene Zugangsdaten können nichts mehr. * **Die Tabelle der Zugangsdaten** sagt, ob die Zugangsdaten überhaupt verwendet werden. **Zuletzt verwendet** ist der Zeitpunkt der letzten Anmeldung und steht auf "Nie verwendet", bis der erste erfolgreiche Aufruf eingegangen ist. Das prüft man bei einer neuen, stummen Integration zuerst. * **Aktivität** sagt, wie viel das System tut und was es herausgeholt hat (siehe unten). * **Das Prüfprotokoll** sagt, was es tatsächlich geändert hat: Öffnen Sie die App **Audit Analytics**, gehen Sie auf **All actions** (Alle Aktionen) und filtern Sie die **Akteur-Art** auf **Integration**. Der **Akteur** einer maschinellen Schreiboperation ist die Integration selbst, unter dem Namen ihrer Zugangsdaten, nicht der Admin, der sie ausgestellt hat. Siehe [Audits](../../administration/audit-trail.mdx). Die Ereignisse zu den Zugangsdaten selbst - erstellt, Secret erneuert, Zugriff geändert, bestätigt, widerrufen - stehen im selben Protokoll und sind dem Admin zugeschrieben, der sie ausgeführt hat. Dass Zugangsdaten abgelaufen oder gesperrt sind, steht ebenfalls darin, der Plattform zugeschrieben, weil niemand es ausgeführt hat. Läufe werden dort zugeordnet, wo Sie sie suchen würden: In der **Historie** einer App trägt ein Lauf, den ein verbundenes System gestartet hat, dessen Namen daneben, auch nachdem die Zugangsdaten widerrufen wurden. Ein Lauf ohne Markierung wurde von einer Person oder der Assistenz gestartet - oder er ist so alt, dass der Protokolleintrag, aus dem die Markierung stammt, nicht mehr vorliegt. ## Was eine Integration tatsächlich getan hat [#was-eine-integration-tatsächlich-getan-hat] Der Tab **Aktivität** in den Details von Zugangsdaten hat drei Teile. **Anfragelimit** ist die Zahl der Anfragen pro Minute, die diese Zugangsdaten stellen dürfen. Anfragen darüber hinaus werden bis zum Beginn der nächsten Minute abgelehnt, und dem System wird gesagt, wie lange es warten soll. Diese Zahl nennen Sie, wenn die Integration gedrosselt wird. **Volumen der letzten 30 Tage** zählt gestartete Läufe, erzeugte Exporte samt ihrer Zeilen und Datenmenge sowie angewendete Importe samt geschriebener Zeilen. Ein fehlgeschlagener Import wird nicht gezählt. Das sind **Mengen, keine Kosten** - Rechenzeit wird für den Workspace als Ganzes gemessen, der Tab **Nutzung** bleibt also die Stelle für den Verbrauch -, aber eine Integration, deren Exportzeilen sich verdoppelt haben, ist der erste Ort, an dem man bei einer steigenden Rechnung nachsieht. **Was diese Integration herausgeholt hat** listet ihre Exporte auf, neueste zuerst: wann, welche App und welche Ausgabe, aus welchem Lauf die Datei stammt, wie viele Zeilen und wie groß, und ob es erfolgreich war. Ein Export ist ein Lesevorgang und erscheint deshalb nicht im Prüfprotokoll - diese Liste beantwortet die Frage, ob eine Integration etwas herausgeholt hat, was sie nicht sollte, und wann. Ein Sternchen bedeutet, dass die Datei am Größenlimit abgeschnitten wurde und nur einen Teil der Ausgabe enthält; "Datei entfernt" bedeutet, dass der Export erfolgreich war, die Datei aber inzwischen aufgeräumt wurde (Dateien nach Tagen, der Eintrag bleibt 90 Tage); und wer den Status **Fehlgeschlagen** überfährt, sieht den Grund, den die Plattform festgehalten hat. ## Widerrufen [#widerrufen] **Widerrufen** setzt Zugangsdaten sofort außer Kraft, auch bereits ausgestellte Token: Die nächste Anfrage wird abgelehnt. Das lässt sich nicht rückgängig machen - den Zugriff wiederherzustellen heißt, neue Zugangsdaten auszustellen und dem System das neue Secret zu geben. Widerrufene Zugangsdaten bleiben in der Liste, markiert mit **Widerrufen** und mit nichts mehr zu ändern, nur ihr Schlüssel lässt sich noch kopieren, und sie behalten ihren Namen im Prüfprotokoll - so bleibt lesbar, was sie getan haben, solange sie gültig waren. # Automation API (/docs/de/integrations/api/overview) This guide walks the whole loop with commands you can paste. The exact shape of every call is in the [API reference](./reference/overview), generated from the contract the API publishes. The contract it describes - field names, scope names, error codes - is English, so this guide and the reference are not translated. ## What the Automation API is [#what-the-automation-api-is] The machine door of the platform, at `https://api.ovidens.com`. An unattended system authenticates with a credential of its own, pushes data into a dataset, runs a pipeline and pulls the result. Its surface is deliberately narrow: read the workspace, write dataset rows, run a pipeline, export a result. It cannot create or delete apps, change a pipeline, edit a Visualisation, invite people or change workspace settings. * **Use the Automation API** when a system acts on its own schedule with its own credential - a nightly ERP export, a warehouse loader, a scheduled reconciliation. * **Use MCP** when a PERSON's AI assistant should act as that person: a different door with a different authorization model, in [Connect your own AI](../mcp). The choice between the two is laid out in [Integrations](../../integrations). Everything a connected system WRITES appears in the workspace's audit trail, attributed to the integration by its credential's name rather than to the administrator who issued it. Reads are not audited; an export leaves an export record instead. ## Getting a credential [#getting-a-credential] A workspace administrator issues one on the **Access** page's **API credentials** tab. Ask for a credential named after the system you are building, its **client id** (`ovc_...`) and **client secret** (`ovs_...`) - the secret is shown once, at creation, and nothing can show it again - and the **access** it should hold, granted as named scopes: | Scope | What it grants | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `workspace:read` | List apps, dataset and output nodes, and their configuration. Read import status, and the status of runs of PUBLISHED configuration. | | `data:write` | Stage, upload and commit a CSV import into a dataset node. | | `pipeline:run` | Run an app's PUBLISHED configuration. | | `pipeline:run_draft` | Run unpublished configuration nobody has reviewed, and see the runs of it - a draft run is invisible to a credential without this scope, including one an editor started in the app. Not granted by default. | | `exports:read` | Generate and download an export of a run result. | | `indicators:write` | Reserved. Nothing checks it today. | | `admin:publish` | Reserved. Nothing checks it today. | A new credential starts with `workspace:read`, `data:write`, `pipeline:run` and `exports:read`; the other three are an explicit choice. Narrowing a credential's access - or revoking it - binds on its very next request, including on a token it is already holding. The administrator's side is [Credentials](./credentials). ## Minting a token [#minting-a-token] OAuth 2.1, `client_credentials`. The authorization server is `https://id.ovidens.com`; the API is a separate resource server that only validates tokens. ```bash title="POST /oauth/token" curl -sS -X POST https://id.ovidens.com/oauth/token \ -u "ovc_YOUR_CLIENT_ID:ovs_YOUR_CLIENT_SECRET" \ -d "grant_type=client_credentials" \ -d "resource=https://api.ovidens.com" \ -d "scope=workspace:read data:write pipeline:run exports:read" ``` * **The credential goes in HTTP Basic** (`-u`). Form fields also work; Basic is the registered method. * **`resource=https://api.ovidens.com` is REQUIRED.** Omitting it, or naming any other resource, is a `400` with `error: "invalid_target"`. * **`scope` is optional.** Omit it for the credential's whole granted set. Naming a scope the credential does not hold fails with `400 invalid_scope` and mints nothing. * **Cache the token for its `expires_in`** rather than minting one per request; the token endpoint is rate limited by source address. * **A credential that has expired or is suspended is refused here too**, with the OAuth error `invalid_client` and, once the secret itself was right, a `code` naming which: `credential_expired` or `credential_attestation_overdue` (see [Error codes](#error-codes)). A wrong secret never carries a `code`. Present it on every API call as `Authorization: Bearer `. Three endpoints need no token, so you can read them before you hold a credential: `https://api.ovidens.com/.well-known/oauth-protected-resource` (where to authenticate), `https://api.ovidens.com/v1/openapi.json` (the contract) and `https://api.ovidens.com/v1/docs` (a redirect to this guide). ## Confirming it worked [#confirming-it-worked] ```bash title="GET /v1/whoami" curl -sS https://api.ovidens.com/v1/whoami -H "Authorization: Bearer $TOKEN" ``` ```json { "clientId": "ovc_7f3a9c1d2e4b6a8c0d5e7f91", "name": "ERP nightly import", "tenantId": "3f1c9a52-4d7e-4b60-9a1f-2c8d5e7b0a34", "workspaceName": "Contoso", "scopes": ["workspace:read", "data:write", "pipeline:run"], "credentialScopes": ["workspace:read", "data:write", "pipeline:run", "exports:read"], "tokenExpiresAt": "2026-08-02T09:30:00.000Z", "credentialExpiresAt": "2027-06-30T09:30:00.000Z", "attestationDueAt": "2026-10-01T09:30:00.000Z" } ``` This is the one call that needs a valid token and no scope, so it separates a bad secret from a missing permission. `scopes` is what THIS token can do (what you asked for, intersected with what the credential holds now); `credentialScopes` is what the administrator granted. On an unexpected `403 insufficient_scope`: in `credentialScopes` but not in `scopes` means your token asked for too little, so mint a new one; in neither means the administrator has to widen the credential. The last two fields say when the CREDENTIAL stops working unless a person acts. Every credential expires (`credentialExpiresAt`, at most a year after it was issued or last rotated), and a workspace administrator must confirm every 90 days that it still needs its access (`attestationDueAt`); a credential whose confirmation is more than 14 days overdue is suspended. A job that reads `whoami` can warn its owner well before either date instead of discovering it as a refusal. ## Discovery [#discovery] ```bash title="Discovery" curl -sS https://api.ovidens.com/v1/apps -H "Authorization: Bearer $TOKEN" curl -sS https://api.ovidens.com/v1/apps/sales_analytics/datasets -H "Authorization: Bearer $TOKEN" curl -sS https://api.ovidens.com/v1/apps/sales_analytics/outputs -H "Authorization: Bearer $TOKEN" ``` Every app, node and column is addressed by its **`technicalId`**, a stable snake\_case identifier that survives a rename. There is no numeric id on this door. **A dataset node's declared column schema IS the integration contract.** `listDatasets` reports, per column, its technical id, its type, whether a value is required, its permitted values when it is an enumeration, and which CSV value options apply. Your file's header must name exactly those columns. Build the file from this response rather than from a sample, and a schema change becomes a diff you detect instead of an import rejected at three in the morning. **A cell of a `dropdown` or `multi_select` column may carry the option's LABEL or its ID**, and either way what the platform stores is the id. `enumValues` lists the labels; beside it, `options` reports the same options in the same order, each with the stable `id` and the `description` its author wrote. A `multi_select` cell packs several of them separated by `,` or `;`. Write the label when a person authors the file and it should read like a spreadsheet; write the `id` when a system generates it, because a label can be renamed and an id cannot. Anything that is neither is refused with the row named. `listOutputs` is the mirror image: the output nodes of the app's latest successful published run, with the columns an export will carry. An app that has never published a successful run answers `200` with `publishedRun: null` and an empty list - a normal state to poll through, not an error. ## The import loop [#the-import-loop] ```text createImport -> PUT the file -> commitImport -> poll getImport -> getImportErrors ``` **1. Stage the import.** The dialect is validated and fully resolved here, before a byte moves. ```bash title="POST /v1/apps/sales_analytics/datasets/customers/imports" curl -sS -X POST \ https://api.ovidens.com/v1/apps/sales_analytics/datasets/customers/imports \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"mode":"replace","filename":"customers.csv","csv":{"delimiter":";","decimalSeparator":","}}' ``` `mode` is `replace` (the file becomes the dataset's contents) or `append`. The response carries the `importId`, the `uploadUrl` and both expiry times. **2. Upload the file** with a plain `PUT`. Send no `Authorization` header - the URL carries its own signature, and a bearer token makes the upload fail. ```bash title="PUT {uploadUrl}" curl -sS -X PUT --upload-file customers.csv -H "Content-Type: text/csv" "$UPLOAD_URL" ``` **3. Commit.** Validates the file against the declared columns and, if every row parses, applies it. ```bash title="POST /v1/apps/sales_analytics/datasets/customers/imports/imp_8c2f4a1d9e63/commit" curl -sS -X POST \ https://api.ovidens.com/v1/apps/sales_analytics/datasets/customers/imports/imp_8c2f4a1d9e63/commit \ -H "Authorization: Bearer $TOKEN" ``` * **Validation is all or nothing.** One unparsable row applies nothing and leaves the dataset as it was. * **Committing the same import id twice is safe.** The first commit answers `202`; a repeat answers `200` with the import's current record and writes nothing, so retrying a timed-out commit is correct rather than a second write. * **Only one import per dataset node validates at a time**; a second is `409 import_in_progress`. **4. Poll the import** until `status` leaves `validating`: ```bash title="GET /v1/apps/sales_analytics/datasets/customers/imports/imp_8c2f4a1d9e63" curl -sS \ https://api.ovidens.com/v1/apps/sales_analytics/datasets/customers/imports/imp_8c2f4a1d9e63 \ -H "Authorization: Bearer $TOKEN" ``` `applied` reports `rowsParsed`, `rowsApplied` and `rowsRejected`. `failed` reports `errorCode`, `likelyCause`, a grouped `errorGroups` summary and, when the header was wrong, a `structure` block naming the expected, detected, unknown and missing columns. A file with no data rows - a header alone, or no bytes at all - fails with `errorCode: "no_data_rows"` in either mode and leaves the dataset exactly as it was; a file whose every row is invalid fails with `row_validation_failed` and leaves it as well. An import never empties a dataset, because an empty file is far more often a failed export than an intent; check the export that produced it before you retry. A row longer than 2,000,000 characters fails the import with `errorCode: "row_too_long"` and leaves the dataset as it was. The file is read row by row and one row is never held whole past that length, so the limit is per row, not per file: shorten or split the longest values on the row the message names, then stage the file again. This read needs only `workspace:read`, so a monitoring job can watch an integration without being able to feed it. If the calling token ALSO holds `data:write` and the import is still awaiting its file, the response carries a fresh `uploadUrl`. **That is how you recover an expired upload URL** - re-read the import, do not stage a new one. **5. Get the full error report** when the grouped summary is not enough: the same path with `/errors` appended answers a short-lived `downloadUrl` for a CSV of every rejected row. There is no report for an import that applied cleanly. Codes that matter here: `upload_missing` (nothing staged, or the staged file expired - stage a fresh import), `file_too_large`, `import_in_progress`, `app_locked`, and the four `csv_dialect_*` codes. ## The run loop [#the-run-loop] ```bash title="POST /v1/apps/sales_analytics/runs" curl -sS -X POST https://api.ovidens.com/v1/apps/sales_analytics/runs \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"target":"published"}' ``` `target` is required. `published` needs `pipeline:run`; `draft` runs unpublished configuration and needs `pipeline:run_draft`, which a credential does not hold by default. **This door never falls back between the two**: an app with no configuration of the target you asked for is refused with `409 no_runnable_version`. The `202` echoes the target it accepted. Only one run per app may be active. A second trigger answers `409 app_locked`, and when the blocker is another run your credential may read, the `detail` carries `activeRunId`. **On `app_locked` WITH an `activeRunId`, poll that run instead of starting another; without one, back off.** That is also the recovery when a `POST` timed out but succeeded: this endpoint is not idempotent, so a blind retry starts a second run. An editor previewing unpublished work in the app locks it the same way, and that run is a draft one: it blocks your trigger, but without `pipeline:run_draft` you cannot read it, so no `activeRunId` is named and backing off is the answer. You are never denied the id of a run you could have polled. Then poll: ```bash title="GET /v1/apps/sales_analytics/runs/4821" curl -sS https://api.ovidens.com/v1/apps/sales_analytics/runs/4821 \ -H "Authorization: Bearer $TOKEN" ``` The run is finished when `status` is no longer `running`. Every node starts `pending`, so a poll taken mid-run reports that for most of the array; on failure, `error` names the node and the message. `GET /v1/apps/sales_analytics/runs` is the paged history, and every run reports `triggeredBy` - an object naming the credential when a system started it, `null` when a person or the workspace assistant did. Both reads need `workspace:read`, and both show runs of published configuration. A run whose `target` is `draft` is a preview of work nobody has reviewed: it is listed, counted in `total` and readable by its id only for a credential that also holds `pipeline:run_draft`. Without that scope, the run id of a draft answers `404 not_found`, the same as an id that names nothing - so a run you started yourself is always readable, because a credential that cannot run a draft cannot have started one. ## The export loop [#the-export-loop] ```bash title="POST /v1/apps/sales_analytics/outputs/revenue_by_region/exports" curl -sS -X POST \ https://api.ovidens.com/v1/apps/sales_analytics/outputs/revenue_by_region/exports \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"runId":4821,"compression":"gzip"}' ``` Every field is optional and the body itself may be omitted, which means "the latest published run, default dialect". An **empty** payload declared as `application/json` is refused by the JSON parser as a `400 invalid_request` before the endpoint sees it, so dropping only the `-d` fails. Send either no body and no `Content-Type`, or an explicit `{}` with the header. This is a property of the HTTP layer, so it applies to any call you strip the body from - if your client sets a JSON content type for every request, send `{}`. ```bash title="POST /v1/apps/sales_analytics/outputs/revenue_by_region/exports (defaults)" curl -sS -X POST https://api.ovidens.com/v1/apps/sales_analytics/outputs/revenue_by_region/exports \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}' ``` **Whether you name a `runId` is the decision that matters.** With none, the file comes from the latest successful published run, and that answer can move between two otherwise identical requests - right for "the current numbers", wrong for a reproducible load. **Naming the `runId` you got from `startRun` pins the file to an immutable run snapshot**, so the same load a month later produces the same bytes. The `202` echoes the resolved `runId` and a `runPinned` flag. Then poll and download: ```bash title="GET /v1/apps/sales_analytics/exports/exp_5b1e7d0a3c92" curl -sS https://api.ovidens.com/v1/apps/sales_analytics/exports/exp_5b1e7d0a3c92 \ -H "Authorization: Bearer $TOKEN" ``` When `status` is `ready` the response carries a `downloadUrl`; fetch it with no `Authorization` header. `rowLimitReached` says the file was cut off at the export size limit and holds only part of the output; `runPinned` says whether the run was yours or resolved. An export writes nothing to the audit trail, but it does leave an export record the administrator can see: which app, which output, which run, how many rows, how large. Codes that matter here: `no_published_run` (nothing published successfully yet - run the pipeline), `run_data_unavailable` (the run exists but its outputs have been removed by retention - run it again), and `503 compute_unavailable`. ## The CSV dialect [#the-csv-dialect] One dialect object describes both directions: what an import PARSES with, an export WRITES with. It is validated at request time, so a bad option is a `400` with nothing uploaded or generated. **Structural options are file-level only.** | Option | Default | Notes | | ----------- | ------- | -------------------------------------------------------------------------------------------------------------- | | `delimiter` | `,` | One character. Must differ from every other single-character option. | | `quote` | `"` | One character. | | `escape` | `"` | One character, and in v1 it must EQUAL `quote`. A distinct escape character is refused rather than mis-parsed. | | `encoding` | `utf-8` | `utf-8` or `windows-1252` on import. Export accepts `utf-8` only. | | `hasHeader` | `true` | Only `true`. Every other option is validated against the declared column names. | **Value options are file-level defaults with an optional per-column override**, under `csv.columns.`. An override may name only value options, and only ones relevant to that column's type: an irrelevant one is `csv_dialect_irrelevant_option`, an undeclared column is `csv_dialect_unknown_column`. | Option | Default | Applies to | | -------------------- | -------------------- | --------------------------------------------------- | | `decimalSeparator` | `.` | `integer`, `decimal`, `percent`, `currency` columns | | `thousandsSeparator` | empty | the same numeric columns | | `dateFormat` | `%Y-%m-%d` | `date` columns | | `timestampFormat` | `%Y-%m-%dT%H:%M:%SZ` | `timestamp` columns | **The supported date and timestamp tokens are `%Y`, `%m`, `%d`, `%H`, `%M` and `%S`.** Anything else is `csv_dialect_unsupported` at request time. Month names are a later addition. Two collisions are refused because they would produce plausible-looking WRONG data: a `thousandsSeparator` equal to the `delimiter` (one value becomes two fields), and one equal to the `decimalSeparator` (`1.5` would become `15`). Beyond the separators, a value in a numeric column may carry a leading `+` or `-`, a parenthesised negative (`(1234)` is `-1234`), surrounding whitespace, a `$`, `€`, `£`, `¥` or `%` symbol, and scientific notation (`2E10`). A TRAILING minus sign is not recognised. On the way OUT, the writer refuses what it cannot produce: `encoding` other than `utf-8`, any decimal or thousands separator outside the set it emits, and a date or timestamp pattern STARTING with a spreadsheet formula trigger (`=`, `+`, `-`, `@`, tab or carriage return) - a pattern is a per-column constant, so it would land a live formula in every cell. ## Error codes [#error-codes] Every failure answers the same body, whatever the endpoint: ```json { "type": "https://ovidens.com/problems/insufficient_scope", "title": "The access token does not carry a scope this operation requires", "status": 403, "code": "insufficient_scope", "requestId": "944debf6-805a-400c-bde6-4a1f437ee42d", "detail": { "requiredScope": "data:write" } } ``` **Branch on `code`, never on `title`.** The code is the contract; the title is the English sentence beside it and may be corrected. `type` is a stable identifier, not a URL to fetch. `requestId` is what to quote to support - it is also on every response as the `x-request-id` header. **`detail` is keyed by the code, and every key in it is optional.** Read `code` first: the keys are not shared between codes, and a given code may answer with no `detail` at all. | Code | `detail` keys, when present | | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `insufficient_scope` | `requiredScope` | | `app_locked` | `reason`, plus `activeRunId` when the blocker is a run this credential may read | | `no_runnable_version` | `target` | | `quota_exceeded` | `resource`, for the three platform-metered ceilings | | `credential_expired` | `expiredAt` | | `credential_attestation_overdue` | `attestationDueAt`, `suspendedAt` | | `compute_unavailable` | `resource`, unless the pause names no ceiling | | `file_too_large` | `bytes`, `max` | | `rate_limited` | `retryAfterSeconds` | | `csv_dialect_conflict` | `fields`, `character`, plus `column` for a per-column override | | `csv_dialect_unsupported` | `field`; then `token` and `supportedTokens` for an unrecognised date pattern, or `reason` plus `quote` or `supported` for a restriction; plus `column` for a per-column override | | `csv_dialect_unknown_column` | `column`, `declaredColumns` | | `csv_dialect_irrelevant_option` | `column`, `option`, `columnType`, `relevantOptions` | Every other code answers with no `detail`. The contract declares the WHOLE code vocabulary on every failure response rather than a per-endpoint subset, so this table, not the contract, is where you learn what a code means: | Code | Status | What it means | What to do | | -------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `invalid_request` | 400 | The body, a parameter or the content type is not valid. | Fix the request. Retrying it unchanged cannot work. | | `invalid_token` | 401 | The token is missing, malformed, expired, or its credential no longer exists. | Mint a fresh token. If that also fails, the credential was revoked. | | `invalid_client` | 401 | The client id or secret was not accepted at the token endpoint. | Check the credential. A rotated secret may have been retired. | | `credential_expired` | 401 | The credential itself has expired. `detail.expiredAt` says when. | Stop and ask a workspace administrator to rotate the credential, which sets a new expiry and issues a new secret. No token will be accepted until then. | | `credential_attestation_overdue` | 401 | The credential is suspended because its periodic confirmation is more than 14 days overdue. `detail` gives the due date and when the suspension began. | Stop and ask a workspace administrator to attest the credential on the Access page. It works again the moment they do; the secret does not change. | | `invalid_target` | 400 | The `resource` indicator is missing or names something this grant may not request. | Send `resource=https://api.ovidens.com`. | | `insufficient_scope` | 403 | The token's effective scope set is too narrow for this operation. | Call `whoami` and compare `scopes` with `credentialScopes`. | | `not_found` | 404 | No such resource. Covers unknown, archived, wrong-type and other workspaces' identifiers alike. | Re-run discovery; do not guess identifiers. | | `conflict` | 409 | A state conflict with no more specific code. | Re-read the resource and decide from its current state. | | `quota_exceeded` | 422 | A workspace limit was reached. `detail.resource` names it for a metered ceiling; the open-import and open-export slot limits name none. | Tell the workspace administrator which limit. Retrying will not clear it. | | `rate_limited` | 429 | Too many requests for this credential. | Wait `Retry-After` seconds. Do not tighten the loop. | | `internal_error` | 500 | An unexpected fault. | Retry once with backoff, then escalate with the `requestId`. | | `csv_dialect_conflict` | 400 | Two dialect options claim the same character. `detail.fields` names both. | Change one of them. | | `csv_dialect_unsupported` | 400 | The dialect asks for something this version cannot do (an unknown strptime token, a distinct `escape`, a non-writable export option). | Read `detail.field`, then the rest of `detail` for the restriction it hit. | | `csv_dialect_unknown_column` | 400 | A per-column override names a column the dataset does not declare. | Re-read `listDatasets`; the schema has changed. | | `csv_dialect_irrelevant_option` | 400 | A per-column override is not relevant to that column's type. | Drop the override, or move it to the right column. | | `upload_missing` | 409 | No staged file is available - none was uploaded, or the staged file expired. | Stage a fresh import and upload again. | | `file_too_large` | 400 | The staged file exceeds the maximum upload size. | Split the file. | | `app_locked` | 409 | A run or an agent job owns the app. `detail.activeRunId` names the run when it is one you may read. | Poll `activeRunId` if present; otherwise back off and retry. | | `import_in_progress` | 409 | Another import for this dataset node is still validating. | Wait for it to finish, then commit. | | `no_runnable_version` | 409 | The app holds no configuration of the requested `target`. | Ask the workspace to publish. The identical request then succeeds. | | `compute_unavailable` | 503 | The platform is deliberately paused, or a platform-wide ceiling was reached. | Back off and retry. This is NOT a fault to escalate. | | `no_published_run` | 409 | The app has no successful published run to export. | Run the pipeline, then export. | | `run_data_unavailable` | 409 | The run exists but its output data has been removed by retention. | Run the pipeline again and export the new run. | **Back-off.** `429` carries a `Retry-After` header and three `X-RateLimit-*` headers reporting the allowance, what is left and when the window resets - respect them rather than guessing. `503 compute_unavailable` means wait, not page someone; no workspace administrator can lift it. `422 quota_exceeded` is the opposite: retrying never helps, somebody in the workspace has to act. So are the two credential codes: `credential_expired` and `credential_attestation_overdue` are 401s, but unlike `invalid_token` minting a new token cannot clear them, so a job that meets one should stop and alert a person rather than retry. A queued import or export whose credential lapses before it runs fails with the same code as its `errorCode`. One whose app was archived or moved into somebody's personal space before it ran fails with `errorCode: "app_not_found"` and writes nothing - the same answer a new request for that app gets. Retrying does not help; ask a workspace administrator where the app went. ## Limits and lifetimes [#limits-and-lifetimes] | Thing | Value | | ----------------------------------- | ------------------------------------------------------------------------------------------------- | | Requests per credential | 120 per minute by default; a workspace can be raised by an operator. `429` carries `Retry-After`. | | Maximum upload | 5 GB per staged file. | | Upload URL | Valid 1 hour. Re-read the import to mint a fresh one. | | Staged file | Kept 7 days, then the import moves to `expired`. | | Download URL (export, error report) | Valid 15 minutes. Re-read the record to mint a fresh one. | | Generated export file | Kept 7 days. | | Import and export records | Kept 90 days after the file is gone. | | Credential | Expires at most 365 days after it was issued or last rotated (`credentialExpiresAt` on `whoami`). | | Credential confirmation | Due every 90 days (`attestationDueAt`); suspended 14 days after a missed one. | **An expired URL never means an expired object.** Every record read mints a fresh URL, so the recovery is always "read the record again", never "start over". ## A worked example [#a-worked-example] The whole loop as one script: mint a token, replace a dataset, run the published pipeline, export an output and download the file, polling at each step and failing loudly. ```bash title="automation-loop.sh" lineNumbers #!/usr/bin/env bash set -euo pipefail API="https://api.ovidens.com" ISSUER="https://id.ovidens.com" CLIENT_ID="${OVIDENS_CLIENT_ID:?set OVIDENS_CLIENT_ID}" CLIENT_SECRET="${OVIDENS_CLIENT_SECRET:?set OVIDENS_CLIENT_SECRET}" APP="sales_analytics" DATASET="customers" OUTPUT="revenue_by_region" FILE="customers.csv" # 1. Mint a token: HTTP Basic, the resource indicator, and the scopes this job needs. TOKEN=$(curl -sS -X POST "$ISSUER/oauth/token" \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d "grant_type=client_credentials" \ -d "resource=$API" \ -d "scope=workspace:read data:write pipeline:run exports:read" \ | jq -r .access_token) AUTH=(-H "Authorization: Bearer $TOKEN") # 2. Prove the credential works before doing anything with it. curl -sS "${AUTH[@]}" "$API/v1/whoami" | jq '{name, scopes, credentialScopes}' # 3. Stage the import. STAGED=$(curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \ -d '{"mode":"replace","filename":"customers.csv","csv":{"delimiter":";"}}' \ "$API/v1/apps/$APP/datasets/$DATASET/imports") IMPORT_ID=$(jq -r .importId <<<"$STAGED") UPLOAD_URL=$(jq -r .uploadUrl <<<"$STAGED") # 4. Upload the bytes. NO Authorization header. curl -sS -X PUT --upload-file "$FILE" -H "Content-Type: text/csv" "$UPLOAD_URL" # 5. Commit. curl -sS -X POST "${AUTH[@]}" \ "$API/v1/apps/$APP/datasets/$DATASET/imports/$IMPORT_ID/commit" >/dev/null # 6. Poll the import. while :; do IMPORT=$(curl -sS "${AUTH[@]}" "$API/v1/apps/$APP/datasets/$DATASET/imports/$IMPORT_ID") STATUS=$(jq -r .status <<<"$IMPORT") [ "$STATUS" = "validating" ] || [ "$STATUS" = "awaiting_upload" ] || break sleep 5 done if [ "$STATUS" != "applied" ]; then echo "import $STATUS: $(jq -r '.errorCode // "unknown"' <<<"$IMPORT")" >&2 curl -sS "${AUTH[@]}" \ "$API/v1/apps/$APP/datasets/$DATASET/imports/$IMPORT_ID/errors" | jq -r .downloadUrl >&2 exit 1 fi echo "applied $(jq -r .rowsApplied <<<"$IMPORT") rows" # 7. Run the PUBLISHED pipeline. On 409 app_locked, adopt the active run. RUN=$(curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \ -d '{"target":"published"}' "$API/v1/apps/$APP/runs") RUN_ID=$(jq -r '.runId // .detail.activeRunId // empty' <<<"$RUN") [ -n "$RUN_ID" ] || { echo "no run: $RUN" >&2; exit 1; } # 8. Poll the run. while :; do DETAIL=$(curl -sS "${AUTH[@]}" "$API/v1/apps/$APP/runs/$RUN_ID") RUN_STATUS=$(jq -r .run.status <<<"$DETAIL") [ "$RUN_STATUS" = "running" ] || break sleep 10 done [ "$RUN_STATUS" = "success" ] || { echo "run $RUN_ID failed" >&2; exit 1; } # 9. Export the output, PINNED to the run we just made: that is what makes it reproducible. BODY=$(jq -nc --argjson runId "$RUN_ID" '{runId: $runId, compression: "gzip"}') EXPORT=$(curl -sS -X POST "${AUTH[@]}" -H "Content-Type: application/json" \ -d "$BODY" "$API/v1/apps/$APP/outputs/$OUTPUT/exports") EXPORT_ID=$(jq -r .exportId <<<"$EXPORT") # 10. Poll the export, then download (again, no Authorization header). while :; do RECORD=$(curl -sS "${AUTH[@]}" "$API/v1/apps/$APP/exports/$EXPORT_ID") EXPORT_STATUS=$(jq -r .status <<<"$RECORD") [ "$EXPORT_STATUS" = "generating" ] || break sleep 5 done [ "$EXPORT_STATUS" = "ready" ] || { echo "export $EXPORT_STATUS" >&2; exit 1; } curl -sS -o "$OUTPUT.csv.gz" "$(jq -r .downloadUrl <<<"$RECORD")" echo "downloaded $(jq -r .rowCount <<<"$RECORD") rows" ``` Next: the [API reference](./reference/overview) for the exact shape of any call, or the live contract at [`https://api.ovidens.com/v1/openapi.json`](https://api.ovidens.com/v1/openapi.json). # Validate and apply the staged CSV (asynchronous; poll the import for the outcome) (/docs/de/integrations/api/reference/commit-import) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Generate an output node's result as CSV (asynchronous; poll the export) (/docs/de/integrations/api/reference/create-export) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Stage a CSV import and mint its upload URL (/docs/de/integrations/api/reference/create-import) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # One app with its inbound and outbound port counts (/docs/de/integrations/api/reference/get-app) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Redirect to the human integration guide (/docs/de/integrations/api/reference/get-docs-redirect) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # One export's status and a short-lived download URL once it is ready (/docs/de/integrations/api/reference/get-export) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # A short-lived download URL for the failed import's full error report (/docs/de/integrations/api/reference/get-import-errors) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # One staged import's status and outcome, with a fresh upload URL while staging (/docs/de/integrations/api/reference/get-import) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The machine contract for this API, generated from the live route schemas (/docs/de/integrations/api/reference/get-open-api-document) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Protected-resource metadata for the automation API (/docs/de/integrations/api/reference/get-protected-resource-metadata) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # One run's status, per-node outcomes and failure summary (/docs/de/integrations/api/reference/get-run) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The calling credential's identity, workspace, effective and granted scopes (/docs/de/integrations/api/reference/get-whoami) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # List the workspace's apps (/docs/de/integrations/api/reference/list-apps) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The app's dataset nodes and their declared column schemas (/docs/de/integrations/api/reference/list-datasets) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The app's output nodes from its latest successful published run (/docs/de/integrations/api/reference/list-outputs) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # The app's run history, newest first (/docs/de/integrations/api/reference/list-runs) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # All operations (/docs/de/integrations/api/reference/overview) {/* Generated by scripts/generate-api-reference.mts from the published contract. Do not edit; regenerate instead. */} ## Contract [#contract] The three routes that need no token: where to authenticate, the machine contract itself, and a redirect to the human guide. Read them before obtaining a credential. Protected-resource metadata for the automation API The machine contract for this API, generated from the live route schemas Redirect to the human integration guide ## Identity [#identity] What the presented credential is and what it may do. The first call to make with a fresh token, and the first call to repeat when one is refused. The calling credential's identity, workspace, effective and granted scopes ## Discovery [#discovery] The workspace's apps and their inbound and outbound ports. A dataset node's declared column schema is the contract a file has to satisfy. List the workspace's apps One app with its inbound and outbound port counts The app's dataset nodes and their declared column schemas The app's output nodes from its latest successful published run ## Imports [#imports] The staged CSV import loop: stage, upload, commit, poll. Validation is all or nothing and a commit is safe to retry. Stage a CSV import and mint its upload URL Validate and apply the staged CSV (asynchronous; poll the import for the outcome) One staged import's status and outcome, with a fresh upload URL while staging A short-lived download URL for the failed import's full error report ## Runs [#runs] Pipeline execution: trigger a run against the published or the draft configuration, then poll it. One run per app at a time. Run the app's pipeline (asynchronous; poll the run for the outcome) The app's run history, newest first One run's status, per-node outcomes and failure summary ## Exports [#exports] Generated CSV of an output node's result: request, poll, download. Pin a run identifier to make a load reproducible. Generate an output node's result as CSV (asynchronous; poll the export) One export's status and a short-lived download URL once it is ready # Run the app's pipeline (asynchronous; poll the run for the outcome) (/docs/de/integrations/api/reference/start-run) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Integrationen (/docs/de/integrations) Andere Software kann auf zwei Wegen mit Ihrem Workspace arbeiten. Von außen sehen sie ähnlich aus, austauschbar sind sie nicht: Sie beantworten unterschiedliche Fragen danach, **wer handelt**. ## Eine KI-Assistenz im Namen einer Person [#eine-ki-assistenz-im-namen-einer-person] Jemand verbindet die eigene KI-Assistenz - Claude oder ein anderes Werkzeug, das MCP spricht - mit dem Workspace. Die Assistenz handelt dann **als diese Person**: Sie sieht, was diese Person sieht, und ändert, was diese Person ändern darf, mehr nicht. Ein Mensch ist beteiligt, willigt in die Verbindung ein und liest, was zurückkommt. Wählen Sie diesen Weg, wenn der Nutzen darin besteht, dass eine Person schneller arbeitet: Fragen an die Daten stellen, eine App beschreiben und bauen lassen, eine Visualisierung entwerfen. Eingerichtet wird das unter [MCP](../integrations/mcp.mdx). ## Ein unbeaufsichtigtes System auf eigene Rechnung [#ein-unbeaufsichtigtes-system-auf-eigene-rechnung] Ein ERP, ein Data Warehouse, ein nächtlicher Job. Es meldet sich mit **eigenen Zugangsdaten** an, nicht mit dem Konto einer Person, und läuft nach eigenem Zeitplan, ohne dass jemand an der Tastatur sitzt. Was es darf, entscheidet allein der Zugriff, den eine Administratorin oder ein Administrator diesen Zugangsdaten gewährt, und jede Änderung steht im Prüfprotokoll unter dem Namen der Zugangsdaten. Wählen Sie diesen Weg, wenn der Nutzen darin besteht, dass Arbeit verlässlich ohne Anwesenheit passiert: ein wiederkehrender Import, ein geplanter Lauf, ein nächtlicher Auszug. Zwei Seiten unter **API** decken das ab, je eine pro Rolle: * **[Zugangsdaten](../integrations/api/credentials.mdx)** - für die Verwaltung: ausstellen, den Zugriff wählen, das Secret erneuern und sehen, was das System getan hat. * **[Overview](./integrations/api/overview)** - für die Person, die die Integration baut: Token holen, CSV übertragen, Pipeline starten und das Ergebnis abholen. Darunter liegt die **API-Referenz** pro Operation. Beides ist auf Englisch. ## Die Wahl zwischen beiden [#die-wahl-zwischen-beiden] | Frage | KI-Assistenz (MCP) | Unbeaufsichtigtes System (Automation API) | | ---------------------------- | ---------------------------------------------- | -------------------------------------------------- | | Wer handelt? | Eine Person über ihre Assistenz | Ein System mit eigenen Zugangsdaten | | Wer autorisiert? | Die Person, durch Verbinden und Einwilligen | Die Verwaltung, durch Ausstellen der Zugangsdaten | | Was begrenzt es? | Die Berechtigungen und KI-Rechte dieser Person | Die den Zugangsdaten gewährten Zugriffsrechte | | Wann läuft es? | Während jemand arbeitet | Nach Zeitplan, unbeaufsichtigt | | Was zeigt das Prüfprotokoll? | Die Person und welche Assistenz | Die Integration unter dem Namen ihrer Zugangsdaten | Wenn beides passt, stehen sie nicht in Konkurrenz: Ein Workspace hat häufig beides, für unterschiedliche Aufgaben. [Overview](./integrations/api/overview) und die darunter liegende API-Referenz sind nicht übersetzt. Sie beschreiben einen maschinenlesbaren Vertrag, dessen Feldnamen, Zugriffsnamen und Fehlercodes englisch sind; eine Übersetzung würde irgendwann etwas beschreiben, was die API nicht tut. # Eigene KI verbinden (/docs/de/integrations/mcp) Neben [Vera](../tips/interact-with-vera.mdx), der in Ovidens eingebauten KI, können Sie **Ihre eigene KI** - zum Beispiel Claude oder eine andere MCP-fähige KI - mit Ihrem Workspace verbinden. Einmal verbunden, arbeitet sie im Workspace **als Sie**: Sie sieht, was Sie sehen können, und darf nur tun, was Sie und Ihr Admin erlaubt haben. Die Einrichtung besteht aus drei Schritten: Ein **Admin** gibt externe KI für Sie frei, **Sie** wählen, was Ihre KI tun darf, und dann fügen Sie die Connector-URL Ihres Workspace in Ihrer KI hinzu. ## Schritt 1: Ein Admin gibt externe KI frei [#schritt-1-ein-admin-gibt-externe-ki-frei] Externe KI ist für jedes Mitglied **standardmäßig aus**; ein Admin muss sie pro Person freigeben. Als Admin: 1. Öffnen Sie die Seite **Zugriff** und suchen Sie auf dem Tab **Mitglieder** die Zeile des Mitglieds. 2. Wählen Sie **KI-Berechtigungen** und wechseln Sie zum Tab **Externe KI**. 3. Haken Sie die Fähigkeitsgruppen an (oder klappen Sie eine Gruppe auf und wählen einzelne Fähigkeiten), die die KI des Mitglieds höchstens nutzen darf, und wählen Sie dann **Speichern**. Das legt das Maximum fest, das Sie als Admin erlauben: das Meiste, was die externe KI dieses Mitglieds in diesem Workspace je tun darf. Das Mitglied kann nur innerhalb dieser Grenze wählen, nie darüber hinaus. **Auf Standard zurücksetzen** stellt für das Mitglied wieder die Plattformvorgabe her. (Zur Seite Zugriff allgemein siehe [Administration](../administration.mdx).) ## Schritt 2: Wählen Sie, was Ihre KI tun darf [#schritt-2-wählen-sie-was-ihre-ki-tun-darf] Als Nächstes prüft das Mitglied die eigenen KI-Einstellungen: 1. Öffnen Sie **Profil** unten in der linken Navigation und wählen Sie **Konto**, dann den Bereich **KI**. 2. Der Bereich öffnet sich mit dem Workspace, in dem Sie gerade arbeiten. Bei mehreren Workspaces wechseln Sie über das Auswahlfeld oben zu einem anderen. Wechseln Sie dann zum Tab **Externe KI**. 3. Alles, was Ihr Admin freigegeben hat, ist **standardmäßig an**. Schalten Sie alles aus, was Ihre KI nicht tun können soll. ## Schritt 3: Verbinden Sie Ihre KI [#schritt-3-verbinden-sie-ihre-ki] Oben im Tab **Externe KI** finden Sie die **Connector-URL** Ihres Workspace. Sie ist je Workspace eindeutig und hat die Form `https://mcp.ovidens.com/t//mcp`. Die Zeile direkt darunter nennt den Workspace, mit dem die URL verbindet - so prüfen Sie vor dem Kopieren, dass Sie Ihrer KI den richtigen übergeben. Wählen Sie **Kopieren** und dann - mit Claude als Beispiel: 1. Öffnen Sie in Claude **Einstellungen > Konnektoren** und wählen Sie **Benutzerdefinierten Konnektor hinzufügen**. 2. Geben Sie einen Namen ein (zum Beispiel den Workspace-Namen) und fügen Sie die kopierte URL ein. 3. Wählen Sie **Verbinden**, melden Sie sich mit Ihrem Ovidens-Konto an und **autorisieren** Sie die Verbindung. Das war es - Claude kann jetzt in Ihrem Workspace arbeiten. Andere MCP-fähige KIs verbinden Sie auf demselben Weg: Fügen Sie die Connector-URL dort hinzu, wo sie einen eigenen MCP-Server akzeptieren, und melden Sie sich an und autorisieren Sie. Sobald die Verbindung steht, können Sie Ihre KI per ID auf alles im Workspace verweisen, statt zu beschreiben, wo es liegt: Kopieren Sie in Ovidens die ID eines Widgets, einer Seite oder eines Knotens und bitten Sie Ihre KI, sie mit `fetch` abzurufen. Siehe [Objekt-IDs und KI-Agenten](./object-ids.mdx). ## Gut zu wissen [#gut-zu-wissen] * Ihre KI sieht nur die Werkzeuge, die Ihre **wirksamen Berechtigungen** erlauben - das Maximum, das ein Admin Ihnen erlaubt, abzüglich allem, was Sie selbst ausgeschaltet haben. * Ihre KI kann in einer App nie mehr tun, als **Sie** dort selbst tun könnten. Ein Werkzeug, das Ihre Berechtigungen erlauben, wird trotzdem abgewiesen, wenn Sie dieselbe Änderung in der betroffenen App selbst nicht vornehmen könnten, und die Ablehnung sagt das. Eine App, die Sie nicht öffnen können, gibt es für Ihre KI nicht: Sie wird nicht aufgelistet, und jedes Werkzeug antwortet, dass sie nicht gefunden wurde. * In einer Indikator-App mit laufender [Erhebungsrunde](../indicator-apps/collection-rounds.mdx) erfasst Ihre KI Zahlen für die Berichtseinheiten, für die **Sie** verantwortlich sind, und für keine anderen - es sei denn, Sie verwalten die Erhebungsrunden dieser App (als Admin oder als berichtsverantwortliche Person der App); dann darf sie die Zahlen jeder Einheit eintragen, genau wie Sie selbst in der App. Fünf Dinge kann keine KI: zuweisen, wer für eine Einheit verantwortlich ist, festlegen, aus welcher Dimension die Berichtseinheiten der App stammen, eine Antwort beurteilen, eine Nachricht zu einer Antwort schreiben oder die Erhebungsarbeit einer Person als erledigt markieren. Die letzten drei würden einer namentlich genannten Person Worte in den Mund legen. Ihre KI kann eine Erhebungsrunde LESEN - Name, Status, Berichtsperiode und die abgefragten Kennzahlen - und sie kann eine einrichten; wen eine Runde fragt und was zu einer Antwort gesagt wurde, bleibt bei den Menschen. * Jedes Werkzeug teilt Ihrem KI-Client mit, ob es nur LIEST und ob es etwas ZERSTÖREN kann. Ein Client, der vor dem Handeln nachfragt, kann sichere Lesezugriffe damit selbst freigeben und trotzdem vor einer Löschung bei Ihnen nachfragen. Sie konfigurieren das nicht; es stammt aus demselben Berechtigungsmodell, das der Workspace ohnehin durchsetzt. * Wenn eine Liste länger ist, als eine Antwort tragen kann, sagt die Antwort das: Sie nennt, wie viele Einträge sie enthält, und - wo die Zahl bekannt ist - wie viele insgesamt existieren. So kann eine gute KI Ihnen sagen, dass sie 100 von 4.312 Werten gesehen hat, und ihre Frage eingrenzen, statt zu antworten, als hätte sie alles gesehen. * Jede Zeitangabe, die Ihre KI über Ihren Workspace liest - wann eine App angelegt oder bearbeitet, eine Version veröffentlicht, ein Lauf gestartet oder ein Wert zuletzt geändert wurde -, erreicht sie als genauen Zeitpunkt in UTC (die Uhrzeit endet auf `Z`). Ovidens zeigt Ihnen denselben Zeitpunkt in der Zeitzone Ihres Browsers und nennt die Zone daneben, zum Beispiel **20:43 MESZ**. Ihre KI wird angewiesen, die Zeit in Ihre Zeitzone umzurechnen und zu sagen, welche Zone sie meint, oder sie in UTC zu nennen - so ist die Zeit, die sie nennt, erkennbar dieselbe wie auf Ihrem Bildschirm. Datums- und Zeitwerte, die Sie in eine Datensatzspalte eingetragen haben, sind etwas anderes: Sie haben keine Zeitzone, und Ihre KI wird angewiesen, sie genau so zu zeigen, wie sie eingetragen wurden. * Bevor Ihre KI die Daten einer App abfragt, kann sie den Workspace fragen, was diese App tatsächlich enthält: welche Tabellen sie lesen darf und welchen Typ jede Spalte genau hat - aus der Abfrage-Engine gelesen statt aus der Konfiguration geraten. Deshalb schreibt eine verbundene KI meist auf Anhieb eine funktionierende Abfrage, und deshalb kann sie Ihnen sagen, dass eine Tabelle noch nicht bereit ist - die Pipeline wurde noch nicht berechnet - statt zu melden, dass es die Tabelle nicht gibt. * Ihre KI kann außerdem prüfen, was eine Abfrage kosten würde, **bevor** sie sie ausführt. Würde eine Frage die gesamte Historie einer App durchsuchen, kann eine gute KI sie vorher eingrenzen oder Ihnen sagen, dass die Frage zu weit gefasst ist, und eine engere vorschlagen - statt Sie warten zu lassen und dann zu scheitern. Bei dieser Prüfung werden keine Daten gelesen und nichts berechnet. * Ihre KI kann die Wissensdatenbank einer App nach Stichwörtern durchsuchen, statt Seite für Seite zu öffnen: Sie bekommt die Seiten zurück, die Ihre Wörter enthalten, jeweils mit der Textstelle, die passt, und liest dann nur diese. Durchsucht wird, was auch Sie in der App sehen - die Arbeitsversion, und nie eine Seite, die Sie in den Papierkorb verschoben haben. Gesucht wird nach ganzen Wörtern, ohne Wortformen zu erraten: "Emissionen" findet "Emission" nicht. Kommt Ihre KI bei einem Thema mit leeren Händen zurück, von dem Sie wissen, dass es dokumentiert ist, lassen Sie sie die Schreibweise probieren, die auf der Seite selbst steht. * Änderungen eines Admins und Ihre eigenen Schalter gelten ab der **nächsten Anfrage** Ihrer KI; ein erneutes Verbinden ist nicht nötig. * Jede verbundene KI erscheint unter **Verbundene KI** im selben Einstellungsbereich, wo Sie sie jederzeit **trennen** können. * Alles, was Ihre KI tut, wird im Aktivitätsprotokoll des Workspace festgehalten - als die KI **plus Sie** als die Person dahinter, genau wie bei Vera. # Objekt-IDs und KI-Agenten (/docs/de/integrations/object-ids) Jedes Widget, jede Seite, jeder Bericht, jeder Knoten, jede Seite der Wissensdatenbank und jeder Skill in Ovidens - und jeder Indikator, jede Dimension, jede Gruppe, jede Konsistenzprüfung, jeder Berichtszeitraum, jede Erhebungsrunde und jede Frage einer Erhebung - hat eine eigene ID, zum Beispiel `wdg_k3m9x2qd7f`. Statt zu beschreiben, wo etwas liegt ("die Sales-App, vierte Seite, die Tabelle Offene Aufträge"), kopieren Sie seine ID und geben sie Ihrer KI. Die KI liest genau dieses Objekt, in einem Schritt, ohne danach zu suchen. Das funktioniert mit jeder KI, die Sie mit Ihrem Workspace verbunden haben - Claude, ChatGPT oder eine andere KI, die MCP spricht (siehe [Eigene KI verbinden](./mcp.mdx)) - und mit [Vera](../tips/interact-with-vera.mdx) in einer Analyse-App. ## So sieht eine ID aus [#so-sieht-eine-id-aus] Eine ID beginnt mit ein paar Buchstaben, die sagen, welche Art von Objekt sie bezeichnet, gefolgt von einem Unterstrich und zehn Buchstaben und Ziffern. Sie enthält keine Leerzeichen und keine Bindestriche, deshalb markiert ein Doppelklick die ganze ID. | Beginnt mit | Objekt | Beginnt mit | Objekt | | ----------- | ----------------------------------------------- | ----------- | ------------------------- | | `app_` | eine App | `ind_` | ein Indikator | | `page_` | eine Seite einer Visualisierung | `dim_` | eine Dimension | | `wdg_` | ein Widget | `grp_` | eine Indikatorgruppe | | `rpt_` | ein Bericht | `chk_` | eine Konsistenzprüfung | | `node_` | ein Pipeline-Knoten | `per_` | ein Berichtszeitraum | | `kb_` | eine Seite oder ein Ordner der Wissensdatenbank | `rnd_` | eine Erhebungsrunde | | `skl_` | ein Skill | `qst_` | eine Frage einer Erhebung | ## Wo Sie eine ID kopieren [#wo-sie-eine-id-kopieren] * **Ein Widget:** Fahren Sie mit der Maus über das Widget - in der App und im Editor -, dann erscheint direkt nach seinem Titel ein kleines ID-Symbol. Ein Klick darauf kopiert die ID. Auf einem Touchscreen ist das Symbol immer zu sehen. * **Eine Seite oder ein Bericht:** Fahren Sie mit der Maus über die Zeile in der Seitenleiste der App. Das ID-Symbol erscheint neben den anderen Aktionen der Zeile. * **Die App:** Fahren Sie in der App-Liste mit der Maus über die App und klicken Sie auf **App bearbeiten** (den Stift). Der Dialog zeigt unter der Technischen ID eine Zeile **App-ID** mit eigenem Kopieren-Knopf. Das gilt für jeden App-Typ. In einer Analyse-App steht das ID-Symbol außerdem neben dem Namen der App in der Kopfzeile. * **Eine Seite der Wissensdatenbank:** Öffnen Sie das Menü der Zeile im Baum der Wissensdatenbank und wählen Sie **ID kopieren**. Eine geöffnete Seite und ein geöffneter Bericht zeigen das ID-Symbol außerdem neben ihrem Namen. * **Alles andere** - ein Knoten, ein Skill, ein Indikator, eine Dimension, eine Gruppe, eine Konsistenzprüfung, ein Berichtszeitraum, eine Erhebungsrunde und eine Frage: Das Panel zeigt unter der Technischen ID eine ID-Zeile (bei einer Gruppe, die keine Technische ID hat, unter ihrem Namen). Die Zeile ist nach der Art benannt, etwa **Knoten-ID** oder **Fragen-ID**, und hat einen eigenen Kopieren-Knopf. Ein Objekt erhält seine ID in dem Moment, in dem es gespeichert wird. Ein Berichtszeitraum, der nur in der Zeitachse existiert - niemand hat ihn umbenannt, zur Prüfung übergeben, freigegeben oder eine Runde darin gestartet -, erhält seine ID, sobald er aus einem dieser Gründe gespeichert wird. **Link kopieren** im Menü der App-Kopfzeile und im Zeilenmenü der Wissensdatenbank kopiert stattdessen einen Link. Der Link öffnet das Objekt in Ovidens, und Ihre KI akzeptiert ihn genauso wie eine ID, denn jeder Link, den Ovidens kopiert, enthält die ID des Objekts. ## Geben Sie Ihrer KI eine ID [#geben-sie-ihrer-ki-eine-id] Fügen Sie die ID mit dem Wort `fetch` davor in Ihre KI ein: ``` fetch wdg_k3m9x2qd7f ``` Oder verwenden Sie sie in einer Frage - "was zeigt wdg\_k3m9x2qd7f für März?" - oder fügen Sie einen kopierten Link ein. Eine verbundene KI übergibt die ID an das Werkzeug **fetch** von Ovidens und erhält das Objekt zurück. Zwei optionale Zusätze ändern, was sie liest: | Zusatz | Was er bewirkt | Beispiel | | ----------------- | -------------------------------------------------------------------------------- | -------------------------------- | | `@published` | Liest die Einrichtung der veröffentlichten Version statt Ihres offenen Entwurfs. | `fetch wdg_k3m9x2qd7f@published` | | `@draft` | Liest den offenen Entwurf. Ist keiner offen, erfährt Ihre KI das. | `fetch page_x2kq7m1c0d@draft` | | `:p2`, `:p3`, ... | Liest die nächste Seite einer langen Antwort. | `fetch node_7m1c0dx2kq:p2` | Version und Seite lassen sich kombinieren: `fetch wdg_k3m9x2qd7f@published:p2`. ## Was Ihre KI zurückbekommt [#was-ihre-ki-zurückbekommt] * **Den Namen des Objekts und einen Link**, der es in Ovidens öffnet. Der Link eines Widgets öffnet seine Seite, scrollt zum Widget und hebt es kurz hervor. * **Wie es eingerichtet ist** - bei einem Widget der Typ, der Knoten, den es liest, seine Spalten und Filter; bei einem Knoten die Konfiguration und die Spalten; bei einem Skill die Anweisungen; bei einer Frage der Text und die Optionen. * **Was es zeigt, so wie die App es zeigt** - die Zeilen eines Tabellen-Widgets mit seinen Filtern, sichtbaren Spalten, Gruppierungen und Summen; die Reihen eines Diagramms; die Zellen einer Pivot-Tabelle; den Wert eines Kennzahl-Widgets; die Auswahl, die ein Parameter-Widget anbietet; das Ergebnis eines SQL-Widgets; die Zeilen eines Knotens; den Text einer Seite der Wissensdatenbank; die erfassten Werte eines Indikators; die Mitglieder einer Dimension. Ein KI-Widget antwortet mit dem Skill, den es ausführt, und das Lesen führt den Skill nie aus. * **Die IDs verwandter Objekte** - die App, die Seite, auf der ein Widget liegt, der Knoten, den es liest, die Seiten einer App -, sodass Ihre KI diese als Nächstes abrufen kann. * **Woher jeder Teil stammt** - aus welcher Version die Einrichtung gelesen wurde und aus welcher Berechnung die Daten stammen. ### Welche Version Ihre KI sieht [#welche-version-ihre-ki-sieht] Ihre KI sieht, was die App Ihnen zeigen würde: die Einrichtung aus Ihrem offenen Entwurf, wenn es einen gibt, sonst aus der veröffentlichten Version, und die Daten aus der letzten erfolgreichen Berechnung der veröffentlichten Version. Ein Widget, das Sie gerade in einem Entwurf hinzugefügt haben, kommt also mit seiner Einrichtung und dem Hinweis zurück, dass es dafür noch keine veröffentlichten Daten gibt. Mit `@published` liest Ihre KI genau das, was alle anderen heute sehen. ### Lange Antworten [#lange-antworten] Eine Antwort enthält bis zu 100 Zeilen. Gibt es mehr, sagt die Antwort das und nennt die ID der nächsten Seite - zum Beispiel `wdg_k3m9x2qd7f:p2` -, die Ihre KI als Nächstes abrufen kann. Eine lange Seite der Wissensdatenbank wird auf dieselbe Weise in Seiten geteilt. ## IDs bleiben beim Objekt [#ids-bleiben-beim-objekt] * **Eine ID ändert sich nie, solange das Objekt existiert.** Beim Umbenennen, beim Verschieben eines Widgets auf eine andere Seite, beim Veröffentlichen, beim Öffnen eines neuen Entwurfs und beim Wiederherstellen einer älteren Version bleibt sie erhalten. * **Eine Kopie einer App erhält neue IDs.** Eine App importieren, eine aus dem [Marketplace](../marketplace.mdx) installieren, eine Erhebung duplizieren und die Beispiel-Apps - all das legt Objekte mit eigenen IDs an, sodass eine ID immer auf ein Objekt in einer App zeigt. * **Die ID eines gelöschten Objekts funktioniert nicht mehr.** Ihre KI erfährt, dass das Objekt gelöscht wurde - oder, wenn Sie es in Ihrem offenen Entwurf entfernt haben, dass es noch veröffentlicht ist. * **IDs gehören zu Ihrem Workspace.** Eine ID, die in einem anderen Workspace kopiert wurde, wird in Ihrem nicht gefunden. ## Gut zu wissen [#gut-zu-wissen] * Ihre KI liest per ID mit Ihren Berechtigungen und liest nur - sie ändert nichts. Ein Admin erlaubt es mit **Objekt per ID abrufen** in der Gruppe **Lesen** der **KI-Berechtigungen** des Mitglieds. Konnte Ihre KI bereits die Visualisierungskonfiguration einer App lesen, kann sie auch per ID abrufen. * Archivierte Apps und die App Audit Analytics stehen KI-Agenten nicht zur Verfügung; ihre IDs werden nicht gefunden. * Daten per ID zu lesen verbraucht die Compute-Credits Ihres Workspace wie jede andere Frage Ihrer KI an die Daten. # Marketplace (/docs/de/marketplace) Der **Marketplace** ist ein Katalog fertiger Apps. Statt eine App von einer leeren Leinwand aus zu bauen, wählen Sie eine Vorlage, die schon ungefähr das tut, was Sie brauchen, installieren sie in Ihren Workspace und passen sie von dort aus an. Sie öffnen ihn über **Marketplace** in der linken Navigation. Alle im Workspace können darin stöbern. ## Was eine Vorlage ist [#was-eine-vorlage-ist] Eine Vorlage ist eine **vollständige, funktionierende App**, die jemand veröffentlicht hat: ihre Datenpipeline, ihre Visualisierung, ihre Wissensdatenbank und ihre Skills. Viele Vorlagen bringen zusätzlich Daten mit, damit die Diagramme sofort beim ersten Öffnen etwas anzeigen. Jede Karte im Katalog sagt Ihnen, was Sie vor sich haben: * Den **Namen** und eine einzeilige Beschreibung. * **Beispieldaten** - die Vorlage bringt Beispielzeilen mit, damit die App beim ersten Öffnen nicht leer ist. Sie sind dazu gedacht, durch Ihre eigenen ersetzt zu werden. * **Referenzdaten** - die Vorlage bringt eine Nachschlagetabelle mit, die sie zum Rechnen braucht, etwa Emissionsfaktoren oder Einheitenumrechnungen. Diese Daten bleiben. * Ein **Standard**-Kennzeichen, wenn die Vorlage einem veröffentlichten Berichtsrahmenwerk folgt. Klicken Sie eine Karte an, um ihre Detailseite zu öffnen. Dort erklärt die Veröffentlichung, was die Vorlage tut, welche Daten Sie beisteuern müssen und wie das Ergebnis zu lesen ist. ## Eine Vorlage installieren [#eine-vorlage-installieren] Wählen Sie auf der Detailseite **Installieren**. Geben Sie der neuen App einen Namen und eine technische ID und bestätigen Sie. Dürfen Sie in mehr als einen Bereich installieren, wählen Sie auch den Bereich, in den die App kommt - der Dialog beginnt mit **Workspace**, wenn Sie dorthin installieren dürfen. Ovidens erstellt eine **neue App in Ihrem Workspace** und öffnet sie. Diese neue App gehört vollständig Ihnen. Sie ist eine Kopie, kein Abonnement: nichts daran wird mit anderen geteilt, und nichts, was Sie damit tun, wirkt sich auf die Vorlage oder auf die Kopie eines anderen Workspace aus. Umbenennen, Knoten ergänzen, SQL umschreiben, Visualisierung ändern - es ist eine gewöhnliche App, sobald sie existiert. Zum Installieren brauchen Sie dieselbe Berechtigung wie zum Importieren einer App; sie gehört zu den Rollen **App-Ersteller** und **Admin** für den Bereich, in den die App kommt - und in **Mein Bereich** darf jedes Mitglied installieren. Eine Vorlage, die Daten mitbringt, verlangt zusätzlich die Berechtigung, Daten zu ändern, denn die Installation schreibt diese Zeilen in die Datasets der neuen App; wer Apps installieren darf, hat sie. Fehlt Ihnen eine davon, nennt die Schaltfläche **Vorlage verwenden** auf der Detailseite sie, bevor etwas erstellt wird. Ist die gewählte technische ID in Ihrem Workspace schon belegt, sagt Ovidens das und erstellt nichts. Wählen Sie eine andere und versuchen Sie es erneut. ## Die Daten, mit denen eine installierte App kommt [#die-daten-mit-denen-eine-installierte-app-kommt] Zwei Arten, die sich genau entgegengesetzt verhalten. Welche welche ist, ist das eine, was Sie lesen sollten, bevor Sie eigene Zahlen erfassen. ### Beispieldaten - zum Leeren gedacht [#beispieldaten---zum-leeren-gedacht] Beispieldaten existieren, damit die App beim ersten Öffnen etwas anzeigt und Sie die Form des Ergebnisses sehen, bevor Sie selbst etwas beigesteuert haben. Es sind Beispiele, nicht Ihre Daten. Wenn Sie mit echten Zahlen arbeiten wollen, öffnen Sie die App und nutzen **Beispieldaten entfernen**. Jedes Dataset, das die Vorlage mit Beispielen gefüllt hat, wird in einem Schritt geleert; die Datasets behalten ihre Spalten, die Struktur der App bleibt also unberührt und Sie können sofort eigene Zeilen hochladen oder eintippen. Beispieldaten entfernen und eine aktualisierte Referenztabelle übernehmen (siehe unten) schließen die Einrichtung der App ab und stehen deshalb allen offen, die sie konfigurieren dürfen: den Rollen **App-Ersteller** und **Admin** auf der App oder ihrem Bereich. Wer nur Daten erfasst, sieht beides nicht. Sie müssen nicht leeren. Sobald Sie selbst in ein Dataset schreiben - ein Upload, ein Einfügen oder eine einzige geänderte Zelle - behandelt Ovidens dieses Dataset nicht mehr als Beispieldaten und bietet es nicht mehr zum Entfernen an. Ihre Daten sind nie etwas, das die App zum Wegwerfen anbietet. ### Referenzdaten - zum Behalten gedacht [#referenzdaten---zum-behalten-gedacht] Referenzdaten sind eine Nachschlagetabelle, die die Vorlage überhaupt zum Rechnen braucht: eine Liste von Emissionsfaktoren, eine Umrechnungstabelle, ein Satz Kategorieschlüssel. Sie sind Teil dessen, wofür die Vorlage da ist. **Beispieldaten entfernen** fasst sie nie an. Sie können sie bearbeiten, wenn Ihre Organisation andere Werte verwendet, und das ändert nichts an ihrer Behandlung. Veröffentlicht die Vorlage später eine korrigierte Fassung einer Referenztabelle, weist die App darauf hin und bietet an, sie zu übernehmen - siehe unten. Das ist der einzige Inhalt, der nach der Installation überhaupt noch in eine installierte App wandern kann. ## Wenn die Vorlage später aktualisiert wird [#wenn-die-vorlage-später-aktualisiert-wird] Weil eine installierte App eine Kopie ist, kommt eine Aktualisierung der Vorlage nie von selbst an. Sie erhalten stattdessen einen Hinweis auf der App: **eine neuere Version dieser Vorlage ist verfügbar**, samt der Angabe, was geändert wurde. Danach entscheiden Sie: * **Eine aktualisierte Referenztabelle übernehmen.** Enthält die Aktualisierung neue Referenzdaten für ein Dataset, das Ihre App als Referenzdaten hält, bietet die App an, die Zeilen dieser Tabelle durch die aktuellen zu ersetzen. Ein Dataset auf einmal, und nur wenn Sie es verlangen. Ihre eigenen Daten bleiben dabei unberührt. * **Den Rest von Hand nachziehen.** Konfigurationsänderungen - ein neuer Knoten, eine geänderte Berechnung, eine neu gestaltete Seite - werden nicht für Sie angewendet, denn Ihre App hat sich seit der Installation weiterentwickelt und nur Sie wissen, welche Ihrer Änderungen bleiben sollen. Lesen Sie, was sich geändert hat, und übernehmen Sie, was Sie brauchen. * **Nicht mehr hinweisen lassen.** Wählen Sie **Nicht mehr anzeigen**, und der Hinweis verschwindet für diese Version der Vorlage. Erscheint danach eine weitere, kommt der Hinweis zurück, denn das ist tatsächlich eine neue Information. Schließen Sie den Hinweis über das **X**, gilt das nur für die laufende Sitzung. Die App zeigt außerdem, woher sie kommt, sodass auch Monate später klar bleibt, aus welcher Vorlage sie einmal entstanden ist. ## Vorlagen veröffentlichen [#vorlagen-veröffentlichen] Veröffentlichen ist nicht Teil eines gewöhnlichen Workspace. Der Katalog ist kuratiert: Vorlagen werden zentral von Ovidens veröffentlicht, sodass jeder Eintrag geprüft ist und sauber installiert, bevor ihn jemand sieht. Wenn Sie etwas gebaut haben, das Sie im Katalog sehen möchten, melden Sie sich. ## Verwandte Seiten [#verwandte-seiten] * [Apps](overview/apps.mdx) - was eine App ist und der Entwurf-und-Veröffentlichen-Zyklus, dem jede App folgt, auch eine installierte. * [Datasets](analytical-apps/pipeline/datasets.mdx) - Daten hochladen und bearbeiten, die eine installierte App von Ihnen erwartet. * [App exportieren und importieren](analytical-apps/export-import.mdx) - eine eigene App zwischen Workspaces verschieben; es ist dasselbe portable Format, das eine Vorlage nutzt. # Apps (/docs/de/overview/apps) Eine **App** ist ein in sich geschlossenes Zuhause für eine Analyse. Sie enthält alles, was diese Analyse benötigt, an einem Ort: ihre Daten, die Logik zur Aufbereitung dieser Daten und die **Visualisierung**, in der Ihr Team arbeitet. Da jede App in sich geschlossen ist, können Sie eine App erstellen, teilen und ändern, ohne die anderen zu berühren. Sie verwalten alle Ihre Apps auf der Seite **Apps** in der linken Navigation. ## Die zwei Arten von App [#die-zwei-arten-von-app] Ovidens hat zwei Arten von App, und Sie wählen die Art, die zu Ihrem Vorhaben passt. * Eine **Analyse-App** dient dem intelligenten Kombinieren von Daten. Verwenden Sie sie, wenn Ihre Antwort daraus entsteht, mehrere Quellen zusammenzubringen, sie zu bereinigen und umzuformen, zum Beispiel um einen Verkaufsexport mit einer Liste von Regionen zu verknüpfen und den Umsatz nach Region darzustellen. * Eine **Indikator-App** dient dem Erfassen von Kennzahlen über die Zeit. Verwenden Sie sie, wenn Sie Werte von Hand erfassen, Periode für Periode, und die Daten mehrdimensional sind, etwa den monatlichen Kraftstoffverbrauch pro Standort oder den vierteljährlichen Personalbestand pro Team, und möchten, dass Ovidens Summen, Verhältnisse und Trends für Sie berechnet. Wenn Sie unsicher sind, fragen Sie sich, welcher Satz besser passt: "Ich muss Daten zu einem Bericht kombinieren und umformen" deutet auf eine Analyse-App hin, während "Ich muss jede Periode Zahlen von Hand erfassen und beobachten, wie sie sich entwickeln" auf eine Indikator-App hindeutet. ## Die App und ihr Editor [#die-app-und-ihr-editor] Jede App hat zwei Gesichter, und eine Schaltfläche bringt Sie von einem zum anderen. * Die **App** selbst ist das, was alle öffnen: die fertige Visualisierung mit den Daten darin und ohne die Bausteuerungen. Das sehen Ihre Teammitglieder. * Der **Editor** ist der Ort, an dem Sie sie bauen: die Datenpipeline, die Wissensdatenbank und die Visualisierung, die Sie zusammenstellen. Um von der App in den Editor zu wechseln, klicken Sie oben rechts auf **Editor öffnen** oder drücken Sie E. Zurück geht es über das App-Symbol oben links in der Editorleiste (es wird beim Zeigen zu einem Pfeil **Zurück zur Ansicht**) oder erneut über E. Siehe [Tastaturkürzel](../tips/keyboard-shortcuts.mdx). ## Entwurf und Veröffentlichung [#entwurf-und-veroeffentlichung] Alles, was Sie im Editor ändern, landet in Ihrem **Entwurf**. Ein Entwurf ist privat: er wird beim Arbeiten laufend für Sie gesichert, und niemand sonst sieht davon etwas. Solange es einen Entwurf gibt, zeigt die Editorleiste **Entwurf (privat)** und erklärt, was das bedeutet: > Nur Sie sehen diesen Entwurf. Betrachter sehen weiterhin die veröffentlichte > Version. Das gilt auch für das Ausführen einer Analyse-App. Eine Ausführung ist eine private Vorschau auf Ihren Entwurf, keine Freigabe: Ihre Teammitglieder sehen weiterhin die zuletzt veröffentlichten Ergebnisse, bis Sie etwas anderes entscheiden. Wenn Sie zufrieden sind, klicken Sie auf **Veröffentlichen**. Ovidens fragt nach einer Bestätigung, lässt Sie der Version optional einen Namen geben (etwas wie "Q3-Kennzahlen" genügt) und weist Sie darauf hin, dass Ihre Betrachter diese Änderungen nun sehen. Bei einer Analyse-App bietet der Dialog zusätzlich **Veröffentlichen und ausführen** an: die App wird veröffentlicht und die Pipeline sofort ausgeführt, sodass Betrachter neue Logik und frische Ergebnisse in einem Schritt bekommen. Jede Veröffentlichung erzeugt eine **Version**: eine Momentaufnahme der gesamten App zu diesem Zeitpunkt. Versionen werden der Reihe nach unter **Verlauf** aufbewahrt, der Uhr-Schaltfläche in der Editorleiste, sodass Sie zurückblicken können, wie die App früher war. Neben dem Entwurf liegen zwei weitere Steuerungen: * **Verwerfen** wirft den Entwurf weg und holt den zuletzt veröffentlichten Stand zurück. Wenn Sie während des offenen Entwurfs auch Daten erfasst haben, werden diese ebenfalls auf die veröffentlichten Daten zurückgesetzt. * **Wiederherstellen** bei einer älteren Version im Verlauf holt diese Version zurück und veröffentlicht sie als neueste. Dabei werden auch die Daten dieser Version wiederhergestellt: seit der Veröffentlichung dieser Version erfasste Daten werden ersetzt. Ovidens weist Sie in der Bestätigung darauf hin, bevor irgendetwas passiert. Bevor etwas bewegt wird, legt Ovidens eine Sicherungskopie des aktuellen Stands als automatische Version an, sodass die seit der letzten Veröffentlichung erfassten Daten danach weiterhin im Verlauf liegen. Hat Ihr Arbeitsbereich sein Limit an Versionen pro App erreicht, ist diese Kopie nicht möglich - dann ersetzt Ovidens keine Daten, die es nicht sichern konnte, sondern lehnt die Wiederherstellung ab und sagt das. Bitten Sie eine Administratorin oder einen Administrator, das Limit anzuheben. In einer Analytics-App berechnet eine Wiederherstellung die App anschließend automatisch neu: Der Editor zeigt die laufende Ausführung und, ohne Neuladen, die wiederhergestellten Ergebnisse, sobald sie fertig ist - oder, wenn sich die wiederhergestellte Version nicht berechnen lässt, welcher Node fehlgeschlagen ist, mit einem Link zu diesem Node und **Erneut ausführen**, sobald Sie ihn behoben haben. Solange eine Pipeline-Ausführung läuft, wird eine Wiederherstellung abgelehnt; warten Sie, bis die Ausführung abgeschlossen ist, und stellen Sie die Version danach wieder her. Wiederherstellen ist Administratorinnen und Administratoren des Arbeitsbereichs vorbehalten, daher sehen nur sie die Schaltfläche. Solange eine App einen offenen Entwurf hat, ist die Datenerfassung für alle anderen pausiert: "Diese App wird gerade bearbeitet. Solange der Entwurf offen ist, ist die Datenbearbeitung gesperrt - Sie sehen die zuletzt veröffentlichten Daten." Veröffentlichen oder verwerfen Sie den Entwurf, um sie wieder freizugeben. ## Wer baut und veröffentlicht [#wer-baut-und-veroeffentlicht] Apps bauen Mitglieder mit der Rolle **App-Ersteller** oder **Admin**. Mit der Rolle für den ganzen Workspace können beide eine App erstellen, ihre Details bearbeiten, die Apps eines gemeinsamen Bereichs ordnen sowie Apps archivieren und wiederherstellen. In einer Analyse-App können sie außerdem alles im Editor ändern - die Pipeline, die Wissensdatenbank, Skills und die Visualisierung - und veröffentlichen, den Entwurf verwerfen und eine ältere Version wiederherstellen. Eine Seite der Wissensdatenbank endgültig aus ihrem Papierkorb zu entfernen, bleibt der Administration vorbehalten. In einer Indikator-App können sie die Struktur bauen - Indikatoren, Gruppen, Dimensionen, die Berichtseinheiten-Dimension und Konsistenzprüfungen - und in einer Erhebung-App das Formular, seine Fragen, die Einstellungen der Erhebung und den Freigabelink; in beiden können sie veröffentlichen, den Entwurf verwerfen und eine ältere Version wiederherstellen. Die Datenerhebung in einer Indikator-App zu steuern - Erhebungsrunden, Berichtszeiträume und ihre Freigabe sowie das Berichtsintervall - ist Admins und **Berichtsverantwortlichen** vorbehalten, einer Rolle, die ein Admin für einen Bereich oder eine App vergibt; der KI-Assistent für Indikatoren bleibt der Administration vorbehalten. Alle anderen öffnen die veröffentlichte App und arbeiten mit ihren Daten. Ovidens zeigt ihnen nur die Bedienelemente, die sie nutzen können: Auf der Seite **Apps** gibt es für sie in einem gemeinsamen Bereich kein Erstellen, Neuordnen oder Archivieren, und im Editor ist die Konfiguration der App schreibgeschützt, während das Erfassen von Daten weiter funktioniert. Jede und jeder behält eigene Favoriten, ordnet die Bereiche für sich selbst und ordnet die Apps in **Mein Bereich**. ## Eine App erstellen und öffnen [#eine-app-erstellen-und-öffnen] Vera kann eine vollständig erstellte App aus einer Beschreibung in normaler Sprache für Sie erstellen. Siehe [Mit Vera erstellen](../analytical-apps/build-with-vera.mdx) für den Analyse-Weg und [Mit Vera erstellen](../indicator-apps/build-with-vera.mdx) für Indikatoren. So erstellen Sie selbst eine: 1. Öffnen Sie die Seite **Apps** in der linken Navigation. 2. Wählen Sie oben rechts **Neue App** und unter **Erstellen in** den Bereich, in den die App kommen soll - oder das **+** in der Kopfzeile dieses Bereichs - und dann die gewünschte Art. In **Mein Bereich** sehen nur Sie die App; in einem gemeinsamen Bereich sehen sie alle, die Zugriff auf diesen Bereich haben. Angeboten werden nur die Bereiche, in denen Sie Apps anlegen dürfen. (In einem Workspace ohne Apps legen die Karten des Willkommensbildschirms die App in **Mein Bereich** an.) 3. Geben Sie ihr einen Namen (und optional eine Beschreibung, damit Teammitglieder wissen, wofür sie gedacht ist). Der Dialog zeigt, in welchen Bereich die App kommt. 4. Bestätigen Sie, um sie zu erstellen. Ihre neue App erscheint in der Gruppe ihres Bereichs auf der Seite **Apps**. Klicken Sie auf ihre Zeile, um sie zu öffnen und mit dem Erstellen zu beginnen. ### Bereiche [#bereiche] Die Seite **Apps** gruppiert Ihre Apps nach **Bereich**: * **Mein Bereich** steht zuerst. Jedes Mitglied hat einen, und jedes Mitglied kann darin Apps anlegen, unabhängig davon, was es sonst im Workspace darf. Niemand sonst sieht die Apps darin - kein Kollege und auch kein Workspace-Admin. * Darunter folgen die gemeinsamen Bereiche, die Sie erreichen, etwa **Workspace**, der Bereich, mit dem jeder Workspace beginnt. Einen gemeinsamen Bereich, den Sie nicht erreichen, sehen Sie gar nicht. **Neue App** oben rechts fragt, in welchen Bereich eine App kommt, und ebenso die Import-Schaltfläche (**App-Datei importieren**), es sei denn, nur ein Bereich wird gezeigt, weil der Bereich entscheidet, wer sie öffnen kann; die Kopfzeile jedes Bereichs hat zudem ein eigenes **+** und eine Import-Schaltfläche, überall dort, wo Sie in diesem Bereich Apps anlegen oder importieren dürfen. Mit **Bereiche** zeigen Sie nur einige Ihrer Bereiche. Gibt es keinen Bereich, in dem Sie Apps anlegen dürfen, oder konnten Ihre Bereiche nicht geladen werden, sagt die Seite das. Admins legen gemeinsame Bereiche auf der Seite **Zugriff** an, benennen sie um und löschen sie - siehe [Rollen und Bereiche](../administration/index.mdx#roles-and-spaces). Eine Indikator-App in **Mein Bereich** erhebt Werte nur von Ihnen: Sie können Werte eintragen, aber sie kann keine Erhebungsrunde durchführen, weil niemand, den Sie fragen, die App sehen könnte. ### Markierungen und die Reihenfolge Ihrer Seite [#markierungen-und-die-reihenfolge-ihrer-seite] * **Markierungen.** Zeigen Sie auf eine App, die Sie öffnen können, und wählen Sie den Stern (**Markieren**). Eine markierte App trägt einen kleinen Stern an ihrem Symbol, und **Nur markierte** beschränkt die Seite auf Ihre markierten Apps. Markierungen gehören nur Ihnen: Ein Kollege sieht seine eigenen, nie Ihre, und eine Markierung ändert nichts an der App. Wählen Sie den Stern erneut, um sie zu entfernen. * **Die Reihenfolge der Bereiche gehört Ihnen.** Wählen Sie **Nach oben** oder **Nach unten** im ⋮-Menü eines Bereichs (oder die eigene Pfeil-Schaltfläche der Kopfzeile, wenn das das Einzige ist, was Sie mit dem Bereich tun können). Die Reihenfolge gilt nur für Sie; ein Bereich, den Sie nie verschoben haben, steht hinter denen, die Sie geordnet haben. * **Die Reihenfolge der Apps in einem Bereich** folgt der Spalte, nach der Sie sortieren: Klicken Sie auf eine Spaltenüberschrift, um danach zu sortieren. Die Seite öffnet mit **Zuletzt geöffnet**, neueste zuerst. ### Eine App in einen anderen Bereich verschieben [#eine-app-in-einen-anderen-bereich-verschieben] Durch Verschieben teilen Sie eine App: Eine App, die Sie in **Mein Bereich** erstellt haben, erreicht Ihre Kollegen, sobald Sie sie in einen gemeinsamen Bereich verschieben. Wählen Sie auf der Seite **Apps** im ⋮-Menü der App **In einen anderen Bereich verschieben** (oder die eigene Schaltfläche mit Ordner und Pfeil in der Zeile, wenn Verschieben das Einzige ist, was Sie mit der App tun können) und dann das Ziel. Sie können die App auch auf die Gruppe eines anderen Bereichs ziehen: Die Bereiche, in die sie darf, werden hervorgehoben, und das Loslassen dort öffnet denselben Dialog. Das Verschieben ändert, wer die App öffnen kann. Bevor etwas gespeichert wird, nennt Ihnen der Dialog daher die Auswirkung als Zahlen - zum Beispiel "12 Personen verlieren den Zugriff" und "30 Personen erhalten Zugriff". Wenn andere Apps diese App importieren, sagt er auch, wie viele der Personen, die diese Apps bearbeiten, sie nach der Verschiebung nicht mehr ändern könnten, weil sie diese App am neuen Ort nicht mehr lesen könnten. Namen nennt er nie, und eine App im persönlichen Bereich einer anderen Person zählt er nie: Steht eine solche im Weg, sagt er nur "Eine App, die Sie nicht sehen können, importiert diese". Nichts wird verschoben, bevor Sie **Verschieben** wählen, und jede Verschiebung wird protokolliert. Sie können eine App verschieben: * aus **Mein Bereich** in jeden gemeinsamen Bereich, in dem Sie Apps erstellen dürfen; * von einem gemeinsamen Bereich in einen anderen, in dem Sie Apps erstellen dürfen, wenn Sie die App ändern dürfen; * in **Mein Bereich** nur als Workspace-Admin, weil die App damit allen anderen entzogen wird. Der Bereich einer anderen Person wird nie angeboten. Der Dialog sagt Ihnen auch, wann eine Verschiebung nicht möglich ist, und warum: * Eine App, die in **Mein Bereich** wandert, kann keine Erhebungsrunde mitnehmen und keine Rolle, die auf der App selbst vergeben ist (entfernen Sie die Rolle zuerst auf der Seite **Zugriff**). * Eine App in **Mein Bereich** kann nur von anderen Apps in **Mein Bereich** importiert werden - dieselbe Regel, die auch beim Hinzufügen eines Import-Knotens gilt. Eine App in **Mein Bereich** zu verschieben, wird daher abgelehnt, solange eine App außerhalb sie importiert, und eine App aus **Mein Bereich** herauszuschieben, solange sie eine App importiert, die dort zurückbleibt. Importe aus gemeinsamen Apps, die Sie lesen dürfen, sind überall erlaubt. ### Sehen, welche Apps eine App importieren [#sehen-welche-apps-eine-app-importieren] Eine App kann von anderen Apps importiert werden (siehe [Analyse-App-Daten](../analytical-apps/pipeline/analytic-data.mdx)). Wer eine importierende App lesen kann, sieht, was sie importiert - die Apps, die Ihre App importieren, gehören also dazu, wer ihre Daten sehen kann. Die Spalte **Importiert von** der Seite **Apps** zählt sie; klicken Sie auf die Zahl, um sie in den Einstellungen der App aufzulisten. Mit Namen sehen Sie die importierenden Apps, die Sie selbst öffnen können; ein Workspace-Admin erfährt außerdem, wie viele weitere Apps in gemeinsamen Bereichen sie importieren. Eine App im persönlichen Bereich einer anderen Person wird nie genannt oder gezählt, und alle anderen erfahren nichts über Apps, die sie nicht öffnen können. ## Eine App verlassen [#eine-app-verlassen] Der Weg zurück zur Seite **Apps** überrascht viele, denn es gibt keine eigene Schaltfläche "Zurück". Der **Name** der App oben in der linken Navigation ist die Schaltfläche: Fahren Sie mit der Maus darüber, und er wird zu einem **Zurück-Pfeil**. Ein Klick darauf bringt Sie zurück auf die Seite **Apps**. Das ist in jeder App gleich, in Analytic Apps wie in Indicator Apps, und lohnt sich deshalb einmal zu lernen. (Im **Editor** gilt dasselbe eine Ebene tiefer: Das App-Symbol oben links in der Editor-Leiste wird zu **Zurück zur Ansicht**.) Wenn Sie es lieber gezeigt als erklärt bekommen, führt die App Sie hindurch: [Tour zum Zurückgehen starten](https://app.ovidens.com/apps?tour=back-to-apps). Die Tour läuft auf einer Übungs-App; nichts, was Sie dabei anklicken, berührt Ihre eigene Arbeit. ## Eine App archivieren und wiederherstellen [#eine-app-archivieren-und-wiederherstellen] Nichts, was Sie von der Seite **Apps** entfernen, ist sofort weg. **Archivieren** im ⋮-Menü einer App (oder die eigene Archivieren-Schaltfläche der Zeile, wenn Archivieren das Einzige ist, was Sie mit der App tun können) öffnet den Dialog **Analytic App archivieren**, der erklärt, was wirklich passiert: > Die App wird archiviert und aus Ihrem Workspace entfernt. Ihre Daten bleiben > während des Wiederherstellungszeitraums erhalten, und Sie können sie unter > "Archivierte Apps" wiederherstellen, bis sie endgültig gelöscht wird. Eine archivierte App verschwindet von der Seite Apps und zählt nicht mehr zur Anzahl der Apps, die Ihr Workspace haben darf. Archivieren gibt also sofort einen Platz für eine neue App frei. Mitglieder mit der Rolle **App-Ersteller** oder **Admin** für den ganzen Workspace sehen im Kopfbereich der Seite Apps eine Schaltfläche **Archiviert** mit der Anzahl der archivierten Apps. Sie öffnet eine Liste, in der jede App **Archiviert am** und **Wird endgültig gelöscht am** mit dem jeweiligen Datum sowie eine Schaltfläche **Wiederherstellen** zeigt. Das Wiederherstellen bringt die App unverändert auf die Seite Apps zurück und belegt ihren App-Platz wieder. Es kann daher fehlschlagen, wenn Ihr Workspace sein App-Limit erreicht hat. Der Wiederherstellungszeitraum ist eine Workspace-Einstellung (30 Tage, sofern Ihre Administration ihn nicht geändert hat). Danach werden die App und ihre Daten endgültig entfernt und können nicht mehr zurückgeholt werden. Ihre technische ID wird dabei nicht frei: Keine neue App im Workspace kann sie übernehmen. So kann eine App, die Daten aus der gelöschten App importiert hat, nie beginnen, eine andere App unter derselben ID zu lesen. ## Eine App exportieren und importieren [#eine-app-exportieren-und-importieren] Eine App lässt sich als einzelne Datei zwischen Workspaces bewegen. * **App exportieren** liegt im Mehr-Menü des Editors (die Schaltfläche ⋮ in der Editorleiste) und lädt die App als portierbares Bundle herunter: eine `.json`-Datei, benannt nach der App. Exportiert wird die veröffentlichte Version der App, veröffentlichen Sie also vor dem Export. Das Bundle enthält den Aufbau der App - ihre Pipeline-Knoten, die Wissensdatenbank und die Visualisierung -, aber nicht die Datenzeilen darin. * Die Import-Schaltfläche (**App-Datei importieren**) liegt oben rechts auf der Seite **Apps**, und die Kopfzeile jedes Bereichs hat eine eigene, überall dort, wo Sie in diesen Bereich importieren dürfen. Wählen Sie eine Bundle-Datei aus, und Ovidens erstellt daraus eine neue App und öffnet sie - in diesem Bereich, aus der Kopfzeile eines Bereichs oder wenn nur ein Bereich gezeigt wird; sonst fragt Ovidens zuerst, in welchen Bereich die App kommt, wenn Sie in mehr als einen importieren dürfen, und beginnt mit **Workspace**, wenn Sie dorthin importieren dürfen. In einem Workspace ohne Apps bietet der Willkommensbildschirm **App importieren** ebenso an. Ist die Datei kein gültiges Bundle, erscheint "Diese Datei ist kein gültiges App-Bundle." und es wird nichts erstellt. Zum Exportieren brauchen Sie die Rolle **App-Ersteller** oder **Admin** für die App. Zum Importieren brauchen Sie sie für den Bereich, in den die App kommt - und in **Mein Bereich** darf jedes Mitglied importieren. # Was ist Ovidens (/docs/de) Ovidens ist der Ort, an dem Ihr Team seine gesamte Analytik betreibt, in einem Tempo, das früher unerreichbar war. Sie beschreiben, was Sie möchten, die KI baut es, und Sie haben in Minuten statt Wochen ein funktionierendes Ergebnis. Was das verlässlich macht, ist, dass nichts eine Blackbox ist: Jeder Logikbaustein, von einer Datentransformation bis zu einer berechneten Kennzahl, bleibt **konfigurierbar**, **prüfbar** und **versioniert**. Sie können immer sehen, wie eine Zahl entstanden ist, und sie mit Zuversicht ändern. Ovidens kümmert sich außerdem um die Teile, über die Sie sich keine Gedanken machen sollten. Es gibt keine Server zu dimensionieren, keine Datenbanken zu betreiben und keine Pipelines am Leben zu halten. Sie bringen Ihre Analytik mit; Ovidens betreibt die Infrastruktur darunter. ## Alles ist eine App [#alles-ist-eine-app] Alles, was Sie in Ovidens erstellen, ist eine **App**: ein in sich geschlossenes Zuhause für ein Stück Analyse, das seine Daten, die Logik zu deren Gestaltung und die **Visualisierung**, in der Ihr Team arbeitet, enthält. Da jede App in sich geschlossen ist, können Sie eine erstellen, teilen und ändern, ohne die anderen zu berühren. Es gibt zwei Arten. * **Analyse-Apps** dienen dem intelligenten Kombinieren von Daten. Greifen Sie zu einer, wenn die Antwort daraus entsteht, mehrere Quellen zusammenzubringen, sie zu bereinigen und umzuformen, zum Beispiel einen Verkaufsexport mit einer Liste von Regionen zu verknüpfen und den Umsatz nach Region darzustellen. * **Indikator-Apps** dienen dem Erfassen von Kennzahlen über die Zeit. Greifen Sie zu einer, wenn Sie Werte von Hand erfassen, Periode für Periode, und die Daten mehrdimensional sind, zum Beispiel Kraftstoffverbrauch oder Strom pro Standort jeden Monat, wobei Ovidens Summen, Verhältnisse und Trends für Sie berechnet. Sie können eine Art für sich allein nutzen oder die Kennzahlen aus einer Indikator-App in eine Analyse-App einspeisen, um sie mit anderen Daten zu kombinieren. ## Lernen Sie Vera kennen, Ihre KI-Assistentin [#lernen-sie-vera-kennen-ihre-ki-assistentin] **Vera** ist die integrierte KI-Assistentin und der schnellste Weg, etwas zu bauen. Sie beschreiben in normaler Sprache, was Sie möchten, zum Beispiel "verfolge unseren monatlichen Stromverbrauch pro Standort und stelle ihn als Diagramm dar", und Vera richtet die App für Sie ein: die Datenschritte, die Kennzahlen und die Visualisierung. Sie hält inne, um nachzufragen, wann immer sie mehr Details benötigt, und alles, was sie baut, gehört Ihnen zum Überprüfen oder Anpassen. Der Rest dieser Anleitung erklärt jeden Teil des Produkts, sodass Sie überprüfen können, was Vera baut, oder Dinge selbst bauen können, wenn Sie das bevorzugen. ## Ihre erste Stunde [#ihre-erste-stunde] Sie starten nicht mit einem leeren Workspace. Ovidens richtet ihn bei der Anmeldung für Sie ein, bereits gefüllt, damit Sie an etwas Echtem lernen können: * **Sample Sales Analytics**, eine Analyse-App mit Daten darin, einer bereits ausgeführten Pipeline und einer fertigen Visualisierung. * **Sample ESG Indicators**, eine Indikator-App, deren Kennzahlen die Verkaufs-App liest. Beide gehören Ihnen: Ändern Sie sie, machen Sie sie kaputt, löschen Sie sie. Nichts daran ist geschützt. ### Geführte Touren [#geführte-touren] Eine Tour führt Sie über einen echten Bildschirm und hebt dabei eine Sache nach der anderen hervor: was ein Pipeline-Knoten ist, wie ein Diagramm mit einem Ergebnis verbunden ist, wie Sie Vera eine Frage stellen und was jedes Bedienelement auf den Administrationsseiten tatsächlich tut, bevor Sie es betätigen. Eine vermittelt eine Grundlage, die früh nützlich ist: [wie Sie aus einer App wieder herauskommen](apps.mdx#eine-app-verlassen), wofür es keine eigene Schaltfläche "Zurück" gibt. Drei vermitteln einen App-Editor: eine Pipeline bauen und ausführen, aus Ergebnissen Diagramme und Tabellen machen und mit Vera arbeiten. Drei weitere zeigen Ihnen eine Verwaltungsseite: die Seite Zugriff, die Seite System und die API-Zugangsdaten. Öffnen Sie **Geführte Touren** unten in der Seitenleiste, um alle zu sehen, gruppiert unter Überschriften - Grundlagen, Analytische App für die drei Editor-Touren sowie Zugriff und System für die Verwaltungsseiten - damit Sie unter der passenden Überschrift suchen, statt eine lange Liste zu durchsuchen. Die Zahl daneben sagt, wie viele Sie abgeschlossen haben, und jede Tour in der Liste wird abgehakt, sobald Sie sie durchlaufen haben - eine abgeschlossene Tour bleibt in der Liste, denn sie noch einmal anzusehen kostet nichts. Die drei Verwaltungstouren tragen außerdem einen Kompass im Seitenkopf, sodass Sie eine davon starten können, während Sie sich auf der Seite befinden, die sie beschreibt - die Tour der API-Zugangsdaten auf dem Tab **API-Zugangsdaten** der Seite Zugriff. Eine Tour zeigt Ihnen nur alles; sie ändert nie etwas. Wo eine Tour Sie selbst etwas anklicken lässt - die Editor-Touren und die zum Verlassen einer App - bringt sie Sie zuvor in eine **Übungs-App**, gekennzeichnet mit dem Hinweis **Übungsmodus**. Alles, was Sie dort tun, wird beim Ende der Tour verworfen, und keine Ihrer eigenen Apps wird berührt. **Tour überspringen** verlässt sie an jeder Stelle, und eine Tour startet nie von selbst. Gelegentlich weist Ovidens mit einem kleinen, als **Neu** markierten Hinweis auf Neues hin; **Verstanden** blendet ihn aus. ## Wohin als Nächstes [#wohin-als-nächstes] * **[Administration](../administration.mdx)** behandelt Ihren Workspace, Ihre Teammitglieder, das Aktivitätsprotokoll und Ihre Nutzung. * **[Analyse-Apps](../analytical-apps/overview.mdx)** führt durch das Kombinieren von Daten und den Aufbau der Visualisierung, in der Ihr Team arbeitet. * **[Indikator-Apps](../indicator-apps/overview.mdx)** erklärt das Erfassen von Kennzahlen und das Ermitteln neuer Kennzahlen mit Berechnungen. * **[Tipps](../tips/interact-with-vera.mdx)** enthält Hinweise zur Arbeit mit Vera, Tastaturkürzel und Suche. # Erhebung-Apps im Überblick (/docs/de/survey-apps/overview) Eine **Erhebung-App** sammelt Antworten von Personen außerhalb Ihres Workspace. Sie bauen ein Formular, veröffentlichen es und teilen einen Link; alle, die den Link haben, können ohne Konto und ohne Anmeldung antworten. Jede Antwort landet als Datenzeile in Ihrem Workspace - bereit zum Auswerten oder für eine Analyse. Während eine analytische App vorhandene Daten umformt und eine Indikator-App Periode für Periode eine Messung erhebt, ist eine Erhebung-App dafür gebaut, **Menschen** etwas zu fragen und ihre Antworten zu behalten. ## Die drei Teile [#die-drei-teile] Eine Erhebung-App hat drei Oberflächen, und die Trennung dazwischen ist das Erste, was sich zu lernen lohnt. * Das **Formular** ist, was Sie bauen: die Fragen, ihre Reihenfolge und die Regeln dafür, welche erscheinen. Änderungen gehen in einen **Entwurf**, und ein Entwurf ist für Teilnehmende unsichtbar, bis Sie veröffentlichen. * **Einstellungen** ist, was Sie betreiben: ob die Erhebung Antworten annimmt, wann sie schließt, wie viele Antworten sie zulässt und welche Links Sie versenden. Das wirkt sofort - ohne Veröffentlichung. * **Antworten** ist, was Sie lesen: eine Auswertung pro Frage, die einzelnen Antworten und der Weg in eine Analyse-App. Der Grund für die Trennung der ersten beiden ist praktisch: „Keine Antworten mehr annehmen" soll sofort wirken. „Eine Frage hinzufügen" sollen Teilnehmende erst sehen, wenn Sie so weit sind. Wer welchen Teil sieht, folgt der Rolle in der Erhebung. Wer eine Erhebung-App öffnen kann, liest ihr veröffentlichtes Formular und ihre Antworten. Der **Editor öffnen**, der Weg zum Formular und seinem Versionsverlauf, wird Personen angeboten, die die Konfiguration der Erhebung lesen dürfen; der Freigabelink und die Einstellungen, die die Erhebung ändern, sind für Personen, die sie konfigurieren dürfen. Wer eine Erhebung nur ansehen darf, sieht nie einen Entwurf. ## Das Formular bauen [#das-formular-bauen] Die Seite **Fragen** ist eine Arbeitsfläche: Jede Seite Ihres Formulars ist eine Karte, und jede Frage eine Karte darin, so angeordnet, wie Teilnehmende sie lesen werden. Links steht eine Gliederung des Formulars (einklappbar über die Schaltfläche über der Arbeitsfläche oder mit ⌘), und ein Klick auf eine Frage oder den Titel einer Seite öffnet rechts deren Einstellungen. Beim Überfahren einer Karte erscheinen ihre Aktionen: am Griff ziehen, um sie neu zu sortieren (eine Frage kann auf eine andere Seite gezogen werden), duplizieren oder löschen. Escape, oder ein zweiter Klick auf die ausgewählte Karte, schließt das Panel. Jede Zeile des Formulars ist entweder eine **Frage** oder ein **Seitenumbruch** (ein Abschnitt). Ein Seitenumbruch beginnt eine neue Seite, sodass eine lange Erhebung abschnittsweise gelesen werden kann. Eine Frage fügen Sie mit der Schaltfläche **Frage** am Fuß der Seite hinzu, zu der sie gehört - der Typ wird zuerst gewählt - und eine neue Seite mit **Abschnitt** unter der letzten. Schalten Sie die Arbeitsfläche auf **Vorschau** (oder drücken Sie P), um das Formular genau so durchzugehen wie Teilnehmende, ungespeicherte Änderungen eingeschlossen: dieselbe Seite, dasselbe Weiter und Zurück, dieselben Pflichtprüfungen. Nichts, was Sie in der Vorschau eingeben, wird gespeichert. Die Smartphone-Breite ist in derselben Leiste einen Klick entfernt. Eine Frage hat einen **Typ**, und der Typ ist das, was Teilnehmende sehen: | Typ | Was Teilnehmende bekommen | | ------------------------ | -------------------------------------------------------------------------- | | Kurzer Text, Langer Text | Eine Zeile oder ein Feld | | Zahl | Ein Zahlenfeld, optional mit Einheit und Bereich | | Einfachauswahl, Dropdown | Eine Auswahl aus einer Liste | | Mehrfachauswahl | Beliebig viele Auswahlen aus einer Liste | | Ja / Nein | Zwei Schaltflächen | | Skala | Eine Reihe nummerierter Schaltflächen mit Beschriftungen an den Enden | | E-Mail, Datum | Ein Feld, das sein eigenes Format prüft | | Datei-Upload | Eine Schaltfläche, die eine Datei anhängt, bis 10 MB | | Tabelle | Ein Raster aus selbst definierten Spalten, Zeilen fügen Teilnehmende hinzu | Eine Frage kann **Pflicht** sein und sie kann **bedingt** sein: „zeige dies nur, wenn eine frühere Frage auf eine bestimmte Weise beantwortet wurde". Eine bedingte Frage, die Teilnehmende nie sehen, wird nie von ihnen verlangt, und ihre Antwort wird nie gespeichert. Eine **Tabellenfrage** ist die richtige Wahl, wenn die Antwort eine LISTE ist: „nennen Sie uns jeden Ihrer Lieferanten", „listen Sie die betroffenen Standorte auf". Sie definieren die Spalten - je eine Überschrift und ein Antworttyp, bis zu zehn davon - und Teilnehmende fügen eine Zeile je Lieferant hinzu, bis zu fünfzig. Eine Spalte kann ein kurzer Text, ein Absatz, eine Zahl, ein Datum oder eine E-Mail-Adresse sein, und jede davon kann Pflicht sein, was bedeutet: eine begonnene Zeile muss sie füllen. Auf der Ergebnisseite und im CSV wird jede Spalte zu einer eigenen Spalte, mit den Einträgen dieser Person in der Reihenfolge, in der sie eingegeben wurden. Eine **Datei-Upload**-Frage bittet um ein Dokument: ein Zertifikat, ein unterschriebenes Formular, ein Foto. Teilnehmende hängen eine Datei pro Frage an, und sie wird bei der Erhebung gespeichert; auf der Seite Ergebnisse und in der CSV steht als Antwort der Dateiname, und wenn Sie eine einzelne Antwort öffnen, gibt es eine Schaltfläche zum Herunterladen. Sie wählen, was angehängt werden darf - **Beliebige Datei**, **Dokumente** (pdf, doc, docx, xls, xlsx, csv, txt) oder **Bilder** (png, jpg, jpeg, gif, webp) - und diese Wahl schränkt die Dateiauswahl ein, die Teilnehmende sehen. Das ist eine Hilfe für sie und keine Sicherheitsprüfung. Ein Erhebungslink lässt sich an beliebige Personen weitergeben, und Ovidens prüft Hochgeladenes weder auf Viren noch sieht es hinein. Eine Datei wird immer heruntergeladen statt im Browser geöffnet, es läuft also nichts allein dadurch, dass sie aufgelistet ist - behandeln Sie sie aber wie einen E-Mail-Anhang von jemandem, den Sie nicht kennen. Eine Auswahlfrage kann **eine eigene Antwort zulassen**. Das fügt eine Auswahl "Sonstiges" mit Textfeld hinzu, unter der Beschriftung, die Sie ihr geben, und sie wird in zwei Spalten gespeichert: die Auswahlspalte hält fest, dass Sonstiges gewählt wurde, und eine zweite Spalte daneben trägt, was eingegeben wurde. Filter und Diagramme über die Auswahlspalte bedeuten weiter, was sie bedeuteten, und eine Analyse-App, die die Antworten importiert, löst weiterhin jede Auswahl zu ihrer Beschriftung auf. Sonstiges fängt Ausreißer in einer Liste auf, die Sie schon durchdacht haben - eine vergessene Kategorie holt es nicht zurück, also lohnt es sich zu lesen, was die Leute eingegeben haben. Sobald Antworten vorliegen, können Sie weiterhin Auswahlmöglichkeiten HINZUFÜGEN und umbenennen; eine Auswahl zu entfernen oder Sonstiges abzuschalten wird abgelehnt, denn beides würde Antworten auf etwas verweisen lassen, das es nicht mehr gibt. ## Veröffentlichen und danach bearbeiten [#veröffentlichen-und-danach-bearbeiten] Beim Veröffentlichen wird eine Momentaufnahme des Formulars zu dem, was Teilnehmende sehen. Es ist dieselbe Versionierung wie in jeder Ovidens-App: jede veröffentlichte Version ist unveränderlich, frühere Versionen bleiben im Verlauf und lassen sich wiederherstellen. Bis eine Erhebung zum ersten Mal veröffentlicht ist, antwortet ihr Link mit „noch nicht geöffnet" statt mit einem leeren Formular. **Sie können auch nach dem Veröffentlichen weiter bearbeiten.** Eine Änderung öffnet einen Entwurf, Teilnehmende beantworten währenddessen weiter die veröffentlichte Fassung, und Ihre Änderungen erreichen sie, sobald Sie erneut veröffentlichen. Nichts von dem, was Sie gesammelt haben, geht dabei verloren oder wird verändert, und das Wiederherstellen einer älteren Version bringt deren FRAGEN als neue Version zurück - niemals alte Antworten und ohne neue zu entfernen. Zwei Dinge sollten Sie wissen, bevor Sie eine laufende Erhebung bearbeiten. * **Eine bereits beantwortete Frage ist teilweise festgelegt.** Sie können sie umformulieren, ihren Hilfetext ändern, sie auf eine andere Seite verschieben, sie als Pflichtfrage setzen oder freigeben, eine Auswahl hinzufügen und eine umbenennen. Bei einer Tabellenfrage können Sie eine Spalte hinzufügen, eine umbenennen und ändern, ob sie Pflicht ist. Sie können nicht ihren Antworttyp ändern, ihren Spaltennamen ändern, eine ihrer Auswahlmöglichkeiten entfernen, ihre Sonstiges-Auswahl ein- oder ausschalten, eine ihrer Tabellenspalten entfernen oder den Typ einer Tabellenspalte ändern - jedes davon würde Antworten auf etwas verweisen lassen, das es nicht mehr gibt. Das Löschen einer beantworteten Frage blendet sie aus dem Formular aus und behält ihre Antworten. * **Manche Änderungen bedeuten, dass die Antworten keine einheitliche Messung mehr sind.** Eine Bewertungsskala von 1-5 auf 1-10 zu ändern, die Bedingung zu ändern, die entscheidet, wer gefragt wird, eine Pflichtfrage freizugeben oder die Spalten einer Tabellenfrage überhaupt zu ändern - auch eine hinzuzufügen - ändert jeweils, was eine Antwort BEDEUTET. Ovidens sagt es Ihnen vor dem Veröffentlichen - die Bestätigung beim Veröffentlichen listet auf, welche Fragen dadurch „neu definiert" würden - und markiert diese Frage danach auf der Ergebnisseite, statt beide Reihen zusammenzurechnen. Eine Frage umzuformulieren oder eine Auswahl hinzuzufügen ist keine solche Änderung: diese Antworten gehören weiterhin zusammen. Wer das Formular geöffnet hatte, als Sie erneut veröffentlicht haben, wird gebeten, neu zu laden, bevor die Antworten angenommen werden - so wird niemandes Antwort einem Fragebogen zugeordnet, der ihm nicht gezeigt wurde. ## Eine neue Erhebung aus dieser beginnen [#eine-neue-erhebung-aus-dieser-beginnen] Um dieselben Fragen erneut zu stellen - die Ausgabe des nächsten Jahres oder eine Variante für eine andere Gruppe - öffnen Sie die Erhebung mit Editor öffnen und wählen im Menü **Weitere Aktionen** (⋮) in der oberen Leiste **Erhebung duplizieren...**. Sie geben der neuen Erhebung einen Namen und wählen, wenn Sie Apps in mehr als einem Bereich anlegen dürfen, den Bereich, in den sie kommt. Die Kopie übernimmt die Fragen in ihrer Reihenfolge, die Beschreibung des Formulars und seine Mitteilungen und beginnt als Entwurf. Alles andere ist neu: Sie erhält einen eigenen Link und eigene Antworten und enthält keine Antworten. Diese Erhebung behält ihren Link und ihre Antworten. Das Menü erscheint nur für Personen, die in mindestens einem Bereich Apps anlegen dürfen, denn ein Duplikat ist eine neue App. ## Teilen [#teilen] Jede Erhebung hat **einen** Link. Wer ihn hat, kann ohne Konto antworten, und jede Antwort zählt. Um Ihr Formular selbst durchzugehen, bevor Sie es versenden, nutzen Sie im Editor die **Vorschau**: Sie ist die Seite der teilnehmenden Person über Ihrem Entwurf, ungespeicherte Änderungen eingeschlossen, und sie prüft Ihre Antworten nach denselben Regeln wie das veröffentlichte Formular - gespeichert wird dabei nichts, eine Probe wird also nie zu einer Antwort. Muss ein Link einmal aufhören zu funktionieren, erzeugen Sie ihn neu: die alte Adresse löst sofort nicht mehr auf und lässt sich nicht zurückholen. ## Was mit den Antworten geschieht [#was-mit-den-antworten-geschieht] Jede Antwort wird zu einer Zeile in der **Antworten**-Tabelle der Erhebung - mit einer Spalte pro Frage sowie einigen Angaben zur Einreichung selbst: wann sie eintraf und welche veröffentlichte Version des Formulars sie beantwortet hat. Diese Tabelle ist ein ganz normaler Ovidens-Datensatz. Sie ist es, was die Antworten-Seite liest, was ein CSV-Export schreibt und was eine Analyse-App über einen **Analyse-App-Daten**-Knoten importiert. Die Tabelle selbst sehen Sie im Editor auf der Seite **Pipeline**: die Antworten-Tabelle als der eine Knoten, den diese App hat, ihre Spalten und Typen im Reiter Konfiguration und die Antworten im Reiter Daten. Sie ist nur lesbar - schreiben kann nur eine teilnehmende Person - und sie zeigt genau das, was jede andere Auswertung zeigt: gelöschte Antworten sind nicht darin. **Wann** eine Antwort eintraf, steht in UTC in der Spalte `submitted_at`. Es ist der Moment der Einreichung, auf die Millisekunde genau, und er wird überall als derselbe Moment gelesen - im Raster, im CSV-Export und in einer Abfrage in einer Analyse-App. Beim Löschen einer Antwort bleibt die Zeile erhalten und wird als gelöscht markiert, damit sich das Löschen rückgängig machen lässt und der Prüfpfad ehrlich bleibt. Eine gelöschte Antwort ist von jeder Auswertung ausgeschlossen. ## Die Grenzen, in denen eine Erhebung arbeitet [#die-grenzen-in-denen-eine-erhebung-arbeitet] Fünf Zahlen begrenzen eine Erhebung, und alle fünf gehören Ihrem Workspace und nicht der Plattform - eine Workspace-Administration sieht sie unter **System -> Nutzung und Limits**, und der Betrieb kann sie anheben. | Grenze | Standard | Was sie begrenzt | | ------------------------ | -------- | ----------------------------------------------------------- | | Antworten pro Erhebung | 100.000 | wie viele Antworten eine Erhebung sammeln darf | | Fragen pro Erhebung | 100 | Zeilen eines Formulars - Fragen und Seitenumbrüche zusammen | | Seiten pro Erhebung | 20 | Seitenumbrüche eines Formulars | | Uploadgröße pro Datei | 10 MB | die größte einzelne Datei, die angehängt werden darf | | Uploadgröße pro Erhebung | 500 MB | alles, was eine Erhebung je erhalten hat, über alle Dateien | Wer eine Datei über der Einzelgrenze anhängt, erfährt das, solange die richtige Datei noch zur Hand ist; wer eintrifft, nachdem die Gesamtsumme der Erhebung erreicht ist, erfährt, dass das Formular keine weiteren Dateien annehmen kann - nie, wie hoch die Grenze ist. Die Antwortgrenze wirkt neben dem optionalen **Antwortlimit**, das Sie in Einstellungen für die Erhebung selbst setzen können; das NIEDRIGERE der beiden schließt das Formular. Wer danach eintrifft, erfährt, dass die Erhebung geschlossen ist - nie, welche der beiden Grenzen es war. Ein eigenes Limit über dem des Workspace wird abgelehnt statt stillschweigend gekürzt, und das Feld nennt den erlaubten Wert. Die **Vorschau** im Editor ist von beiden Grenzen unberührt, weil sie nichts speichert - ein volles Formular lässt sich also weiterhin proben. Zwei weitere Grenzen sind fest und nicht pro Workspace: 50 Optionen bei einer Auswahlfrage und 4.000 Zeichen in einer Freitextantwort. # Antworten lesen (/docs/de/survey-apps/responses) Eine Erhebung-App öffnet sich auf zwei Seiten, die Sie in der Leiste links umschalten: **Informationen** - der Freigabelink, wie viele Antworten eingegangen sind, wie viele Fragen gestellt werden, wann die Erhebung schließt und ob sie noch annimmt - und **Ergebnisse**, das, was Menschen geantwortet haben. Wer die Erhebung öffnen kann, liest beide: das Formular so, wie es zuletzt veröffentlicht wurde, und jede Antwort. Der **Editor öffnen**, der Weg in den Formular-Editor, wird jedem angeboten, der die Konfiguration der Erhebung lesen darf - wer sie nicht ändern darf, öffnet den Editor schreibgeschützt -, und das Formular zu bauen erfordert die Berechtigung, die Erhebung zu konfigurieren. Wer die Erhebung nur ansehen darf, sieht nie die Fragen, die gerade entworfen werden. Informationen ist die Seite, die Sie öffnen, um jemandem den Link zu schicken. Der Link dort wird nur Personen gezeigt, die die Erhebung konfigurieren dürfen, und er ist schreibgeschützt - kopieren -, während das Neu-Erzeugen unter Einstellungen im Editor liegt, hinter derselben Berechtigung, weil es jeden bereits verschickten Link ungültig macht. Die Erfassungsregeln werden für alle ebenso nur ausgelesen; geändert werden sie unter Einstellungen, und der Hinweis darauf wird nur Personen gezeigt, die das können. Ergebnisse ist die Seite, die Sie öffnen, um zu lesen, was Menschen gesagt haben, und sie bietet zwei Lesarten derselben Erhebung: die **Übersicht**, ein Satz Diagramme, gebaut für das Formular, das Sie veröffentlicht haben, und **Einzeln**, eine Zeile pro Antwort. ## Übersicht [#übersicht] Die Übersicht ist ein Dashboard, gebaut für Ihre Erhebung und immer auf ihrem Stand. Sie legen es nicht selbst an: es folgt dem Formular. Jede Seite Ihres Formulars wird zu einer Seite mit Diagrammen, davor steht eine Seite **Überblick**. Fragen, die Sie vor dem ersten Seitenumbruch stellen, erhalten eine eigene Seite namens **Fragen**. Die Seite wählen Sie in der Leiste über den Diagrammen. Jede Frage nimmt eine Zeile ein, in der Reihenfolge, in der das Formular sie stellt, und jede Kachel ist gleich hoch, sodass die Seite durchgehend im selben Rhythmus liest - unabhängig davon, wie viele Optionen eine Frage anbietet. Die Breitensteuerung im Seitenkopf verengt das Dashboard auf eine zentrierte Lesespalte; das ist Ihre eigene Einstellung, dieselbe wie im Analyse-Viewer, und sie begleitet Sie von App zu App. Der Überblick trägt vier Kennzahlen - wie viele Antworten, wie viele Angaben, wann die erste eingegangen ist und wann die letzte -, ein Diagramm der Antworten pro Tag und einen Balken, wie viele Angaben jede Frage erhalten hat. Darüber steht eine **Filterleiste**: der Eingang der Antwort, dazu ein Filter für jede der ersten Einfachauswahl-Fragen Ihres Formulars. Wer dort eingrenzt, grenzt die vier Kennzahlen und die beiden Diagramme **auf der Seite Überblick** ein - so lesen Sie das ganze Bild für ein Team, einen Standort oder einen Monat. Die Diagramme der einzelnen Fragen auf den folgenden Seiten erreicht sie nicht: die werden über die vollständige Optionsliste jeder Frage gezeichnet, einschließlich der Optionen, die niemand gewählt hat - eine andere Frage als "was hat dieses Team gesagt". Um die Antworten einer Gruppe Frage für Frage zu lesen, übernehmen Sie die Antworten in eine Analyse-App - siehe das Ende dieser Seite. Jede Frage erhält dann das Diagramm, das ihr Antworttyp verdient: * Eine **Auswahlfrage** erhält einen Ring, wenn sie zwei oder drei Optionen anbietet, sonst ein liegendes Balkendiagramm, über die vollständige Optionsliste * eine Option, die niemand gewählt hat, wird mit null gezeichnet, denn das ist sehenswert. Eine Mehrfachauswahl zählt für jede genannte Option einmal, die Balken können also über der Zahl der Teilnehmenden liegen. Lässt die Frage eine eigene Antwort zu, steht der eingegebene Text als Liste unter dem Diagramm. * Eine **Zahlen-** oder **Skalenfrage** erhält eine Verteilung, einen Balken pro genanntem Wert. * Eine **Datumsfrage** erhält einen Verlauf über die Zeit, denn genau das ist eine Datumsangabe. * Eine **Text-** oder **E-Mail-Frage** erhält eine durchsuchbare, sortierbare Tabelle des Geschriebenen, mit eigenem **Export** als CSV oder Excel. Die Datei enthält die Antworten, die die Tabelle zeigt - die dieser Frage und keiner anderen - mit den Filtern und der Suche der Tabelle. * Eine **Dateifrage** listet die angehängte Datei je Antwort mit Namen; zum Herunterladen öffnen Sie die Antwort unter Einzeln. * Eine **Tabellenfrage** erhält eine Kreuztabelle ihrer Zeilen und Spalten. Wenn Sie ändern, was eine Frage MISST - die Endpunkte einer Skala, ob sie Pflicht ist, wem sie gezeigt wird -, sind die Antworten davor und danach nicht dieselbe Messung. Die Diagramme zählen nur die aktuelle; die früheren Antworten stehen weiterhin unter Einzeln, im Detailfenster und im Export. Nur den WORTLAUT einer Frage zu korrigieren ändert nichts: diese Antworten bleiben alle vergleichbar. Fragen, die Sie **archiviert** haben, erhalten kein Diagramm, denn sie gehören nicht mehr zum Formular; ihre Antworten stehen weiterhin unter Einzeln und im Export. Fragen, die Sie dem Entwurf hinzugefügt, aber noch nicht veröffentlicht haben, erhalten ebenfalls keines - die Übersicht folgt dem Formular, das Ihre Befragten tatsächlich beantworten. ## Einzeln [#einzeln] Eine Zeile pro Antwort, über dieselben Spalten, die auch eine Analyse-App sähe. Ein Klick auf eine Zeile öffnet das **Detailfenster**, das die Antworten einer Person von Anfang bis Ende zeigt - einschließlich der Fragen, die ihr nie gezeigt wurden, gekennzeichnet als nicht beantwortet, damit ein leeres Feld nie erraten werden muss. Die Tabelle bietet die gewohnten Steuerelemente: Suche, Spaltenfilter, Spalten ausblenden und **CSV-Export**. Das **Zeitfenster** darüber - gesamt, 7, 30 oder 90 Tage - grenzt ein, was Tabelle und Export lesen; in der Übersicht übernimmt die Filterleiste dieselbe Aufgabe. Das Herunterladen der Antworten ist eine eigene Berechtigung, die zu den Rollen **Mitwirkender**, **Analyst**, **App-Ersteller** und **Admin** gehört; wer die Erhebung nur ansehen darf, liest die Antworten, bekommt aber keinen **CSV-Export** angeboten. ## Eine Antwort löschen [#eine-antwort-löschen] Aus dem Detailfenster. Wer in der Erhebung Daten erfassen darf - mit **Mitwirkender**, **App-Ersteller** oder **Admin** -, kann eine Antwort löschen und wiederherstellen. Das Löschen ist umkehrbar: die Zeile bleibt erhalten und wird als gelöscht markiert, eine Meldung bietet **Rückgängig** an, und der Prüfpfad hält beide Hälften fest. Eine gelöschte Antwort ist aus der Tabelle, der Übersicht, einem Export und allem ausgeschlossen, was eine Analyse-App importiert. ## Die Antworten in einer Analyse-App verwenden [#die-antworten-in-einer-analyse-app-verwenden] Eine Erhebung-App sammelt Antworten; sie verarbeitet sie nicht. Ihre Pipeline hält nur, was die Plattform erzeugt - die Antworten und die Sichten darüber, die die Seite **Pipeline** im Editor nur lesbar zeigt - ohne SQL und ohne etwas auszuführen. Und das mit Absicht: die Aufgabe eines Formulars ist es, beantwortbar zu sein, und jede Frage, die Sie an die Antworten stellen könnten, ist eine Frage an **Daten**. Sobald Sie die Antworten also mit einem anderen Datensatz verbinden, darstellen oder in einen Indikator einspeisen wollen, überführen Sie sie in eine Analyse-App - dort leben Transformationen und Läufe. Genau das tut ein **Analyse-App-Daten**-Knoten. Fügen Sie ihn im Editor der Analyse-App zur Pipeline hinzu und wählen Sie die Erhebung als Quelle: die Quellenliste führt jede veröffentlichte Erhebung neben Ihren Analyse-Apps auf, gekennzeichnet mit *(Erhebung)*, und eine noch nicht veröffentlichte Erhebung ist ausgegraut, weil es nichts zu importieren gibt. Von da an sind die Antworten gewöhnliche Pipeline-Eingabe: ein SQL-Knoten kann sie filtern oder umformen, eine Visualisierung kann sie darstellen, und eine weitere App kann das Ergebnis importieren. Es sind **zwei Schritte**, und der zweite gehört Ihnen: 1. Der Knoten landet im **Entwurf** der Analyse-App, genau wie jeder von Hand hinzugefügte Knoten. Dabei wird nichts veröffentlicht und nichts ausgeführt. 2. Führen Sie die Pipeline aus, um die Antworten hindurchfließen zu sehen, und veröffentlichen Sie, wenn das Ergebnis stimmt. Da eine Erhebung keine eigenen Ausführungen hat, liest der Import die Antworten stets in ihrem **aktuellen** Stand, und jede Ausführung der importierenden App hält sie in ihrem eigenen unveränderlichen Verlauf fest. Gelöschte Antworten wandern nie mit. # Mit Vera interagieren (/docs/de/tips/interact-with-vera) **Vera** ist die in Ovidens integrierte KI-Assistentin. Sie ist der No-Code-Weg, Dinge zu erledigen: Sie beschreiben in normaler Sprache, was Sie möchten, und Vera erledigt die Arbeit, ob das nun das Erstellen einer ganzen App oder das Vornehmen einer kleinen Änderung ist. Sie finden Vera im Panel **Vera (AI)** im Editor einer analytischen oder Indikator-App. ## Was Sie Vera fragen können [#was-sie-vera-fragen-können] Vera kann die meisten Dinge erledigen, die Sie sonst von Hand erstellen würden. Zum Beispiel können Sie sie bitten: * **eine App zu erstellen** aus einer Beschreibung Ihrer Daten und Ihres Ziels, * **ein Widget hinzuzufügen** zur Visualisierung, etwa ein Diagramm des Umsatzes nach Monat, * **eine Transformation zu schreiben**, sodass Sie kein SQL beherrschen müssen, * **eine Berechnung aufzubauen**, die aus den von Ihnen erfassten Kennzahlen eine neue Kennzahl berechnet, * **die Daten zu erklären**, um zu verstehen, was eine App zeigt, * **Dokumentation zu erstellen oder zu ändern** in der Wissensdatenbank, * **Ovidens zu erklären**, mit Links zur Benutzerdokumentation. Vera verwendet die für Sie freigegebenen und am jeweiligen Ziel erlaubten Werkzeuge. Sie wählt die für Ihre Anfrage nötigen Schritte. Fehlt ein Werkzeug oder eine Berechtigung, erklärt sie das kurz. ## Wie Sie Anfragen gut formulieren [#wie-sie-anfragen-gut-formulieren] Vera leistet ihre beste Arbeit, wenn Ihre Anfrage klar und gut belegt ist. Ein paar Gewohnheiten helfen: * **Seien Sie konkret.** "Gesamtumsatz nach Region für dieses Jahr, als Balkendiagramm" gibt Vera weit mehr, womit sie arbeiten kann, als "erstelle einen Bericht". * **Geben Sie Kontext direkt im Chat.** Erklären Sie die Bedeutung Ihrer Spalten und die gewünschten Regeln. Die [Wissensdatenbank](../analytical-apps/knowledge-base.mdx) kann bei Bedarf helfen, ihre Vorbereitung ist aber optional. Bitten Sie Vera darum, eine Regel dort zu dokumentieren, wenn Sie sie dauerhaft festhalten möchten. * **Hängen Sie eine Datei an**, wenn es hilft, zum Beispiel eine Beispiel-Tabellenkalkulation, damit Vera Ihre Spaltennamen lernt. * **Beantworten Sie Veras Fragen.** Wenn sie innehält, um nach einem Detail zu fragen, leitet Ihre Antwort den Rest ihrer Arbeit. ## Setzen Sie das Gespräch fort [#setzen-sie-das-gespräch-fort] Eine Anfrage ist ein Schritt in Ihrem Gespräch. Schreiben Sie nach Veras Antwort im selben Nachrichtenfeld weiter, etwa „Mache dieses Diagramm blau“ oder „Erkläre die letzte Berechnung“. Ihre jüngsten Anfragen, Antworten auf Rückfragen und Veras Antworten helfen ihr, Bezüge zu verstehen. Nennen Sie bei älteren Themen die wichtigen Angaben erneut. Ihre Gespräche sind persönlich und gehören zu diesem Workspace. **Gesprächsverlauf** öffnet eine mittig angeordnete Tabelle mit Titel, ursprünglichem App-Kontext, Erstellungszeit, letzter Aktivität und letzter Öffnung. Suchen Sie nach Titeln und blättern Sie mit den Seiten-Bedienelementen durch das Archiv. Es wird jeweils nur eine Seite geladen. Wählen Sie **Auswählen** neben einem Gespräch, um es wieder zu öffnen. **Neues Gespräch** erstellt einen weiteren benannten Verlauf; **Gespräch umbenennen** ändert den Titel. Der vollständige Verlauf bleibt gespeichert; **Frühere Nachrichten laden** zeigt ältere Beiträge. Vera öffnet Ihr letztes Gespräch automatisch nur in derselben App und innerhalb von 6 Stunden nach Ihrer letzten Interaktion wieder. Das Öffnen des Bereichs und Hintergrundabfragen verlängern dieses Zeitfenster nicht. Andernfalls beginnen Sie mit einem leeren Chat; der Verlauf wird mit der ersten Nachricht gespeichert. Über den Gesprächsverlauf können Sie ältere Gespräche jederzeit manuell öffnen, auch aus anderen Apps. Die aktuelle Seite liefert den Kontext Ihrer nächsten Anfrage. Vera kann auf andere Apps zugreifen, wenn Ihre Berechtigungen es erlauben. Ist das Ziel unklar, fragt sie nach der gemeinten App. Eine noch nicht gesendete Nachricht bleibt für das ausgewählte Gespräch oder vor der ersten Nachricht für die aktuelle App in diesem Browser-Tab gespeichert, getrennt nach Person und Workspace. Ein Gesprächswechsel beendet keine laufende Aufgabe. Wählen Sie **Diese Anfrage stoppen**, um sie zu stoppen. Der vollständige Gesprächsverlauf bleibt zum Nachlesen verfügbar. Beim Fortsetzen nutzt Vera den zuletzt gespeicherten fortlaufenden Kontext und die jüngsten Nachrichten, auch nach einigen Tagen. Ältere Nachrichten dienen dem Nachlesen; Vera arbeitet weiterhin mit dem aktuellen App-Zustand und dem aktuellen Workspace-Modell. Bei der Verdichtung können Einzelheiten verloren gehen. Nennen Sie eine wichtige Angabe erneut, wenn Vera sie nicht mehr im Kontext hat. Vera verdichtet älteren Kontext, wenn sie sich der verfügbaren Kontextkapazität des gewählten Modells nähert, statt nach einer festen Anzahl von Chat-Nachrichten. Auch Werkzeugergebnisse belegen Kontext. Die sichtbare Anzahl der Nachrichten allein beschreibt daher nicht dessen Größe. Die Verdichtung verändert den Arbeitskontext für das Modell; sie löscht nicht den gespeicherten Gesprächsverlauf. Die aktuellen Anweisungen und freigegebenen Werkzeugdefinitionen bleiben bei jeder Anfrage verfügbar. Wenn ein älteres Ergebnis der Werkzeugsuche aus dem Arbeitskontext fällt, kann Vera die aktuellen Werkzeuge erneut ermitteln. ## Wie Vera arbeitet [#wie-vera-arbeitet] Vera zeigt ihren Fortschritt, liest relevanten Kontext und verwendet verfügbare Werkzeuge gemäß Ihrer Anfrage, ohne feste Einrichtungsfolge. Fehlt eine wichtige Angabe oder ist sie unklar, stellt Vera eine gezielte Frage und wartet auf Ihre Antwort. Klare Anfragen können ohne Bestätigungsrunde beginnen. Vera antwortet kurz, was passiert ist oder was sie nicht tun konnte. **Details** öffnet einen Dialog mit einem getrennten Bereich für KI-Verbrauch, Zeitpunkten und einem gemeinsamen Schrittverlauf. Fortschritt und tatsächliche Werkzeugergebnisse erscheinen zusammen. Fehlgeschlagene Aufrufe zeigen Vorgang, Ziel und Grund; ein späterer Erfolg bleibt ein eigener Aufruf. Allgemeine Fortschrittsmeldungen behaupten keinen Erfolg. Bei älteren Aufrufen ohne Zeitpunkt steht „Zeitpunkt nicht erfasst“. Der Dialog wiederholt weder Ihre Nachricht noch Veras Antwort. Angeforderte Änderungen erstellen oder verwenden den Entwurf der App und folgen denselben Berechtigungs-, Audit- und Versionsregeln wie manuelle Änderungen. Fragen nach Hilfe oder Erklärungen erzeugen keinen Entwurf. Prüfen Sie Änderungen vor dem Veröffentlichen. Vera veröffentlicht nur auf Ihre Anfrage und mit einem für Sie freigegebenen Veröffentlichungswerkzeug. Visualisierungsspeicherungen können einen neueren gemeinsamen App-Zustand nicht überschreiben. Der Editor pausiert während KI-Schreibzugriffen und beim Neuladen gemeinsamer Änderungen. Scheitert das Neuladen, bleibt die Bearbeitung bis zu einem erfolgreichen erneuten Versuch pausiert. Diese Abstimmung verrät keine privaten Gespräche anderer Personen. Vera durchsucht eine mit der Anwendung ausgelieferte Kopie der öffentlichen Benutzerdokumentation und liest relevante Abschnitte über Dokumentationswerkzeuge. Ein allgemeines Werkzeug zum Surfen im Internet hat sie nicht. Die Kopie wird mit der Anwendung aktualisiert; beim Antworten ruft Vera die Website nicht live ab. Quellenlinks sind unterstrichen und öffnen einen neuen Tab, auch wenn die Antwort eine reine URL enthält. Ihr Gespräch bleibt geöffnet. ## Hochgeladene Dateien [#hochgeladene-dateien] Dateien an gesendeten Anfragen bleiben privat und haben derzeit kein automatisches altersabhängiges Ablaufdatum, solange ihre Verlaufseinträge bestehen. Das Senden oder Importieren löscht die Originaldatei nicht. Verlassene, nie gesendete Uploads können nach 24 Stunden bereinigt werden. Beim Löschen einer App bleiben Ihre privaten Gespräche und deren Quelldateien erhalten. Die Dateianzeigen im Chat zeigen derzeit Name und Größe ohne Download-Aktion. Ein Datensatzexport lädt die importierten Daten herunter, statt die ursprüngliche Arbeitsmappe oder das Dokument. Das sechsstündige Zeitfenster für die automatische Gesprächswiederherstellung löscht weder den Verlauf noch hochgeladene Dateien. ## Aufgabendetails und Versionsverlauf [#aufgabendetails-und-versionsverlauf] Öffnen Sie **Details** neben dem Antwortzeitpunkt, um Token-Verbrauch, Schrittergebnisse und Zeitpunkte zu prüfen. Nachrichten zeigen Sende- und Antwortzeiten; Rückfragen und Antworten verwenden ihre erfassten Zeitpunkte. Der Gesprächsverlauf zeigt Beginn, Aktualisierung der gespeicherten Metadaten und letzten Öffnungszeitpunkt des Gesprächs. Auf kleinen Bildschirmen füllt Vera den Bildschirm; mit **Schließen** kehren Sie zum Editor zurück. Beim Wechsel zurück zum Desktop bleiben die gespeicherte Panelbreite und Ihr Nachrichtenentwurf erhalten. Öffnen Sie das kompakte Menü **Selbstständigkeit** neben der Anhang-Schaltfläche im Nachrichtenfeld und wählen Sie mit dem Regler, wie eigenständig Vera handelt: **0 · Erst fragen**, **4 · Ausgewogen** (Standard) oder **9 · Selbstständig**, mit Zwischenstufen. Sie können die Stufe schon vor der ersten Nachricht wählen. Diese private Gesprächseinstellung gilt für neue Chat-Anfragen. Bei wichtigen fehlenden fachlichen Angaben fragt Vera auf jeder Stufe nach. Der Versionsverlauf zeigt **Version veröffentlicht von**, **Mitwirkende an der Version** und **KI-Modelle und Clients** direkt in beiden Tabellen: Versionen und Ausführungen. Die Liste erfasst Personen und KI, die an Änderungen für diese Version beteiligt waren. Zu jedem Modell steht die Person, die es beauftragt hat. Externe KI-Verbindungen zeigen ihren Client-Namen, weil ihr genaues Modell nicht übermittelt wird. In der Tabelle der Ausführungen beziehen sich diese Angaben auf die verknüpfte Version, deren Nummer und Name in der Zeile stehen. Mitwirkende einer übernommenen oder wiederhergestellten Ausgangsversion werden nicht kopiert. Der Versionsverlauf enthält kein pauschales KI-Symbol und keinen Link zu einer privaten Vera-Aufgabe. Das Änderungsprotokoll der App ist mit entsprechender Berechtigung weiterhin verfügbar. Veröffentlichen Sie einen von Vera mitbearbeiteten Entwurf selbst, ist es Ihre Veröffentlichung. Der Abschluss einer Vera-Anfrage erfordert oder erzwingt keine Veröffentlichung. ## Ursprüngliche Datendateien anhängen [#ursprüngliche-datendateien-anhängen] Sie können die ursprüngliche CSV-Datei (bis 512 MiB) oder Excel-Datei im Format `.xlsx` (bis 64 MiB) an eine Nachricht anhängen. Wählen Sie für ältere `.xls`-Dateien zuerst das Format `.xlsx`. Der Upload bleibt privat, bis Sie Vera beauftragen, ausgewählte Daten in die gemeinsame App zu importieren. Der Browser bildet die Dateizeilen nicht als Chat-Text nach. Sie können auch Dokumente, etwa eine PDF, anhängen und Vera bitten, sie zu lesen, zusammenzufassen oder Fragen dazu zu beantworten. Das Lesen eines Anhangs fügt ihn nicht zur Wissensdatenbank hinzu und ändert die App nicht. Bitten Sie ausdrücklich darum, wenn Informationen importiert oder gespeichert werden sollen. Ausstehende Änderungen an Seiten und Titeln werden gespeichert, bevor Vera damit arbeitet. Schlägt das Speichern fehl, bleibt Ihre Anfrage im Nachrichtenfeld. Ändert jemand eine gespeicherte Seite oder ein Layout, bleibt Ihre Bearbeitungskopie erhalten: Mit **Gespeicherte Kopie laden** übernehmen Sie die Änderungen, oder Sie ersetzen die gespeicherte Kopie ausdrücklich durch Ihre eigene. Ungespeicherte Aufgabenänderungen bleiben im Formular; Vera verwendet die gespeicherte Aufgabe, bis Sie Ihre Änderungen speichern. ## Fragen Sie nach Hilfe zu Ovidens [#fragen-sie-nach-hilfe-zu-ovidens] Sie können fragen: „Wie lade ich Daten hoch?“, „Wie veröffentliche ich diese App?“ oder „Was ist eine Kennzahlendimension?“. Vera kann in der gepflegten Benutzerdokumentation nachsehen und auf passende Anleitungen verlinken. Sie müssen dazu keine Hilfeseite in die Wissensdatenbank Ihrer App kopieren. ## Modell und Datenverarbeitung [#modell-und-datenverarbeitung] Vera verwendet die Anthropic-API direkt. Ein Workspace-Admin wählt ein Modell: Claude Haiku 5.5, Claude Sonnet 5.5 (Standard) oder Claude Opus 5.5. Jeder Chat, jedes KI-Widget und jede interne KI-Aufgabe verwendet dieses Workspace-Modell. Neue und fortgesetzte Anfragen verwenden die aktuelle Einstellung, auch in älteren Gesprächen. **Die KI-Verarbeitung ist nicht auf die EU beschränkt.** Ihre Anfrage, relevanter Gesprächskontext, Anhänge und Werkzeugergebnisse können den Anbieter erreichen. Einzelheiten finden Sie unter [KI-Transparenz](../infrastructure/ai-transparency.mdx). ## KI-Widgets sind schreibgeschützt [#ki-widgets-sind-schreibgeschützt] Ein [KI-Widget](../analytical-apps/skills.mdx) führt eine wiederverwendbare Aufgabe auf bestehender App-Konfiguration und veröffentlichten Daten aus. Es liefert eine Antwort, ohne Daten oder Konfiguration zu ändern, die Pipeline auszuführen, zu veröffentlichen oder einen Entwurf zu erstellen. Das gilt auch, wenn die Person die App bearbeiten darf. Eine Vorschau kann einen bestehenden Entwurfs-Skill lesen, ohne ihn zu ändern. Erfordert eine Widget-Anfrage eine Änderung, erklärt Vera kurz, dass sie diese nicht vornehmen kann. Widget-Antworten sind vom Gespräch im Vera-Bereich getrennt. ## Wenn Vera nicht fertig wird [#when-vera-cannot-finish] Vera antwortet kurz, was sie tun konnte oder wodurch die Anfrage beendet wurde. Fehlt eine abschließende Antwort, erscheint stattdessen eine kurze Erklärung. **Details** zeigt erfasste Werkzeugaufrufe, Gründe, Token-Verbrauch und Zeitpunkte. Frühere fehlgeschlagene Aufrufe bleiben sichtbar, auch wenn ein späterer Aufruf Erfolg hat und die Aufgabe abgeschlossen wird. Ein **Interner Fehler bei der KI-Anfrage** bedeutet, dass der Anbieter die von Vera vorbereitete Anfrage nicht annehmen konnte. Versuchen Sie es erneut und wenden Sie sich bei wiederholten Fehlern an den Ovidens-Support. Dies wird getrennt von einer nicht verfügbaren KI-Verbindung, fehlender Konfiguration oder einem ausgelasteten Anbieter gemeldet. **Details** bleibt verfügbar, auch wenn die Anfrage vor dem ersten Werkzeugaufruf fehlgeschlagen ist. Wurde die Token-Nutzung nicht erfasst, sagt der Dialog dies ausdrücklich; seine Zähler belegen dann nicht den vollständigen Verbrauch beim Anbieter. Nutzen Sie das normale Nachrichtenfeld, um Angaben zu ergänzen, eine Anfrage umzuformulieren oder erneut zu versuchen. Angeforderte Änderungen bleiben im Entwurf, bis Sie sie veröffentlichen oder verwerfen. Prüfen Sie diese Änderungen über die entsprechenden Bedienelemente der App. ### Wenn Ihre Verbindung abbricht [#wenn-ihre-verbindung-abbricht] Eine unterbrochene Verbindung hält Vera nicht auf: Die Anfrage läuft auf der Seite von Ovidens weiter. Kann das Panel Vera kurzzeitig nicht erreichen, zeigt es **Verbindung zu Vera unterbrochen - neuer Versuch läuft** und versucht es weiter. Ist Vera nach einer Weile immer noch nicht erreichbar, zeigt es **Vera ist nicht erreichbar. Der Lauf läuft möglicherweise noch.** und bietet die Schaltfläche **Erneut prüfen** an, die die Anfrage an der Stelle wieder aufnimmt, an der sie gerade ist. ### Wenn das Vera-Panel geschlossen ist [#wenn-das-vera-panel-geschlossen-ist] Im Editor einer analytischen App zeigt die Schaltfläche **Vera** einen kleinen Punkt, solange das Panel geschlossen ist: blau, während Vera arbeitet, gelb, wenn Vera auf Ihre Antwort wartet, und rot, wenn eine Anfrage geendet hat, ohne alles zu erledigen, worum Sie gebeten haben. Der rote Punkt bleibt, bis Sie das Panel öffnen. ## Vera kennt das System, nicht Ihren Anwendungsfall [#vera-kennt-das-system-nicht-ihren-anwendungsfall] Vera versteht Ovidens in- und auswendig: wie Pipelines, Indikatoren und die Visualisierung funktionieren und wie man sie baut. Was sie **nicht** automatisch kennt, ist Ihr Geschäft, Ihre Daten und was Sie tatsächlich zu erreichen versuchen. Geben Sie ihr diesen Kontext, und sie kann sehr schnell und sehr gut bauen; lassen Sie ihn weg, fragt sie nach fehlenden Angaben. Bevor Sie also eine Anfrage senden, fragen Sie sich, ob Sie Vera alles gesagt haben, was sie aus geschäftlicher Sicht benötigt: was die Zahlen bedeuten, welche Regeln gelten und wie ein gutes Ergebnis aussieht. Diesen Kontext direkt in Ihre Anfrage oder bei Bedarf in die [Wissensdatenbank](../analytical-apps/knowledge-base.mdx) zu schreiben, hilft Vera, das gewünschte Ergebnis zu erreichen. Und wie jede KI **kann Vera Fehler machen**. Ihre Änderungen landen im Entwurf der App, den Betrachter erst nach der Veröffentlichung sehen. Prüfen Sie ihre Arbeit also stets, bevor Sie sich darauf verlassen, besonders die Logik hinter einer wichtigen Zahl. ## Ein Hinweis zur KI-Nutzung [#ein-hinweis-zur-ki-nutzung] Die Arbeit mit Vera verbraucht **KI-Credits**, von denen Ihr Workspace ein monatliches Kontingent hat. Sie können auf der Seite [Administration](../administration.mdx#usage) sehen, wie viel Sie verbraucht haben. Wenn das monatliche Limit erreicht ist, pausieren KI-Funktionen, bis sich das Kontingent zu Beginn des nächsten Monats zurücksetzt, während der Rest der App normal weiterarbeitet. Vera fordert für wiederverwendbare Eingaben einen Prompt-Cache mit fünf Minuten Laufzeit an. Die Wiederverwendung hängt vom Anbieter, einem unveränderten Eingabepräfix und der Cache-Laufzeit ab. Der gespeicherte Gesprächsverlauf ist von diesem vorübergehenden Cache getrennt. Eine Verdichtung kann einen neuen Cache-Schreibvorgang und einen weiteren Modellaufruf erfordern. Ein kürzerer Kontext ist daher nicht immer günstiger. # Tastaturkürzel (/docs/de/tips/keyboard-shortcuts) Ovidens hat Tastaturkürzel an zwei Stellen: einige wenige, die **überall** in der App funktionieren, und einen umfassenderen Satz für die **Editoren**, wenn Sie etwas aufbauen. Jedes davon können Sie nach Ihren Wünschen ändern. **mod** steht für die Modifikatortaste Ihres Rechners: Command auf dem Mac und Strg unter Windows und Linux. ## Überall [#überall] Diese funktionieren in ganz Ovidens, auf welcher Seite Sie auch sind. | Kürzel | Wirkung | | ---------------------------------- | -------------------------------------------------------------------------- | | mod + K | Suche öffnen und zwischen Apps wechseln | | mod + B | Seitenleiste zwischen breiter Ansicht und schmaler Symbolleiste umschalten | | Umschalt + / | Hilfe zu den Tastaturkürzeln anzeigen | Die Suche bringt Sie direkt zu jeder App, jedem Indikator und jedem Knoten. Siehe [Suche](./search.mdx). Das Umschalten der Seitenleiste hat in einem App-Editor nichts zu tun, weil der Editor sie durch seine eigene Leiste über die volle Breite ersetzt. ## Im App-Editor [#im-app-editor] Der Rest gilt, während Sie eine App bearbeiten. ### Bereiche [#bereiche] Bereichsumschalter sind einzelne Tasten - sie lösen nur aus, wenn Sie **nicht** in ein Feld schreiben, kommen Ihrem Text also nie in die Quere. | Kürzel | Wirkung | | --------------------------------------------------- | ---------------------------------------------- | | P | Bereich **Datenpipeline** umschalten | | V | Bereich **Visualisierung** umschalten | | K | Bereich **Wissensdatenbank** umschalten | | mod + J | **Vera (KI)** umschalten | | mod + Umschalt + . | Aktuellen Bereich maximieren oder zurücksetzen | ### Bearbeiten und Navigieren [#bearbeiten-und-navigieren] | Kürzel | Wirkung | | ------------ | ------------------------------------------------------------------------------------ | | E | Editor aus der App öffnen, und aus dem Editor **zurück zur Ansicht** | | / | Die aktuelle Ansicht durchsuchen - die Pipeline-Arbeitsfläche oder die Liste daneben | ## Ein Kürzel ändern [#ein-kürzel-ändern] Öffnen Sie **Einstellungen** über Ihr Kontomenü und wählen Sie **Tastenkürzel**. Klicken Sie in einer Zeile auf die Tasten und drücken Sie die gewünschte Kombination. * Esc bricht ab und lässt das Kürzel unverändert. * Rücktaste entfernt das Kürzel, die Aktion hat dann gar keine Taste. * Die Schaltfläche **Zurücksetzen** neben einer geänderten Zeile stellt die ursprüngliche Taste wieder her, **Alle zurücksetzen** stellt alles auf einmal zurück. Ihre Kürzel werden in Ihrem Konto gespeichert und begleiten Sie daher in jeden Browser, auf jeden Rechner und in jeden Workspace, dem Sie angehören. Einige Kombinationen sind nicht verwendbar, weil Ihr Browser sie nimmt, bevor Ovidens sie überhaupt sieht - darunter mod + W, T, N, Q, L, R und die Zifferntasten. Ovidens sagt Ihnen das beim Drücken, statt ein Kürzel zuzulassen, das nie funktionieren würde. Wenn die gedrückte Kombination auf derselben Ansicht bereits für etwas anderes verwendet wird, fragt Ovidens, ob Sie sie verschieben möchten. Bei Ja bleibt die andere Aktion ohne Kürzel zurück, und das wird angezeigt. ## Gut zu wissen [#gut-zu-wissen] * Drücken Sie jederzeit Umschalt + /, um ein Hilfefenster mit den Kürzeln zu öffnen, die dort funktionieren, wo Sie gerade sind. Die vollständige Liste steht in den Einstellungen. * Kürzel aus einer einzelnen Taste werden ignoriert, während Sie in ein Textfeld, einen Editor oder eine Abfrage schreiben, und kommen so nie in die Quere. Das gilt auch für die wenigen Kürzel, die sonst beim Schreiben funktionieren: Geben Sie einem davon eine einzelne Taste, wirkt es in Textfeldern nicht mehr, und Ovidens weist beim Festlegen darauf hin. * Für Ausführen und für Veröffentlichen gibt es bewusst kein Kürzel: beides sind bewusste Schritte über die Schaltflächen **Ausführen** und **Veröffentlichen** in der Editorleiste. Ihre Änderungen werden laufend in Ihrem privaten Entwurf gespeichert, das Verlassen des Editors verliert also nie Arbeit. * mod + S bewirkt im Editor nichts. Ovidens fängt den Tastendruck nur ab, damit Ihr Browser nicht aus Gewohnheit sein eigenes Fenster "Seite speichern" öffnet. In einer Datentabelle speichert es Ihre bearbeiteten Zeilen. # Suche (/docs/de/tips/search) Die Suche ist der schnellste Weg, dorthin zu gelangen, wo Sie hinwollen. Anstatt sich durch Menüs zu klicken, tippen Sie ein paar Buchstaben und springen direkt zu dem, was Sie brauchen. Es gibt einige Suchfelder, jedes für eine andere Aufgabe. ## In Ihrem gesamten Workspace suchen [#in-ihrem-gesamten-workspace-suchen] Das Hauptsuchfeld bringt Sie zu allem in Ihrem Workspace: einer **App**, einem **Indikator** oder einem **Knoten** in einer Pipeline. Öffnen Sie es aus der linken Navigation (oder mit seinem Tastaturkürzel, siehe [Tastaturkürzel](./keyboard-shortcuts.mdx)), beginnen Sie zu tippen, und die Ergebnisse aktualisieren sich während der Eingabe, gruppiert danach, was sie sind. Wählen Sie ein Ergebnis, um direkt dorthin zu gelangen. ## Die Dokumentation durchsuchen [#die-dokumentation-durchsuchen] Diese Dokumentation hat ihr eigenes Suchfeld oben auf der Seite. Tippen Sie ein Wort, zum Beispiel "Pivot" oder "Formel", und es findet jede Seite, die es erwähnt, sodass Sie auf der richtigen Anleitung landen, ohne das Menü zu durchsuchen. ## Einen Knoten auf der Arbeitsfläche finden [#einen-knoten-auf-der-arbeitsfläche-finden] Eine große Pipeline kann viele Knoten enthalten, was es schwer machen kann, einen bestimmten zu erkennen. Die Arbeitsfläche hat genau dafür ihre eigene Knotensuche. Öffnen Sie sie, tippen Sie einen Teil des Namens, Typs oder der ID eines Knotens, und die passenden Knoten werden hervorgehoben. Sie können die Treffer durchschreiten, und die Arbeitsfläche bringt jeden ins Blickfeld, sodass selbst eine große Pipeline leicht zu navigieren bleibt. # Die Dokumentation mit einem KI-Assistenten nutzen (/docs/de/tips/use-the-docs-with-ai) Diese Dokumentation ist für Menschen geschrieben, aber für einen KI-Assistenten genauso nützlich. Jede Seite gibt es in einer Klartextform, die ein Assistent direkt lesen kann, und die ganze Website lässt sich in eine einzige Unterhaltung einfügen. Ob Sie ChatGPT, Claude oder einen anderen Assistenten verwenden, die Schritte sind dieselben. ## Eine Seite kopieren [#eine-seite-kopieren] Jede Anleitung hat oben, über dem Text, ein Menü **Seite kopieren**. Es bietet vier Aktionen: * **Als Markdown kopieren** legt die Seite als reines Markdown in Ihre Zwischenablage, fertig zum Einfügen in einen Chat. * **Als Markdown anzeigen** öffnet diese Klartextversion in einem neuen Tab, sodass Sie sie lesen oder speichern können. * **In ChatGPT öffnen** und **In Claude öffnen** starten eine neue Unterhaltung in diesem Assistenten, bei der die Adresse der Seite bereits im Prompt steht, sodass er die Anleitung liest, bevor Sie etwas fragen. ## Jede Seite hat eine Markdown-Adresse [#jede-seite-hat-eine-markdown-adresse] Die Klartextversion einer Seite liegt direkt neben der Seite selbst: Nehmen Sie die Adresse der Seite und hängen Sie `.md` an. Die Anleitung zu Datensätzen liegt zum Beispiel unter `/docs/de/analytical-apps/pipeline/datasets`, ihre Markdown-Version unter [`/docs/de/analytical-apps/pipeline/datasets.md`](/docs/de/analytical-apps/pipeline/datasets.md). Die Startseite ist die eine Ausnahme: Sie hat keinen eigenen Seitennamen, an den sich `.md` anhängen ließe, ihre Markdown-Version liegt daher unter [`/docs/de/index.md`](/docs/de/index.md) und auf Englisch unter [`/docs/index.md`](/docs/index.md). Ein Assistent, der Webadressen lesen kann, lässt sich direkt auf diese Datei verweisen und sieht denselben Text wie Sie, ohne die Menüs drumherum. ## Einem Assistenten die ganze Website geben [#einem-assistenten-die-ganze-website-geben] Zwei Adressen decken die gesamte Dokumentation auf einmal ab: * [`/docs/llms.txt`](/docs/llms.txt) ist das Verzeichnis: jede Seite mit Titel und Adresse als kurze Liste. Fügen Sie es in eine Unterhaltung ein, damit der Assistent weiß, was es gibt, und nach den Seiten fragen kann, die er braucht. * [`/docs/llms-full.txt`](/docs/llms-full.txt) ist jede Seite in einer Datei. Fügen Sie sie ein oder verweisen Sie den Assistenten darauf, wenn er aus der vollständigen Dokumentation antworten soll statt aus einer einzelnen Anleitung. ## Was Sie einfügen und was Sie fragen [#was-sie-einfügen-und-was-sie-fragen] Beginnen Sie mit der Seite, die zu Ihrer Frage passt. Wenn Sie einen Import einrichten, kopieren Sie die Anleitung zu Datensätzen; wenn Sie eine Transformation schreiben, die Anleitung zu SQL-Knoten. Fügen Sie sie ein und fragen Sie in eigenen Worten: "Welche Spaltentypen kann ich verwenden?" oder "Warum würde mein Import abgelehnt?". Bei einer Frage, die das ganze Produkt betrifft, verwenden Sie stattdessen das Verzeichnis oder die vollständige Datei und nennen Sie die Funktion, an der Sie arbeiten, damit der Assistent den richtigen Abschnitt findet. Nicht sicher, welche Anleitung Sie brauchen? Die Dokumentationssuche findet jede Seite über ein Wort, das sie enthält; siehe [Suche](./search.mdx).