# Home

**Kawa is a unified, collaborative analytics workspace** that takes you **from source to app** — connect data, model in sheets and formulas, explore with views and charts, automate actions, and publish dashboards and apps in one place with a shared source of truth, so teams ship faster.

***

## How Kawa works

#### Run enterprise operations with AI — securely and at scale

KAWA AI connects enterprise systems, teams, agents, and internal apps in a single execution layer. It helps organizations coordinate workflows, enforce business logic, manage approvals, and execute actions with full governance, traceability, and control.

#### What you can do

* **Connect:** ERP, CRM, data warehouses, APIs, trading systems, and internal tools.
* **Orchestrate:** design, automate, and execute workflows across systems and teams.
* **Apply business logic:** enforce rules, policies, and conditions at every step.
* **Run AI agents:** enable agents to plan, decide, and act within governed processes.
* **Validate and approve:** add routing, escalation, and human oversight where needed.
* **Audit and govern:** keep logs, traceability, security, access, and compliance built in.
* **Operate at scale:** support human teams, AI agents, and internal apps from one secure execution layer.

<figure><img src="/files/PGWQLzl5L0cfpDKNMJEP" alt=""><figcaption></figcaption></figure>

***

#### Start Your First Project Now

Ready to dive in? Start here:

[Getting started](/00_01_getting_started)

#### Learn by watching

Short videos to get productive fast.

[Video tutorials](https://docs.kawa.ai/video-tutorials/english-tutorials)

***

## What's Inside

| Section                                             | Description                                                                                        |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| [Data Integration](/01_00_data_integration)         | Connect to internal or external systems to feed data in KAWA                                       |
| [Data Modeling](/02_00_modeling)                    | Create your sheets to share powerful data models with your teams                                   |
| [Computation](/03_00_computations)                  | Apply formulas, lookups, and mappings to transform and enrich your data with ease                  |
| [Visualization](/04_00_visualization)               | Create charts, grids and pivot tables to explore your data in a few clicks                         |
| [Reporting and Publishing](/05_00_publishing)       | Your data models are ready? Let's publish them with Dashboards or even Data applications           |
| [AI Integration](/06_00_ai_integration)             | Chat with your data in natural language and create a team of agents to help you with your tasks    |
| [Workflows](/07_00_workflows)                       | Design, run, and monitor your own workflows, powered by your data                                  |
| [Administration and Sharing](/08_00_administration) | Learn more about data security, sharing and permissions                                            |
| [Python Integration](/09_00_python_integration)     | Run Python code to automate tasks, process data, or build custom logic into your workflows         |
| [Architecture](/10_00_architecture)                 | Discover how Kawa manages storage, caching, and native connections behind the scenes               |
| [Exploitation](/11_00_exploitation)                 | Back up your databases and create monitoring views to track user activity and script execution     |
| [Release Notes](/12_00_release_notes)               | Stay up to date with KAWA releases: new features and improvements                                  |
| [Terminology](/13_00_terminology)                   | Learn the language of Kawa: clear definitions of key terms and how they relate across the product. |

***

## Useful Links

* [Python SDK](https://github.com/kawa-analytics/kywy-documentation)

*Get Python running on Kawa in minutes—no hassle.*

* [Install the KAWA Platform](https://github.com/kawa-analytics/kawa-docker-install)

*A step-by-step guide to installation and setup.*

* [KAWA official website](https://www.kawa.ai)

*Explore all features on the official site.*

***

<div align="center"><img src="/files/4wekBNln4oLwLt9oRstv" alt="" width="120"></div>


# Getting started

KAWA contains 5 main sections:

* Connect your data in the **Data Source** section
* Build your models and explore in the **Sheet** section
* Automate actions and processes in the **Workflows** section
* Publish in the **Dashboard** and **Applications** sections

## 1. Create your first Data Source

Loading data into KAWA is the first step, and it is done via the Data Source section.

> If the data that you want to explore is already loaded in KAWA, go to the next paragraph.

Let's create one from a simple CSV file. This example will use a sample CSV file you can download - but feel free to bring in your own.

{% file src="/files/Ei8auZL8Qfx5c7Ir8ZsW" %}

* *Step 1:* Go to the Data Source section (Top icon in the left bar), and click on the **(+ Data Source)** button. Choose: **(Upload a file)**.

<div data-with-frame="true"><img src="/files/0BqCHmGjcr7C70AQ9tZb" alt=""></div>

* *Step 2:* Drag and Drop your file in the drop zone and once your data is displayed, click on **(Next)**

<div data-with-frame="true"><img src="/files/CroOpn1BdwHMSifi9X3N" alt=""></div>

* *Step 3:* Click directly on **(Save)**. In the form, choose a good name for your new Data Source, then click on **(Save and run)**.

<div data-with-frame="true"><img src="/files/133d3yeR3FyVnuilycyz" alt=""></div>

Your first Data Source has been created. The system will take a few seconds to process the entire file. Once it is done, you will see a green **SUCCESS** badge that indicates that the data has been successfully loaded.

Before going to the next paragraph, go ahead and click on the **Data** tab, next to overview. There, you will see the profile of your uploaded dataset.

<div data-with-frame="true"><img src="/files/KZd44jbxUibZUcgMJgTj" alt=""></div>

You can click on the various columns to get detailed information about them.

## 2. Create your first Sheet

There are two ways to create a Sheet on top of a Data Source.

* From the Data Source section, click on your Data Source and click on the **(Explore)** button at the top. You will be redirected it the new sheet.
* From the Sheet section (Second icon from the top in the left bar), click on the **(+ Sheet)** button, and select your Data Source. Click on **(Next)**, pick a name for your Sheet and **(Create)**.

A Sheet works like an Excel Workbook. When created, it has only one tab, which is the default Grid. This is the ideal tool to explore your data in KAWA.

### 2.1 Manipulate your data in the grid

Feel free to explore the main actions by using the buttons on the top right of the Grid.

<div data-with-frame="true"><img src="/files/L0AsDywnvYqsgePN62cR" alt=""></div>

A good starting point is Grouping. Click on the **(Group)** button (on the top right), and on the **(+)** button. Choose the *State* field (Or any other dimension of your dataset), and click on **(Apply)**.

<div data-with-frame="true"><img src="/files/QLlhm8Us2n30PZbaRXnw" alt=""></div>

Feel free to explore: Sorting, Hide field, Filtering. All the options of the grid view are documented in details in the [Grid View section](/04_00_visualization/04_01_grid_views). There is a special section about [Filtering](/04_00_visualization/04_04_filtering).

### 2.2 Create a new simple formula

The **(Enrich Data)** button is the way to add data in your sheet to complement the one coming from your data source. It lets you join with other data sources (Lookup column), create ad-hoc mapping tables, create your own formulas and even connect to Python scripts.

Let's create a simple formula to compute the unit profit by dividing the *Profit* by the *Quantity*.

> This is based on the example file from the first paragraph, feel free to adapt to your own business logic.

* *Step 1:* Click on the **(Enrich Data)** button, then **(Formula)**.

<div data-with-frame="true"><img src="/files/q5Iwm3Kb7qP51Ul3B9Th" alt=""></div>

* *Step 2:* If AI is enabled on your platform, you can just type: "Calculate unit profit". If it is not, just type `Profit / Quantity` (Do not copy paste, type in the formula). KAWA will assist you on the way.
* *Step 3:* Type in a name for your formula and **(Create)**. The new column will be added in your grid, at the last position.

<div data-with-frame="true"><img src="/files/eUoSILVIXwYKo3Tyxp64" alt=""></div>

### 2.3 Create a first simple chart

In order to explore your data in a more visual way, let's now create a chart. In our example, let's have a look at a profit trend over the years.

* *Step 1:* On the top left, click on **(Create View)**, and pick **(Graph/Chart)**.

<div data-with-frame="true"><img src="/files/BvXmYhi0ed62lwNgwmnN" alt=""></div>

* *Step 2:* In the configuration panel, search for the *Profit* column and drag and drop it in the Series Zone.
* *Step 3:* In the configuration panel, search for the *Order Date* column and drag and drop it in the Group by Zone.
* *Step 4:* Next to the Order Date in the Group By Zone, click on the small Calendar and pick: **(Year/Month)**. This will sample the data by month.
* *Step 5:* Click on the line chart icon.

<div data-with-frame="true"><img src="/files/9KTg7xcjrsMmUsScaTBM" alt=""></div>

Please refer to the complete [Chart Documentation](/04_00_visualization/04_02_chart_views) to learn how to customize your charts.

### 2.4 Create a Pivot Table

Pivot tables in KAWA work like in any spreadsheet software, except that they can scale on billions of rows.

* *Step 1:* On the top left, click on **(Create View)**, and pick **(Pivot Table)**.

<div data-with-frame="true"><img src="/files/VB5RLQKbqnX2HUKFfglS" alt=""></div>

* *Step 2:* In the Rows section, add the *State* column. This can be done either by drag and drop or by clicking on the **(+)** and selecting the State column.
* *Step 3:* In the Columns section, add the *Segment* column.
* *Step 4:* In the Values section, add the *Sales* column.

<div data-with-frame="true"><img src="/files/o363mNYkmWipzZZraWjt" alt=""></div>

To know what is possible with the pivot tables, go to the [Pivot Documentation](/04_00_visualization/04_03_pivot_table_views).

## 3. Put it together in a Dashboard

Go to the **Dashboard** section (Third icon from the top in the left bar). Click on **(+ Dashboard)**, then **(Dashboard)**.

You will be redirected to a new empty dashboard. Pick your sheet from the **(Add Widgets)** panel and add the view you created in the previous steps.

Widgets can then be arranged in the layout of your choice. Dashboard are a powerful tool to bring data from all your sheets in one place.

<div data-with-frame="true"><img src="/files/jkSx2oSaXylQF7hm3VT8" alt=""></div>

## 4. What's next?

Those three paragraphs: **Data Source**, **Sheet** and **Dashboard** are giving a good overview of the central structure of the KAWA platform. It is the base on which you can start building and sharing your data applications.

| Objective                                                    | Link                                                     |
| ------------------------------------------------------------ | -------------------------------------------------------- |
| Connect your Python libraries to KAWA                        | [Python integration](/09_00_python_integration)          |
| Build and share data applications                            | [Publishing](/05_00_publishing)                          |
| Connect KAWA to your Jupyter notebook to load and query data | [Python SDK](/09_00_python_integration/09_01_python_sdk) |
| Automate your workflows                                      | [Workflows](/07_00_workflows)                            |
| Use AI to chat with your data                                | [AI Integration](/06_00_ai_integration)                  |
| Build and share data models                                  | [Data Modeling](/02_00_modeling)                         |
| Build impactful visualizations                               | [Visualizations](/04_00_visualization)                   |


# Data integration

See definitions in [Terminology](/13_00_terminology#data-source) section.There are several ways to connect KAWA with your data. KAWA supports: Importing files, Connecting to external systems such as CRMs, databases, APIS etc, Linking to existing tables in the main warehouse, Importing unstructured data, Importing data from Python scripts, creating a data source from scratch, importing data from Knowledge.

## 1. Data Source columns

In KAWA, a Data Source corresponds to one data table. Each Data Source has a list of columns or`Indicators`. See definitions in [Terminology](/13_00_terminology#indicator) section.

### 1.1 Column types

Each Data Source column has a given type:

| Type      | Example             | Comment                                                                                                                                                |
| --------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| date      | 2025-06-25          | Dates do not have any timezone                                                                                                                         |
| date time | 2025-06-25 21:12:12 | Date times have a timezone and the precision depends on the settings of the KAWA instance. By default, date times are precise down to the millisecond. |
| text      | 'Wayne'             |                                                                                                                                                        |
| boolean   | `true` or `false`   |                                                                                                                                                        |
| integer   | 1, 2, 0, -3         |                                                                                                                                                        |
| decimal   | -0.3, 45.56         |                                                                                                                                                        |

KAWA also supports lists of texts and list of numbers.

### 1.2 Primary keys

Every Data Source must define one primary key. A primary key is a column — or a fixed combination of columns — whose values uniquely identify each row in the dataset.

Let's take the example of a Data Source with some market data.

| 🔑 Symbol | 🔑 Date    | Price |
| --------- | ---------- | ----- |
| WAYNE     | 2025-06-25 | 123.4 |
| WAYNE     | 2025-06-26 | 123.5 |
| WAYNE     | 2025-06-27 | 123.6 |
| WONKA     | 2025-06-25 | 234.5 |
| WONKA     | 2025-06-26 | 234.6 |
| WONKA     | 2025-06-27 | 234.7 |

It has three columns:

* **Symbol:** a text column, first primary key.
* **Date:** a date column, second primary key.
* **Price:** a decimal column

Each row of the dataset is identified by the value of the symbol column and the date indcolumn. There cannot be any duplicate pairs (Symbol, Date).

### 1.3 Column definitions in the KAWA GUI

In the KAWA GUI, the overview tab of each Data Source gives information about its structure.

<div data-with-frame="true"><img src="/files/PGS8ozwrE5bJRgbsyWGm" alt=""></div>

On the right part, you can see all the columns of the Data Source, with an icon representing the type, and a red key to represent whether or not an column is a primary key. Here, there is composite primary key: Symbol and Date.

## 2. Data profile and Data preparation

In the GUI, under the **Data** tab of any Data Source, you can access global information such as the total number of rows, value distributions for each column, and other dataset-wide statistics.

<div data-with-frame="true"><img src="/files/8bjyWJg2WlULEXTQBS65" alt=""></div>

*Above, the data profile for the Market Data set.*

Each column header give information about that particular column. The right panel gives contextual statistics. Clicking on any column header will move the context to that particular column.

<div data-with-frame="true"><img src="/files/14kmtSRlZI7suBUALio5" alt=""></div>

*Here for example, after clicking on the Price column, we get statistics about it. We can also add some computation to sanitize the values of the column.*

## 3. Configure the import type

This is valid for most of the Data Sources. KAWA supports import type configuration for CSV import, Connecting to an external system, Loading with Python. Three options are available:

| Option              | Description                                                                                                                                                                                                                 |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reset Before Insert | Each time the import will run, the data in KAWA will be entirely replaced with the content of the file.                                                                                                                     |
| Incremental         | The data from the incoming file will be appended to the data that was already imported. Based on the primary key, if some rows that were already present in KAWA are present in the file, then the values will be replaced. |
| Snapshot            | This mode will append all the content of the file to the existing data. Two additional columns will be created: `Snapshot Id` and `Snapshot DateTime`.                                                                      |

Please refer to the three examples below:

### 3.1 Reset Before Insert

**Initial import:**

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 23       |
| WONKA    | 12       |

Data in KAWA after initial import:

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 23       |
| WONKA    | 12       |

> The data will be copied in KAWA after the initial import, exactly as is.

**Second import:**

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 25       |
| STARK    | 26       |

Data in KAWA after second import:

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 25       |
| STARK    | 26       |

In the Reset Before Insert mode, the data is deleted in KAWA before importing the new position. As a result, KAWA will contain exactly a copy of the second import. My position on WONKA will be removed and a new position on STARK will be added.

### 3.2 Incremental

**Initial import:**

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 23       |
| WONKA    | 12       |

Data in KAWA after initial import:

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 23       |
| WONKA    | 12       |

> The data will be copied in KAWA after the initial import, exactly as is.

**Second import:**

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 25       |
| STARK    | 26       |

Data in KAWA after second import:

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 25       |
| WONKA    | 12       |
| STARK    | 26       |

In the incremental mode, the content of the second import will be appended to the existing data. This explains why the STARK position is added, and why the WONKA position is not affected. Because the WAYNE stock was already present, its value will be updated (The initial 23 will be replaced with a 25).

### 3.3 Snapshot

**Initial import:**

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 23       |
| WONKA    | 12       |

Data in KAWA after initial import:

| 🔑 Stock | Position | 🔑 Snapshot id | 🔑 Snapshot date time |
| -------- | -------- | -------------- | --------------------- |
| WAYNE    | 23       | 1              | 2025-25-06 12:34:56   |
| WONKA    | 12       | 1              | 2025-25-06 12:34:56   |

> Two additional columns will be added: snapshot id and snapshot date time, both of which become part of the composite primary key alongside the existing key columns. For the first import, because this is the initial snapshot, every row will receive a snapshot id of 1 and a snapshot date time set to the moment of import (e.g. 2025-06-25 12:34:56).

**Second import:**

| 🔑 Stock | Position |
| -------- | -------- |
| WAYNE    | 23       |
| WONKA    | 12       |

Data in KAWA after second import:

| 🔑 Stock | Position | 🔑 Snapshot id | 🔑 Snapshot date time |
| -------- | -------- | -------------- | --------------------- |
| WAYNE    | 23       | 1              | 2025-25-06 12:34:56   |
| WONKA    | 12       | 1              | 2025-25-06 12:34:56   |
| WAYNE    | 23       | 2              | 2025-27-06 12:58:13   |
| WONKA    | 12       | 2              | 2025-27-06 12:58:13   |

> Subsequent imports will increment the snapshot id (to 2, 3, …) and record a new snapshot date time, allowing each import to be stored as a distinct, non-destructive snapshot in the same table.


# CSV import

This is the most straightforward way to import data in KAWA.

## 1. Importing a new CSV file

In order to import a new CSV file, from the **Data Source** section, click on **(+ Data Source)** then **(Upload a File)**.

### 1.1 Step 1 of 2: Select the data to import

In the first step, use the file picker on the right to drag and drop a file or select a file from your system.

The following formats are accepted:

* .csv
* .csv.gz
* .csv.zip

> It is recommended to use compressed csv formats when the volume of data exceeds 100MB.

### 1.2 Configure the CSV import parameters

Once the file has been loaded on KAWA and a preview has been generated, use the right pane to configure the import.

> Most of the time, no additional configuration will be required as KAWA tries to auto detect all the parameters.

Here are the available parameters:

| Parameter           | Description                                                                                                                                                                                               |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Column Delimiter    | Will define the delimiter used to separate columns. In the US or in the UK, it is often the comma. In France the semicolon is used.                                                                       |
| Quote Character     | In a CSVfile, the quote character (usually a double quote ") is used to enclose field values that contain special characters such as: separators, new lines.                                              |
| Escape Character    | Is used to escape the Quote characters withing the quoted values. For example if the quote character is `"` and the escape character is `\`: The following text is a valid value: `"She said: \"Hello\""` |
| Decimal Separator   | Delimits the whole number part from the fractional part in a numeric value.                                                                                                                               |
| Thousands Separator | The thousands separator divides large numbers into groups of three digits to improve readability.                                                                                                         |
| Date Format         | Specifies the format of the dates                                                                                                                                                                         |
| Timestamp Format    | Specifies the format of the date times                                                                                                                                                                    |
| Encoding            | The default is UTF-8. If the CSV file comes from a Windows system and includes special characters like é or à, ensure the appropriate encoding is selected to display the characters correctly.           |
| Time Zone           | Will assume that all date times without explicit Time Zone will be in the configured Time Zone.                                                                                                           |
| Header Row          | Specifies the position of the header row in the CSV file. For example, a value of 1 indicates that the header is on the first line, 2 means it's on the second line, and so on.                           |


# Connect external system

KAWA allows you to connect to an external system. You may choose your connector from the existing list.

<div data-with-frame="true"><img src="/files/0RjkFUON2M5peCLrySoW" alt=""></div>

> Your connection details will be encrypted in KAWA’s database and will be decrypted only to import data from your bases when an ETL runs (Manual trigger on the GUI or according to your schedule). It is NOT accessible to KAWA support team.

## 1. Connecting with Google Sheet

### 1.1 Configure the provider on KAWA

By default, KAWA comes in with a way to load data from public Google Sheets.

<div data-with-frame="true"><img src="/files/Co94JWsVovTdWP2YY987" alt=""></div>

If you wish to connect to restricted sheets, follow this guide.

In the data source section:

* Click on “+ Data source” button and choose: “Connect to an external system”
* Then, click on “+ Create new connection” button next to the data import section
* Pick the **Google Sheet** provider

### 1.2 Configure your service account JSON key

<div data-with-frame="true"><img src="/files/qSgXUeIOSvpMYF4Q3Urr" alt=""></div>

The JSON key must be similar to this:

<div data-with-frame="true"><img src="/files/0PIzCxHig0BafNbdgH49" alt=""></div>

Your service account key can be found on your Google Cloud Platform console, in the IAM section.

<div data-with-frame="true"><img src="/files/UqLqF7Yj9dBiHeBO8Cj1" alt=""></div>

Please refer to this section for help creating a service account: <https://cloud.google.com/iam/docs/service-accounts-create>

> While keeping your sheets protected, share them with the service account whose key you’ve used.

<div data-with-frame="true"><img src="/files/AoUKtGskSxzurYYeiqTM" alt=""></div>

Click on "Test Connection" then "Create".

<div data-with-frame="true"><img src="/files/44YsfZZyjZXnh3q4Ttgf" alt=""></div>

In the Select a connection field you’ll see your new Google Sheet connector. Paste your spreadsheet link into "Sheet URL" and enter the sheet’s name in "Tab name". Click "Preview data" to check the first rows and make sure it loaded correctly. Choose your Feed Type (Reset before insert, Incremental, or Snapshot). When you’re ready, click "Next" and complete creating the new data source.

## 2. Connecting with Airtable (personal Access token)

### 2.1 Create a personal access token to your airtable data

Follow this link > <https://airtable.com/create/tokens>

* *Step 1:* Click on “Create new Token” from the “Personal access tokens” section

<div data-with-frame="true"><img src="/files/tfz5dwY8DbDX3WZiPEtY" alt=""></div>

* *Step 2:* Configure your access token as follows

<div data-with-frame="true"><img src="/files/F3a3uezutOVFZ3vUFfKn" alt=""></div>

* *Step 3:* Copy your token

<div data-with-frame="true"><img src="/files/U3ihOxlloerTHGcLELfk" alt=""></div>

### 2.2 Configure the provider on KAWA

In the data source section:

* Click on “+ Data source” button and choose: “Connect to an external system”
* Then, click on “+ Create new connection” button next to the data import section
* Pick the **Airtable** provider

Fill in the following form:

<div data-with-frame="true"><img src="/files/aGqTskpjKVtMTWgfPsEN" alt=""></div>

Click on "Test Connection" then "Create".

<div data-with-frame="true"><img src="/files/GuNPrk3PFmGwcMXpLPIr" alt=""></div>

### 2.3 Import data

In order to import data, select the provider that you created in the previous step.Copy and paste the URL of your table in the first field and the Tab Name in the second one:

<div data-with-frame="true"><img src="/files/r9QmgiaPAJGrDhqI12ch" alt=""></div>

<div data-with-frame="true"><img src="/files/UsxDwbPiRnWRHs0Uv5FK" alt=""></div>

Click on "Preview Data", then "Next".

## 3. Connecting to Oracle DB (via JDBC)

### 3.1 Configure the provider on KAWA

In the data source section:

* Click on “+ Data source” button and choose: “Connect to an external system”
* Then, click on “+ Create new connection ” button next to the data import section
* Pick the **JDBC Custom** provider

Enter the required connection details for your Oracle database:

* Fill in the name of your database
* Fill in the JDBC URL: jdbc:oracle:thin:user/\*\*\*\*\*\*@hostname:port:SID

<div data-with-frame="true"><img src="/files/iQIOubK1YVtTgAAnaIZ8" alt=""></div>

> Here is a documentation about how to build the JDBC URL: [https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/index.html#:\~:text=Examples%3A,mydbhost1%2Cmydbhost2%3A1521%2Fmydbservice](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/index.html)

<div data-with-frame="true"><img src="/files/LmZ4p2qrrGru00PeDAXE" alt=""></div>

Click on "Test Connection" then "Create".

You can import data either by picking a database and a table or by writing your own query.

<div data-with-frame="true"><img src="/files/owUljtbkRDly9LPZ7YFw" alt=""></div>

## 4. Connecting with PostgreSQL

### 4.1 Create a PostegreSQL user

We strongly advise creating a dedicated user for KAWA to enhance tracking of usage and performance metrics with appropriate permissions. To set up this user, please refer to the following resources:

#### a. Creating a User Account:

Follow the instructions provided in the PostgreSQL documentation at this link: [Create User in PostgreSQL](https://www.postgresql.org/docs/current/role-membership.html).

#### b. Assigning Appropriate Roles:

Grant the necessary roles to the user to enable querying of your analytics data. Detailed guidance can be found here: [Role Membership in PostgreSQL](https://www.postgresql.org/docs/current/role-membership.html).

### 4.2 Configure the provider on KAWA

In the data source section:

* Click on “+ Data source” button and choose: “Connect to an external system”
* Then, click on “+ Create new connection ” button next to the data import section
* Pick the **PostegreSQL** provider

Enter the required connection details for your PostgreSQL database:

* Hostname or IP address of the PostgreSQL server.
* Port number (default is 5432).
* Username and password for database access.
* Database name (the name of the PostgreSQL database you want to connect to).

<div data-with-frame="true"><img src="/files/LPdD9lQdHaLLuNdJvpJR" alt=""></div>

Click on "Test Connection" then "Create".

## 5. Connecting with Snowflake

### 5.1 Configure the provider on KAWA

In the data source section:

* Click on “+ Data source” button and choose: “Connect to an external system”
* Then, click on “+ Create new connection ” button next to the data import section
* Pick the **Snowflake** provider

Enter the required connection details for your Snow Flake account:

* Host: The host is formed as follows: `<account_identifier>.snowflakecomputing.com`. Your url can be found on your snowflake account page:

<div data-with-frame="true"><img src="/files/gz72DRAGmUSIpN1gZTLZ" alt=""></div>

* Port: The port is always 443
* Database: The database you want to connect to
* Username/Password: Your credentials
* URL param: can be left empty.
* Please keep USE SSL and USE KERBEROS turned off.
* You can turn on “Data Provider is restricted” If you want only privileged users to access this data provider.

Here is how a valid configuration looks like:

<div data-with-frame="true"><img src="/files/xevgsNezMO8kNS2XKcoE" alt=""></div>

Click on "Test Connection" then "Create".

## 6. Connecting with Google BigQuery

### 6.1 Create a BigQuery service account

* *Step 1:* Create a service account in GCP by going to [IAM > Service accounts](https://console.cloud.google.com/iam-admin/serviceaccounts).
* *Step 2:* Select + Create Service Account from the top of the page.
* *Step 3:* When prompted you will need to add four IAM roles below:
* `BigQuery User`
* `BigQuery Data Viewer`
* `BigQuery Job User`
* `BigQuery Metadata Viewer`
* *Step 4:* Create JSON key

Your service account is now created. Click into the service account and select the `Keys` tab.

Click `Add Key` and make sure you create a JSON key (not a P12 key). The key will be automatically downloaded to your computer.

> Refer to this section for more help creating a service account: <https://cloud.google.com/iam/docs/service-accounts-create>.

### 6.2 Configure the provider on KAWA

In the data source section:

* Click on “+ Data source” button and choose: “Connect to an external system”
* Then, click on “+ Create new connection ” button next to the data import section
* Pick the **SBigQuery** provider

Enter the required connection details for your BigQuery database:

* Data provider name.
* JSON Key: Open the file downloaded on Step 4 and copy paste the content.

<div data-with-frame="true"><img src="/files/PhI5wbRiRx1CMrxKiLvA" alt=""></div>

Click on "Test Connection" then "Create" .


# Manual edit

This feature lets you manually edit data from the GUI. (This feature is under active development)

## 1. Data edition

KAWA has three main mechanisms for manual data edits.

* Direct edits (Like in Excel or Google Sheet)
* Manual patching
* Via mapping tables

All edits will be stored on the original tables and *will propagate in the entire KAWA application, for all users.*

> KAWA will not write in `Live Connect` type datasources. Only in table that were created through the platform.

> Edit permissions are handled on the data source level and provide a flexible system to control who can edit what in each data source.

### 1.1 Direct edits (Excel like)

Direct edits can only be done in specific data sources: `Create from Scratch`.

They behave like an Excel sheet, meaning that you can:

* Add rows
* Add columns
* Remove rows
* Edit cells
* Copy ranges of cells

**The main differences with Excel are:**

* Each column of the sheet is strongly typed (`text`, `integer`, `decimal`, `date`, `date time` or `boolean`). Once the type of a column is set, it cannot be changed.
* Columns cannot be removed (but can be hidden)

#### a. Create an editable datasource

In order to create an editable data source, navigate to the DataSource section, (+ Data source), Create from scratch.

<div data-with-frame="true"><img src="/files/jG2AwPDd5BoTahpcBB5f" alt=""></div>

And then, click on explore to go to the associated sheet.

<div data-with-frame="true"><img src="/files/1Bu5Gf6ofxcpwpK0gfYc" alt=""></div>

#### b. Edit data from the sheet section

**Sheets** based on an editable data source can be edited from the sheet page by entering edit mode. Note that grouping is not available for editable sheets.

To start editing, click the **Edit data** button in the action bar. This opens an edit session for the sheet.

<div data-with-frame="true"><figure><img src="/files/I3OtYVojmLG4EEhpodxL" alt=""><figcaption></figcaption></figure></div>

In edit mode, the grid always shows all the editable columns by default. Computed columns remain visible but cannot be edited.

You can:

* Add new rows (Add row button in the action bar on the top)
* Add new columns (Add column button in the action bar on the top)
* Remove entire rows by right clicking on any cell and choosing: *Remove row*

To edit a cell, either double-click the cell and enter a value, or use the arrows on the row you want to edit.

<div data-with-frame="true"><figure><img src="/files/ei5235RUThUFMf74GNiw" alt=""><figcaption></figcaption></figure></div>

Changes made during the edit session are not sent to the backend after each action. Instead, KAWA keeps them as pending edits and sends them only when the user clicks **Save**. If the user clicks **Cancel**, all pending edits are discarded. This is useful when users need to make several updates in one pass, because it avoids reloading or recomputing the sheet after every individual change.

While edit mode is active, standard grid controls are unavailable, including **Enrich data**, **Add fields**, **Color**, **Hide fields**, **Sort**, **Top**, and **Filters**.

#### c. Edit data from dashboards or applications

The editable views can be added to your dashboards or your applications to provide more interactivity.

**In a dashboard**, simply pick the editable view in the widget selector:

<div data-with-frame="true"><img src="/files/Fs8diMd2jLgTFHwsjGNg" alt=""></div>

*The editable widget has all the options of the editable view, directly in the dashboard*.

**From within an application**:

Make sure to have the "Edit data in grid" toggle enabled.

<div data-with-frame="true"><img src="/files/FgUhvxN5x7xuF16sBU00" alt=""></div>

*Users of the application will then be able to edit directly from the application page*.

### 1.2 Data patching

When your datasource is not created only for edits, manual edits are still possible. Those edits behave like patches that can be easily reverted.

**Example**: The DEALS datasource is connected to my trading system and is refreshed every morning. If a user detects an issue in the table during the day (for example: name of the counterparty is wrong), they can edit that value in KAWA - the edit will propagate in all the connected analytics. Once the data is fixed in the original system, that edit can then be removed.

<div data-with-frame="true"><img src="/files/TVHLE9lR6JjjI9VKDUkO" alt=""></div>

*On the image above, the Segment has been patched (yellow border). If you hover your mouse over the patch, you will see that you can remove that patch*.

> When removing a patch, the value will be reverted to the latest value that was synchronized from your remote system.

*Example:*

1. My deals are synchronized this morning at 9AM in my deals data source.
2. I detect a mistake at 10AM: Deal 1 has the wrong counterparty - *Wonka Corp* instead of *Wayne Industries*.
3. I manually edit my data through my editable view and correct the counterparty. I also report this issue to my data team.
4. The next day at 9AM, my deals are synchronized again and the data team fixed the issue. I can now safely remove my patch and the corrected value will be taken in account.

### 1.3 Mappings

Mappings are a way to edit data directly at the sheet level. Data will not be written in the underlying data source.

#### a. Create a new mapping (also called: manual input columns)

From the sheet section (Grid, but also charts and pivots), click on the Manual Input item in the (Enrich data) menu.

<div data-with-frame="true"><img src="/files/siiCAloaKPrRzL1UjF9l" alt=""></div>

Then, in the configuration modal, choose:

* The column to map.
* The type of the mapping (Text or Numeric)
* The name of the new column
* The bottom of the screen lets you define the behavior to adopt when the value is not mapped.

<div data-with-frame="true"><img src="/files/hlp4gRt5ydRqftC9XZul" alt=""></div>

More details about mappings are available here: [Mapping columns](/03_00_computations/03_03_mapping_columns)

#### b. Edit mapping column values directly in the Grid

KAWA lets you edit mapping values directly in the grid.

> For this to work, you must have AT LEAST one defined primary key that is not the `automatic_uuid` column. This is very important for *LIVE CONNECT* data sources.

If your data source is *LIVE CONNECT*, go to the datasource model, and select the correct primary keys for your data source. Make sure that their combination yields a unique definition of each record in your table.

<div data-with-frame="true"><img src="/files/GnS6u5UkMCeqDk6ue2vV" alt=""></div>

In order to edit data directly from the grid, open a grid view (It can be in a dashboard widget, in an application or in a sheet). You can then double click on any cell (on a mapping column) to edit its value.

<div data-with-frame="true"><img src="/files/ij6eDHuGZFfkW7qHtf9d" alt=""></div>

> If the mapping is not at the level of the primary key (in the above example, it is a comment per state), each edit will result in multiple impacted cells. If I input a comment for the state of California, all the rows for that state will receive the same value.

## 2. Data edition permissions

### 2.1 Set edit permissions at the data source level

KAWA provides a profile based mechanism to restrict edition permissions on data sources.

In order to configure the data edition permissions, navigate to the data source you wish to configure and click on: (Edition Rules).

<div data-with-frame="true"><img src="/files/VcAjxTc8r0UvUQBphHtP" alt=""></div>

This screen lets you create some edition profiles.

> If there are no profiles, the data source is NOT editable.

In order to add a profile, click on the (+ Add Profile) button.

Each profile has the following parameters:

* *Public access*: If turned on, all users with READ access to the sheet will be able to access that profile.
* *Applies to*: If the profile is not public, then a list of teams can be selected to decide who this profile will apply to.
* *Can add / remove rows*: When toggled on, this profile allows users to add and remove rows to the data source.
* *Can add new columns*: When activated, users can add new columns to the data source.
* *Editable columns*: Defines the set of columns that can be edited in a datasource. This will be set to `All Columns` if the profile lets users add and remove rows.

> As soon as the data source is editable (at least one profile), the owner of the data source can access all the permissions, meaning that they can edit all columns, add and remove rows and add columns.

<div data-with-frame="true"><img src="/files/rcsxxTciKMBmIaxVseWb" alt=""></div>

*Here, the ADMINS team can add and remove rows, add columns and edit all fields. All other users (public profile) can only edit the `Title` column.*

### 2.2 Set edit permissions for the mapping columns

To control whether a mapping column is editable by users other than its creator, you need to share the sheet it resides in.

<div data-with-frame="true"><img src="/files/pZ2vTUWkCmzR8enA5EcQ" alt=""></div>

In order to share a mapping column, go to the model section of the sheet it belongs to and click on the (Shared) checkbox.

> If this option is disabled for you, it means that you do not have enough permission on the sheet. Contact the sheet owner to get this permission.

**Only users with whom the sheet will be shared as Editors will be able to edit the mapping columns.**


# Create from scratch

This is the simplest way to set up a blank, manual-input data source in KAWA.

To create a new data source in the **Data Source** section, click on **(+ Data Source)** then **(Create from Scratch)**.

Fill in the data source details:

* A modal titled Create new empty data source appears.
* In Data source name, enter a unique name for your new source.
* (Optional) In Description, jot a brief note about what this source will hold.
* Click Create.

<div data-with-frame="true"><img src="/files/cdUoT5U4EFut16PUaWM2" alt=""></div>

After creation, you land on the Overview tab for your new source.

Once you switch to the Data tab, you can begin populating your Data Source. Here’s what you’ll see and how to use it:

* Grid view A blank grid appears with your column headers across the top (e.g. Title) and empty rows numbered down the left.

<div data-with-frame="true"><img src="/files/8sGG8yqfSTVPL80EOwMp" alt=""></div>

* Add rows Click the + button at the bottom-left of the grid to insert a new row. Each click adds one more blank row where you can type in values.

<div data-with-frame="true"><img src="/files/Xjx0e4gISi5JCp52ffIi" alt=""></div>

* Add columns Hover over the top right of the header area and click the + icon to define a new column. You’ll specify:
  * A column name
  * A data type (Text, Number, Date, etc.)

<div data-with-frame="true"><img src="/files/XwROs1y3V9X4q2vjgm7t" alt=""></div>


# Load with Python

In the Load with Python section you’ll find two ways to create a new data source with your own ETL code:

* Quick deploy in the KAWA File Store – just write or paste your load\_data() function through the UI;
* Full DevOps workflow – connect your GitHub/GitLab repo as Source Control, version your scripts in Git, and KAWA will automatically scan the branch and list them under Python tools. Both approaches let you easily manage data updates and set up scheduled runs. You can read more about this in the [Python tools](/09_00_python_integration/09_02_python_tools) section. You can find example workbooks and additional documentation here: [KAWA Tools Github Repository](https://github.com/kawa-analytics/kawa-toolkits).

The next step is to create a new data source by running your own Python script in the **Data Source** section, click on **(+ Data Source)** then **(Load with Python)**.

Select Your Python Tool:

* In the DATA IMPORT panel, open the Python tool dropdown.
* Choose your script from the list (e.g. profit).

<div data-with-frame="true"><img src="/files/NYw2G8wjuQIUDUBtXNvy" alt=""></div>

Your script’s code will appear on the right. Click Preview data—KAWA will run your function and display the rows of the resulting data table.

<div data-with-frame="true"><img src="/files/klrNpWAOzRdtALdxD6B0" alt=""></div>

At the bottom, select one of the Feed type options: Reset before insert or Incremental. Then click Next.

On the Configure your data screen, review column names and data types. Toggle Is key on the column you’ll use for incremental updates. Click Save to lock in your schema and finish creating the Data Source.

<div data-with-frame="true"><img src="/files/pZulaEJ18uMBXWH09G2c" alt=""></div>

In the Create new data source dialog, enter:

* Data source name (e.g. profit).
* Description (optional).
* Data source type (usually Classic data source).
* Access Control default permissions. Click Save and run.

<div data-with-frame="true"><img src="/files/L88tMqNBIIPzUk3Qu2rF" alt=""></div>


# Modeling

In KAWA, sheets can be used in several ways. See definitions in [Terminology](/13_00_terminology#sheet) section.

The easiest way is to use them like an Excel Workbook, adding multiple views (Tabs) to explore and visualize your data: [The Visualization section](/04_00_visualization).

However, sheets are much more powerful than that and can be used to build and share your enterprise data models.

## 1. The structure of a Sheet

A sheet is composed of the following main elements:

* A main data source
* Several linked data sources (Optional)
* Some views, with at least a Grid view
* Some columns (similar to the columns of a table in a database)
* Some metadata

A sheet also has a control panel and some global sheet filters. All these concepts will be explored in specific pages.

### 1.1 The main data source

The main data source of a Sheet ([Data Integration](/01_00_data_integration) for details about data sources) will dictate the sheet data granularity. In other words, it will define the primary keys of your sheet.

For example, if you choose a position system as a main data source, your sheet will show data per asset and portfolio.

> The primary data source is selected during sheet creation and defines the sheet granularity and primary keys. On supported sheets, you can later replace it from the **Model** tab using [Change Data Source](/02_00_modeling/change-data-source).

### 1.2 Linked data sources

You can add several linked data sources to an existing sheet. They will be used to enrich your sheet with data coming from various systems and data sets. Because main data source imposes its granularity, the linked data sources will not affect the number of row, but rather adding columns to existing ones.

In a database vocabulary, the linked data sources will be joined using `LEFT JOIN` only.

> Working with a Star Schema for example, where you would have one central Transaction table and related dimension tables: you would create a sheet on the Transaction table and then join in the various dimension tables.

### 1.3 Views

A Sheet contains views that offer diverse perspectives on its data. You can read more about this in the [The Visualization section](/04_00_visualization).

### 1.4 Columns

The columns of a sheet represent the individual fields or attributes that define the structure of its data. (See definitions in [Terminology](/13_00_terminology#column) section.) Each column corresponds to a specific type of information, such as:

* **Dimensions** – descriptive fields like names, dates, or categories.
* **Measures** – numeric values that can be aggregated, like sales or quantities.
* **Calculated Fields** – custom columns derived from expressions or formulas based on other columns.
* **Metadata Fields** – technical or structural data, such as IDs or timestamps.

Columns define what kind of data the model captures and how it can be queried, grouped, and visualized.

Columns can originate directly from the underlying data sources or be derived through formulas, mappings, Python scripts, and more. Their values can be displayed as-is, or further aggregated and formatted for analysis and visualization.

### 1.5 Metadata

In a sheet, metadata refers to the descriptive information that defines and contextualizes the structure and behavior of the underlying data model.

It includes details such as

* Global description of the sheet (consumed by AI)
* Column names,
* Column descriptions,
* Data types,
* Relationships between data sources,

It also captures how data should be aggregated, formatted.

This layer of information ensures consistency, enhances data discoverability, and enables more meaningful and efficient analysis across various views and visualizations.


# Control panel

The Controls Panel allows users to dynamically customize and interact with data views within the Sheets workspace. It is designed to provide flexibility, improve data exploration, and support user-driven input directly from the interface. See definitions in [Terminology](/13_00_terminology#control-panel) section.

A control is an interactive interface element that is linked to the data on the sheet and allows the user to dynamically filter, configure, or enter values directly in the interface for the immediate updating and viewing of the corresponding data.

The Controls Panel is accessible via the "Controls" button located in the top-right corner of the toolbar, adjacent to the Share button. When expanded, it reveals an area where interactive controls can be configured and added to the current sheet view.

By default, the panel displays the message: “No controls available yet …” This indicates that no controls have been configured for the active view. Users are prompted to begin customization via the “Configure” button.

After clicking the Configure button, the Add new control panel opens from the right side of the screen. After opening it, a categorized list of configurable control types is presented.

<div data-with-frame="true"><img src="/files/IGR0G9LmUP8MTB9Qmceh" alt=""></div>

These include:

## 1. Filter Control

The Filter control lets users interactively narrow down the data in the current sheet view by selecting specific values from a chosen column. It is part of the Controls Panel system and is especially useful for narrowing down datasets interactively without modifying the underlying data or requiring complex formulas.

Filter is created from the Add new control modal.

Here is an example of creating a filter for a text field.

<div data-with-frame="true"><img src="/files/MYD7qXjtW2p9dBBglFd4" alt=""></div>

Filter behavior at runtime after saving:

* The filter control appears in the Controls Panel.
* Users can select one or multiple values from the dropdown menu.
* The sheet automatically updates to display only the records that match the selected filter criteria.

When setting up a Filter control for a text field, the Values list Display type is used by default. It automatically generates a dropdown menu containing all distinct values from the selected column. This option is ideal for filters, as it allows users to easily select from existing dataset entries without the need to type values manually.

<div data-with-frame="true"><img src="/files/lwgcWopmKXHXcyYh7r2P" alt=""></div>

*When configuring a filter control, the Values list display type is selected by default if the column contains text.*

Use Values list to:

* Provide a user-friendly way to filter by predefined column values.
* Avoid errors associated with free text input.
* Enable dynamic population of the control based on real data.
* If Multi selection is enabled, users can select multiple values.

The Range display type is used in Filter controls to enable users to filter data by specifying a numeric interval using From and To inputs.

<div data-with-frame="true"><img src="/files/VAWKhnCMCN867XY2ZZLG" alt=""></div>

It is automatically selected by default when the target column contains numeric values.

<div data-with-frame="true"><img src="/files/M4pdLCAnSVFwbVGaGBxK" alt=""></div>

When configuring a Filter Control, selecting the Free input option as the Display type enables users to manually enter values for filtering — rather than choosing from a predefined list. This mode is ideal for numeric, text, or computed columns where user-entered criteria offer more flexibility.

<div data-with-frame="true"><img src="/files/M8atfSyZMJ5P1LQGYHHV" alt=""></div>

*In the Add new control tab when creating a filter, select Free input Display type.*

When Free input is selected, an additional field titled Operator becomes available. This lets users define how the inputted value will be compared against the data in the selected column.

* Default Operator: Equals is set by default.
* Customizable: Users can choose from a list of comparison operators to tailor the filter logic.

<div data-with-frame="true"><img src="/files/viJMqQKMiPa9dsFUtCNh" alt=""></div>

When to Use Free Input?

* For numeric columns like Profit, Quantity, or Score where exact values or thresholds are important.
* When filtering on fields with large value ranges where dropdowns are impractical.
* When needing precision control over dataset behavior.

For fields with a different data type (e.g., date, datetime), the Display Type selection is not supported.

<div data-with-frame="true"><img src="/files/AjL2cv2hmZOiSeXD5xs8" alt=""></div>

## 2. Button Control

Button controls serve as interactive triggers for preconfigured Python tool. They can execute Python scripts (code inside a Python tool that can be inserted, but it is not tied to the UI) directly from the interface, making them a powerful feature for data transformation, filtering, statistical analysis, or any automated logic embedded in the sheet. Button controls serve as interactive triggers for preconfigured workflows. They can execute Python scripts directly from the interface, making them a powerful feature for data transformation, filtering, statistical analysis, or any automated logic embedded in the sheet. This control is particularly useful for users who want to apply logic, run analytics, or initiate processes manually with a single click.

To perform an action after clicking a Button Control, you must configure the Action field to Run Python tool. This links the button to a specific Python script or tool available within the current sheet environment.

Once configured, clicking the button will immediately execute the selected Python tool, allowing you to perform operations such as calculations, data transformations, or any custom logic defined in the script.

To link a tool, use the “Python tool” dropdown to choose from available tools, or add a new one from the library.

<div data-with-frame="true"><img src="/files/V6ETW9eK755MKcHcqkoP" alt=""></div>

*Selecting an available Python tool.*

<div data-with-frame="true"><img src="/files/1iUlL5dq0kIPlsNhkTgi" alt=""></div>

*Add a new Python tool from the library.*

## 3. Variable control

Variable Controls allow users to enter values that can be used in other parts of the sheet, such as formulas and filters. They work like parameters that can be changed at any time, without needing to edit the structure of the table. Variables are independent elements — they do not filter data on their own, but can be linked to other components.

### 3.1 Available Variable Controls

The following variable types are available when adding a new control:

| **Control Type**       | **Description**                                                                                                  |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Text**               | Accepts single-line freeform text. Useful for keywords, search strings, or identifiers.                          |
| **Number**             | Accepts a single numeric value (integer or float). Ideal for threshold settings or numeric filters.              |
| **Number range**       | Allows users to define a numeric interval (min and max). Commonly used for price, quantity, or metric filtering. |
| **Date**               | Accepts a single calendar date.                                                                                  |
| **Date range**         | Allows users to select a start and end date. Useful for temporal filtering or period-based analysis.             |
| **Date time**          | Captures a full date and time value.                                                                             |
| **Datetime range**     | Allows selection of a time-bound interval with both date and timestamp.                                          |
| **Switch**             | A binary toggle (on/off, true/false). Used for enabling/disabling conditions or options.                         |
| **Single select list** | Dropdown menu allowing the user to choose one value from a predefined list of options.                           |

### 3.2 Text variable

A Text control allows the user to manually enter any string value. It is useful for filtering, defining conditions in formulas, or dynamically providing parameters — such as a city name, region, or keyword.

#### a. Behavior:

* Displays as a single-line text input field.
* Accepts any user-entered string.
* Stores the value as a named variable that can be used in filters and formulas.
* The variable updates in real time and applies within the current sheet view.

#### b. Example Use Case:

You can bind the Location control to a filter on the City column with the following condition: Operator: Begins with → Variable: Location

<div data-with-frame="true"><img src="/files/TzrBvEP3MRy8HzhshBFX" alt=""></div>

This enables dynamic filtering of records based on user input, such as a partial city name.

<div data-with-frame="true"><img src="/files/gU1hhyNvRUoXpshmTjo7" alt=""></div>

Using a Text Control in a Formula: Suppose you have a variable named Location, created using a text control. You want the formula to check whether the city name in the City column contains the text entered by the user in the Location variable.

<div data-with-frame="true"><img src="/files/PJmDMie6XnwbJooIHzd9" alt=""></div>

How it works:

* Location is a variable that accepts free text input from the user.
* City is a column in the table containing city names.
* CONTAINS() checks whether the entered value appears within the city name.
* The result is "Match" for rows where a match is found, and "No match" otherwise.

<div data-with-frame="true"><img src="/files/IaCdAtgzQRjyYygFbkPM" alt=""></div>

Example:

* The user enters "con" in the Location field.
* The formula returns "Match" for Concord.

<div data-with-frame="true"><img src="/files/Qlb5fkdQySQpcmiKORHj" alt=""></div>

### 3.3 Number variable

A Number control allows the user to manually enter a single numeric value. This value is stored as a variable and can be used in formulas and filters to create parameterized logic.

#### a. Behavior:

* Displays as a single-line numeric input field in the Controls Panel.
* Accepts any valid number (integer or decimal) entered by the user.
* Stores the input as a named variable that can be referenced in filters, formulas, and Python tools.
* The value updates in real time and is applied instantly across the current view wherever the variable is used.

#### b. Example Use Case:

You can bind a Number control to a Profit numeric column in a filter. For example: Operator: Greater than or equals → Variable: Min Profit

<div data-with-frame="true"><img src="/files/4aWWbrN0SUvcdeIdvMpt" alt=""></div>

This means: only rows where Profit is greater than or equal to the value entered by the user in the Min Profit field will be shown.

<div data-with-frame="true"><img src="/files/sae6shrgXqRsuIS30Fr5" alt=""></div>

Usage in Formulas. A number variable can be used in a formula for conditional calculations:

* Profit is a numeric column in the table.
* MinProfit is a variable linked to a Number control.
* The formula returns "High" for rows where profit is greater than the variable's value, and "Low" otherwise.

<div data-with-frame="true"><img src="/files/Qt5RJE6LhKF991RNy3Sl" alt=""></div>

Example:

* The user enters 150 000 in the Min Profit field.
* The formula returns "High" for rows where Profit > 150 000.
* The formula returns "Low" for rows where Profit <= 150 000.

<div data-with-frame="true"><img src="/files/fEye4GAgWe7eqWv3FIny" alt=""></div>

### 3.4 Number range variable

The Number range control allows users to enter a minimum and maximum numeric value to define an interval.

<div data-with-frame="true"><img src="/files/vED9mgw2gGazhk3aFI2t" alt=""></div>

<div data-with-frame="true"><img src="/files/RhUQljeF7GXH9qInmsB8" alt=""></div>

#### a. Example Use Case:

Usage in Formulas. To reference the range in a formula, use both boundaries:

<div data-with-frame="true"><img src="/files/fjFE320zYO9fJfdasOS9" alt=""></div>

<div data-with-frame="true"><img src="/files/yctVVe7DQt2MBvtUetNE" alt=""></div>

### 3.5 Date variable

The Date control allows users to select a specific calendar date through a visual date picker. It stores the selected value as a variable that can be used in filters and formulas and - enabling dynamic, date-driven logic in the sheet view.

#### a. Behavior

* Renders a single date picker input
* Accepts any valid calendar date
* Stores the selected date as a named variable
* Updates all connected components (filters, formulas) in real time

#### b. Example Use Case:

You can bind a Date control to a Order Date column in a filter. For example: Operator: Is on or after → Variable: StartDate

<div data-with-frame="true"><img src="/files/7RJOnyrwB7u2apaMoKD0" alt=""></div>

This filter will display only the records where the order was placed on or after the selected date.

<div data-with-frame="true"><img src="/files/6z6k1lfaEnkvBstTjAzu" alt=""></div>

Usage in Formula

<div data-with-frame="true"><img src="/files/oaZqzGAgMgLEwchyEt2Q" alt=""></div>

This formula uses the date selected in the control to label rows based on whether the order date is on or after the specified day.

<div data-with-frame="true"><img src="/files/QEyvd3WvObOvuDnRokL8" alt=""></div>

### 3.6 Date range variable

The Date range control allows users to select a start and end date to define a time interval.

#### a. Example Use Case:

Usage in Formulas. To reference the range in a formula, use both boundaries:

<div data-with-frame="true"><img src="/files/OIJcfdRbF5NsJS06p7AZ" alt=""></div>

<div data-with-frame="true"><img src="/files/rLYenmyJg1zS0Mm5dysC" alt=""></div>

### 3.7 Date time variable

Date Time control allows users to input a precise date and time value, stored as a variable. This control is ideal for scenarios where both the calendar date and the specific time of day are important for filtering data, running calculations.

#### a. Behavior:

* Appears as a combined date and time picker
* Saves the selected value as a named variable (e.g. StartTime)
* Variable can be referenced in filters or formulas
* Updates dynamically — changes take effect across all components using the variable

#### b. Example Use Case:

You can bind a Date time control to a Created At column in a filter. For example: Operator: Is on or after → Variable: StartTime

<div data-with-frame="true"><img src="/files/cQ7sSlwr6VkTurJ62RdQ" alt=""></div>

This filters all records that were created on and after the selected date and time.

<div data-with-frame="true"><img src="/files/rGwWY4FdCMasfkVdCNng" alt=""></div>

Usage in Formula

<div data-with-frame="true"><img src="/files/3TM4D7FAiG6BKYVDqxZ3" alt=""></div>

This formula returns "Late" for records created after the specified time, and "On Time" otherwise.

<div data-with-frame="true"><img src="/files/Zq3RS5YMBmxSlyA810mx" alt=""></div>

### 3.8 Datetime range variable

The Datetime range control allows the user to select a range that includes both the start and end date and exact time.

#### a. Example Use Case:

Usage in Formulas. To reference the range in a formula, use both boundaries:

<div data-with-frame="true"><img src="/files/K0wpzVf15QutldSxdPd3" alt=""></div>

<div data-with-frame="true"><img src="/files/xVKKcL0nUUoK4t6xHH4c" alt=""></div>

### 3.9 Switch variable

The Switch control is a toggle input designed for binary user choices such as true/false, yes/no, or on/off conditions.

#### a. Example Use Case:

Usage in Formulas

<div data-with-frame="true"><img src="/files/aLgdRFvCwbTzEQjt7Uwc" alt=""></div>

* With MySwitch = Off, SwitchStatus shows “Disabled”
* Toggling MySwitch = On changes all SwitchStatus values to “Enabled” instantly

### 3.10 Single select list variable

The Single Select List control allows users to choose one option from a predefined dropdown list. The selected value is stored as a variable that can be used throughout the sheet - in filters and formulas.

#### a. Behavior:

* Appears as a dropdown menu with a fixed list of values
* Only one value can be selected at a time
* Stores selection as a named variable (e.g. Location)
* Variable can be referenced in filters and formulas

A user can add values to a Single select list variable in two ways:

* Custom values The user manually enters a set of predefined values, from which only one can be selected.

<div data-with-frame="true"><img src="/files/dUxJ5MUNkQx7MwrSEWjp" alt=""></div>

* From a column The user selects a column from a table, and all unique values from that column are automatically used as options.

<div data-with-frame="true"><img src="/files/T6AMDuZSt0K2JxgMYV0W" alt=""></div>

#### b. Example Use Case:

You can bind a Single Select List control to a City column in a filter. For example: Operator: Equals → Variable: Location

<div data-with-frame="true"><img src="/files/9yEE8lYhWseVrRzo3zmD" alt=""></div>

Shows only records that belong to the selected city from the dropdown.

<div data-with-frame="true"><img src="/files/B9eawMZOVokgq1IIImR8" alt=""></div>

Usage in Formula

<div data-with-frame="true"><img src="/files/SAzNFpewdVAPorD67DvD" alt=""></div>

Returns Sales only for rows matching the selected category; otherwise returns 0.

<div data-with-frame="true"><img src="/files/TIoWjC2wCxPsBWFZcXjQ" alt=""></div>

### 3.11 Multi select list variable

The Multi select list variable control allows users to choose multiple values from a predefined or dynamically loaded list. This control is useful when filtering or computing data based on more than one matching value.

#### a. Behavior:

* Appears as a dropdown menu with a fixed list of values
* Multiple value can be selected at a time
* Stores selection as a named variable (e.g. Location)
* Variable can be referenced in filters and formulas

A user can add values to a Multi select list variable in two ways:

* Custom values The user manually enters a set of predefined values, from which only one can be selected.
* From a column The user selects a column from a table, and all unique values from that column are automatically used as options.

#### b. Example Use Case:

Usage in Formula

<div data-with-frame="true"><img src="/files/LxpDF87uvTM6b6ZiagVb" alt=""></div>

Returns Selected only for rows matching the selected category; otherwise returns 0ther.

<div data-with-frame="true"><img src="/files/xcNqEnFIKdBuaLTTriUT" alt=""></div>

## 4. Interaction with controls

Each created control in the Controls panel includes the correct visual and functional elements: reorder handle, type icon, display name, eye icon (Toggle visibility), pencil icon (Edit control) and trash icon (Delete control).

<div data-with-frame="true"><img src="/files/InthjPwvVSa8oruQE5bq" alt=""></div>

The reorder handle is used for drag-and-drop to change the order of controls in the list.

Toggle visibility enables or disables showing the selected control on the panel. When the control is visible (icon active), its filter applies to the table and users can use it. When the control is hidden (icon crossed out), it does not appear in the interface and its settings don’t affect the data view.

<div data-with-frame="true"><img src="/files/ggeGh7tgxhD1OotQVZhA" alt=""></div>

Edit control opens the selected control in edit mode. Clicking it opens the side panel where you can change the filter’s type, data column, default values, label, and other settings.

<div data-with-frame="true"><img src="/files/39tu9x5Kk4sybE3TTuEP" alt=""></div>

Delete control permanently removes the selected control from the panel. After clicking, a confirmation dialog “Are you sure you want to delete this filter control?” appears to prevent accidents. Once deleted, the control no longer applies any filter and disappears from the interface.

<div data-with-frame="true"><img src="/files/fauoMKlBpr1J4cODf5Uo" alt=""></div>

## 5. Tab-Specific Control Behavior

* Sheets

You can create Filters, Buttons, and Variables here.

* Dashboards & Applications

You can reuse Buttons and Variables defined in Sheets, but:

* Filters must be created separately on each tab and have no connection across tabs.
* Variables, while shareable, store values independently per tab (changing a variable’s value in Dashboards won’t affect its value in Sheets, and vice versa).

This approach lets you define Buttons and Variables once in Sheets for consistency, yet configure Filters and adjust Variable inputs independently on every tab.

## 6. How Sheet Controls Impact Views

Filters set up in the Sheets tab are automatically applied to every View linked to that Sheet. All other control types (buttons, variables) do not affect what data appears in the View.

## 7. Access to Controls in Sheets, Dashboards and Applications

Users can set access for the artifacts they create (Sheets, Dashboards, or Applications) in each tab.

### 7.1 Example: Setting access for a Sheet

* Private If a user marks their Sheet as Private, only they can view it and use all controls created for that Sheet.
* Public If a user marks their Sheet as Public, only members of their workspace or of a team within the workspace can access it and its controls.
  * General access – every member of the workspace.
  * Teams with access – only the specified teams.
  * If both options are selected, General access takes priority.

<div data-with-frame="true"><img src="/files/tNvVUMWn7AaHP0ASIdW0" alt=""></div>

Access levels within Public

* Viewing
  * Users can apply the controls the author created, but they use their own values and run their own analysis.

<div data-with-frame="true"><img src="/files/lYbAzeZQTedexdvtUHjE" alt=""></div>

* Editing
  * Users can create, edit, and delete their own controls on that Sheet.
  * The Sheet’s author also has access to these new controls and can use them regardless of who created them.


# Change Data Source

Use **Change data source** to replace the primary data source of an existing sheet.

Use this feature when you want to:

* switch the sheet to a source with the same structure
* replace the source with one that has fewer columns
* replace the source with one that has additional columns

## 1. Before you start

* The feature is available from the **Model** tab.
* It is not available for **multi-sheets**.
* The current data source is shown for reference, but it does not appear in the replacement list.
* You can switch to a source with fewer, the same number of, or more columns than the current source.

> You can use this feature even if the sheet already contains charts, pivot tables, grouping, filters, formulas, lookup columns, or mapped columns.

This article uses the following example files:

* `01_base_sheet_source.csv` — the original source
* `02_target_fewer_columns.csv` — a compatible replacement source with fewer columns
* `03_target_auto_match_plus_3.csv` — a compatible replacement source with additional columns

## 2. Open the dialog

1. Open the sheet.
2. Go to **Model**.
3. Click **Change data source**.

<div data-with-frame="true"><figure><img src="/files/aBqwPxDqllcj0bfWcWEd" alt=""><figcaption></figcaption></figure></div>

KAWA opens a 4-step flow:

1. **Select source**
2. **Map columns**
3. **New columns**
4. **Review**

<div data-with-frame="true"><figure><img src="/files/7AeEXUh2puyiFQZxDls0" alt=""><figcaption></figcaption></figure></div>

## 3. Step 1 — Select source

Choose the new data source for the sheet.

For each available source, KAWA shows:

* the source name
* the number of columns
* the difference compared with the current source

Badges show the column-count difference:

* **+N extra** — the new source has more columns
* **N fewer** — the new source has fewer columns

### 3.1 Example

Suppose the sheet currently uses `01_base_sheet_source.csv`.

In the source list, you may see:

* `02_target_fewer_columns.csv` with **2 fewer**
* `03_target_auto_match_plus_3.csv` with **3 extra**

This tells you that one replacement will leave some existing columns without data, while the other may let you add new columns to the sheet.

Click **Next** to continue.

## 4. Step 2 — Map columns

KAWA matches columns from the current sheet to columns in the new data source.

KAWA automatically matches columns when their **name** and **type** are compatible. Review these matches before continuing.

### 4.1 Current source columns

This section shows columns that are currently mapped from the active primary source.

Rules:

* **Key columns must remain mapped.**
* **Non-key columns can be left unmapped.**
* Columns with incompatible types cannot be mapped to each other.
* One target column can be used only once.
* Mapping must remain **1:1**.

If a non-key column is left unmapped, it stays in the sheet but no longer receives data from the new source.

### 4.2 Example: replace with fewer columns

Suppose `01_base_sheet_source.csv` contains these columns:

* `record id`
* `account_id`
* `country`
* `city`
* `segment`
* `signup_date`
* `order_count`
* `lifetime_value`
* `last_order_date`

Now replace it with `02_target_fewer_columns.csv`, which contains:

* `record id`
* `account_id`
* `country`
* `city`
* `signup_date`
* `order_count`
* `lifetime_value`

In this case, KAWA automatically matches 7 columns and leaves these columns unmapped:

* `segment`
* `last_order_date`

These two columns remain in the sheet, but they will no longer receive data after the change.

<div data-with-frame="true"><figure><img src="/files/w8fKhbkSFiKNculkM248" alt=""><figcaption></figcaption></figure></div>

### 4.3 Previously unmapped columns

If the sheet already contains columns that lost their source mapping during an earlier replacement, KAWA shows them in a separate **Previously unmapped columns** section.

Use this section to reconnect those columns to the new source.

Important behavior:

* KAWA can auto-match these columns when possible.
* These columns are optional to remap.
* A target column from the new source can be used only once across the entire mapping step, including both Current source columns and Previously unmapped columns.

### 4.4 Example: restore unmapped columns later

Suppose you first replace `01_base_sheet_source.csv` with `02_target_fewer_columns.csv` and leave `segment` and `last_order_date` unmapped.

Later, you run **Change data source** again with a source that contains compatible versions of these fields. KAWA shows them in **Previously unmapped columns**, so you can connect them again.

<div data-with-frame="true"><figure><img src="/files/zqPVGgd5qceCFAkBq0Ag" alt=""><figcaption></figcaption></figure></div>

Click **Next** after reviewing the mappings.

## 5. Step 3 — New columns

This step shows columns from the new source that are not used in any existing mapping.

Select the columns you want to add to the sheet.

> Extra columns are not added automatically. Only the columns you select in this step are added.

### 5.1 Example

Replace `01_base_sheet_source.csv` with `03_target_auto_match_plus_3.csv`.

In this case:

* existing compatible columns are mapped first
* unused columns from the new source appear in **New columns**
* only the columns you select are added to the sheet

If you do not select any extra columns, they are ignored.

<div data-with-frame="true"><figure><img src="/files/KY5xNeqf71ap0ekb84QT" alt=""><figcaption></figcaption></figure></div>

Click **Next** to continue.

## 6. Step 4 — Review

Before applying the change, KAWA shows a summary of the result.

The summary includes:

* the current data source
* the new data source
* **Columns mapped**
* **Columns with no data**
* **New columns**

### 6.1 Example: fewer columns

If you replace `01_base_sheet_source.csv` with `02_target_fewer_columns.csv`, the review may show:

* **Columns mapped:** `7 / 9`
* **Columns with no data:** `segment`, `last_order_date`
* **New columns:** `None`

<div data-with-frame="true"><figure><img src="/files/c1rruIxUUS1edJIQMBiC" alt=""><figcaption></figcaption></figure></div>

### 6.2 Example: additional columns

If you replace `01_base_sheet_source.csv` with `03_target_auto_match_plus_3.csv`, the review shows:

* the mapped existing columns
* the extra columns you selected in **New columns**

Review the summary, then click **Apply changes**.

<div data-with-frame="true"><figure><img src="/files/nYzDdvuvJmb8F7BPpo3I" alt=""><figcaption></figcaption></figure></div>

## 7. What happens to columns with no data

If a non-key column is left unmapped:

* the column remains in the sheet
* it no longer receives data from the primary source
* its cells remain empty
* its data source is shown as **N/A** in field information
* it is excluded from parts of the UI that require an active source mapping

### 7.1 Example

After replacing `01_base_sheet_source.csv` with `02_target_fewer_columns.csv`:

* `segment` remains visible in the sheet
* `last_order_date` remains visible in the sheet
* both columns stay empty until they are remapped in a later replacement

This lets you keep the sheet structure and decide later whether to remap or remove the column.

<div data-with-frame="true"><figure><img src="/files/NjYisoMGgFTonRgiUzyX" alt=""><figcaption></figcaption></figure></div>


# Computations

* [Formulas](/03_00_computations/03_01_formulas)
* [Lookup Columns](/03_00_computations/03_02_lookup_columns)
* [Mapping Columns](/03_00_computations/03_03_mapping_columns)


# Formulas

## 1. General Concepts

### 1.1 Formulas

Formulas are created from the Formula editor, available from the enrich data button. There are three modes to input formulas:

* Text mode (Excel like)
* Visual mode (Using blockly)
* AI assisted mode, via prompt.

Formulas can be composed of:

* Other columns
* Operators or Functions
* Variables (From the control panel)
* Constants

Here is an example of a simple formula, in Visual mode and in Text mode.

<div data-with-frame="true"><figure><img src="/files/cltuppSMKbI7yAGw1WuD" alt=""><figcaption></figcaption></figure></div>

<div data-with-frame="true"><figure><img src="/files/ku4ZlBlZuYa3jqia450Z" alt=""><figcaption></figcaption></figure></div>

This formula returns `ABOVE` if the profit is above `ProfitThreshold` and it returns `BELOW` otherwise.

Note that:

* `Profit` is a column of the sheet
* `ProfitThreshold` is a variable from the control panel
* We use the IF/THEN/ELSE operator as well as the Greater Than one.

The return type of this formula is a `Text`, as indicated on the UI (Top left in text mode and top right in visual mode).

### 1.2 Types and Levels of detail

#### a. Types

Formulas are typed. It means that they only return one type of data which can be:

* `Text`
* `Integer`
* `Decimal`
* `Date`
* `Date Time`
* `Boolean`

Their type is derived from the operators they use and their various columns, constants and variables. For example: Concatenating two texts will yield a text, adding two integers will yield an integer etc...

You can always refer to the documentation of each operator directly on the GUI for that purpose:

<div data-with-frame="true"><img src="/files/4oPc1PQp9flnTawIyprt" alt=""></div>

#### b. Levels of detail

In addition to being typed, formulas also have a Level of Detail. It can be either: `ROW`, `GROUP` or `ANY`.

* **The ROW level of detail:**

A ROW level of details means that the formula will be computed for each row of the data.

For example, the formula `Profit / Quantity` will have a value for each row. When we sum the values of this formula - that might happen in Grid, Pivots and Charts - we apply the sum of the division for each row: `SUM( Profit / Quantity )`

In other words, the computation is done at the row level and we simply aggregate those results together.

* **The GROUP level of detail:**

A GROUP level of details means that the formula will be computed for both the rows and directly for the group. The main difference is that a group function manipulates aggregating operators, such as SUM or AVERAGE etc..

It allows us to write powerful formulas such as Weighted averages etc..

Here is how you would write a weighted average using a GROUP formula:

```
WeightedAverage = SUM(weight * value) / SUM(weight)
```

* **Quick example:**

The example below illustrates the difference between the two:

Look a the two columns on the right of the grid:

* `Profit / Quantity`
* `SUM(Profit) / SUM(Quantity)`

They both have the exact same value on each row, but the global aggregation of both, on the bottom row, is different.

Because the first one is computed ONLY for each row and then aggregated, the global aggregation will be:

```
SUM(Profit / Quantity) = 
Profit1/Quantity1 + ... + Profitn/Quantityn
```

On the other hand, because the second one is computed directly at the group level, its aggregation will be:

```
SUM(Profit) / SUM(Quantity) = 
(Profit1 + ... + Profitn)/(Quantity1 + ... + Quantityn)
```

Note that for the second formula, the aggregation at the bottom of the grid cannot be changed (the same goes for charts and pivots), because it is imposed by the formula itself.

<div data-with-frame="true"><img src="/files/VftTuvDPG1Gynb7z8l7s" alt=""></div>

## 2. Window Functions

### 2.1 What is a Window Function?

A Window Function is a ROW LEVEL computation operation. This means that it will compute one value for each row of data.

Without window function, all the computations only "see" one row of data at a time. If you write for exampe: `col1 + col2`, this means that `col1` and `col2` will be added for each row.

With window functions, computations can now see a Window of rows around the current row. We can now access rows such as: Previous and Next rows, all the rows before, all the rows after etc...

The image below illustrates this:

<div data-with-frame="true"><img src="/files/JzQJmcGl7IrmSnk6nKzR" alt=""></div>

When you write a window function, you need to define which rows will be accessible from each computation.

### 2.2 Examples

* **Example 1: Average of surrounding rows**

Below, an example of an Average of the row before, the row after and the current row, per client.

<div data-with-frame="true"><img src="/files/eOfe0IwbExtwFqNgiEz8" alt=""></div>

* **Example 2: Cumulative Sum**

Here, we compute the Chronologic Cumulative sum per client. Notice the use of the `NO_LIMIT` block in the Window range. This lets you define unbounded windows.

It is also important to note that the ORDER defined in the WINDOW function is independant from the one defined in the main view. The WINDOW function will operate on its own ORDER function and ignore the one of the view.

<div data-with-frame="true"><img src="/files/L5txDSSFqMUggU57oHH5" alt=""></div>

* **Example 3: Delta between today's price and yesterday's price for Stock**

**Case 1: Using the ROWS operator**

Notice how we use the `ROWS` operator to define the range. This operator will just look at the rows and ignore gaps in the dates. Here `STOCK2` has gaps but the delta ignores them (always -$1). In short, the `ROWS` operator will load N rows before and P rows after without caring about the content of those rows.

<div data-with-frame="true"><img src="/files/UgpA6Z6TVPpMAx6V7r9e" alt=""></div>

**Case 2: Using the RANGE operator**

With this syntax, we are using the `RANGE` operator.

As opposed to the `ROWS` operator, the `RANGE` looks at the values from the `ORDER` clause.

In this example, we order by date and we are looking at the actual previous date. So if a row is dated on the 2nd of January 2025, It will fetch the row dated on the 1st of January 2025 (Within the defined partition, here: The STOCK). If there is no such row, then the window function will not find any row.

In the below screenshot, it explains why, for STOCK2, the delta is defined only for the 16th of Feb 2025: it is because the WINDOW is able to pick up the previous date which exists.

As a result, the `ROWS` operator is interesting when you order by date and you want to account for missing dates in your computation.

<div data-with-frame="true"><img src="/files/0gvKkTtR984ylXzPnKbV" alt=""></div>

### 2.3 Creating and defining window functions

In order to configure Window function, create a new formula and use the Blockly mode. It has several prebuilt functions to help you getting started.

<div data-with-frame="true"><img src="/files/mkhtFdQFoTXgLa570k9R" alt=""></div>

In order to define your Window, you must set the following parameters:

1. **The Partition**:

The partition will define which dimension(s) will be used to build the Window function. It defines where to pick the rows that will constitute the Window.

2. **The Order By**:

Within each Window, this clause define how the data will be sorted.

3. **The Window Range**:

This defines how many rows before and after the current row we will look at in the window.

There are two ways to define a range:

* Using the `ROWS` operator: This will be based on the number of rows before and after the current one.
* Using the `RANGE` operator: It will look for consecutive values. It makes sense for dates and date times.


# Lookup columns

## 1. What is a lookup column?

Lookup columns are similar to the VLookup operation in Excel. They allow to mix data from various sources in one place. In KAWA, they allow to join data sources together in an intuitive and powerful way, keeping consistency in all derived computations.

Throughout this paragraph, we will use the following setup: Two sheets, one showing orders made by clients in an online store, and another one showing events to which clients were invited.

*Table 1: Orders placed by clients*

<div data-with-frame="true"><img src="/files/8lz1FijScywGK0f5i7DC" alt=""></div>

*Table 2: Events where customer where invited*

<div data-with-frame="true"><img src="/files/45FOIbjFDj0JE30wUnsQ" alt=""></div>

> Those two sheets have a column in common: `Customer name` in the Orders sheet and `guest name` in the Events sheet. Those columns have different names but their content is similar - they both refer to the same dimension and have values in common.

### 1.1 Creating our first lookup column

If we want to bring in the Orders sheet the cost of all events clients went to, we would need to create a lookup column:

Clicking on Enrich Data > Lookup column brings up the following configuration form:

<div data-with-frame="true"><img src="/files/7UFA7XktGLaHxKsfCDrj" alt=""></div>

* *Select sheet:* Which sheet do we want to bring column from. Here, working from within the Orders sheet, we want to bring data from the Events sheet.
* *Columns to add:* From the Events sheet, we are interested in the SUM of event cost.
* *Map Lookup Columns:* In this step, we define how the mapping between the Orders sheet and Events sheet will be made. Here, we ar looking up guest name in Events with Customer name in Orders.

Clicking on APPLY will add a new column to the Orders sheet, as shown in red below:

<div data-with-frame="true"><img src="/files/Z6JL3Xm0nCiKxsEjyFkX" alt=""></div>

> The new column has a purple icon in its header that indicates that it is a Lookup Column. It contains, for each customer, the TOTAL of event cost they attended.

> A One-to-Many join has been made: One customer corresponds to Many events.

> Notice also that this lookup columns will automatically perform a SUM DISTINCT per customer. Looking at "Aaron Bergman", you notice that the value 350 (which is the SUM of TOTAL event cost), is the same on the Group level and on each row. This is because, each customer will be counted only once in each SUM.

### 1.2 Parameters of lookup columns

Lookup columns have the following parameters:

* A main sheet: This is the sheet to which the lookup column belongs (The Orders sheet).
* A target sheet: this is where data will be fetched from (It can be the same as the main sheet). Here: the Events sheet.
* A metric from the target sheet: This is the column from the target sheet with an aggregation method, that we are connecting through the lookup column. In our example: The SUM of EVENT COSTS.
* A mapping (or join) definition: It defines how the lookup will take place. This mapping requires a list of column pairings: One from the main sheet, with one of the target sheet. In our example:

| Main Sheet: Orders | Target Sheet: Events |
| ------------------ | -------------------- |
| Customer Name      | Guest Name           |

*With this mapping configuration, "Customer name" from the main sheet will be matched against "Guest name" in the target sheet.*

It is very helpful to think of the mapping definition as the **Granularity** of the Linked column. In our example, our linked column has a Granularity at the guest name level.

In addition to these parameters, Lookup columns can also have filters. Those restrict the scope of what is available for Lookup. For example, I can filter by `event_year == current year`. This will restrict the scope and only return events for the current year. Our SUM of EVENT COSTS will only be SUM of EVENT COSTS for this year only.

> Filters cannot be configured when creating the columns themselves. They can only be added on once they already exist.

### 1.3 How are lookup columns computed

Let's keep our example: The TOTAL COST of events per customer. Here is the raw data:

*Events sheet:* It shows various events with guest name and cost

| event date | Guest name  | Cost   |
| ---------- | ----------- | ------ |
| 01/01/2025 | Bruce Wayne | $1,000 |
| 01/02/2025 | Bruce Wayne | $2,000 |
| 01/03/2025 | Bruce Wayne | $3,000 |
| 01/01/2025 | Lucius Fox  | $4,000 |
| 01/02/2025 | Lucius Fox  | $5,000 |

*Orders sheet:* It shows various orders with profit and customer

| order id | Client name | Profit  |
| -------- | ----------- | ------- |
| O-12345  | Bruce Wayne | $10,000 |
| O-12346  | Bruce Wayne | $15,000 |
| O-12349  | Lucius Fox  | $10,000 |
| O-12350  | Lucius Fox  | $12,000 |
| O-12351  | Lucius Fox  | $10,000 |
| O-12352  | Lucius Fox  | $14,000 |

#### 1.3.1 Step 1: Partitioning and aggregating the target sheet (Events)

The Target sheet (Events) is partitioned by the Granularity (Here the guest name). Each partition will have:

* One guest name (which is the partition name)
* A TOTAL COST for that guest name.

| Partition   | Total Cost |
| ----------- | ---------- |
| Bruce Wayne | $6,000     |
| Lucius Fox  | $9,000     |

*After step 1,the Event sheet is grouped by guest name and the Cost of all events is aggregated with the SUM aggregation*

> Note that the aggregation (here SUM) can be set to anything. It is a parameter of the linked column.

#### 1.3.2 Step 2: Injecting the partitioned data in the main sheet (Orders)

Once the partition has been computed, we can now inject their value in the main sheet. In the table below, see how we inject $6,000 to the lines where the customer is Bruce Wayne and how we inject $9,000 to the lines where the customer is Lucius Fox.

| order id | Client name | Profit  | Total Event Cost |
| -------- | ----------- | ------- | ---------------- |
| O-12345  | Bruce Wayne | $10,000 | $6,000           |
| O-12346  | Bruce Wayne | $15,000 | $6,000           |
| O-12349  | Lucius Fox  | $10,000 | $9,000           |
| O-12350  | Lucius Fox  | $12,000 | $9,000           |
| O-12351  | Lucius Fox  | $10,000 | $9,000           |
| O-12352  | Lucius Fox  | $14,000 | $9,000           |

#### 1.3.3 Step 3: Aggregating the injected data in the main sheet (Orders)

Whether we are working in grids, pivot tables or charts, we almost only deal with aggregated data. Data from lookup columns can also be aggregated in the main sheet.

In the table below, the aggregated values are indicated in the bottom row.

| order id       | Client name     | Profit      | Event Cost per Client |
| -------------- | --------------- | ----------- | --------------------- |
| O-12345        | Bruce Wayne     | $10,000     | $6,000                |
| O-12346        | Bruce Wayne     | $15,000     | $6,000                |
| O-12349        | Lucius Fox      | $10,000     | $9,000                |
| O-12350        | Lucius Fox      | $12,000     | $9,000                |
| O-12351        | Lucius Fox      | $10,000     | $9,000                |
| O-12352        | Lucius Fox      | $14,000     | $9,000                |
| -------------- | --------------- | ----------  | ------------------    |
| COUNT:         | COUNT UNIQUE:   | SUM:        | SUM:                  |
| **6**          | **2**           | **$71,000** | **$15,000**           |

When adding the profit together, notice that KAWA does a simple SUM across all rows. In fact, `$10,000 + $15,000 + $10,000 + $12,000 + $10,000 + $14,000 = $71,000`.

> However, when adding the event cost by client, KAWA does a sum distinct, counting each client only once. More generally, at each level of grouping, KAWA will keep track of all the partitions met and count them only once.

Here, there is only one group, containing all the rows (The grand total is performed on all the rows). KAWA detects that the partition `Bruce Wayne` appears twice and that the partition `Lucius Fox` is here three times. It then counts each one once only when performing the Sum. That explains why the SUM of event cost by client is: `$6,000 + $9,000 = $15,000` and not `$6,000 + $6,000 + $9,000 + $9,000 + $9,000 + $9,000`.

#### 1.3.4 Step 4: Mixing local columns with lookup columns in a sheet

To complete our example, we want to compute the net profit per customer. This is done by subtracting the Total profit and the Event cost, for each customer.

Here, the net profit for Bruce Wayne would be:

`NET PROFIT('Bruce Wayne') = ( $10,000 + $15,000 ) - $6,000 = $19,000`

Notice that in this formula, we are dealing with two different granularities:

* Profit is at the *Order granularity*, there is one value per order.
* Event cost is at the *Customer granularity*, there is only one value per customer.

In KAWA, you would write this simply like this:

<div data-with-frame="true"><img src="/files/y7crbv0kTxK61CzIFt5r" alt=""></div>

When aggregating values of this formula together, meaning computing:

`SUM( Net Profit) = SUM( Profit - Cost by Client)`

KAWA will look at all the granularities within the aggregation and will automatically apply the Distinct functions for each one, at the right level. Here, profit will be summed at the Order granularity and Cost by client at the client granularity.

> This system will not work when one aggregation operation deals with multiple granularities originating from multiple linked columns. To make those cases work, please refer to the [Level of detail documentation](https://docs.kawa.ai/03_00_computations/pages/N0Lf8zS6i9wqaFl0JdpY#id-1.2-types-and-levels-of-detail). You would need to wrap each Linked column in its own group function.

| order id       | Client name     | Profit      | Cost per Client   | Net Profit    |
| -------------- | --------------- | ----------- | ----------------- | ------------- |
| O-12345        | Bruce Wayne     | $10,000     | $6,000            | $4,000        |
| O-12346        | Bruce Wayne     | $15,000     | $6,000            | $9,000        |
| O-12349        | Lucius Fox      | $10,000     | $9,000            | $1,000        |
| O-12350        | Lucius Fox      | $12,000     | $9,000            | $3,000        |
| O-12351        | Lucius Fox      | $10,000     | $9,000            | $1,000        |
| O-12352        | Lucius Fox      | $14,000     | $9,000            | $5,000        |
| -------------- | --------------- | ----------  | ----------------- | ------------- |
| COUNT:         | COUNT UNIQUE:   | SUM:        | SUM:              | SUM:          |
| **6**          | **2**           | **$71,000** | **$15,000**       | **$56,000**   |

The Total net profit is `$71,000 + $15,000 = $56,000`. Also note that the net profit at row level do not make much sense as we subtract the total cost per client with the profit for one order only.

## 2. Using Lookup Columns with a Single Sheet

### 2.1 When is this useful?

You don’t need two sheets to use Lookup Columns. Even with a single sheet, you can match rows to each other. This is helpful when:

* You want to compare detailed data to totals (e.g. each order vs. total by region).
* You want to bring in group-level metrics (like sums or averages) next to individual rows.
* You want to avoid using formulas or manually duplicating summary data.

### 2.2 Example: Sales Data

Let’s say you have a sheet called **Sales** with each row as an order:

| Order ID | Region | Year | Product | Sales Amount |
| -------- | ------ | ---- | ------- | ------------ |
| O-001    | West   | 2024 | Laptop  | 500          |
| O-002    | West   | 2024 | Phone   | 300          |
| O-003    | East   | 2024 | Laptop  | 450          |
| O-004    | East   | 2024 | Phone   | 550          |
| O-005    | West   | 2025 | Laptop  | 600          |
| O-006    | East   | 2025 | Laptop  | 700          |

### 2.3 Step-by-step: Adding Lookup Columns in the same sheet

#### 2.3.1 Step 1 — Total sales per Region & Year

* In the Sales sheet, click on “Enrich Data” → choose Lookup Column.
* Under Select sheet, choose the Current sheet (Sales).
* In Columns to add, choose the column Sales Amount and set aggregation to SUM.
* Under Map lookup columns, match:
  * Current Sheet: Region ↔ Lookup Sheet (also Sales): Region
  * Current Sheet: Year ↔ Lookup Sheet (also Sales): Year
* Click Create.
* Get a column: Sales Amount by Region, Year.

#### 2.3.2 Step 2 — Average sales per Region

In the same Lookup Column setup:

* Column to add: Sales Amount → Aggregation: AVERAGE.
* Map lookup columns:
  * Region ↔ Region
* Get a column: Sales Amount by Region.

#### 2.3.3 Step 3 — Order count per Region

* Column to add: Order ID → Aggregation: COUNT.
* Map lookup columns:
  * Region ↔ Region
* Get a column: Order ID by Region .

#### 2.2.4 Result

Your table will now look like this:

| Order ID | Region | Year | Product | Sales Amount | Sales Amount by Region, Year | Sales Amount by Region | Order ID by Region |
| -------- | ------ | ---- | ------- | ------------ | ---------------------------- | ---------------------- | ------------------ |
| O-001    | West   | 2024 | Laptop  | 500          | 800                          | 466.7                  | 3                  |
| O-002    | West   | 2024 | Phone   | 300          | 800                          | 466.7                  | 3                  |
| O-003    | East   | 2024 | Laptop  | 450          | 1 000                        | 566.7                  | 3                  |
| O-004    | East   | 2024 | Phone   | 550          | 1 000                        | 566.7                  | 3                  |
| O-005    | West   | 2025 | Laptop  | 600          | 600                          | 466.7                  | 3                  |
| O-006    | East   | 2025 | Laptop  | 700          | 700                          | 566.7                  | 3                  |

These three examples show how Lookup Columns can give you different perspectives on your data without leaving the original sheet.

* **Sales Amount by Region**, Year helps you quickly compare each row’s sales to the total for its region in a specific year.
* **Sales Amount by Region** shows the typical sales value for that region, useful for benchmarking performance.
* **Order ID by Region** reveals the volume of transactions, allowing you to spot high-activity areas.

By combining these aggregations, you can analyze sales performance, identify trends, and uncover patterns — all dynamically, without manual grouping or formulas.

### 2.3 Different grouping levels

* Group by 1 field → e.g., Region only → total sales for all years and products in that region.

<div data-with-frame="true"><img src="/files/Sr6htIfaa1kj9P6OJXNd" alt=""></div>

* Group by 2 fields → e.g., Region + Year → total sales for that region in that year.

<div data-with-frame="true"><img src="/files/E13amxPGNadUAtQ58qO7" alt=""></div>

* Group by 3 fields → e.g., Region + Year + Product → most granular aggregation.

<div data-with-frame="true"><img src="/files/4EyNYQXAXEvViOz5Aum9" alt=""></div>

The grouping level is defined by the fields you map in Map lookup columns.

* With fewer grouping fields, you get a higher-level summary for broad trends and strategic insights.
* With more grouping fields, you can drill down into detailed performance, compare specific product lines, or analyze results within precise segments.

This flexibility means you can move seamlessly between a big-picture overview and granular investigation, all within the same table and without creating separate reports.

### 2.4 Why this is powerful

* Easy comparison: See both row-level and group-level data side by side.
* Auto aggregation: KAWA calculates the totals — no formulas needed.
* Flexible: You can use this with categories, dates, customers, etc.

## 3. Editing lookup columns

> This section explains how to edit an existing Lookup Column and change the grouping level using aggregations, filters, and partitions (fixed or dynamic), using a single Sales table as the example.

### 3.1 Example: from simple counting to flexible analysis

#### 3.1.1 Step 1 — Open editing

Click the **Sales Amount by Region, Year** column header.

#### 3.1.2 Step 2 — Open editing

* In Column name, enter Sales Amount by Region, Year (2024).
* In Filters, turn off Sync with main view (so it won’t inherit global filters).
* Click + → add Year = 2024.

<div data-with-frame="true"><img src="/files/9K4IaG4YULIRgqvf7LNJ" alt=""></div>

* Click Apply.

<div data-with-frame="true"><img src="/files/oFnYDMVsMgIhJNr0LDgw" alt=""></div>

You now have a column with a local 2024 filter, independent of other views.

#### 3.1.3 Step 3 — Fixed grouping: Region & Year (Fixed partitions tab)

* Next to Static Partition: Region, click Edit icon → Configure partition.

<div data-with-frame="true"><img src="/files/ErxkHnIU7LHca7T1MTvj" alt=""></div>

* Switch to Fixed partitions.
* In Configure links, delete:
  * Year ↔ Year

<div data-with-frame="true"><img src="/files/Sq9aicKbo6Fgcpj82DY0" alt=""></div>

* Click Apply.

<div data-with-frame="true"><img src="/files/qQBJ2R72Scnnp34nrSxr" alt=""></div>

What you’ll see (as in the screenshots):

* Before: the header shows Static Partition: Region, Year, and the preview now has Region: \[ ] Year: \[ ] will be \[ ].
* After: the header updates to Static Partition: Region, and the preview has a single input Region: \[ ] will be \[ ].
* The aggregation chips at the bottom (e.g., Count: 6, First: West) remain, but their values will respond to the new partition once you test keys or apply filters.

Use the preview to validate:

* Enter Region = West → will be 1400

<div data-with-frame="true"><img src="/files/IVTShDvUIV2GJ2furGcP" alt=""></div>

* Enter Region = West, Year = 2025 → will be 1700

> Tip: If you still have a Year = 2024 filter from the previous step, counts for 2025 will show 0.

#### 3.1.4 Step 4 — Adaptive grouping (Dynamic partitions tab)

* Next to Static Partition: Region, click Edit icon → Configure partition.
* Switch to Dynamic partitions.
* Columns to include: select Region.
* Columns to exclude: select Year.

<div data-with-frame="true"><img src="/files/ZyOhXzNhBYChMkHguMOS" alt=""></div>

* Click Apply.

How it works: Automatically Region = East → 1700.

<div data-with-frame="true"><img src="/files/UIdROdDBenXiEOdMy1VI" alt=""></div>

Year never affects grouping (it’s in exclude).

Dynamic partitions adapt to the fields visible in the current view and its filters. Use them for exploratory analysis; for fixed KPIs prefer Fixed partitions.

#### 3.1.5 Step 5 — Aggregations

<div data-with-frame="true"><img src="/files/JlqAc7NVwkNZWj8CIAr0" alt=""></div>

* Click the aggregations link (under the column name).
* Choose a function: SUM, COUNT, AVERAGE, MIN, MAX, etc.
* Added totals appear at the bottom as chips (e.g., Sum: 3,100, First: West).
* Totals are calculated within the current partition and respect the Filters.

### 3.2 Summary — Why this matters

Editing Lookup Columns lets you reshape metrics without rebuilding or writing formulas.

* Fixed vs Dynamic partitions: keep KPIs stable or let groupings adapt to the view.
* Column-level filters: scope a metric (e.g., 2024 only) without touching other views.
* Instant feedback: preview + aggregation chips show results immediately.

Net result: from one Sales table you can get the right totals, counts, and averages at the right granularity, faster analysis, and cleaner, reusable reports.


# Mapping columns

Mapping columns are a useful feature for performing ad-hoc data enrichment by manually associating values from a dimension with custom mapped values.

## 1. Creating mapping columns

Mapping columns can be created from the Enrich Data section > **Manual Input**.

<div data-with-frame="true"><img src="/files/eDh2wHrJLQioPAVHL3hd" alt=""></div>

They let you input manual mappings for the values of your dimension columns.

For example, you can manually define the desks at which your traders work:

<div data-with-frame="true"><img src="/files/6nF1gGIYX2j1DaQmNzXo" alt=""></div>

*Here, we are defining a mapping column for the Trader dimension. A Desk name was manually selected for each trader of the firm.*

> When a dimension has a high cardinality, some of the available values might be missing from the displayed list. It is recommended to use CSV files to configure exhaustive mappings of large dimensions.

<div data-with-frame="true"><img src="/files/NNSi4nj9QZlZxn66SmrC" alt=""></div>

The name of the newly created mapping column should be entered in the header of the input column, as shown on the screenshot above.

**CSV imports and exports:** Your mapping can be exported as a CSV file and imported into the software.

Below, the CSV associated with the trading desk mapping:

```csv
trader,desk
Alice Brown,EXO
Bob Johnson,EXO
Jane Smith,EXO
John Doe,EXO
Joojoo Boy,CRYPTO
Tom Davis,CRYPTO
```

This CSV file can be edited manually and loaded back in KAWA.

**Handling values without mapping:**

When the mapping does not cover all the values of your dimension, for example: after an ETL, a new trader was added into the system - and they were not assigned to a desk yet.

* *Empty cell:* All the unmapped values will not be filled and left empty.
* *Original value:* Unmapped values will stay unchanged. A trader without a desk will be rendered as the trader name without change.
* *Custom value:* You can define a placeholder there for unmapped values like: N/A etc...

## 2. Editing a mapping column

In the 3 dots menu of a mapping column, click on **Edit Mapping**. You can then change the mapped values (either manually or by loading a new CSV file), you can rename the column and change the behavior for unmapped values.


# Visualization

In KAWA, data can be visualized through charts, grids, and pivot tables, offering flexible and intuitive ways to explore and analyze information. (See definitions in [Terminology](/13_00_terminology#view) section.) Whether you're tracking trends over time, comparing metrics across dimensions, or drilling down into granular details, KAWA's visualization tools help turn raw data into actionable insights.

A sample of some charts, grids and pivot tables to Visualize various aspects of your datasets:

<div data-with-frame="true"><img src="/files/SzVOqRrOT1HuIhNBQVgV" alt=""></div>

## Manage your views in sheets

Views are created directly in sheets and accessible via the main Tabs.

The tabs can be either Pinned (shown on the entire width of your screen) or Unpinned (Shown in a drop down list). In order to switch between one mode and another, click on the pin icon at the left of the tab bar / or at the right of the tab drop down menu.

*Create a new view:* Click on the create view button situated at the top left (or inside the view drop down menu, when in the Unpinned mode). Then pick the type of view you need: Grid, Chart or Pivot.

*Reorder tabs:* Tabs can reorder using drag and drop.

Clicking on the three dots menu next to each tab will display options. Here are the ones common to all types of views:

* Rename your view - the tab will be renamed as well
* Add a description to your view
* Duplicate a view
* Delete a view

The sharing of sheets and views is documented here: [Sharing](https://docs.kawa.ai/pages/fB7ymYh8AaF8Z1j56W5U#id-2.1-sharing-sheets-and-views)

<div data-with-frame="true"><img src="/files/8YESVaqX5I26qrea6KcS" alt=""></div>

Here is a video that gives a global overview about views and sheets.

[Video tutorials about sheets and views](https://www.youtube.com/playlist?list=PL-glwHeQFKj4jEWqXz16I_RjzslAn2GO-)


# Grid views

Grid view is the most powerful type of visualization in KAWA. They support an unlimited number of rows and have a very wide range of features.

> **Fields:** Each column of the grid is called a *field*. See definitions in [Terminology](/13_00_terminology#field) section. They are connected to your data and each one can be configured independently from the others. Note that you can connect many fields to the same data, if you want to show it in multiple ways in the same grid.

In order to create a new Grid View, click on the plus button at the top left of your sheet, and pick: Grid.

<div data-with-frame="true"><img src="/files/aQWhfhnf3MYOCTmzeWhS" alt=""></div>

## 1. Manage the fields of your grids

### 1.1 Field visibility

You can show and hide your fields using the Hide Fields button situated at the top right of the grid.

<div data-with-frame="true"><img src="/files/2KKXt5rUC6kWozLOIMXn" alt=""></div>

This menu lets you choose which fields you want to show or hide by clicking on the small eye icon in front of each field.

You can also opt to hide or show all of the fields by clicking on the hide all and show all button.

Also note that you can directly reorder fields in the grid by rearranging them from this component (drag and drop).

> Hiding a field does not mean that it is removed from your Grid.

### 1.2 Adding and removing fields

In order to add new fields to your Grid, go to the add fields menu, situated at the top of the Grid. This will let you choose various types of data you can create fields from.

* Data Sources contains all the data from your underlying data sets.
* Linked Views contain columns from the views that were linked to that Grid.
* Formulas are the computations that were added to your sheet
* Python Tools let you add fields that are computed with Python scripts.

Once you find the fields you wish to add, drag and drop it from the add field panel into the grid, at the desired location.

> You are not limited to one field from the same source.

<div data-with-frame="true"><img src="/files/LSj2hmgFLMTBWhw4RizG" alt=""></div>

Removing fields from the grid is done via the three dots menu on the header of the field. Click on the delete field option.

### 1.3 Reordering your fields

To reorder your fields, you have several options:

Usually if the fields of your Grid can fit on your screen, you can directly drag and drop them at the desired position.

> When you move fields over other ones, you will see contextual menus appearing with several options. To reorder the field, please do not drop in a given option but below them all.

Another option to reorder your field is via the contextual menu in the header.

<div data-with-frame="true"><img src="/files/zepfgOMfcjXnLntcPVQW" alt=""></div>

From there, you can either move your field to the first and last position, or move it next to an existing field.

> From the same menu, you can also Freeze your field. Frozen fields are moved to the first position and become insensitive to horizontal scrolling.

### 1.4 Formatting the fields

You have many ways to format your fields in KAWA.

#### a. Conditional formatting

Conditional formatting will apply some style to your fields based on their values. It will either set a style if a value matches a condition or apply some gradient coloring depending on where the values fall within a range.

This type of formatting, click on the Color button at the top of your grid.

#### i Rule based formatting:

In order to create a new Rule based formatting, Click on Color > + Rule > Single color.

Here are the items to configure:

* *Apply to:* It will define whether to color the Groups (Aggregated values) or the Rows (Row values).
* *Combine conditions:* You can have multiple conditions (Adding conditions is done via the + Condition button). If you do, you can pick how to combine them: `AND` (in that case, the style will be applied if ALL the conditions are verified) or `OR` (here, you only need one condition to match to apply the style).
* *Define conditions:* Pick which field will be subject to a condition and configure it following the options given to you. They depend on the type of the field you pick.

> If you are working at the group level, you also must decide how to aggregate your field. For example, you can want to color based on the average of profit or the minimum of sales.

* Lastly, pick the color you want to apply and where you want to apply it: only one field or all the fields.

<div data-with-frame="true"><img src="/files/OTnbHFv8rW86iYdNgStW" alt=""></div>

*Here we color in orange all row values above 100,000 `OR` below 0. The color is applied to the profit field.*

#### ii Gradient based formatting:

In order to configure a new Gradient based formatting rule, click on Color > + Rule > Color Scale.

Similarly to the rule based formatting, pick the field you are basing the gradient on (not that you can compute the gradient on one field and color another).

* *Apply to:* Gradient can either be applied to ROW values or GROUP values (Aggregations). If you pick "GROUP", you must also choose the aggregation to be used. For example: The average of Sales or the Maximum of profit.
* *Gradient configuration:* Pick the color you want and to which values they correspond. Note that the values below the lowest bound will be colored with the lowest bound color. The same goes for the highest bound.

<div data-with-frame="true"><img src="/files/PDwfG2WKRI6SICR3L8mY" alt=""></div>

*Here we apply a gradient to the Row values of the quantity field*

#### iii Managing your rules

The rules can be removed and reordered to define in which priority they are applied. The first one will be applied first, then the second, and so on.

<div data-with-frame="true"><img src="/files/zGVqelULUmZhR43GrCor" alt=""></div>

#### b. Formatting the fields values

Another way to format the fields is to apply a static configuration on all values.

Depending on the type of the field, you will be able to format the content.

Regardless of the type, you can:

* apply a color to the entire field
* pick data position (left, center or right)

<div data-with-frame="true"><img src="/files/PuwFvzg3LsXvvMZ1ikFD" alt=""></div>

**Rendering URLs in the Grid:**

When a column of the grid contains a URL, you can apply a formatter to make that URL clickable:

<div data-with-frame="true"><img src="/files/JZFRNgzUnydHFkI4Ikqv" alt=""></div>

In order to do so, click on the three dot menu of the column containing the URL, and click on **Format Data**.

Check the option: *Format data as link*. You can then pick the column to use to render the labels. For example here, the column CITY was used to render the URL name. As a result, when clicking on the New York city value, the link associated with this row will be opened in a new tab.

## 2. Grouping and Aggregation

Grids are a powerful tool to visualize both row data and aggregated data. You can define aggregation methods for each of your fields and define a hierarchy of groupings to apply.

### 2.1 Define groupings

In order to group your data, you can use the Group button at the top left of the grid. Here, you can add new groups to your grid, reorder the existing groups and delete unwanted ones.

<div data-with-frame="true"><img src="/files/uNuxhE4rRDOdfHtYCbfn" alt=""></div>

*Here, you can see that two levels of grouping were applied, State then City. On the grid you can see the aggregated data per State, and per City and deploy the nodes to reach the row level data.*

> Note that the action of deploying a group will result in a query to the database. It ensures that just the right amount of data is loaded to the interface for optimal performances.

#### 2.1.1 Time Sampling

When applying grouping on temporal fields (`date` or `date time`), you have the option to apply time sampling on the groups.

This is accessible via the small calendar icon from the grouping component.

You will have the possibility to choose the time period you wish to sample your data on.

<div data-with-frame="true"><img src="/files/4ovCkzfzVUHtArNsFdAy" alt=""></div>

*In this example, we group twice on the date column to obtain a year on year monthly comparison. The first group is defined on the Month of the date, while the second one is defined on the Year of the date.*

#### 2.1.2 Number Binning

When applying grouping on a numeric filter, you can apply binning to analyze distributions for example.

This is accessible via the cog icon next to your defined groups. It lets you pick the binning methodology you wish to apply:

* *Auto binning partitions:* This specifies the number of bins you want to divide your data into. If you pick 20, then you will end up with (about) 20 groups. The overall range of your data will be divided in 20 even bins.
* *Fixed bin Width:* this defines the width of each bin. If you define 20, then you will cut your overall range in bins of 20. A range of 1000 will result in 50 groups.
* *Custom bins*: Lets you define how your data will be divided by inputting the boundaries of the bins. For example: `0,10,100,1000` will group together all the numbers below 0, then all the numbers between 0 and 10 (excluded), then 10 and 100, 100 and 1000 and then above 1000.

<div data-with-frame="true"><img src="/files/2FIBZXV3QUAzB1bk24Oo" alt=""></div>

### 2.2 Define aggregations

For each field, you can specify the method used to aggregate data at the group level.

> For example, if we deal with a profit column and we group our Grid by State, we need to define how to aggregate the profit per state. Are you interested in the TOTAL profit per state, or the AVERAGE, etc..

> Note that the aggregation method is identical for all the levels of grouping and does not depend on what you group your data by.

To define the aggregation method for a field, click on the bottom row of the grid and select an option from the list. The available options will depend on the type of field you are looking at: text, number, date, boolean etc...

<div data-with-frame="true"><img src="/files/uybFNWwZCkC2m2UBrwHD" alt=""></div>

The aggregated values will then be shown at the group level, and the overall aggregation on the bottom fixed row.

> You may be interested in multiple aggregation for the same data, say: MIN, MAX and AVG. In that case, create 3 fields on the same data and set the three desired aggregations.

## 3. Linked Views

Linked views can be created from the grid views.

<div data-with-frame="true"><img src="/files/vpu2UPTSd7kWhUYI5TFb" alt=""></div>

*On the image above, the top view is the main Grid view. It is linked to two views that open in the bottom section. Those views are connected to the main one: clicking on a row of it will filter the bottom one. Notice here (in green) the bottom view is filtered on Lindsay Castell.*

> Linked views can be created from any sheet within the workspace, providing an interactive and intuitive way to explore data across multiple data models.

### 3.1 Creating a linked view

To create a linked view, identify a field to which you want to link a view to. In the above example, the link was made with the *Customer Name*. Then, either click on the blue link icon in its header or go in the three dots menu > "Link View With".

The configuration popup will ask you to complete the following steps:

* First: select the sheet to link. It can be the same sheet (current sheet), or any other sheet of the workspace.
* Second: from that sheet, pick the view you want to link. It can be any type of view: Grid, Chart and Pivot.
* Last: configure the link itself by pairing up fields from the main view with fields of the linked views. There is no limit to the number of fields you can pick.

Click on Apply - and a new linked view will be added. Clicking on any row of the top view will filter the bottom view on the field that you picked to create the link.

### 3.2 Managing linked views

On the three dot menu on the link view tabs, you can:

* Delete a linked view
* Rename one
* Export them as CSV (for Grid views)

The linked view panel can be expanded to occupy the full screen and collapsed.

### 3.3 Creating lookup columns

One of the main usage of linked views is to allow the creation of linked columns which are one of the most powerful feature of the platform. Please have a look at the [Documentation on Linked columns ](/03_00_computations/03_02_lookup_columns)for more details.

## 4. Global settings

### 4.1 Context menu

By right clicking on any cell of the grid, you access the global settings and some shortcuts.

* *Go to field:* Is very useful when you are working with a grid containing a lot of fields. It will scroll to and focus on any field that you are looking for.
* *Select Row height:* Will let you pick the height of your rows.
* *Auto size columns:* This option will trigger a resizing of all the columns of your grid, either based on the content off the cells or on the values of the headers.
* *Headers:* Gives you access to the wrapping setting of the headers. You can configure if you want the content of the header to occupy many rows if the title is too long (Wrapped) or if you'd rather truncate them (Ellipsis).

### 4.2 View settings

From the context menu: right click > View Settings, you can access some global settings of the view.

* *Hide empty fields:* Will automatically hide the fields for which ALL the values are empty. This can be useful for sparse grids.
* *Compute overall aggregations:* When you have too much data in your grid, say billions of rows, computing the overall aggregations (In the bottom row) can slow down the interactions. You can then opt to turn it off.
* *Auto compute:* When turned off, modifications in the configuration of your Grid (sorting, grouping, filters, etc) will not be immediately taken into account. You will need to explicitly click on a compute button to trigger the calculation taking in account the new parameters. This is very useful if your grid takes several seconds to compute.


# Chart views

Chart views let you build powerful visualizations of your data. Many different types of charts are available and each one supports a wide range of configuration parameters.

In order to create a new Chart View, click on the plus button at the top left of your sheet, and pick: Chart.

## 1. Configure your Chart

Configuring your chart is made through the Configure chart menu, accessible from the top right.

<div data-with-frame="true"><img src="/files/NCpIsXkOT5W0BPIbH9Eu" alt=""></div>

*There are three main sections in the chart configuration panel: Data, Design and Settings.*

You can pick your chart type at the top of the configuration panel.

**The settings tab:** Contains global settings that apply for most types of charts. They let you control legend, behavior of the axis, display properties and zoom.

The number of labels and label rotation work together. If you want to add more labels, you might need to rotate them by 45 or 90 degrees (vertical).

### 1.1 Configure a bar chart

In the data section, pick the Series and the Groupings. A bar chart can have one or two levels of grouping and does not have any limit in the number of series.

> It is strongly recommended to have only one series when two levels of grouping are present.

#### a. Bar chart with one level of grouping and one or more series

In a bar chart, the first level of grouping will always be the X-Axis.

When configuring a chart with multiple series and one grouping, you can decide to stack the series or show them separately.

The stacking option is found in the design section, in the upper horizontal tab.

<div data-with-frame="true"><img src="/files/h6teyZJSHPYr8oj3L8Ck" alt=""></div>

<div data-with-frame="true"><img src="/files/dVgVQjP3Lzrt6wcOV5sQ" alt=""></div>

*Bar chart showing the profit and sales per sub-category, with and without Stacking*

#### b. Bar chart with two levels of grouping and one series

With two levels of grouping, the first level will be the X-Axis and the second level will be reflected as the various portions of each bar.

<div data-with-frame="true"><img src="/files/Nb0sXRGbK5s8XWCPLYSU" alt=""></div>

*In the above example, you can see that the first level of grouping (Months) is shown on the X-Axis, while the second level of grouping is reflected on each bar (One color per year)*

#### c. Specific settings for the bar charts

In the design section, you have several options to customize your bar charts:

* Formatting of each series
* Setting colors to each series or each point for the second level of grouping
* Defining gaps between bars
* Show the totals on top of each bar
* Make the bar chart horizontal (Only works for one level of grouping)

#### 1.2 Configure a line chart

Line charts are similar to Bar charts, but are generally preferred to plot trends or evolutions of metrics.

In the data section, pick the Series and the Groupings. A line chart can have one or two levels of grouping and does not have any limit in the number of series.

> It is strongly recommended to have only one series when two levels of grouping are present, otherwise the chart will be difficult to read.

#### a. Line chart with one level of grouping and one or more series

In a bar chart, the first level of grouping will always be the X-Axis.

<div data-with-frame="true"><img src="/files/OEg6GX3j0gVvs1Vznzjj" alt=""></div>

*Simple line chart showing the evolution of profit per day*

When plotting multiple series for one grouping, you can decide whether to plot all series on the same axis or on separate axes. If you choose the split chart option, you can have one chart per axis.

<div data-with-frame="true"><img src="/files/lQPYfYzweRjH4tcsSHi2" alt=""></div>

*Plotting both Quantity (Ranges from -2 to 26 on a given date) and the Profit (Ranges from -200K to 3M on a given date) on two different axes.*

#### b. Line chart with two levels of grouping and one series

Adding a second level of grouping on top of a temporal dimension is useful to see the breakdown of some indicator per another dimension.

<div data-with-frame="true"><img src="/files/QgNyEBP2lqSRom3r2e3q" alt=""></div>

*Here, we added the Segment as the second level of grouping - as a result, we have one line showing the evolution of each segment over the days*

#### c. Specific settings for line charts

In the design section, you can access the following parameters, specific to line charts:

* *Align zero:* If we have many different axes, this setting will determine whether or not to align all the 0 horizontally.
* *Fill in temporal gaps:* If the X-Axis is a temporal one, date or date time, empty points will be added in case the dataset is missing dates. For example, if the dataset has a point for the 1/1/2020, and one for the 1/3/2020 - the chart will add a tick on the X-Axis on the 1/2/2020.

For each series, you can also configure if you want to:

* Smooth the lines
* Draw an area beneath the lines
* Add a trend line (Polynomial or Linear)
* Change line width / line style

### 1.3 Configure a pie chart

In the data section, pick the Series and the Groupings. A pie chart can have one or two levels of grouping and does not have any limit in the number of series.

> When adding more than one series, KAWA will create as many pie charts as series.

> Only show series containing positive values. Otherwise the pie chart can lead to misinterpretation of the data.

<div data-with-frame="true"><img src="/files/jb5f1GrbFkbdkqbYm5oV" alt=""></div>

*A simple pie chart showing the sales per state. A Pie chart will show the ten biggest categories and then aggregate all the others in an `Other` category*.

<div data-with-frame="true"><img src="/files/OxbpqLmAVttP91twq7f1" alt=""></div>

*Make sure to select the Sunburst option in the settings tab for a pie chart with two levels of groupings*.

> When using more than one level of grouping on a pie chart, you should use the `SUM` aggregation.

The design tab offer special options for Pie charts:

* *Doughnut:* Plots as a doughnut instead of a pie chart
* *Labels outside:* Shows the labels outside of the chart. Can be more readable in some configurations.

### 1.4 Configure an indicator chart

Indicator charts are suited to show high level indicators or KPIs in dashboards. They can be used either to show the global aggregation of a measure for the entire dataset (say the global average of profit) or to show the latest value for a metric and compare it to the previous one.

#### a. Show a global metric

This is the most basic usage of an indicator chart. In order to configure an indicator chart this way, just pick one series and no grouping.

<div data-with-frame="true"><img src="/files/yyLWgeurrl9FmyUxt4ht" alt=""></div>

#### b. Show the last value for a metric, and compare it to its previous values

Indicator charts can be configured to show the last value for a metric. In order to do so, pick one series and one grouping. The chart will show the last point for that metric applying the defined grouping.

<div data-with-frame="true"><img src="/files/U6NPLLwWSYx26qMaqVzh" alt=""></div>

The compare to menu at the bottom (below the Group by) is useful to set up some comparisons:

You can pick many among:

* *Previous value:* To see the evolution of a metrics from one group to the next.
* *Minimum:* To compare the last value with the minimum
* *Maximum:* To compare the last value with the maximum
* *Average:* To compare the last value with the average
* *Constant goal:* To compare the last value with a fixed value - that can represent a goal to reach for instance.

> In the design section, you can set the color of the trend arrows - by default an Upward trend is Green, Downward is red and constant is Black.

### 1.5 Configure a scatter plot

A scatter plot is a chart that displays the values of two (or three) variables (series) as points on a chart. Each point on the plot represents one item (or group) and shows how two values are related to each other.

Purpose of a scatter plot:

* Visually assess the relationship (correlation) between two values.
* Identify trends, patterns, clusters, outliers, or possible groupings.
* Examine whether a change in one variable affects another.

#### a. Scatter plots with one grouping level

Scatter plots require at least **one grouping level and two series**. The chart will build a correlation graph between the two series for each group:

* The first series will be plotted on the X-axis (independent variable).
* The second on the Y-axis (dependent variable reacting to X).
* One point represents each group.

On the plot:

* If all points lie along an upward line → positive correlation.
* If they go downward → negative correlation.
* If they are scattered without pattern → no correlation.

<div data-with-frame="true"><img src="/files/UX3VU4Yo4CTWZVsLbqQY" alt=""></div>

*In the example above, you can see the relationship between the average life expectancy of people (shown on the X-axis) and GDP per capita (Y-axis). Grouped by country. Each point corresponds to a group (country). This chart shows a positive correlation: the higher the GDP per capita, the longer people live*.

When the user adds **a third series**, each dot on the scatter plot shows one observation (or group). The position of the dot is based on two values: one for the X axis and one for the Y axis.The color of the dot shows the value of the third variable.

<div data-with-frame="true"><img src="/files/SqlE67RfCAC5Qia4TgjX" alt=""></div>

When you add Population as a third variable, it is shown by the color of each point:

* Darker colors represent countries with a larger population
* Lighter colors show countries with a smaller population This allows you to explore three variables at once:
* Position shows GDP and life expectancy
* Color shows how many people live in each country You can still see a positive trend: countries with higher GDP per capita often have higher life expectancy — and now you can also notice how population size varies across them.

#### b. Scatter Plots with Two Levels of Grouping

**Scatter plots with two series**\
Each point represents one group. Points differ in color depending on which group they belong to. Their position is determined by two variables (X, Y).

<div data-with-frame="true"><img src="/files/PVu4iAqPYB2uFSGQQAGT" alt=""></div>

In the example above, you can see the relationship between the average life expectancy of people (shown on the X-axis) and GDP per capita (Y-axis) for different countries in two years — 1995 and 2025.

* Each point represents a specific country in one of the selected years.
* The color of the point indicates the year (blue — 1995, red — 2025).
* The chart shows a positive correlation — countries with higher GDP per capita tend to have higher life expectancy.
* The grouping is done by the fields Country and Year, allowing comparison of trends across years.

**Scatter plots with three series**\
Each point represents one group. Points differ in color depending on which group they belong to. Their position is determined by two variables (X, Y), and the size indicates the value of a third variable.

<div data-with-frame="true"><img src="/files/FPuu5bfgCbJrKu2E5rRS" alt=""></div>

When a third variable is added, each point not only shows a country's GDP per capita (X-axis) and life expectancy (Y-axis), but also uses size to represent an additional value — for example, population.

* Position shows GDP and life expectancy
* Color indicates the year
* Size represents the third variable (e.g., population) This lets you see three types of data at once and compare countries by economy, health, and population size.

### 1.6 Configure a Sankey chart

Sankey charts work with one series and as many levels of grouping that you need. It requires no specific configuration in addition to this.

<div data-with-frame="true"><img src="/files/llE1hhXdif14JrKlpTCx" alt=""></div>

*This chart shows how the Sales of each quarter are distributed across Segments then States.*

### 1.7 Configure a Map chart

Use the map to visualize a metric grouped by country. The map supports a single Group by Series. Drill-down is not supported.

<div data-with-frame="true"><img src="/files/SyIvPTCf5rWZVT4Wih2b" alt=""></div>

*This map shows how the total cost of wine is distributed across countries.*

Color scale on the right shows the min/max values and the color gradient:

* Hovering over the scale highlights the countries that fall in that range and shows value labels on them.
* Labels display the aggregated value from Series (e.g., Sum of price).
* Moving the cursor away restores the map’s default colors.

<div data-with-frame="true"><img src="/files/o0DmyYtXDBORwsvsMs2w" alt=""></div>

## 2. Series and Groups

Series and Groups define what data will be plotted in the charts. They play different roles in different types of charts.

### 2.1 Chart series

<div data-with-frame="true"><img src="/files/dh0ggJ1blf95EnCvrIqd" alt=""></div>

Each field has several configuration options:

#### 2.1.1 Display options

You can configure the field name (how it will appear on the charts) as well as the format of labels associated with this field. (For example set a unit or a number of digits to show after the decimal point). All these parameters are accessible on the three dots menu of each field.

#### 2.1.2 Aggregation

The charts will show aggregated values for each field. For example: The sum of profit per segment or The average of sales per region, etc... The first drop down menu contains a list of aggregation to pick from.

> **IMPORTANT:** Be mindful of the aggregation method when you are plotting time series with time sampling. It is recommended to use aggregations such as Average, Min or Max (and avoid SUM as the value will grow with the number of samples falling within each bucket).

#### 2.1.3 Value calculation (Show As)

In charts, you can define ad-hoc calculations to apply to your fields by setting the Show As parameter.

**Table 1:** Calculation Methods for one level of grouping:

| Calculation          | Description                                                                                                                             |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Original Value       | Displays the aggregated value of each measure without any transformation.                                                               |
| % of total           | Displays the percentage contribution of each aggregated value to the overall total.                                                     |
| % of group           | For one level of grouping, behaves the same as the % of total.                                                                          |
| Cumulated            | Displays the running total, combining the current value with all previous ones.                                                         |
| Difference with      | Displays the difference between the current value and the one before it. No value will be shown for the first data point.               |
| % of difference with | Displays the difference between the current value and the one before it in proportion. No value will be shown for the first data point. |
| % of previous value  | Shows each point as the percentage of the previous one.                                                                                 |

**Table 2:** Calculation Methods for two levels of grouping:

| Calculation          | Description                                                                                                                                                 |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Original Value       | Displays the aggregated value of each measure without any transformation.                                                                                   |
| % of total           | Displays the percentage contribution of each aggregated value to the overall total, across all points.                                                      |
| % of group           | Display the percentage contribution of each aggregated value within its parent group.                                                                       |
| Cumulated            | Displays the running total within each group, combining the current value with all previous ones, still within each group.                                  |
| Difference with      | Displays the difference between the current value and the one before it, in the same group. No value will be shown for the first data point.                |
| % of difference with | Displays the difference between the current value and the one before it, within each group, in proportion. No value will be shown for the first data point. |
| % of previous value  | Shows each point as the percentage of the previous one in the same group.                                                                                   |

**Moving calculations:** Moving calculations can also be configured from within the Show As menu.

* *Apply to:* Defines the level of grouping to which you apply the calculation
* *Aggregation:* Defines how to aggregate the data points together. This aggregation will be used to combine aggregated values together. (For example: the cumulative SUM of AVERAGES)
* *Previous Values:* Specifies how many previous values to include, defined as a positive integer.
* *Next Values:* Specifies how many next values to include, defined as a positive integer.
* *Current value:* Specifies whether or not to include the current value.

<div data-with-frame="true"><img src="/files/Kz1m05jveomPS9mN6mkV" alt=""></div>

*Above the result of using a moving calculation with: Apply to: Date, Aggregation: Average, Prev: 10, Next: 0, Current value: Yes. It computes the sliding average over the last 10 points.*

### 2.1 Chart groupings

#### 2.1.1 Role of groupings in various types of charts

Groupings of a chart can be configured via the Group by section of the Data tab. Depending on the type of chart that you pick, grouping will play different roles.

The table below illustrates the given configuration:

Profit (Series) by Date (First level of grouping) and Segment (Second level of grouping)

| Chart Type        | 1st lvl of Grouping                                                                                                    | 2nd lvl of Grouping                                                                                                                            |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Bar Chart         | X-Axis. One tick on the X-Axis per date                                                                                | Defines how each bar will be broken down. Each bar will be split between segments to show the profit breakdown on a day, per segment.          |
| Line Chart        | X-Axis. One tick on the X-Axis per date                                                                                | Will correspond to the plotted lines. Here, we will have one line per segment, each one representing the evolution of profit for that segment. |
| Pie Chart         | Pie categories. Here, one pie slice per day.                                                                           | When used as a sunburst chart, it will define the second level of slices.                                                                      |
| Scatter Chart     | There will be one point on the scatter chart per value of the first level of grouping. Here, one point per date.       | Ignored.                                                                                                                                       |
| Indicator Chart   | WIll display the value of the last group if a first level of grouping is defined (Instead of the overall aggregation). | Ignored.                                                                                                                                       |
| Candlestick Chart | X-Axis. Same as bar chart                                                                                              | Ignored.                                                                                                                                       |
| Boxplot           | X-Axis. Same as bar chart                                                                                              | Ignored.                                                                                                                                       |
| Sankey Chart      | First level of the Sankey                                                                                              | Second level of the Sankey (There is no limit to the number of groups for the Sankey charts)                                                   |
| Map Chart         | The first level of grouping must be a geographic denomination like a Country or a State etc..                          | Ignored.                                                                                                                                       |

#### 2.1.2 Configuration of Groupings

Groupings can be configured in the same way as in the Grid view. Like in Grids, chart groups support Time sampling and Number binning.

> Number binning can be used to plot the distribution of a measure across a dataset:

<div data-with-frame="true"><img src="/files/RqPPUmRkUriwIIYqkwRa" alt=""></div>

## 3. Drill down and Underlying data

A chart always displays aggregated data, but KAWA offers options to either view the underlying raw data for each point or drill down into other dimensions. Both these options can be explored by doing a right click on a data point.

### 3.1 Show underlying raw data

Right clicking on a bar, and selecting "Show Underlying Data", a new section opens below the chart. It contains a Grid view filtered on the selected bar.

Because it is a Grid View, you an feel free to interact with it to get further insights about this particular data point.

This bottom view can be expanded (full screen) or dismissed (closed) by clicking on the controls on its top right.

<div data-with-frame="true"><img src="/files/eC5qTdDkYb5ckcfKQsPk" alt=""></div>

*Raw data is shown for Q3 2022.*

### 3.2 Drill down into other dimensions

Right clicking on a bar, and selecting "Drill Down By" will open a sub menu that will let you select into which dimension you would like to drill down into.

This can be repeated as many time as you need by doing consecutive drill downs. It can also be combined with show underlying data at any point.

To help you keep track of what dimensions were picked, a breadcrumb will be displayed at the top left of the chart. CLicking on the (x) will reset the chart back to its original configuration.

<div data-with-frame="true"><img src="/files/PCKJwmgeYrXrlMmj6dJc" alt=""></div>

*Here, we are showing the Total Sales per City having drilled down on Q2 2020 and then the state of California.*


# Pivot table views

Pivot tables are a great way to dynamically reshape and aggregate data across multiple dimensions. in KAWA, pivot tables have the same powerful features as in the most advanced spreadsheet softwares. The main difference is that it can operate on huge datasets, with billions of rows.

In order to create a new Pivot Table, click on the plus button at the top left of your sheet, and pick: Pivot Table.

## 1. Rows, Columns and Values

In the following example, we configured a Pivot with:

* *Two rows:* Category and Sub-Category. It creates two nested row groupings. The pivot has one row per Category (showing the aggregated values per category), and one row per Sub-Category for each Category.
* *Two columns:* Similarly to the rows, we created two columns: Year and Segment. This results in two nested column groupings. The pivot features one column per year with the yearly aggregations of the values. It also shows one column per segment per year, with the corresponding aggregations.
* *One Value:* We are interested in the Sum of profit. This is the metric that will be shown in each cell of the pivot.

<div data-with-frame="true"><img src="/files/jM9dKuBmlbXMYmQcYN1h" alt=""></div>

> Note that you can easily fold and unfold your rows and columns by clicking on the (plus) and (minus) signs. When multiple rows or columns are nested, clicking on upper level (plus) and (minus) lets you control which nodes are deployed in bulk.

### 1.1 Configuring the values

Adding values to the pivot table can be done by clicking on the (+) button next to the Values header or by directly dragging and dropping data from the Columns panel into the Value section.

Value can be removed by using the Trash Can icon or simply disabled by toggling them off.

For each value field, you can configure its format and name, its aggregation, and its calculation (Show as).

#### 1.1.1 Value format and name

In order to rename a field, click on the three dots menu next to it and click on: Rename Field.

The format can be changed too by clicking on the three dots menu and Format data. The dialog will depend on the type of the field.

#### 1.1.2 Value aggregation

**Simple aggregations:** The pivot table will always show aggregated values. In the first drop down of the value cards, pick the aggregation that you wish to use. The list of available aggregations will depend on the type of the data.

> When adding multiple values on the same measure (For example: Sum of Sales and Average of Sales, make sure to rename the fields to avoid confusions).

<div data-with-frame="true"><img src="/files/CrcvoGO3rAHqsebmPtSy" alt=""></div>

*In the above example, notice two value fields: Sum and Average of Sales. They have been renamed to reflect the aggregation. A dollar prefix has been added in the format data section.*

**Multi aggregations:**

When selecting the last value of the aggregation drop down (Multi-aggregation), a configuration popup will be displayed:

It lets you configure 5 parameters:

* *Basic:* Corresponds to the aggregation that will be used for the pivot values (as opposed to the totals per rows/columns or grand totals).
* *Total per row:* This defines how the Basic aggregations are further aggregated to come up with the totals per row (The last columns of the pivot table, shown in blue).
* *Total per column:* This defines how the Basic aggregations are further aggregated to come up with the totals per column (The last row of the pivot table, shown in purple).
* *Axis and Grand Total:* Axis will define what will be used to compute the Grand total: Choose Rows if you want it to be computed from the totals per row. Choose Columns if yoy prefer it to be computed from the totals per column. The Grand Total aggregation defines which aggregation to use in order to further combine the values of the chosen Axis.

<div data-with-frame="true"><img src="/files/4KXsM74Sg13W3XKCrcit" alt=""></div>

*In the above example:*

* *Basic: SUM.* This means that the values of the pivot will be summed from the Row values of the dataset.
* *Total per row: MIN.* It means that the last column for this value (In Blue on the illustration) will be computed by taking the MIN of all the SUMS on each row.
* *Total per column: MAX.* It means that the last line for this value (In Purple on the illustration) will be computed by taking the MAX of all the SUMS on each column.
* *Axis: Row and Grand total: Sum.* Here, we will compute the Red cell by adding together the Blue cells. It will be the SUM of the MINS of the SUMS.

#### 1.1.3 Value calculation (Show As)

This is the same documented here (Similar as the one available in the Chart Views): [Configure chart series](https://docs.kawa.ai/04_00_visualization/pages/VnqNzSJwMRm9Mcq1vHMX#id-2.1.3-value-calculation-show-as)

### 1.2 Configuring the Rows and Columns

Rows and Columns have identical configuration on the Pivot table.

Adding rows/columns to the pivot table can be done by clicking on the (+) button next to the Rows/Columns header or by directly dragging and dropping data from the Columns panel into the Rows/Columns section.

Rows/Columns can be removed by using the Trash Can icon or simply disabled by toggling them off.

*Configure the Binning or Time sampling:* When you use temporal data as row or column, you can configure Binning (Click on the cog icon next to the field name) or Time sampling (Click on the calendar next to the field name). These features are described in the Grid View: [Configure Binning](https://docs.kawa.ai/04_00_visualization/pages/NQYwqlup7wgE4ltPeTL1#id-1.3-reordering-your-fields) and [Configure time sampling](https://docs.kawa.ai/04_00_visualization/pages/NQYwqlup7wgE4ltPeTL1#id-2.1.1-time-sampling).

*Configure order:* The order of each row and column can be defined by picking a direction (Ascending, Descending, Absolute value Ascending and Descending for numeric metrics) and a field.

*Toggling on and off the total:* You can toggle on and Off the Total checkbox to show or hide the corresponding totals for this Row/Column.

## 2. Drilling Down

### 2.1 Standard Drill Down

A Pivot table only show aggregated values in its cells. Accessing the Raw data for each of them is done by clicking on the magnifying glass in any cell of the pivot.

A bottom section will then open, showing the raw data that was used to computed that particular value.

<div data-with-frame="true"><img src="/files/9fJFclIQI2LMDujYiSFx" alt=""></div>

*In this example, you can see the data that was used to compute the 3,242,055 in Sales for Binders in Florida. Note that this drill down view is a Grid View and can be further configured. You could group by year and explore how this total is distributed across various periods of time.*

### 2.2 Visual Drill Down

You can configure the pivot to open specific views instead of the basic drill down grid when users click on particular cells.

In the three dots menu of each Row and Column, clink on Link View. You can then associate various existing views with each value of your rows or columns.

<div data-with-frame="true"><img src="/files/C6or5oq0XqnIUV91NAWS" alt=""></div>

*In this example, a monthly sales evolution chart was associated with the Accessories sub category. When a user clicks on a cell in this particular sub category, the chart will open, filtered in the Selected state. Notice that you can also go back to the drill down view by clicking on the corresponding tab on the lower section.*

## 3. Conditional formatting

You can configure conditional formatting in Pivot tables in a similar way that you would in Grid views. In the pivot tables, there is no notion of Row Data, so you can only work on conditions operating at the group level. Please refer to this [Conditional Formatting](https://docs.kawa.ai/04_00_visualization/pages/NQYwqlup7wgE4ltPeTL1#a.-conditional-formatting).

Both rule based and gradient formatting are available in the pivot tables.

<div data-with-frame="true"><img src="/files/y0uHBObWjY4NcwlQJ0mI" alt=""></div>

*This pivot table was configured as a heatmap to show the breakdown of Quantity sold per quarter and category.*

## 4. Specific interactions

### 4.1 Auto sizing mode

Right clicking anywhere on the pivot will bring specific display options. The Auto size mode is either: *By content* or *Disabled*. When *By content* is selected, each time the configuration of the pivot changes, the width of its columns will be recomputed based on the content of its cells. If you manually change a column width, this change will be persisted.

### 4.3 Aggregation position

Controls where the metric name (Sales, Profit, etc.) appears in multi‑level column headers.

#### 4.3.1 How to use

Right clicking anywhere on the pivot → Choose Aggregation position → Top (default) or Bottom.

<div data-with-frame="true"><img src="/files/I50XdIW5lUs3dHGTnFhv" alt=""></div>

#### 4.3.2 What it does

* **Top** — the metric name is above the dimension headers.

<div data-with-frame="true"><img src="/files/n11IhXWMOsElhSEpcknk" alt=""></div>

* **Bottom** — the metric name is below the dimension headers.

<div data-with-frame="true"><img src="/files/slfGTUUpybulLlxWNwBv" alt=""></div>

The setting applies to the entire Pivot View. It changes header layout only; exports and totals are not affected.

### 4.3 Cell multi select

You can multi select cells on the pivot to access local statistics. Copy and paste into spreadsheet softwares works too.

<div data-with-frame="true"><img src="/files/A0E0yhglpEyRKmUWU1q9" alt=""></div>

### 4.4 CSV export

By clicking on the tab and selecting the "Download CSV" option, you will get a CSV representation of your pivot table.


# Filtering

Filters can be applied to all types of view through the same filter panel. KAWA comes with powerful built-in filters, adapted to all your data types: text, number, booleans, date, date time. See definitions in [Terminology](/13_00_terminology#filter) section.

## 1. The filter panel

### 1.1 Overview

The filter panel is situated at the right of all the views (Charts, Grids and Pivots). It can be toggled on and off by clicking on the green "Filters" button at the top right. This button indicates how many active filters are applied on your view.

<div data-with-frame="true"><img src="/files/6outhSOHGcTyfDGCCdFV" alt=""></div>

*Here, three filters are configured, but only one is applied. The toggle button on each filter card toggles on and off the corresponding filters.*

In this panel, you can configure the filters that are applied to your view, add new ones or remove existing ones.

To add new filters, click on the (+) button situated at the top of the panel. You can then choose what you want to filter. You have the choices between the fields of the current view or the columns of the underlying sheet. **You do not need to add the filtered columns to your views.**

### 1.2 Filtering grouped data

When your view is grouped (It is always the case with pivot tables, often the case with Charts, and sometimes with Grids), you can decide at which level of grouping you want to apply your filter.

In the apply to menu, pick which group and which aggregation you want to apply your filter on.

<div data-with-frame="true"><img src="/files/J6zkaU9V1KOG2HoSPG7h" alt=""></div>

*In this example, we want to keep only the states with a negative Total. We picked: Apply to State, aggregation: SUM.*

## 2. Configuring your filters

### 2.1 The Text filters

#### 2.1.1 Filtering particular values

This mode (The first tab of the filter) will only keep rows whose values for the picked column fall in a selection.

<div data-with-frame="true"><img src="/files/4rQe8KyYXC4ISqTGU8oh" alt=""></div>

*Here, KAWA will only keep rows for which the city is Aberdeen or Akron*

> IMPORTANT: The list of available values might not be exhaustive if there are too many (Above 300). This limitation exists to prevent millions and potentially billions of values to be sent to users. However, The search box has access to the entire list of values. For example, if the city `Miami` is not there in the initial set, typing the letter `M` will most likely filter down the list and make `Miami` available.

> IMPORTANT: As a consequence of the previous note, if you have text columns with a very high cardinality, do not use this mode to filter everything out EXCEPT a few values. (Clicking on select all followed by unchecking a few)

#### 2.1.2 Filtering based on conditions

The second tab of the text filter lets you configure multiple conditions.

* You can add / remove conditions.
* When you have more than one condition, you can pick whether you want them ALL to match (`AND`) or at least one (`OR`).
* Each condition has an operator picked from the list: Begins with, Ends with, Contains, etc... and a text value (except for: Is empty and Is not empty).
* Important: All the conditions are not case sensitive.

**Examples:**

* Begins with *W* `AND` Ends with *E* will match the word Wayne.
* Begins with *W* `OR` Ends with *E* will match the words Bruce, Walter and Wayne.

<div data-with-frame="true"><img src="/files/jHKLdMMRsLImNtoyZ11a" alt=""></div>

*Keeps only the cities whose name starts with A, ends with A and contains T.*

#### 2.1.3 Advanced settings for text filters

The third tab lets you configure the behavior of the filter card itself.

**Selection Mode:** Can be set to multi-select or single-select. This will define how many can be selected in the selection (first tab).

**Configure options:** Lets you define the exact content of the list in the selection. You can either restrict the values that are offered to the users or group them into categories.

### 2.2 The Numeric filters

The numeric filters have two modes:

* *Simple range:* Filter the number with a simple closed range. Note that both boundaries are optional. For example, if you specify only the Min, you will get all the values greater or equal to the Min.
* *Conditions:* Works the same way as the text filters with numeric conditions.

<div data-with-frame="true"><img src="/files/o2AYVXirUrjdo66mFZXv" alt=""></div>

*The above filters only keep quantities that are either strictly lesser than 10 or strictly larger than 100.*

### 2.3 The Boolean filters

The boolean filter has only one mode: You can decide whether to keep `True`, `False` or `Empty`. There is a toggle between multi and single select modes at the top right of the filter card. (It lets you for example pick both `False` and `Empty`).

### 2.4 Temporal filters

Temporal filters are the most complex filters, they apply to both date and date time filters.

#### 2.1.1 Filtering using presets

Presets are the quickest way to filter your temporal data. Each filter has their own sets of presets. By default, the Popular ones are shown:

| Preset  | Description                                                                                                                                                                                   |
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| D       | Shows today's data                                                                                                                                                                            |
| D-1     | Shows yesterday's data                                                                                                                                                                        |
| M-1     | Shows data for previous Month                                                                                                                                                                 |
| YoY YTD | Year on Year Year to Date: Shows data across all years until today's day. For example, if today is the 25th of June 2025, we will show data for 2025, 2024, 2023, ... until the 25th of June. |
| YTD     | Year To Date: Show all dates from the beginning of the year until today's date.                                                                                                               |
| MTD     | Month To Date: Show all dates from the beginning of the month until today's date.                                                                                                             |
| Future  | Shows all the data after today                                                                                                                                                                |
| Past    | Shows all the data before today                                                                                                                                                               |

You can also display presets by time unit:

* Minute (For date time filters only)
* Hour (For date time filters only)
* **D** Day
* **BD** Business day (all days except weekends)
* **W** Week
* **M** Month
* **Q** Quarter
* **S** Semester
* **Y** Year

Each category will show a list of presets similar to D+1 or D-1. The capital letter indicates the unit (Y for year, BD for business day, etc..) and the number indicates the offset. For example: BD+1 indicates the next business day. Q-1 indicates the previous Quarter.

#### 2.1.2 Filtering using ranges

There are three types of range available.

**a. Fixed ranges:** They correspond to a range defined by two fixed dates that you can pick. For example: all dates between 1/1/2020 and 2/1/2020. The min and max are included in the returned datasets. If you omit the Max, KAWA will give you all dates after the Min, and vice versa.

**b. Rolling ranges:** They correspond to a rolling range, relative to today. It contains 3 parameters:

* *From:* A number to indicate how many units we start from (Minimum)
* *To:* A number to indicate how many units we go to (Maximum)
* *Unit:* A time unit

Here are a few examples to illustrate this: *Let's suppose that today is the 25th of June 2025.*

`From -1 To 1 Month`: Means all dates from the 25th of May 2025 to the 25th of July 2025. (One month back to one month forward).

`From -10 To 0 Day`: Means all dates from the 15th of June 2025 to the 25th of June 2025. (10 days back, 0 days forward).

Special case for **Available date**: Available date can be picked as a Unit. Picking for example: -1 to 0 Available date will filter the data on the last available date in your dataset.

**c. By Period:** Lets you choose one period of time like: the Year 2023 or the month of March, or the month of April 2020.

#### 2.1.3 Filtering using conditions

This mode is similar to text and number filters. It lets you pick multiple conditions and choose how you want to combine them: `OR` or `AND`. It operates only on static dates.

For example, you can specify: any date after the 10th of July 2024 or before the 1st of Sept 2020.


# Workflows

**Workflows** is a builder in KAWA for creating multi-step processes. See definitions in [Terminology](/13_00_terminology#workflow) section. A workflow can be started:

* on a schedule,
* manually,
* after a data source is successfully updated,

and the steps run **one by one** or **in branches** (using Logic steps).

## 1. Create a workflow

To create a new workflow, go to the **Workflows** section and click on **+ Workflow**.

<div data-with-frame="true"><img src="/files/DrhC95h2D5Aycdv03skF" alt=""></div>

## 2. Set trigger

KAWA Workflows support three trigger types. Pick one in **WHEN** → Select a trigger.

### 2.1 At a scheduled time

Use this when the workflow must run automatically.

* Choose **At a scheduled time**.
* Configure:
  * **Daily** / **Intraday** / **Weekly** / **Monthly**.
  * **Run time**. **Daily** — Run at → pick an exact time (e.g., 09:00). **Intraday** — Run every N \[seconds/minutes/hours]; optionally turn on Set custom time period. **Weekly** — Run on (choose weekdays) at (time). **Monthly** — Run every month on (day of month) at (time).
  * **Timezone**.
  * **Only on business days** (optional).
* The header shows the summary (e.g., Runs daily at 09:00).

<div data-with-frame="true"><img src="/files/48sTQLaO5Zdd81w8BX94" alt=""></div>

### 2.2 Manual run

Use this when the workflow is started on demand by a user.

* Choose **Manual run**.
* (Optional) Click **+ Add input** to define parameters that the user must enter each time they start the run: **Text**, **Select**, **Number**, **Date**, **Date Time**, **Boolean**.
* These inputs are available to later steps via bindings:
  * In Email / AI prompt editors: click **+** → Use data from → Choose data, then choose the input.
  * In Run python script, Parameters: click **+** → Use data from → Choose data, then choose the input.

<div data-with-frame="true"><img src="/files/Y0sqo0Tvpij9YVAAhdg2" alt=""></div>

### 2.3 On data refresh

Use this trigger to run a workflow right after a data source is successfully refreshed.

* Choose **On data refresh**.
* Click **+ Add data source**, find it in the list, and select it. Multiple sources are supported.

<div data-with-frame="true"><img src="/files/CRNDdtVHUFSUJModtz30" alt=""></div>

How it fires:

* Fires after a successful refresh of any selected source (Data source -> Overview -> Change data import).
* With multiple sources it uses OR logic — refresh of any selected source triggers the workflow.

## 3. Add action

### 3. Add action

Click **Add action** to open the Actions menu. Actions are grouped into categories:

**KAWA Actions**

* Run python script
* Send email
* AI prompt
* Enrich data with AI
* Report
* Build a chart
* Generate output
* User task
* Run workflow

**Data Operations**

* Load data
* Export to data source
* Join datasets
* Stack datasets

**Logic**

* If / Else
* Routing
* Loop
* Interrupt workflow

<figure><img src="/files/oEAi7TuLbIk8Ak7S2Mpu" alt=""><figcaption></figcaption></figure>

Below is how to set up each step type in practice.

### 3.1 Load data

* Choose a **Sheet** from the list.

<div data-with-frame="true"><img src="/files/acYwFfwQN6abbgOIgoPN" alt=""></div>

* If needed, open Enrich data and use the quick transform icons.

<div data-with-frame="true"><img src="/files/oVFqWISx7gTidd6r7lTG" alt=""></div>

* In the **Behavior** block, set:
  * If no rows are found → Interrupt workflow / Continue without a result.

### 3.2 Run python script

* In **Select python tool from the library**, choose a tool.
* In **Match tool inputs with any of the previous task outputs**, connect the required inputs to the results of previous steps (for example, a dataframe from **Load data**).

> If a required input is empty or mis‑bound you’ll see **Invalid task bindinqs**.

<div data-with-frame="true"><img src="/files/8mPmbjXvGj2zs69DCDJ4" alt=""></div>

* **Parameters** (optional/named options) → they can also be bound to outputs from previous steps.

> If a parametrs is empty you’ll see **Invalid parameter bind**.

* **Behavior** — the same row-based guards as in Transform data appear at the bottom.

### 3.3 Send email

**Recipients**, **Subject**, and **Body** can be entered manually, or use the **+** on the right to insert values from previous steps (e.g., aggregates, tables, generated text, a link to a report, etc.). You can also add files to the email in the **Attachments** section.

> If a required fields is empty you’ll see **Invalid parameter bind**.

<div data-with-frame="true"><img src="/files/OM2zzXWuQKlX9Zrx1CBE" alt=""></div>

### 3.4 AI prompt

* Enter the prompt text. Use the **+** button to insert variables/fragments from previous steps (transformation results, script outputs, etc.).

<div data-with-frame="true"><img src="/files/P1xegitELxwEAm0HtHuM" alt=""></div>

* Use the prompt for summarization, recommendations, and generating explanations.

### 3.5 Enrich data with AI

#### **3.5.1 How to set up**

* **Input table**

Select a table from the previous step (for example, the result of Transform data). Without a selected table, the step won’t run.

* **Prompt**

Describe what you want to generate for each row. In the prompt, you can reference fields from the input table (use data from the previous step as shown in the UI).

* **Outputs** (what the model should generate). Define the output schema in one of these ways:
  * Generate from prompt — the system will try to create the outputs structure automatically from your prompt.
  * Specify manually — manually add the required output fields/columns (recommended if you need a stable format).
* **Preview / Test**

Check the result in Preview. Click Test to run generation and see how outputs are filled.

* **Behavior**

Set what should happen if no rows are found in the input table (for example, Interrupt workflow).

<div data-with-frame="true"><img src="/files/NOihNbCYW3LuF2oOZsyB" alt=""></div>

#### **3.7.2 Result**

This step returns a table with extra AI columns (outputs). You can use it in the next actions (email, export, report, logic, etc.).

### 3.6 Report

In the **Report** step, open the list, use **Search** if needed, and select the required report from the workspace so you can use it later as an artifact (for example, to send it in an email).

<div data-with-frame="true"><img src="/files/bGT5WXXABQvd4JQGD1Is" alt=""></div>

### 3.7 Build a chart

In the **Build a chart** step, choose a table from:

* **Previous Tasks** (for example, the result of **Run python script** or **Transform data**), or
* **Sheets** (any available sheet in the workspace).

<div data-with-frame="true"><img src="/files/LRo0t3xNfFayJbLKlIcX" alt=""></div>

* **Configure the chart** (the Chart configuration panel)

<div data-with-frame="true"><img src="/files/66fZzDNzinC1y9Znwjjf" alt=""></div>

### 3.8 Export to data source

* In the **Previous Tasks** list, select the step that returns a table (for example, Transform data or Run python script). This choice determines what will be exported.

<div data-with-frame="true"><img src="/files/rDFbmVEJN8WbfQrhbhLQ" alt=""></div>

* In the **Add task – Export to data source** window, fill in the data source settings:
  * **Data source name** — required (the destination name).
  * **Description** — optional.
  * **Feed Type** — export mode.
  * **Default global policy** — access policy.
* Click Create.

<div data-with-frame="true"><img src="/files/9VrocuAerrk2dsK8ZoUG" alt=""></div>

After creation, the **Export to data source** task screen will open. Then if needed, click **Go to data source** to open the created data source and change its settings.

<div data-with-frame="true"><img src="/files/l3LNGBHHmBtPuHJGSy6J" alt=""></div>

### 3.9 Generate output

* In the **Text** block, enter the message that the workflow should return. Use the **+** button to insert variables/fragments from previous steps (transformation results, script outputs, etc.).

<div data-with-frame="true"><img src="/files/2S9i4OV93o9PFjZxUAOR" alt=""></div>

* Purpose: to generate a text output as the result of the workflow run.

### 3.10 User task

**User task** is a human-in-the-loop step. It **pauses the workflow** and creates a task for a user to complete a form. When the assignee submits the form, the workflow continues and the submitted values become available to the next steps.

Typical use cases:

* approval / validation before sending an email or exporting data;
* collecting missing parameters from a business user;
* routing to a colleague for a decision before continuing automation.

#### 3.10.1 How to set up

* Add action → **User task**.
* Fill in:
  * **Assign Task To** — choose the assignee(s) who will complete the task.
  * **Task Name** — the task title shown to the assignee.
  * **Task Description** — instructions (what to check, what to enter, acceptance criteria, etc.).

<div data-with-frame="true"><img src="/files/8BmCXqPxwu6BNGkueuz5" alt=""></div>

* In **Task Input Form**, click **+ Add input** and add at least one field. Supported input types:
  * **Text Input**
  * **Select**
  * **Number Input**
  * **Date Input**
  * **Date & Time Input**
  * **Toggle**

<div data-with-frame="true"><img src="/files/GENQfHMX9c3xpIy5e1Ej" alt=""></div>

> If you don’t add any inputs, the step is invalid (you’ll see an error like “At least one form input is required”).

#### 3.10.2 Run behavior

* When the workflow reaches **User task**, it creates a task and notifies the assignee (typically by email with a link to the form).
* The assignee opens the form, fills the fields, and clicks **Submit**.
* After submission, the workflow resumes from the next step.

<div data-full-width="false" data-with-frame="true"><img src="/files/njjdbFc2sjgKxB4UuYWG" alt=""></div>

Use submitted values in later steps

* In any next step that supports bindings (e.g., **Send email**, **AI prompt**, **Run python script parameters**, **Generate output**), click **+** and choose: **Use data from** → **User task** → **Form variables**, then select the required field.

<div data-with-frame="true"><img src="/files/VWrOAcwsHMiQGBB5J3Gk" alt=""></div>

### 3.11 Run workflow

**Run workflow** runs an existing workflow as part of the current one. The called workflow executes to completion before the next step proceeds. Use it to reuse shared logic across workflows — for example, calling a standardised data-processing pipeline from multiple parent workflows.

#### 3.11.1 How to set up

* In a Workflow, click **+ Add action** and select **Run workflow**.
* The step appears in the **THEN** column. Open the **Select a workflow to run** dropdown and pick the workflow you want to call.
* If the selected workflow has **Workflow inputs** defined in its Manual run trigger (e.g. Text Input, Number Input), they appear in the **Parameters** section. Bind each parameter using the **+** button — you can bind values from previous tasks, Row values (inside a Loop), or static values.

  If a required parameter is not bound, the card shows **"Invalid parameter binding"** and the workflow cannot be saved or run.

<div data-with-frame="true"><figure><img src="/files/wgArTaEmjpChbbP3vy2J" alt=""><figcaption></figcaption></figure></div>

#### 3.11.2 Run behaviour

The called workflow runs as a **sub-workflow** — all its tasks execute in order within the parent run. The parent workflow waits for the sub-workflow to complete before moving to the next step. If the sub-workflow fails, the parent workflow stops.

In **Run history**, the step displays a **Sub-workflow** badge and a completion counter (e.g. **3 / 3**). Click **˅** to expand and inspect the sub-workflow's individual task results (start time, duration, status, output).

### 3.12 Load artifact

**Load artifact** loads a stored artifact from the workspace so its properties can be used by later steps. Use it to bring a saved file — a document, dataset, image, presentation, PDF, or application — into a workflow run, for example to reference its details in a **Send email**, **AI prompt**, or **Generate output** step.

#### **3.12.1 How to set up**

* In a Workflow, click **+ Add action** and, in **KAWA Actions**, select **Load artifact**.
* The step appears in the **THEN** column with the title **Load artifact**.
* Open the **Artifact** dropdown (**Select an artifact**) and pick the artifact to load. Use the **Search** box to find it by name. Until an artifact is chosen, the card shows **"Artifact is not selected"** and the workflow cannot be saved or run.

<div data-with-frame="true"><figure><img src="/files/0vBXBU80kERMgpRv3b3p" alt=""><figcaption></figcaption></figure></div>

* Open the **Version** dropdown and choose which version to load:
  * **Latest version** — always loads the most recent version (the default). Use this when the workflow should always pick up the newest file.
  * **A specific version** (v1, v2, v3 …) — pins the step to a fixed version, shown with its timestamp and author. Use this when the workflow must always use one exact revision.

<div data-with-frame="true"><figure><img src="/files/zG0K7373OPP4tAAgExIF" alt=""><figcaption></figcaption></figure></div>

#### **3.12.2 Use the artifact in later steps**

Once an artifact is loaded, its **properties** become available to later steps via bindings. In any step that supports bindings (for example, **Send email**, **AI prompt**, **Generate output**, **Run python script**), click the **+** button and choose **Use data from** → **Load artifact** → **Properties**, then select:

* **Author** — the user who created the artifact.
* **Version** — the loaded version label.
* **Description** — the artifact's description.

<div data-with-frame="true"><figure><img src="/files/9dPBIumJhDDUr6LBe7Vq" alt=""><figcaption></figcaption></figure></div>

> If a later step references the loaded artifact but the binding is empty or invalid, the step shows **Invalid parameter binding** and the workflow cannot be saved or run.

### 3.13 Save artifact

**Save artifact** stores a file produced by the workflow as a new **version** of an artifact. Use it to persist a workflow's output — an exported dataset, a generated chart, a report, or any file from a previous step — back into the workspace's [Artifacts](/artifacts) library, where it can be previewed, downloaded, versioned, and shared.

#### **3.13.1 How to set up**

* In a Workflow, click **+ Add action** and, in **KAWA Actions**, select **Save artifact**.
* The step appears in the **THEN** column with the title **Save artifact**.
* Open the **Artifact** dropdown and choose where to save the file:

<div data-with-frame="true"><figure><img src="/files/X9FVDYYk6bE1UhO0k10i" alt=""><figcaption></figcaption></figure></div>

* **+ Create new artifact** — create an empty artifact to store the output. In the **Create artifact** dialog, enter a **Name** (required) and choose a **Type** (for example, Binary), then click **Create**.
* **An existing artifact** — pick it from the list (use **Search** to find it by name). The file is saved as a new version of that artifact.

Until an artifact is chosen, the card shows **"Artifact is not selected"** and the workflow cannot be saved or run.

<div data-with-frame="true"><figure><img src="/files/QBH4kQtAoesvJa9dHFxe" alt=""><figcaption></figcaption></figure></div>

* In the **File** field, click the **+** button and choose **Select from** → a previous step, then under **Select option** pick the file output to save (for example, a **CSV Export** from **Load data from sheet**, or a generated chart). Until a file is bound, the card shows **"File is not selected"** and the workflow cannot be saved or run.

#### **3.13.2 Run behaviour**

When the workflow reaches the **Save artifact** step, it writes the bound file to the selected artifact as a **new version**. Earlier versions are kept, so each run that saves to the same artifact adds another entry to its version history. The saved version is then available in the **Artifacts** tab and to any later **Load artifact** step.

### 3.14 Join datasets

Use **Join datasets** to combine two tables produced by previous workflow steps into a single result table, using classic join types: **Inner**, **Left**, **Right**, and **Full Outer**.

<figure><img src="/files/OzxbyRWwr819zTDocjvw" alt=""><figcaption></figcaption></figure>

#### 3.14.1 How to set up

* In a Workflow, click **Add action** and select **Join datasets**.
* Select the two datasets

  In **Join configuration**, choose:

  * **Dataset A** (left side)
  * **Dataset B** (right side)

  Each selector is tied to a workflow step that returns a table (for example **Transform data** or **Run python script**).
* Choose the **Join Type**

  Pick one of:

  * **Inner Join** — keeps only rows that match in both datasets.
  * **Left Join** — keeps all rows from Dataset A and matches from Dataset B.
  * **Right Join** — keeps all rows from Dataset B and matches from Dataset A.
  * **Full Outer** — keeps all rows from both datasets and matches where possible.

  The task icon in the workflow updates to reflect the selected join type.
* Define **Join keys**

  In **Join keys**, define one or more key pairs:

  * Select a column from **Dataset A**
  * Select a column from **Dataset B**
  * Click **+ Add join key** to add additional key pairs
  * Use the trash icon to remove a join key pair

  Notes:

  * Column choices are type-filtered as you pick keys, to help you select compatible columns.
  * If a previously selected column becomes unavailable (for example after changing an upstream step), it is shown as invalid/stale (highlighted) and must be fixed before saving.
  * Columns that are already used in another join key row are hidden from the join key dropdowns.
* Choose **Output columns**

  In **Output columns**:

  * **Key columns** are always included in the output and are shown separately. They are read-only as “included”, but support **rename** (so you can control the output column names).
  * Add non-key columns from **Dataset A** and/or **Dataset B** using **+ Add column** in each dataset section.
  * For each selected output column, you can **rename** it and **remove** it. If a column was renamed, the original name is shown as a grey sub-label.
  * You can remove all selected output columns from one dataset using the trash icon in that dataset section header.
  * The **All columns** option is a shortcut to include all available columns from both datasets; **columns used as join keys are excluded from this list**. If needed, rename columns so the final output has unique column names.

  Notes:

  * Output column names must be unique in the resulting table. If two output columns end up with the same display name (including across **Key columns**, **Dataset A**, and **Dataset B**), the task shows the validation error **“Output column names must be unique”** and blocks saving. Conflicting columns are highlighted in magenta with a tooltip.
  * Output columns cannot include join key columns. Join key columns are already included under **Key columns**. Columns already used as join keys are hidden from the output column pickers. If a column was added to Output columns and then later used as a join key, it becomes highlighted in magenta with tooltip **“This column is already used as a join key”**, and saving is blocked until the conflict is resolved.
* **Behavior**

  At the bottom of the task, set the row guardrail:

  * **If no rows are found** → **Interrupt workflow** or **Continue without a result** (depending on your workflow logic).

#### 3.14.2 Result

The task outputs a **single joined table**, which can be used as input for later steps (email, AI prompt, export to data source, logic steps, etc.).

### 3.15 Stack datasets

Use **Stack datasets** to combine 2 or more tables produced by previous workflow steps into a single result table by stacking them vertically (Union). The task merges rows from all selected datasets into one output table.

<div data-with-frame="true"><figure><img src="/files/tuYj5e4UiF0kUznInOj9" alt=""><figcaption></figcaption></figure></div>

#### 3.15.1 How to set up

In a **Workflow**, click **Add action** and select **Stack datasets**.

**Stack configuration**

* Add at least two datasets.
* For each dataset slot, select a workflow step that returns a table (for example, Load data from sheet).
* Datasets are shown as A, B, C… and are stacked from top to bottom in the order shown in the list.
* Click **+ Add another dataset** to add more inputs.
* If you remove a dataset, its selections are removed from the mapping table.

**Column matching method**

Use the **Column matching method** panel to quickly populate or replace the column mappings. Three methods are available:

* **By column name** — automatically matches columns that have the exact same name across all selected datasets.
* **By column order** — matches columns based on their left-to-right position in each dataset, regardless of column names.
* **Auto-mapping** — smart matching that uses fuzzy logic and data types. Calls an AI endpoint to intelligently match columns across stacked datasets by name similarity and data type. While the request is in flight, a loading spinner is shown on the button and all matching buttons are disabled.

> If mapped columns already exist when you click any of the three matching methods, an **"Overwrite existing mapping?"** confirmation dialog appears. Confirm to replace the current mappings or cancel to keep them.

**Mapped columns**

* A dataset column appears only after a source dataset is selected.
* Click **+ Add mapping row** to add a new output column.
* A row may contain mappings for only some datasets; dataset cells with no mapping can be left empty.
* Each mapping row must contain at least one selected dataset column. A completely empty mapping row blocks saving/running the task.
* When you select a source column, the source column type icon is shown in the **Output column name** cell.
* Use the trash icon to remove a mapping row.
* Click **Clear all** to remove all mapped columns at once.

#### 3.15.2 Result

The task outputs a **single stacked table**. Each mapping row becomes an output column, and rows from all selected datasets are appended into one result. This result can be used in later steps (for example, Generate output, Send email, AI prompt, export to data source, or logic steps).

### 3.16 If / Else

**If / Else** is a logic step that splits a workflow into two branches:

* **IF** – runs when all conditions are true.
* **ELSE** – runs when at least one condition is not met.

The step uses data from previous actions: **Transform data**, **Run python script**, **AI prompt**, **Send email**, etc.

#### 3.16.1 Add an If / Else step

* In the THEN section, click **+ Add action**.
* In **KAWA Actions**, scroll to the **Logic** section.
* Select **If / Else**.

A new block appears in the steps list with two tabs:

* **IF** – conditions and actions for the “true” case.
* **ELSE** – actions for the alternative path.

Each tab has its own **+ Add action** button to build the branch.

#### 3.16.2 Add path rules

The behavior of **If / Else** is controlled by **path rules** – rows of conditions in the panel on the right.

<div data-with-frame="true"><img src="/files/FUgDEplwQemZlwAiLaNh" alt=""></div>

Each rule has three parts:

* **The first field is what you check**

(for example, an aggregate like “high”, a table “Grid”, or text like “Generated Content”). You pick it via **Use data from** → **Choose data**.

* **The second field is the comparison type**

Available operators depend on the data type.

<div data-with-frame="true"><img src="/files/Zn74Il2Te1QTmasRh9wM" alt=""></div>

* **The third field is what you compare against**

You can:

* type a value manually (for example, 0, warning, TRUE), or
* click the + button on the right and select data from previous steps.

<div data-with-frame="true"><img src="/files/rnb9LmFfzlxM1GVEHdcL" alt=""></div>

This lets you compare:

* an aggregate with a fixed threshold;
* AI-generated text with another column;
* properties from different steps with each other.

**Multiple rules**

* Click **+ AND** to add another condition.
* All rules inside an **If / Else** are combined with **AND** – they all must be true for the **IF** branch to run.
* The total number of rules is shown in the step name (for example, “5. 3 rules”).

#### 3.16.3 Actions in the IF and ELSE branches

After you set up the rules, define what each branch should do.

* Select the **IF** or **ELSE** tab in the **If / Else** block on the left.
* Click **+ Add action** inside that branch.
* Add the actions you need, for example.

Execution logic:

* If **all rules are true**, only the **IF** branch runs and the **ELSE** branch is skipped.
* If **any rule is false**, the **ELSE** branch runs (if it has actions).

### 3.17 Routing

**Routing** is a logic step that lets you split the processing of one table into multiple routes (R1, R2, R3 …). In each route, you set up your own data “slice” (view) and add a separate set of actions.

#### 3.17.1 How to add Routing

Choose the table source for Routing:

* Previous Tasks (for example, Run python script / Transform data), or
* Sheets (any sheet in the workspace).

<div data-with-frame="true"><img src="/files/olSrngJrphiMqoWIPJPl" alt=""></div>

After that, a table preview for Routing will open on the right.

#### 3.17.2 How routes work (R1 / R2 / R3)

Routes are shown as tabs: **R1**, **R2**, **R3**…

* To open a route — click its tab.
* To add a new route — click + next to the tabs.

<div data-with-frame="true"><img src="/files/CHMg1EJKbF33meVn2sSZ" alt=""></div>

#### 3.17.3 Add actions inside a route

* Select the route you need (for example, R1).
* In the route block, click Add action and add the steps you need (Send email, Export to data source, Report, etc.).
* Repeat for R2, R3… if needed.

#### 3.17.4 Result

Routing creates multiple independent branches where:

* the input is one table,
* each branch (route) can have its own data view/slice,
* each branch runs its own set of actions.

### 3.18 Loop

**Loop** iterates over the rows of an input table, repeating a set of nested actions for each row. Use it when the same sequence of steps must run once per row — for example, exporting one report per client, calling a workflow per position, or generating output for each item in a queue.

#### 3.18.1 How to set up

* In a Workflow, click **+ Add action** and select **Logic → Loop**.
* The step appears in the **THEN** column with the title **For each row in `[source task]`**.
* Open the **Input table** dropdown and choose the upstream task whose table will be iterated over. The dropdown lists previous tasks of the current workflow that produce a table — for example, **Load data from view**, **Run python script**, **Enrich data with AI**, **Join datasets**, or **Stack datasets**. If no input table is selected, the card shows **"No input table selected"** and the workflow cannot be saved or run.
* Click **+ Add action** inside the Loop container to add the steps that should run once per row. Any workflow action can be placed inside the loop body. An empty Loop body shows the validation error **"Missing action"**.

<div data-with-frame="true"><figure><img src="/files/C3rvu9QNatqlttMRzat6" alt=""><figcaption></figcaption></figure></div>

#### 3.18.2 Run behaviour

* For each row, all nested actions run in order before the loop advances to the next row.
* If a nested action fails on any iteration, the workflow stops at that iteration. Subsequent iterations are not run.

In **Run history**, the Loop step displays the total number of iterations (e.g. **6 iterations**) and a completion badge (e.g. **6 / 6**). Click the **>** arrow on the Loop row to expand and inspect individual iterations.

### 3.19 Interrupt workflow

This task has **no settings**: you simply place it where you need it in the chain. Its purpose is to **immediately stop** the workflow execution at the point where this step is added. All steps after it will **not** run.

<div data-with-frame="true"><img src="/files/JPEnwyz4lJlwAn4WJK9y" alt=""></div>

For example, it works like an “emergency stop” inside **If / Else** or **Routing** branches, so you can explicitly end routes you don’t need.

## 4. Save

Сlick **Create workflow**.

<div data-with-frame="true"><img src="/files/X6blV6vXEqLFBkVREG31" alt=""></div>

## 5. Update workflow

When you open an existing workflow from **Home** → **Workflows**, the editor shows the current **WHEN** (schedule) and **THEN** steps. The **Create workflow** button is replaced with **Update workflow** (bottom-right). Update workflow saves changes to an existing workflow (schedule, steps, bindings, Behavior). After saving, both scheduled and manual runs use the latest version.

## 6. Manual run & history

Use manual runs to test or trigger a workflow on demand. A manual run does not change the schedule.

### 6.1 Run from the workflow editor

* Click **Run history** (top-right).
* In the modal, press **Run** to start immediately.
* The table shows Start, End, Status, and Error for each attempt. Use the date filters and counters (Total / Success / Failed) to review past runs.

<div data-with-frame="true"><img src="/files/8rRShAMmViZTEVGtYfSJ" alt=""></div>

<div data-with-frame="true"><img src="/files/XWVQWc0kczYEg1Q0ML2K" alt=""></div>

### 6.2 Run from Home → Workflows

* Open **Home** → **Workflows**.
* In the row of a workflow, open the three-dot menu → **Run**.
* The current **Status** appears in the list; open the workflow and Run history for details.

**Status** shows the last result: **Success, Failed, Running, Stopped**.

### 6.3 Run from the Control panel

You can also start a workflow from the **Control panel** using a button:

* Open **Controls** → **Configure** → **Add new control**.
* In Control type, choose **Button**.
* In **Action**, select **Run workflow**.
* In the **Select workflows** modal, select one workflow that this control should run.
* Click **Save**.

<div data-with-frame="true"><img src="/files/wf55o4z4w5HaPiFEm3Fp" alt=""></div>

After that, users can run the selected workflows directly from the controls panel.

### 6.4 Run from AI Chat (Agent commands)

You can run a workflow from **AI Chat** as an agent command, but first you need to add it to the AI agent.

#### a. Step 1. Add the workflow to the AI agent

* Go to **Agents** and open the agent you need.
* In the **Commands** section, click + (add command).
* In the **Select workflows** modal:
  * choose one or more workflows,
  * click **Apply**.

<div data-with-frame="true"><img src="/files/MGOz9LiZnd0IZpDC0I6D" alt=""></div>

After that, the selected workflows will appear as available commands for the agent.

#### b. Step 2. Run the workflow from AI Chat

* Open the **AI Chat** tab.
* Select the agent in the left panel.
* In **Agent commands**, click the workflow command you need to start it.

<div data-with-frame="true"><img src="/files/0uOnHYofwbBHPgmi5OQw" alt=""></div>

This is handy when you want to run a workflow without going to the editor or the Workflows list.

## 7. Reorder / Delete steps

* **Reorder**: drag & drop steps in THEN. If you move a step above its data source, dependent steps show Invalid task ref; open them and re-bind fields via +.
* **Delete**: trash icon on a step. Steps referencing a deleted step also show Invalid task ref — fix or remove those bindings. Deletion can’t be undone.


# Workflows case 1

This scenario shows how to build an automated trading pipeline (Workflow) in KAWA.

The pipeline runs on a schedule and, step by step, does the following: loads and prepares market data, runs a Python script to calculate signals, and sends notifications and a short report.

## 1. Create a workflow

* Go to Home → Workflows and click **+ Workflow**.
* Enter a name, e.g., **Daily — Signals & Report**.

> Recommendation: create separate workflows for different frequencies (Daily / Intraday) and markets.

## 2. Trigger

* In the WHEN area, choose At a scheduled time.
* Frequency: Daily
* Time: 06:10 (after market close).
* Time zone: the exchange’s time zone (e.g., America/New\_York).
* Only on business days: On.

## 3. Action steps

### 3.1 Step 1 — Transform data: prepare market data

#### 3.1.1 Go to Add action

→ Transform data and choose the OHLCV source.

#### 3.1.2 Time

Use a single time column for all records. Ensure the timestamp is UTC ISO-8601 (e.g., 2025-10-17T20:00:00Z).

#### 3.1.3 Order matters

Sort by timestamp (New -> Old) so EMA/RSI are calculated correctly.

#### 3.1.4 Adding simple metrics (Enrich → Formula)

**Body ratio** — share of the candle body in the day’s range

Name: `body_ratio`

Formula:

`ROUND( ABS(close - open) / ( ABS(high - low) + 1 / POWER(10, 7) ), 4 )`

Meaning (0…1):

* 0 ≈ neutral/doji (open ≈ close)
* 1 ≈ strong one-direction day (big body, small wicks)

**Range %** — relative daily volatility

Name: `range_pct`

Formula:

`ROUND( (high - low) / ( (high + low + close) / 3 + 1 / POWER(10, 7) ), 6 )`

Meaning: Higher = a more volatile day. Good for comparing different tickers.

**Close position in range** — where the close sits within the day’s range

Name: `close_pos_in_range`

Formula:

`ROUND( (close - low) / ( ABS(high - low) + 1 / POWER(10, 7) ), 4 )`

Meaning (0…1):

* 0 = closed near low
* 0.5 = around the middle
* 1 = closed near high

#### 3.1.5 Enrich → Lookup column

**Goal**: pull the exchange and currency for each ticker.

* Source sheet: ref\_symbols
* Columns to pull: exchange, currency

Result: the source table gets new columns exchange and currency (e.g., NASDAQ, USD) for each symbol.

#### 3.1.6 Enrich → Manual input

**Goal**: add simple constants for easier filtering and report/email subjects.

* Add: **session**
* Value: "**EOD**"

Why: marks the daily run; used in the AI report and filters.

* Add: **market**
* Value: "**US**"

Why: market tag; inserted into email subject and used for grouping.

#### 3.1.7 Data quality checks

* OHLC rules: high ≥ max(open, close), low ≤ min(open, close), high ≥ low.
* Volume: volume ≥ 0; optionally flag volume = 0.
* Uniqueness: no duplicate symbol + timestamp.
* Types: price/volume columns are numeric; no stray text.

#### 3.1.8 Row guardrails (Behavior)

For our scenario set exactly these parameters:

* If no rows are found → Interrupt the workflow.
* If more than \[Max num of rows] are found → Interrupt the workflow.

> The 1,000 threshold in this field is fixed (not editable).

* Max num of rows → set a value greater than the current number of rows in the table.
  * Example: if the source has \~800 rows, set 1000.
  * If you add new tickers/history, increase this value accordingly.
  * If Max num of rows ≤ the table size, the workflow will not start.

### 3.2 Step 2 — Run python script

**Goal**: Using the data from Step 1 (OHLCV), calculate EMA(10) and EMA(20), and produce a simple BUY/SELL signal for the last bar of each symbol.

#### 3.2.1 Create a Python tool in the library

* Go to **Home** → **Tools** → **+** and create a script named, for example, **compute\_indicators\_and\_signals**.
* Paste the code and click **Save**.

```python
import logging
import pandas as pd
from kywy.client.kawa_decorators import kawa_tool

logger = logging.getLogger("script-logger")

@kawa_tool(
    inputs={"symbol": str, "close": float},
    outputs={"ema_fast": float, "ema_slow": float, "signal": str},
)
def main(df: pd.DataFrame, symbol=None, close=None) -> pd.DataFrame:
    try:
        if df is None or df.empty:
            return _empty_result()

        d = df.copy()

        # Ensure required columns (allow simple aliases)
        if "symbol" not in d.columns:
            alt_symbol = next((c for c in ["ticker", "sym"] if c in d.columns), None)
            if alt_symbol:
                d.rename(columns={alt_symbol: "symbol"}, inplace=True)

        if "close" not in d.columns:
            alt_close = next((c for c in ["price", "last", "close_price"] if c in d.columns), None)
            if alt_close:
                d.rename(columns={alt_close: "close"}, inplace=True)

        if ("symbol" not in d.columns) or ("close" not in d.columns):
            logger.warning("Missing required columns; returning empty result")
            return _empty_result()

        # Types & filtering
        d["symbol"] = d["symbol"].astype(str)
        d["close"] = pd.to_numeric(d["close"], errors="coerce")
        d = d[d["close"].notna()]
        if d.empty:
            return _empty_result()

        # Detect/normalize time column
        time_col = next((c for c in ["timestamp", "time", "datetime", "date", "dt"] if c in d.columns), None)
        if time_col is not None:
            if not pd.api.types.is_datetime64_any_dtype(d[time_col]):
                d[time_col] = pd.to_datetime(d[time_col], utc=True, errors="coerce")
            d = d.sort_values(["symbol", time_col])
        else:
            d = d.sort_values(["symbol"])

        # EMA(10) / EMA(20)
        g = d.groupby("symbol", sort=False)
        d["ema_fast"] = g["close"].transform(lambda s: s.ewm(span=10, adjust=False).mean())
        d["ema_slow"] = g["close"].transform(lambda s: s.ewm(span=20, adjust=False).mean())

        # Latest per symbol
        if time_col is not None and d[time_col].notna().any():
            idx_last = g.apply(
                lambda x: x[x[time_col].notna()][time_col].idxmax()
                if x[time_col].notna().any() else x.index.max()
            ).values
        else:
            idx_last = g.apply(lambda x: x.index.max()).values

        out = d.loc[idx_last, ["symbol", "close", "ema_fast", "ema_slow"]].copy()

        if time_col is not None and d[time_col].notna().any():
            out["timestamp"] = d.loc[idx_last, time_col].values
        else:
            out["timestamp"] = pd.Timestamp.utcnow()

        out["signal"] = (out["ema_fast"] > out["ema_slow"]).map({True: "BUY", False: "SELL"})
        out = out[["timestamp", "symbol", "close", "ema_fast", "ema_slow", "signal"]]
        return out.reset_index(drop=True)

    except Exception as e:
        logger.exception("compute_indicators_and_signals failed: %s", e)
        return _empty_result()


def _empty_result() -> pd.DataFrame:
    return pd.DataFrame(columns=["timestamp", "symbol", "close", "ema_fast", "ema_slow", "signal"])
```

#### 3.2.2 Add an action to the Workflow and link it to Step 1

* In your Workflow, click **Add action** → **Run python script**.
* **Select python tool from the library** → choose compute\_indicators\_and\_signals.
* In **Match tool inputs with any of the previous task outputs**:
  * **df** → choose 1. Transform data (output of Step 1).
  * **symbol** → the symbol column from Step 1.
  * **close** → the close column from Step 1.

> The script is created separately in Tools; the linking to Step 1 data is done here, in the Workflow.

#### 3.2.3 Behavior (guardrails) for Step 2

* If no rows are found → Interrupt workflow.
* If more than 1000 rows are found → Interrupt workflow.
* Max num of rows → set a value higher than the current table size (with a 10–20% buffer).
  * Example: table ≈ 800 rows → set 1000 or more.
  * If you add history/new tickers, increase this value.

### 3.3 Step 3 — AI prompt

**Goal**: generate a short EOD (end-of-day) report in Markdown using the results from Step 2 — Run python script (one last bar per symbol).

#### 3.3.1 Add an action

Click Add action → AI prompt.

#### 3.3.2 Paste the prompt text

Copy this text into the **Prompt** field:

```
You are a pragmatic trading assistant. Produce a concise end-of-day (EOD) report in **Markdown**.

## Data
Use the table inserted below. Each row is the latest bar per symbol with columns:
timestamp, symbol, close, ema_fast, ema_slow, signal.

If the table is empty, output exactly: `No fresh data for this session.`

## Calculations
- Compute **ema_spread_pct = (ema_fast - ema_slow) / close * 100**.
- Count symbols by signal (BUY / SELL). Any other value → treat as **HOLD**.
- Formatting: prices → 2 decimals; percentages → 2 decimals with `%`. Neutral tone; no advice.

## Output (Markdown)

### EOD Summary
- BUY: <count>, SELL: <count>, HOLD: <count>.
- Notables: mention any symbols with **|ema_spread_pct| ≥ 1.00%**.

### Highlights
**Top BUY** (up to 3 by highest positive ema_spread_pct):  
List as `TICKER — close $X.XX (spread Y.YY%)`.

**Top SELL** (up to 3 by most negative ema_spread_pct):  
List as `TICKER — close $X.XX (spread −Y.YY%)`.

### Details
Provide a compact table sorted by `symbol` with columns:  
`symbol | close | ema_fast | ema_slow | ema_spread_pct | signal`.

### Notes
Data comes from Step 2 output. All numbers must be computed from the provided table only.
```

#### 3.3.3 Connect data from Step 2 (Grid)

* Place your cursor under the ## **Data** section.
* Click the **+** button on the right of the toolbar → **Use data from: 2. Run python script → Grid**.

### 3.4 Step 4 — Send email

**Goal**: send the EOD report generated in the previous step (AI prompt), with basic run context.

#### 3.4.1 Add an action

**Add action** → **Send email**.

#### 3.4.2 Fill in the fields

* **Recipients** — specify recipients (comma-separated).
* **Subject** — Daily — Signals & Report.

#### 3.4.3 Build the Body

Insert the base text:

```
## Daily — Signals & Report

Run (UTC): 
Market: 
Session: 

---

### Report
```

Now add dynamic “chips” (click the + button on the right side of the toolbar):

* For the lines Run (UTC) / Market / Session:
  * Transform data → Aggregated values → **timestamp**
  * Transform data → Aggregated values → **market**
  * Transform data → Aggregated values → **session**
* Under the Report heading:
  * AI prompt → Choose data → **Generated Content** (this is the Markdown text generated by Step 3).

## 4. Finish

* Click **Create workflow**.
* In **Run history**, make sure the flow reaches the Send email step with **Success** status.

## 5. Result — receiving the email

After the Workflow runs successfully, the report arrives by email.

<div data-with-frame="true"><img src="/files/m1wHnHFkDkm0Te5yc0gK" alt=""></div>

<div data-with-frame="true"><img src="/files/NZdVfabD9bk3fjgVll8S" alt=""></div>

## 6. Conclusion

We built a fully automated EOD pipeline in KAWA:

* Transform data prepares OHLCV and adds helper fields.
* Run python script calculates EMAs and signals.
* AI prompt creates a short Markdown report.
* Send email delivers the final result.

Following the guardrails (especially Max num of rows) and inserting only the needed fields in Steps 3–4 ensures reliable email delivery and no failures—even on “empty” days.


# Reporting and Publishing

* [Reports](/05_00_publishing/05_01_reports)
* [Applications](/05_00_publishing/05_02_applications)


# Reports

See definitions in [Terminology](/13_00_terminology#reports) section.

Reports in KAWA serve as dynamic, interactive interfaces that consolidate and visualize data from various sources, enabling users to monitor key metrics and derive actionable insights in real-time. By integrating charts, grids, and pivot tables, KAWA reports facilitate a comprehensive view of business operations, allowing for efficient data analysis and decision-making. These reports are designed to be customizable, catering to diverse user needs and preferences.

<div data-with-frame="true"><img src="/files/SzVOqRrOT1HuIhNBQVgV" alt=""></div>

<div data-with-frame="true"><img src="/files/3B9Nt6TPoCTT3nuvDBhU" alt=""></div>

## 1. Creating new Reports

In order to create a report, go to the Reports section and click on (+ Report). You can then pick one of the three available layouts:

* **Dashboard:** It is a blank canvas with unlimited height
* **Slides:** It is similar to power point and lets you create slides of fixed size.
* **Doc:** It is similar to Notion, a document-style report (free-length page) for rich text with embedded widgets.

<div data-with-frame="true"><img src="/files/T66tuTFi4IcXoeXxOxOJ" alt=""></div>

## 2. Dashboards

### 2.1 Adding widgets to dashboards

When clicking on the (Add Widget) button, the widget selector will be displayed. It lets you choose views from sheets, widgets from existing dashboards or static blocks to add to your dashboard.

<div data-with-frame="true"><img src="/files/ZjaxgrgFgrTe7usrtE1G" alt=""></div>

#### a. Adding widgets from existing sheets

In the widget selector, click on the (Sheets) tab. You will see the list of all the sheets in the workspace. Clicking on a sheet will show all the views contained in that sheet.

From that point, you can either:

* Pick an existing view from that sheet
* Create a new view, that will exist only in the dashboard

> All the views that are added to the dashboards will be copies of the ones from the sheet section.

#### b. Adding widgets from another dashboard

In the dashboards section of the widget selector, you will see all the widgets of each existing dashboard. Simply click on a widget to insert it in the current dashboard.

> All widgets will be copied and will not retain any link with the originals.

#### c. Adding content blocks

There are two types of content block:

* A Text block

*They provide an easy way to insert static texts to illustrate your dashboards. They support rich text formatting.*

* An Image block

*They let you upload any image into your dashboards.*

<div data-with-frame="true"><img src="/files/cBzZtkRyjHNm7CQlZJBZ" alt=""></div>

* A Code Block

*Accepts HTML content only and immediately renders it in place.*

### 2.2 Managing your widgets

All your widgets can be resized and moved on the dashboard:

* Move with the the drag handle at the top left of each widget.
* Resize with the resize handle at the bottom right of each widget.

<div data-with-frame="true"><img src="/files/XaStDvcF5d5Un852Cc3v" alt=""></div>

Here are all the actions that you can perform on the widgets. They are available in the menu at the top of each widget (additional options are available in the three dots menu).

* *Refresh:* Will recompute a given widget. If one of the underlying datasources changed, the widget will be rendered reflecting that change.
* *Summary:* This is available for chart widgets. It will show an automatic summary of the data that is displayed.
* *Edit widget:* This opens the widget in full screen mode and lets you edit its parameters.
* *Full screen widget:* Opens the widget in view-only full-screen mode.
* *Rename:* This allows to change the name of the widget on the dashboard.
* *Disable all filters:* Make the selected widget insensitive to the filters applied on the dashboard level (filters from the control panel, filters on the filter menu, cross filters).
* *Duplicate:* This creates a copy of a widget.
* *Synchronize:* Pulls the latest configuration and schema from the sheet/view, so the widget reflects recent changes; keeps the widget’s size and position on the dashboard.
* *Go to sheet:* Navigates to the sheet/view and opens the underlying view used by this widget.
* *Delete:* This removes the widget from the dashboard. If it was added from a sheet, it will not affect the original view.

### 2.3 Cross Filters

Cross filters let you explore a dashboard by clicking directly on a widget (bar, slice, point, cell, etc.). Each click adds a filter chip to the top bar, and all other linked widgets recalculate. It’s fast, in-context analysis without opening editors or changing the base views.

> Need a widget to ignore dashboard filters (including cross filters)? In its menu, turn on Ignore filters.

<div data-with-frame="true"><img src="/files/K922tK8Z0eZMLXWER9u6" alt=""></div>

#### 2.3.1 How it works

* *Source:* Clicking a value in a Chart, Grid, or Pivot Table adds a filter for that dimension (e.g., Segment = Consumer).
* *Scope:* By default, the filter applies to all linked widgets on the dashboard. Widgets with Ignore filters do not react.
* *Combination logic:* Selections from different fields combine with AND (e.g., State = Texas AND Segment = Consumer).
* *Local vs. dashboard:* Local filters inside a widget still apply; cross filters further narrow the result.

#### 2.3.2 Interacting with widgets

* *Single select:* Click a bar/slice/point or a value in a table.
* *Multi-select (same field):* Hold Ctrl/Cmd and click more items.
* *Transparency:* Tooltips show the active selection.

<div data-with-frame="true"><img src="/files/SIHXD10hNHbHWIk7VwTR" alt=""></div>

#### 2.3.3 Configure via the Config panel

Manage cross-filter behavior for the whole dashboard in Configure → Cross filtering.

#### 2.3.4 Enable

Go to Cross filtering -> Turn on the Cross filtering toggle — after this, clicks in widgets will add filter chips.

<div data-with-frame="true"><img src="/files/RUeSYbXkTe5a5DAmCHy2" alt=""></div>

#### 2.3.5 Cross-filter groups

A cross-filter group is a named set of columns (often from different datasets/widgets) that should be filtered together.

* Click **+ Add cross filter**.
* In Select 1 or more columns to create a filter:
  * Find fields via Search or expand datasets (e.g., orders, sales).
  * Select one or more columns with the same meaning (e.g., State (orders) and State (sales)).
  * Enter a clear Filter name (e.g., Location, Customer, Product).

<div data-with-frame="true"><img src="/files/a3T4oG60EpvZaoGpye7N" alt=""></div>

* Click Create Filter — the group appears in the list (with **edit** and **delete** icons).

<div data-with-frame="true"><img src="/files/N87TUE5w7mz9dVzYKfng" alt=""></div>

What happens on click: If a user clicks a value in a column that belongs to a group, the system creates a chip and applies the same filter to all other columns in that group across related widgets. Groups do not affect widgets that don’t use the group’s fields or that have Ignore filters enabled.

#### 2.3.6 Examples:

* *Location:* State (orders) + State (sales) — clicking a state in the orders table filters sales widgets by that state.
* *Product:* Product (orders) + Product (sales) — clicking a product in a pie chart filters KPIs and tables from another dataset.

### 2.4 Exploration mode

* *What it is:* Quickly change how a widget shows data.
* *How to use:* Click **Explore** (binoculars).
* *What you see:* A **Exploration mode** bar in the green board appears.

<div data-with-frame="true"><img src="/files/6eGJrCqzbtEvaNsjdf0N" alt=""></div>

* *Save or undo:*
  * **Save** — keep the new view for this widget.
  * **Cancel** — discard the changes.
* *Scope:* Affects **only this widget**. The dashboard layout and the source sheet stay the same.

### 2.4 View (read-only) mode

Reports include a dedicated View (read-only) mode designed for presenting and consuming content.

* A View mode toggle is available.
* When a user has read-only access, Reports open in View mode by default.
* The Control panel works in View mode: if at least one control exists, it is shown so viewers can apply filters without editing the report.
* Export to PDF is available in View mode.

<div data-with-frame="true"><img src="/files/OmDQIiWZ0apQ1HOu4wic" alt=""></div>

## 3. Slides

## 4. Doc

Doc are a document-style report with flexible page length. They let you build structured documents with text, headings, images, and interactive widgets. This type is especially handy for analytics presentations, technical reports, or hybrid pages that combine explanations and visualizations.

### 4.1 Create

Go to **Reports** → click **+ Report** → choose **Doc** (next to **Dashboard** and **Slides**).

### 4.2 Structure and Editing

* **Adding blocks.** Use **+** or type **/** and choose: Text, Heading 1–3, Bulleted list, Numbered list, Checklist, Сode, Image, Add widget.

<div data-with-frame="true"><img src="/files/YhDsf94LGrHU0nnex7mP" alt=""></div>

* **Block context menu.** Open it via the Block handle (six-dots icon): Convert to, Move up / Move down, Delete.

<div data-with-frame="true"><img src="/files/UqGbxzmx2ohwTz4GhKue" alt=""></div>

### 4.3 Adding widgets

* Click **Add widget**.

<div data-with-frame="true"><img src="/files/Fni9B42gOM7wl6qEj4ur" alt=""></div>

* In the dialog, choose **Sheet**.
* Pick **Existing widget** or **New widget** (chart, grid, pivot).
* Click **Apply** — the widget will be inserted at the cursor.

> The full list of actions (**Refresh, Summary, Edit widget, Full screen widget, Rename, Disable all filters, Duplicate, Synchronize, Go to sheet, Delete**) is described in **Dashboard → Managing your widgets**.

### 4.4 Formatting content

Text blocks support **Rich text** (bold, italics, underline), **H1–H3 headings, lists, checklists, code snippets**, and **images**. This lets you build full analytical documents with explanations and dynamic data.

<div data-with-frame="true"><img src="/files/Joapd3zq25bYmjLlqkzA" alt=""></div>

### 4.5 Filters and exploration

**Control panel filters** also work for widgets embedded in a Doc.

**Exploration mode** and **View mode** for Doc widgets works the same as on a dashboard (see **Dashboard** → **Exploration mode**, **View mode**).

### 4.6 When to choose a Doc

* When you need a narrative plus visualizations on one canvas.
* For overview reports where charts are interleaved with explanatory text, images, and notes.


# Applications

Applications in KAWA let you create and deploy full-featured web apps directly on your datasets, using Views and Dashboards as pages. Each page can be customized to control user interactions such as filtering, sorting, grouping, and deploying/folding nodes.Also, the applications now include a built-in AI assistant for natural-language interaction with the data you see on the page.KAWA Applications make it quick and simple to turn raw data set into tailored interfaces that speed up work and help you make better decisions.

## 1. Creating applications

To create a new application, go to the Applications section and click on (+ Application).

<div data-with-frame="true"><img src="/files/urd60ucMZ0LGfIellevJ" alt=""></div>

The next step after clicking the + Application button is to choose the sheets that your application will be based on.

<div data-with-frame="true"><img src="/files/s5iFTRCzVNr5qN1Jxndy" alt=""></div>

After you pick the sheets, a Select agents window appears where you choose which chat agents (AI assistants) will be available in your application.

<div data-with-frame="true"><img src="/files/zTESPqrdgDzX9NxEgDFo" alt=""></div>

## 2. Editing applications

Once your application is created, you’ll arrive at the Page Editor, which is organized into four main areas:

<div data-with-frame="true"><img src="/files/rjBUgs0x32iO31CwxrZs" alt=""></div>

### 2.1 Left Sidebar (Navigation)

The left sidebar lets you fully control your application’s structure:

#### a. Application Title & Rename

* Your application’s name appears at the top.
* Click the pencil icon next to the title (e.g. “Events App”) to rename it in place.

#### b. Add New Pages

* Hit the + button to add a new page - choose from View, Dashboard, or Presentation.

<div data-with-frame="true"><img src="/files/rsWha4wXCOnNmcVEYSnf" alt=""></div>

#### c. Manage Existing Pages

* Each page (e.g. “events”, “orders”, “% of sales per segment”) is listed below the title.
* Click the three-dot kebab menu ⋮ on any page to Rename or Delete it.

#### d. Reorder Pages

* Drag any page up or down the list to change its position.
* The sidebar—and your app’s navigation—will update immediately to reflect the new order.

### 2.2 Main Canvas (Content Area)

Renders your active page - whether it’s a sheet, dashbord, presentation, or any custom view—in a live, fully interactive workspace.

Above it, the toolbar gives you one-click access to:

* Configure: after clicking the Configure button, the Add new control panel opens from the right side of the screen. You can read more about this in the [Control Panel](/02_00_modeling/02_01_control_panel) section.
* Added active Controls.

Within the canvas itself, users can filter, sort, group, collapse/expand nodes, and even edit records directly. Which of these features are available at any moment depends on your settings in the Right Sidebar (Page Settings): toggling off “Add/Remove filters” will cause the filter icon of each column to disappear and in the canvas itself and block the creation of new filters, and disabling the "Change Aggregation" feature will prevent you from changing the aggregation of each column. In this way, every control on the canvas reflects the toggles you choose, ensuring that the page’s behavior always matches your configuration.

### 2.3 Right Sidebar (Page Settings)

Provides on/off toggles and text fields to customize the active page. The right sidebar offers context-sensitive controls for tweaking the active page. Depending on whether you’re editing a View, Dashboard or Presentation, you’ll see options to rename the page, turn on or off interactive features (like filtering, grouping or inline editing), adjust aggregation and field visibility, and configure other page-specific behaviors. Use these settings to fine-tune both the look and functionality of each page in your application.

<div data-with-frame="true"><img src="/files/mw93efbOEwbtosMhFpHQ" alt=""></div>

<div data-with-frame="true"><img src="/files/wJlAHKteA96a62MAP2C7" alt=""></div>

### 2.4 Header (Page Toolbar)

Above the canvas, the header gives you quick access to global page actions:

#### a. Sheets

Delete or add the underlying data source for this page.

<div data-with-frame="true"><img src="/files/SHyTymiVNaHBH7ryFqtw" alt=""></div>

#### b. Share

Adjust who can view and edit it.

<div data-with-frame="true"><img src="/files/C1i3yYgMep7qUAEcGuNp" alt=""></div>

#### c. Pages/Agents Toggle

Flip between page layout controls and AI-assistant settings.

When you switch to the AI Agents tab, you can adjust every setting of the selected agent. This flexible setup lets you customize each assistant for different datasets, user roles, and use cases—making sure your team always has the right AI helper on hand.

<div data-with-frame="true"><img src="/files/jGAtH9OPDTDGzvDWFZ7D" alt=""></div>

#### d. Done

When you’ve finished configuring, click Done in the top-right corner to save and exit the editor.

## 3. Viewer Mode

KAWA will save your changes and automatically switch from the editor into Viewer Mode, displaying the production-ready interface of your application.

<div data-with-frame="true"><img src="/files/iDWmU3tKoF25kfllWvt6" alt=""></div>

In Viewer Mode, your team sees the clean, final version of the app—no editing tools, just the controls you’ve exposed:

### 3.1 Left Sidebar

#### a. Back Arrow:

Return to the Home - Applications page.

#### b. App Title:

Shows your application’s name.

#### c. Page List:

Navigate between all published pages (Views, Dashboards, Presentations) in the order you defined.

#### d. Chat with your data:

Launch the context-aware AI assistant for natural-language queries against the visible dataset. Open a focused workspace where you pick an AI agent, choose from suggested prompts, or type your own questions. The agent uses your current page’s data to deliver context-aware answers—charts, tables, or summaries—in a clean, standalone view. Use the back link to return to the main application when you’re done.

<div data-with-frame="true"><img src="/files/64Arhx73a4g3GIRe017g" alt=""></div>

<div data-with-frame="true"><img src="/files/YZWuFjRi1vKFJlYpZiJJ" alt=""></div>

You can read more about this in the [AI Integration](/06_00_ai_integration) section.

### 3.2 Main Canvas (Content Area)

* Renders the selected page—grid, chart, form, or custom view—in a fully interactive, read-only layout.
* Users can filter, sort, group, collapse/expand nodes, and drill down into details according to the toggles you enabled during editing.

This Viewer Mode is what your end users will interact with day-to-day: a streamlined, secure interface for exploring data and leveraging AI assistance without any editing distractions.

<div data-with-frame="true"><img src="/files/lHQYloxTtzL0O9tThapd" alt=""></div>

#### a. Exploration mode

On this toolbar you’ll see the Explore button (binoculars icon) with a dropdown. It lets you instantly switch between three data exploration modes.

<div data-with-frame="true"><img src="/files/wh23YOI1fh2OHGhNW2ZR" alt=""></div>

With one click, you can pick the best view for your current dataset—no need to rebuild pages or start from scratch.

<div data-with-frame="true"><img src="/files/pVjAWcDAz4BOVDu57SFG" alt=""></div>


# Artifacts

See definitions in [Terminology](/13_00_terminology#artifact) section.

The **Artifacts** tab is a workspace-level library for the files you bring into KAWA. It gives you a single place to upload, store, preview, version, publish, and download files — PDFs, images, presentations, spreadsheets, text, application files, and other binaries — without leaving the platform.

Where sheets hold your modeled data and dashboards hold your live views, the Artifacts tab holds your **files**: the documents and deliverables you want to keep, version, and share with your team.

### 1. Creating an artifact

An artifact is created first as an empty container; you then add a file to it as its first version.

Click (+ Artifact) at the top right — or the (+) button at the top of the left panel — to open the **Create artifact** dialog. Enter a **Name** (required), then choose a **Type** from the dropdown: `Application`, `PDF`, `Image`, `PowerPoint`, `Excel`, `Word`, `Binary`, or `Text`. Click (**Create**).

<div data-with-frame="true"><figure><img src="/files/3g8rEO2hTTmjoce9x2gr" alt=""><figcaption></figcaption></figure></div>

The new artifact appears in the list, labeled with its type (for example, `POWERPOINT`), and opens with an empty preview reading *No versions yet*. Add a file to populate it — see [ **Versions**](https://docs.kawa.ai/pages/prU6ylmGbf9DHliIM9Pi#id-3.-versions) below.

### 2. Viewing an artifact

Click an artifact's name to open its detail page. The detail page has three parts.

#### 2.1 Details panel

The left of the detail page summarizes the artifact:

* **Creator** — the user who created it.
* **Access** — the artifact's current visibility (managed in **Sharing and permissions**).
* **Created** and **Updated** — the creation and last-modification timestamps.

#### 2.2 Versions panel

Below the details, the **VERSIONS** panel lists every version of the artifact (v1, v2, and so on), each with its timestamp and author. The current version is marked with a check. Select any version to preview it, or use the (+) button to add a new version (see section [**Versions**](https://docs.kawa.ai/pages/prU6ylmGbf9DHliIM9Pi#id-3.-versions)).

#### 2.3 Preview panel

The main area previews the selected version's contents. For supported types, KAWA renders the file directly in the browser — for example, a text data file is shown as a table, and a presentation is shown slide by slide. Use the zoom controls (`−` / `+`, the percentage readout, and (Reset)) to adjust the view. The preview header offers these actions:

* **Download** — save the current version to your computer in its original format.
* **Publish** — choose a version and a visibility level for the artifact (see section [**Publishing**](https://docs.kawa.ai/pages/prU6ylmGbf9DHliIM9Pi#id-4.-publishing)).
* **Open** — open the current version in a new browser tab.
* **Details** — show or hide the details and versions panel.

<div data-with-frame="true"><figure><img src="/files/sQ5OMx2oFTG7Opi7B6vY" alt=""><figcaption></figcaption></figure></div>

### 3. Versions

An artifact keeps a full history of its **versions**. A new artifact starts empty (*No versions yet*); the file you add first becomes v1.

To add a version, click the (+) button in the VERSIONS panel to open the **Add new version** dialog. Upload the file — drop it onto the file area or click (Choose a file); the file chooser lists only files that match the artifact's type. Once a file is added, it appears as a chip with its type, name, and size, and an optional **Description** field appears for this version. Click (Add version) to save: KAWA stores it as a new version and keeps the previous ones.

<div data-with-frame="true"><figure><img src="/files/o4j9HE6oBJc913gDAjBU" alt=""><figcaption></figcaption></figure></div>

This lets you update a file — a revised report, an updated deck, a corrected dataset — without losing what it replaced. The latest version is shown by default, and you can select any earlier version to preview or download it.

### 4. Publishing

Publishing controls who can open an artifact and through which link. Click **Publish** in the detail header to open the **Publish artifact** dialog.

Choose the **Version** to publish (for example, *Latest version (v1)*), then set the visibility:

| Visibility               | Who can open it                                          |
| ------------------------ | -------------------------------------------------------- |
| **Restricted**           | No shareable link — only reachable inside the workspace. |
| **Signed-in users**      | Anyone signed in who has the link can open it.           |
| **Anyone with the link** | No sign-in required — anyone with the link can open it.  |

Click (Apply) to save. The artifact list shows the current state in the **Published** column.

<div data-with-frame="true"><figure><img src="/files/awJkxQZFV7JcB2n8xKqE" alt=""><figcaption></figcaption></figure></div>


# AI integration

## 1. AI Integration to assist configuration

In KAWA, AI is integrated in various places to assist you configuring your data models and analytics.

### 1.1 Formula editor

Within the sheet section, the formula editor comes with an AI section that can write formulas from a description in natural language.

<div data-with-frame="true"><img src="/files/AjnH7KcuZQVweE9wT04B" alt=""></div>

The AI also provides a list of suggested prompts that you can leverage.

### 1.2 Chart creation

When creating new charts, the AI can help by generating the appropriate configuration from a prompt.

<div data-with-frame="true"><img src="/files/hjuyS8gJCbv9Wun3mYiR" alt=""></div>

Like for the formulas, it suggests a list of relevant charts that you can use.

### 1.3 Python script generation

From within the Script section, you can use AI to generate scripts with the correct syntax, tailored to your data and context.

<div data-with-frame="true"><img src="/files/RTledtgLeCax7VgJBDkX" alt=""></div>

## 2. Integrated AI Chat

You can interact with your data intuitively using Kawa's integrated AI Chat. It utilizes a comprehensive understanding of sheet structures and employs a multi-agent approach to facilitate seamless data exploration. This feature allows you to engage with your data naturally, enhancing the efficiency and effectiveness of data analysis. By integrating AI capabilities, Kawa ensures that you can easily access and interpret complex datasets, making informed decisions based on real-time insights.

### 2.1 Using the chat

The chat can be found in three places:

* In a sheet (Access on the top of the page, `Ask AI`)
* In a dashboard (Access on the top of the page, `Ask AI`)
* In an application (Access at the bottom left: `Chat with your data`)

<div data-with-frame="true"><img src="/files/dIJaaXzmpPjMoSzm2YSq" alt=""></div>

**Starting a conversation:** To start a new conversation, click the (+Chat) button at the top. Each conversation is private and retains the full history of messages exchanged with Kawa's agents.

**Selecting the correct agent:** When you send a message in a conversation, it's directed to a specific agent. The available agents for your current context are listed in the Agents section at the top left. You can switch to a different agent at any time during the conversation.

> When you switch agents, you'll see the relevant commands and suggestions defined by that agent's configuration in the right panel.

**Data Analytics:** The agents have access to all the sheets of your context.

| Chat        | Context                                        |
| ----------- | ---------------------------------------------- |
| Sheet       | The current sheet                              |
| Dashboard   | All the sheets of the Dashboard                |
| Application | All the sheets associated with the Application |

When asked a question that is relative to data stored in one (or many sheets), the agent will often query the data to provide an answer.

> The agent will use the security policy of the logged in users and will only access data that is authorized.

Each query that the agents make will result in a visual and interactive section with three tabs:

* A chart that shows a subset of the data in a visual manner
* A Grid that shows the actual data that was retrieved
* A Code section that shows the actual query

The outcome of an analytics session can be bookmarked and, when working within a dashboard, added directly as a widget for easy access.

<div data-with-frame="true"><img src="/files/E896bRGxcfRNB8HADfDu" alt=""></div>

## 3. Configuring Agents and Metadata

This defines the context for the messages you send in the chat. Each interaction includes:

* Information from the sheets, such as column definitions and metadata
* Instructions and knowledge associated with the agent you're interacting with
* The full history of previous messages within the active conversation

Together, these elements ensure the AI has the necessary context to generate accurate and relevant responses.

### 3.1 AI Agents

Each workspace includes a default AI agent named Paul. Agents are configured with specific instructions, connected knowledge sources (including unstructured data), a set of commands, and defined capabilities to assist with tasks and queries. See definitions in [Terminology](/13_00_terminology#agent) section.

Agents can be configured from the agent section:

<div data-with-frame="true"><img src="/files/oE4j2cihlWJWJ4bxjWIN" alt=""></div>

Agents can be added, updated and removed. Note: The default agent can be edited but not removed.

> All agents are always shared on the entire workspace. Anyone with the `Edit Agent` flag can edit them.

Agents are accessible within the various AI chats, allowing users to interact with them directly to retrieve information, execute commands, or perform specific tasks based on the agent's configuration.

### 3.2 Sheet Metadata

When chatting with data, the AI model has access to sheet metadata from multiple sources:

* Column details from the sheets, including each column's type, name, and description.

In order to configure the description for each column, go to the sheet model: Model > Three dots menu of any column > Update description

<div data-with-frame="true"><img src="/files/NtAckolaYmNUndF7vdub" alt=""></div>

* AI Metadata, which is a free-text field defined in the model. This allows you to provide any additional, context-specific information about your sheets to guide and enrich the AI's responses.

Configuring the AI Metadata can also be done through the sheet model: Model > AI Metadata (third tab on the right)

## 4. Connecting KAWA to your LLM through the Chat API

This section is reserved for platform administrators with an ADMIN account. In order to connect to a LLM, KAWA must be configured to an Open AI API on any provider. The API must be strictly compatible with Open AI specifications. Please contact <support@kawa.ai> for any requests to adjust KAWA to your API.

Examples of compatible providers:

* DeepSeek ([DeepSeek API](https://api-docs.deepseek.com/))
* OpenAI ([OpenAI API](https://platform.openai.com/docs/api-reference/chat))
* Groq Cloud ([Groc Cloud API](https://console.groq.com/docs/overview))
* Azure Open AI ([Azure OpenAI API](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference))

We can provide connectivity to the following providers on request.

* Claude AI
* Mistral AI
* Gemini

> In order for KAWA to be fully operational, your model MUST support function calling through the `tools` parameter in the Completion API.

This configuration is done through the KAWA Python SDK. Please cf [Kawa SDK on Github](https://github.com/kawa-analytics/kywy-documentation) to install the SDK.

```python
from kywy.client.kawa_client import KawaClient as K

kawa = K.load_client_from_environment()
cmd = kawa.commands

# Command to configure OPEN AI Completion API
# CF the below table for the list of available parameters.
cmd.replace_configuration('OpenAiConfiguration', {
    'activated': True, 
    'supportsStreaming': True,
    'openAiApiKey': 'sk-.........',
    'model':'gpt-4o',
    'openAiApiUrl': 'https://api.openai.com/v1',
})
```

List of configuration parameters to configure the connection the the completion API:

> There is NO need to restart the KAWA platform for these parameters to be taken in account.

| Parameter                 | Required | Default Value               | Description                                                                                                                                                                 |
| ------------------------- | -------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| activated                 | Yes      | False                       | Set to True to enable AI support.                                                                                                                                           |
| supportsStreaming         | No       | True                        | Set to True if the API supports Server Sent Events                                                                                                                          |
| openAiApiKey              | Yes      | Empty text                  | Use your provider's API Key. If omitted, AI support will be disabled                                                                                                        |
| model                     | Yes      | gpt-4o                      | The LLM to use. Check with your provider for the list of available models                                                                                                   |
| openAiApiUrl              | Yes      | <https://api.openai.com/v1> | KAWA will use this URL: `$(openAiApiUrl)/chat/completions`                                                                                                                  |
| completionApiUrl          | No       | Empty text                  | If set, KAWA will ignore `$(openAiApiUrl)` and POST requests directly on this url                                                                                           |
| additionalHeaders         | No       | Empty text                  | Additional headers to send to the completion API. Syntax is: `HEADER1=VALUE1,HEADER2=VALUE2`                                                                                |
| authenticatesWithKerberos | No       | False                       | If set to True, wil authenticate to the completion API with kerberos. The prerequisite being that all the Kerberos credentials are properly configured on the host machine. |


# Riyu — AI co-builder

**Riyu** is KAWA's AI co-builder: you describe the application you need in plain language, and Riyu plans it, connects the data, and ships it. It is a separate working surface from the KAWA analytics workspace — KAWA is where your data models live, Riyu is where you build things on top of them.&#x20;

<div data-with-frame="true"><figure><img src="/files/61JE3DeoquzDBnQYGQfE" alt=""><figcaption></figcaption></figure></div>

*The Riyu home screen.*

## 1. What you can build

Riyu turns a prompt into a working deliverable. Depending on what you ask for, that can be an application, a dashboard, a presentation, a document, or an analysis of your data. Everything Riyu produces is stored as an artifact and can be revised by continuing the conversation.

Riyu is distinct from the AI features built into the KAWA workspace itself. Those assist you while you configure formulas, charts and scripts, and let you chat with your data. Riyu builds and ships the finished application. See [AI Integration](/06_00_ai_integration) for the in-product AI features.

## 2. Accessing Riyu

Riyu runs on its own address: `riyu.kawa.ai`. Sign in with your KAWA account. If you do not have access yet, the sign-in page offers a request access form where you can describe what you would like to build.

## 3. The workspace

The left sidebar is the main navigation.

| Item        | Details                                                                                                            |
| ----------- | ------------------------------------------------------------------------------------------------------------------ |
| New thread  | Starts a new conversation with Riyu                                                                                |
| Chats       | All your conversations, with search and filters for shared and archived threads                                    |
| Projects    | Groups artifacts, chats and data into a project your team can share                                                |
| Templates   | Reusable bundles — datasources, sheets, dashboards, workflows — captured from a workspace and shared with the team |
| Agents      | In development                                                                                                     |
| Skills      | Reusable capabilities Riyu can call, from your workspace, your team, or the marketplace                            |
| Workflows   | In development                                                                                                     |
| Design      | Visual identities for the apps you build, synced across the workspace                                              |
| Help & docs | Opens the KAWA documentation site                                                                                  |
| Settings    | In development                                                                                                     |

Your account details, role and usage are shown in the menu at the bottom of the sidebar.

## 4. Starting a build

There are three ways to begin: write a prompt, point Riyu at your data, or start from a template.

### 4.1 Writing a prompt

Riyu works from what you tell it, so the more specific the request, the closer the first result lands. A useful prompt names three things: the data to use, what should be produced, and who will use it.

| Instead of                       | Try                                                                                                                      |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Build a sales dashboard          | Build a dashboard from the orders table showing revenue by region and by month, with a filter for product category       |
| Analyse our support data         | Analyse the tickets table and show which issue categories take longest to resolve, broken down by team                   |
| Make an app for the finance team | Build an app where the finance team can review pending invoices, mark them approved, and export the approved list to CSV |

You do not have to get everything into the first message. Riyu asks for missing details, and the plan it produces is where you correct course — adjusting it there is cheaper than rebuilding afterwards.

### 4.2 Quick actions

Below the composer, five shortcuts pre-frame the request so you do not have to phrase it yourself:

| Action               | Details                                                   |
| -------------------- | --------------------------------------------------------- |
| Build an application | A full internal app with screens and permissions          |
| Create presentation  | A slide deck generated from your data                     |
| Build dashboard      | A metrics dashboard                                       |
| Analyze data         | An analysis of a dataset, returned as a document or chart |
| Research topic       | A research summary                                        |

### 4.3 The composer

The composer carries a few controls alongside the prompt field:

| Control           | Details                                                                                                             |
| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| Attach            | Adds files to the conversation as context                                                                           |
| Build settings    | Commit behaviour, plus **Pull from KAWA** to refresh Riyu's view of the live workspace — see section 5.1            |
| Theme             | Sets the visual identity the built app will use — see section 8                                                     |
| Mode              | Sets how Riyu should work on the request — see section 4.4                                                          |
| Work in a project | Scopes the conversation to a project, so it inherits that project's data connection, instructions and other context |

#### 4.4 Modes

The mode sets how far Riyu goes on its own: whether it stays in discussion, works out an approach for you to review, or builds.

| Mode         | Details                                              |
| ------------ | ---------------------------------------------------- |
| Conversation | Ask questions, gather requirements                   |
| Plan mode    | Explore workspace, design the use case, write a plan |
| Build mode   | Edit files, create entities, run `kawa commit`       |

The control in the composer shows whichever mode is currently selected, so its label changes as you switch between them.

In Plan mode, Riyu investigates the workspace using read-only queries and writes a plan to its own tab in the panel beside the conversation. The plan sets out what it found, what it proposes to build, the steps it would take, and — explicitly — what will not be included and what adding each of those would involve. Nothing is applied and the workspace is left untouched, so a plan is safe to ask for even when you are not sure you want the work done.

A plan is kept rather than discarded: you can download it or delete it from the panel.

<div data-with-frame="true"><figure><img src="/files/5LXkBRyBIxKXE5sSmUEs" alt=""><figcaption></figcaption></figure></div>

*A plan written in Plan mode.*

There is no approval button on a plan. When you are satisfied with it, switch to Build mode and ask Riyu to carry it out.

#### 4.5 Starting from a template

A template is a reusable bundle captured from an existing workspace — datasources, sheets, dashboards and workflows packaged together and shared with the team. Starting from one gives you a working structure to adapt instead of building from an empty prompt.

The Templates page groups them by domain, and each entry shows who published it and when it was last edited. **Use template** creates a new piece of work from the bundle, which you can then refine in conversation with Riyu.

### 5. How a build runs

A build moves through the same broad stages each time, and Riyu narrates them in the conversation as it goes.

**Establishing context.** Riyu first checks what it has to work with. If something essential is missing or ambiguous — no data attached, a table it cannot find, or work that already exists and would be duplicated — it stops and asks. The question appears as a card in the conversation with suggested answers — sometimes one to choose, sometimes several — and a free-text option if none of them fit. You can also skip it and let Riyu decide.

**Planning.** Riyu breaks the request into a task list and shows it in the conversation, ticking items off as it completes them. The list is visible throughout, so you can see both what it intends to do and how far along it is.

<div data-with-frame="true"><figure><img src="/files/pDz99eSyubH8hERZNU4J" alt=""><figcaption></figcaption></figure></div>

*Riyu working through a build.*

**Building and verifying.** Riyu builds against the live data rather than assumptions. It checks each query it intends to use and reconciles the figures against each other before publishing anything.

**Publishing.** The finished result is registered as an artifact and becomes available in the panel beside the conversation.

**Reporting back.** Riyu closes with a summary of what it built, and — importantly — flags the assumptions it had to make and anything it noticed in the data along the way.

> 💡 Read the closing summary rather than skipping to the result. If your request did not match the data exactly, this is where Riyu tells you what it did instead, and where it surfaces caveats about the data itself. The checks run during a build confirm that the figures are consistent with each other; they cannot tell you that the underlying data is the slice you meant. A restricted or filtered source will reconcile perfectly and still answer the wrong question.

#### 5.1 Approvals

How much a build pauses for you depends on the commit behaviour set in the composer:

| Setting              | Details                                                 |
| -------------------- | ------------------------------------------------------- |
| Ask before commit    | Riyu will ask for approval before applying each commit  |
| Commit automatically | Riyu will apply commits immediately, no approval needed |

With commits set to automatic, Riyu carries the request through in a single run, stopping only if it needs an answer from you. With **Ask before commit**, it pauses before writing and asks whether to apply the change. You can open the pending changes to review them first: each one lists what will happen, the entity type and name it affects, and why Riyu wants to make it.

An approval covers exactly the set of changes you reviewed. If Riyu revises them while you are still deciding, the approval no longer matches and it asks for a fresh one, so what gets applied is always what was approved.

> 💡 The setting governs changes to the workspace itself — sheets, columns, datasources and other entities. Publishing an artifact is a different operation, so a build that only creates or edits an artifact runs straight through even with **Ask before commit** on. Expect the prompt when Riyu needs to change the data model underneath, not when it changes the thing it built for you.

The same menu holds **Pull from KAWA**, which refreshes Riyu's view of the live workspace. Use it if the workspace has changed since the conversation started — for example if someone has edited a sheet in KAWA directly — so that Riyu builds against the current state rather than what it saw earlier.

#### 5.2 Versions

Each build produces a new version of the artifact, and the version is shown next to its name. Earlier versions remain available, so you can see how the artifact changed as you refined it in conversation.

### 6. Projects and chats

A project groups artifacts, chats and data into something your team can share, so that every conversation inside it starts with the same context.

Each project has its own page. The composer there opens conversations already scoped to the project, and the panels alongside it hold the context Riyu draws on for all of them:

| Element        | Details                                                                       |
| -------------- | ----------------------------------------------------------------------------- |
| Base           | The data connection the project is built on                                   |
| Instructions   | Standing guidance Riyu follows in every chat in the project                   |
| Skills         | Reusable capabilities available to this project                               |
| Data providers | External file sources the agent can read knowledge from, such as an SFTP drop |
| Autopilots     | Scheduled read-only advisors that run against the project                     |
| Artifacts      | Everything Riyu has built in the project                                      |

Connecting a data provider asks for the host, credentials and a root directory, and lets you label the source and describe what it holds. The password is stored securely and is not shown again afterwards.

<div data-with-frame="true"><figure><img src="/files/Dwdf7ordZpFtOpwLlSaH" alt=""><figcaption></figcaption></figure></div>

*A project and its context.*

The page also lists the project's recent chats and its activity, and **View current context** shows exactly what Riyu is working from at that moment — useful when a build behaves unexpectedly and you want to check what the project is actually feeding it.

A project carries a description and a cover image, records who created it and when it was last updated, and gives access to its permissions and sharing from the header. The Projects page separates the ones you own from those shared with you.

A conversation does not have to belong to a project. Chats started outside one are marked as standalone, and the Chats page shows which project each conversation belongs to, alongside filters for shared and archived threads.

### 7. Artifacts

Everything Riyu produces is an artifact. Each one carries its type — an application, a sheet, a document, a deck — and a version, and lives in the panel beside the conversation, so the result sits next to the chat that produced it.

The panel lists artifacts at two scopes. **This chat** shows what Riyu built in the current conversation, which stays private to that thread. **This project** aggregates everything built across the project. Alongside artifacts, the panel also reaches the other parts of the workspace a build touches — skills, sheets, workflows and autopilots.

<div data-with-frame="true"><figure><img src="/files/N4AOOraa6Fh5eZSR3daS" alt=""><figcaption></figcaption></figure></div>

*The artifacts panel.*

**Topology** maps the workspace as a diagram: which datasources feed which sheets, and which artifacts read from them. It is the quickest way to see where an artifact's figures come from, or what else would be affected by changing a sheet underneath it.

<div data-with-frame="true"><figure><img src="/files/5crYYPFlncQtKDrV8c5u" alt=""><figcaption></figcaption></figure></div>

*An artifact open beside the conversation that built it.*

From the panel you can open an artifact, download it, publish it, or inspect its details. Asking for a change in the same conversation produces a new version rather than a separate artifact.

See the Artifacts page for how artifacts are managed, published and shared.

### 8. Design and themes

A **theme** is a visual identity — typeface, colour palette, spacing and overall styling — applied to the apps you build. Themes live on the Design page and are synced across the workspace, so everything built there shares a consistent look.

<div data-with-frame="true"><figure><img src="/files/819MT7bR873IAZQqFrNj" alt=""><figcaption></figcaption></figure></div>

*The Themes page.*

One theme is active at a time, and that is the one applied to what you build next. You can also pick a theme directly from the composer before starting a build. Applying a different theme later does not rewrite apps that were already built — each app keeps its own version history.

Each theme card previews the typeface, a specimen line, the colour palette, and a short descriptor. It also declares whether the theme is light or dark and how dense its spacing is. Themes can be pinned, and the page tracks how many apps use each one.

Themes are grouped by where they came from:

| Origin     | Details                                              |
| ---------- | ---------------------------------------------------- |
| Mine       | Themes you created in this workspace                 |
| From chats | Themes generated during a conversation with Riyu     |
| From brand | Themes derived from an organisation's brand identity |
| Imported   | Themes brought in from outside the workspace         |

You can also create a theme from scratch with **New theme**, or search the gallery by name.

### 9. Promoting to production

Applications built in Riyu follow KAWA's standard promotion path when they move from a development workspace to production, including the review and evidence requirements that come with it. See [SDLC — governed delivery & SOX controls](/sdlc) for the full pipeline.


# Administration and sharing

* [Sharing and permissions](/08_00_administration/08_01_permissions)
* [Data security](/08_00_administration/08_02_security)


# Sharing and permissions

## 1. User management and permissions

Users in KAWA have a global profile that applies across all workspaces and some workspace related permissions.

### 1.1 Authentication

#### a. KAWA internal authentication

KAWA can be configured to authenticate users without relying on SSO. In this mode, administrators can either choose to let people create accounts using email activation codes or to manage the user accounts themselves through KAWA's Python API.

KAWA will store the user's unique identifiers (must be the email address if people are making use of email activation), their first and last names, and their secure password hashes.

When using KAWA internal authentication mechanism, the following page will be shown to users:

<div data-with-frame="true"><img src="/files/pveJ2ismllBiJEHAthjm" alt=""></div>

> **Important:** Note that the "Register in KAWA Analytics" section can be turned off if you do not wish users to sign up by themselves. The feature flag name is: `user-sign-up`.

> **Important:** If you want to allow users to sign up, you need to configure SMTP support

#### b. Using external IDPs and SSO

When configured to work with SSO, KAWA will automatically create users in its database when new authenticated users will connect.

**Open ID Connect**: KAWA is compatible with OIDC to authenticate users. It can be integrated with providers such as OKTA, AWS Cognito, Auth0, Azure Active Directory, etc...

**Kerberos SSO**: Kawa can be configured to integrate with Kerberos to authenticate users.

**HTTP Header-based Authentication**: Users can be authenticated with HTTP Header based authentication. In that setup, the user information will be extracted from the incoming HTTP requests.

#### c. Using API Keys

KAWA provides a mechanism to allow users to generate API keys in order to authenticate. This is mainly used to work with KAWA's Python API.

### 1.2 Application wide profile

Application wide profiles are configured through KYWY, KAWA's python client.

Please refer to this repository: <https://github.com/kawa-analytics/kywy-documentation> which contains all the details and examples regarding the usage of this library.

#### a. The user roles

There are 3 global roles in KAWA. Each user has one role that is valid for the entire application.

Each time a new user is added in KAWA, they will get the *REGULAR USER* role.

**ADMINISTRATORS**

Administrators can access all the admin functionalities of the platform. They can create, delete, activate, deactivate users. They can also modify user passwords and change the global roles of other users.

Administrators can also access all workspaces and all assets in all of the workspaces without restrictions.

They can access all data bypassing all the row level and column level security.

> **Warning:** Typically, very few admin accounts should be created. They should be reserved for IT, global support and maintenance.

**SETUP ADMINISTRATORS**

There is only one such user for the entire KAWA platform. This user has all the privileges of ADMINISTRATORS. It can never be disabled, deleted or switched to another user role.

**REGULAR USERS**

Most of the users should have this role. It allows them to benefit from all the features of the platform.

#### b. The restricted data source types

There are 7 data source types in KAWA:

* **USER FILES**: To create data sources of that type, users can upload CSV files from the GUI.
* **EXTERNAL SYSTEMS**: This type of data source is created by connecting to an external system directly from the GUI: Database, API, etc...
* **KYWY** (Python client): This corresponds to data sources created from the Python client using the data loader.
* **LIVE CONNECT**: Live connections are created by querying tables or views that are stored in the main data warehouse, without ETL or synchronization.
* **PYTHON ETL**: This type of data source is created by scheduling a Python script decorated by the `@kawa_tool` decorator.
* **FROM SCRATCH**: This allows users to create data sources directly in the GUI and edit the values manually, like Excel.
* **TRANSFORMATIONS**: Those are created by materializing existing views into new warehouse tables.

Each user (*Regular users*) can only create data sources of a type that was not explicitly restricted by administrators.

This allows administrators to control what the various users can load into the platform.

In the GUI, when users create a data source, the types they are allowed to work with are shown here:

<div data-with-frame="true"><img src="/files/sCvAMh5jRjtoD823A2Ga" alt=""></div>

> **Important:** by default, users will not have access to the **LIVE CONNECT** data type.

#### **c. The overall permissions**

Overall permissions are a list of features that individual users have access to. For example, to benefit from all the Generative AI features, the permission: `GENERATIVE_AI` must be granted. This grant will apply to the entire KAWA platform.

### 1.3 Workspace permissions

Each workspace functions as a separate isolated tenant. See definitions in the [Terminology](/13_00_terminology#workspace) section.

In order to access a given workspace, a user must be explicitly invited in it. A workspace can also be configured as public. If that is the case, all the registered users in the platform can access it without restriction.

Workspace permissions are handled directly in the GUI, from the settings section (cog icon at the bottom left): **Settings → Permissions**. They are configured on two tabs:

* **Users** — what signed-in members can access and do in the workspace.
* **Anonymous** — what unauthenticated users can do through anonymous artifacts.

#### 1.3.1 Users

In each workspace, users benefit from a set of permissions, organised into three groups:

**Share and Write:**

* Share sheets and views
* Share data sources
* Share reports
* Share applications
* Manage artifacts
* Manage AI agents
* Manage Python tools
* Manage workflows
* Manage directories

**Execution:**

* Run Python scripts
* Run workflows

**Admin (danger zone):**

> **Warning:** Reserved to administrators of the workspace.

These permissions grant direct or indirect access to all data and are grouped into a highlighted "danger zone" marked with a warning icon:

* Access restricted data and restricted data providers
* Manage row-level and column-level security
* Manage workspace members
* Edit workspace settings

The member list can be searched and filtered by role.

<div data-with-frame="true"><figure><img src="/files/243rktz0dgJbyYKeZqCI" alt=""><figcaption></figcaption></figure></div>

> **Warning:** Administrative and data-access permissions give users the ability to directly or indirectly access all data. Those permissions should be reserved to workspace administrators only.

> **Warning:** Application-wide Administrators benefit from ALL those permissions by default.

#### 1.3.2 Anonymous

Anonymous access controls which actions can be performed anonymously — by unauthenticated users, through anonymous artifacts — in the workspace. By default nothing is allowed: any action that is not explicitly enabled is disallowed.

> Choose which actions anonymous artifacts are allowed to perform in this workspace. Any action left off is disallowed.

Each available action has its own toggle. Actions are grouped into four categories:

**Read** — Actions that read data, status or definitions.

* Query
* Get workflow status
* Get workflow definition
* List workflow run tasks
* Get ETL status
* Download file

**Write** — Actions that modify or upload data.

* Patch data
* Upload file

**Execution** — Actions that run or start processing.

* Run workflow
* Run ETL

**Warning** — Sensitive actions — enable with caution.

* Generate by AI

Enable the actions you want to allow, then click **Apply** to save. **Apply** stays disabled until you make a change, and saving closes the settings dialog.

> **Warning:** These actions are performed by unauthenticated users. Only enable the actions you are comfortable exposing anonymously.

Only workspace administrators who have the permission to configure anonymous access can change these settings. For everyone else the toggles and **Apply** are disabled (read-only).

If the environment exposes no anonymous actions, the tab shows: *"No anonymous actions are available in this environment."*

<div data-with-frame="true"><figure><img src="/files/usshHfgu9zdg5Dz1KPBw" alt=""><figcaption></figcaption></figure></div>

### 1.4 Teams

Within each workspace, users can be grouped in Teams. See definitions in [Terminology](/13_00_terminology#team) section. Teams let you share entities — such as applications, dashboards, sheets and data sources — with a whole group of users at once, instead of sharing with each person individually.

Entities can also be shared directly with individual users, so a team is no longer required to share with specific people.

Teams are configured on the GUI, from the settings menu.

<div data-with-frame="true"><img src="/files/fFx1ozxKK0sCaRLRKycb" alt=""></div>

There are two types of teams:

* Sharing teams: Those teams are mainly used to share objects between users.
* Security teams: They work exactly as sharing teams with an additional property: a `security name`. Those are used in row level security and column security configuration policies.

#### 1.4.1 Designating team administrators

When adding members to teams, you can designate them as team administrators. Team administrators do NOT need any specific privileges to manage the members of the teams they administrate.

<div data-with-frame="true"><img src="/files/Gel3jRP0Db4BwJFQ3T4Y" alt=""></div>

## 2. Sharing

The main assets of KAWA can be shared across the workspace to which they belong. Sharing allows to set up publishing and collaborating flows between members of the KAWA workspaces.

No information contained in Sheets or Dashboards is confidential, those are mere configuration objects. The security in KAWA only stems from two sources:

* Workspace isolation
* Data ROW and COLUMN level security.

All entities consuming the data (Sheets, Dashboards, Python computations) are without exception subject to the configured RLS and CLS policies.

> **Important:** Entities can not be shared across workspaces.

**Sharing options**:

Entities can be shared with individual users, with particular teams, or with all the users who can access the workspace. When sharing an entity:

* Set a general access policy:
  * RESTRICTED means users of the workspace cannot access the entity
  * VIEWER means that users of the workspace can access the entity but cannot publish any change on it
  * EDITOR means that all the users of the workspace can publish changes on the entity
* Set a per user access policy: the same VIEWER / EDITOR levels apply, chosen per individual.
* Set a per team access policy: the same levels apply.

> **Important:** A user affected by more than one policy will benefit from the higher available access. If a user is targeted by RESTRICTED, VIEWER and EDITOR simultaneously (through different teams, a direct user share, or the general access policy), they will have the EDITOR policy on that entity.

### 2.1 Sharing Sheets and Views

A Sheet contains multiple views, such as charts, grids and pivot tables. They also contain the business logic, expressed through formulas and python scripts.

#### a. Sheets and Views

A sheet can be shared in Read or Write mode with other users or teams of the workspace.

Within a sheet, views can be shared or private. When a view is shared, it inherits the sharing mode (Read or Write) from its parent sheet.

If a sheet is shared with TeamA for Write, then all the shared views within that sheet will be editable by members of TeamA.

In order to configure sharing parameters for a sheet, use the icon at the top right.

<div data-with-frame="true"><img src="/files/klwW0Uia261kz9JJsYae" alt=""></div>

<div data-with-frame="true"><img src="/files/zT94aKOAmpn1oNsYpBTl" alt=""></div>

> **Information:** You can also individually share views, either from the sheet sharing parameters, or from the option menu within each individual view.

**Note about views:**

When views are shared, a control becomes available on them. It lets users rollback to the latest published version, publish a new version (if the user has write permission on the view) or save the view as a new object.

#### b. Columns: Formulas, Links, Mappings, Python

Within sheets, you can share your columns:

* Formulas,
* Mappings,
* Linked columns,
* Python columns.

This can be done through the sheet model, using the shared checkbox.

Sharing columns will make them available to all users accessing the sheet. They will be able to use those shared columns within their own formulas, and add them to their views - grids, pivots and charts.

<div data-with-frame="true"><img src="/files/6cCvwshTGi72vvVbQmfJ" alt=""></div>

### 2.2 Sharing Data Sources

#### a. Data Sources

Data Sources can be shared like sheets, within the application.

> **Important:** If a data source is shared with some users, those users might **NOT** see it if the row level security (RLS) configuration is set to **DENY ALL**. Make sure to either upload some specific RLS rules or set the general access to **ALLOW ALL**.

When a data source is shared with writing permissions, users can configure them and manually override data.

In order for a user to be able to configure row level and column security on a data sources, two conditions must be met:

* The user must be able to edit the data source (shared with writing permissions)
* The user must have the `Manage Data Source Security` flag enabled.

#### b. Data Providers

Data providers are databases or external APIs to which KAWA is connected to import data. When a data provider is restricted, only users with the `Access restricted data and restricted data providers` flag can access them to create new data sources.

<div data-with-frame="true"><img src="/files/deIVNGbio3BDWY2cGDeZ" alt=""></div>

### 2.3 Sharing Dashboards

Dashboards can be shared in the UI, in the same way as sheets and data sources. All the widgets of the dashboards follow the dashboard sharing policies and publications.

It means that the PUBLISH and ROLLBACK buttons on the dashboard will affect all views of the dashboards automatically.

<div data-with-frame="true"><img src="/files/rxLIXxwR7v622MfG5zS8" alt=""></div>

When sheets are used in shared dashboards, modifying elements of their model (like formulas for example), will result in a warning for the users:

<div data-with-frame="true"><img src="/files/Oxy9fjqF7T3O2NV87Ajw" alt=""></div>

### 2.4 Sharing Apps

Applications can be shared in the UI, in the same way as sheets and data sources. All the pages of the apps follow the zpp sharing policies and publications.

It means that the PUBLISH and ROLLBACK buttons on the app will affect all pages (views, dashboards) of the app automatically.

<div data-with-frame="true"><img src="/files/xHsrqhonO7CGVCxohk57" alt=""></div>

When sheets are used in shared apps, modifying elements of their model (like formulas for example), will result in a warning for the users.

### 2.5 Sharing Workflows

Workflows can be shared in the workspace in the same way as the other entities.

If a user does not have the required permission on a sheet that is used in a workflow, they cannot modify elements of their model. In this case the workflow editor will show an error, and the update of the view will not be applied until a user with the appropriate permissions modifies the workflow or grants the missing access.

<div data-with-frame="true"><img src="/files/jOJK38QRsiFXu4SBR3fh" alt=""></div>

### 2.6 Sharing Artifacts

Artifacts can be shared in the workspace in the same way as the other entities. Use the **Share** action on an artifact to open the **Share artifact** dialog, add the teams and/or users who should have access, and set the general access policy.


# Data security

In KAWA, securing the data can be done both at row (Row level Security) and column (Column level Security) level. This is always done at the data source level.

In order to configure security on data sources, users must either own them or have the 'Manage row level and column level security' permission and be granted write access on the data source.

## 1. Column level security

Column level associates to one column in a data source a set of rules to limit access to that column.

> **Important:** If one of the below condition is matched, the user will **BYPASS** the column level security and see the entire dataset regardless of what is configured: User OWNS the data source, User is an application ADMIN, or User has the `Access restricted data and restricted data providers` workspace flag.

A rule targets a given Audience (Teams or individual users), and has a particular action:

* Completely hides the column
* Obfuscate the column's values (one way hash that makes the value impossible to read but preserves the repartition)
* Keeps the value unaltered

If a user is targeted by more than one rule, the most restrictive applies.

`HIDES is more restrictive than OBFUSCATES is more restrictive than SHOW`

<div data-with-frame="true"><img src="/files/zw2JIbYc41NJRKpo0KJi" alt=""></div>

## 2. Row level security

Row level security will filter the rows of a given data source based on a set of rules.

> **Important:** If one of the below condition is matched, the user will **BYPASS** the row level security and see the entire dataset regardless of what is configured: User OWNS the data source, User is an application ADMIN, or User has the `Access restricted data and restricted data providers` workspace flag.

#### a. Security Mapping

Those rules will be defined as a mapping between user or team names and particular values for dimensions.

For example:

| Team Name        | Country |
| ---------------- | ------- |
| Thunderbolts     | USA     |
| Samurai Warriors | Japan   |
| Desert Falcons   | UAE     |
| Nordic Vikings   | Sweden  |
| Nordic Vikings   | Finland |
| Amazon Jaguars   | Brazil  |

In the above table (called: Security Mapping), all users members of the `Thunderbolts` team will be allowed to see only the row in the data source whose country is USA. The secured dimension here is the Country.

Security mappings support special values:

* `#MATCH_MANY_TOKEN#` :If a user or team is assigned that value, they can see all elements of the secured dimension.
* `#BLANK_VALUE_TOKEN#` :If a user or team is assigned that value, they can see all elements with a blank value for the secured dimension.

For example:

| Team Name            | Country               |
| -------------------- | --------------------- |
| Thunderbolts         | `#MATCH_MANY_TOKEN#`  |
| Nordic Vikings       | Sweden                |
| Nordic Vikings       | Finland               |
| Nordic Vikings       | `#BLANK_VALUE_TOKEN#` |
| `#MATCH_MANY_TOKEN#` | Belgium               |

In the above table, Thunderbolts can see all Countries (including blank Countries). Nordic vikings can see: Sweden, Finland and rows without Countries. All users can see: Belgium, including the Nordic vikings.

The mapping between user or team identification is saved in a special data source called: Access Table.

<div data-with-frame="true"><img src="/files/IRmmPZjeBxF34dZcAXgm" alt=""></div>

This type of data source behaves like a normal data source. It can be connected to external systems, CSV files etc...

Access Tables require the 'Manage row level and column level security' to be created.

In addition to the normal data source configuration, Access Tables require its creator to explicitly designate which column contains a user identifier (which can be user ids or security team `security names`).

#### b. Security Rules

Security rules allow to define how a Security Mapping will be applied to a data source to secure it.

<div data-with-frame="true"><img src="/files/t4czzTjksHmhgJEE06j5" alt=""></div>

Section by section on the above screenshot:

* Section 1: The name of the rule itself
* Section 2: The access table to use for that rule
* Section 3: The data source whose access we want to restrict
* Section 4: The dimension to secure (Above: we are securing `Segment` in the orders data source, matching it against the `segment` column in the security mapping)
* Section 5: Defines what to do for users who are not present (directly or through a team) in the Security Mapping.

#### c. Global rule

The global rule of a data source is applied for a given user in case NO RLS rule applies for that user. It can be either: ALLOW ALL or DENY ALL.

<div data-with-frame="true"><img src="/files/krqlcMNdt7ikIPDMLYq5" alt=""></div>

If at least one rule applies the user, then the Rule for missing user in each rule applies (Section 5 above)

#### d. Example

*fig1: Access Table:*

| User Id            | Segment              |
| ------------------ | -------------------- |
| <bruce@wayne.com>  | Consumer             |
| <bruce@wayne.com>  | Enterprises          |
| <lucius@wayne.com> | `#MATCH_MANY_TOKEN#` |

User id column is the user identifier: The user id (not the team name)

*fig2: ata source to secure:*

| profit | category    |
| ------ | ----------- |
| 12     | Consumer    |
| 34     | Enterprises |
| 56     | R\&D        |

*Global Rule*:

The Global rule is set to Deny All.

*Rule*:

* Section 1: Segment control
* Section 2: The Access Table in fig1
* Section 3: The Data Source in fig2
* Section 4: The `category` of the data source will be paired with the `Segment` of the access table.
* Section 5: Deny All for users that are not in the access table.

*Result*:

* User <bruce@wayne.com> will see row 1 and row 2 of the data source:

| profit | category    |
| ------ | ----------- |
| 12     | Consumer    |
| 34     | Enterprises |

* User <lucius@wayne.com> will see the entire data source (`#MATCH_MANY_TOKEN#` is applied):

| profit | category    |
| ------ | ----------- |
| 12     | Consumer    |
| 34     | Enterprises |
| 56     | R\&D        |

* All other users will be denied access to the data source (Global rule applies)


# Security & Trust

KAWA is committed to protecting our customers' data and systems. Our security program is governed by documented policies and enforced through technical and organizational controls that are continuously monitored. This page lists the controls currently in place across our infrastructure, organization, product, internal procedures, and data handling, so customers, prospects, and auditors can see exactly how we protect information.

> This page describes how KAWA (the company) secures its operations and customer data. For in-product controls — roles, permissions, row-level security, and sharing — see [Data security & permissions](/08_00_administration/08_02_security).

## 1. Security partners

KAWA's security program is continuously monitored and independently assessed by recognized security partners.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="image">Cover image</th></tr></thead><tbody><tr><td>Security controls continuously monitored through Vanta.</td><td><a href="/files/zcNwjM0VLe3yJhJL6jGc">/files/zcNwjM0VLe3yJhJL6jGc</a></td></tr><tr><td>Independent security assessment performed by ADVSEC.</td><td><a href="/files/EpElhXGHrHH03SU1z163">/files/EpElhXGHrHH03SU1z163</a></td></tr></tbody></table>

## 2. Infrastructure security

* **Unique production database authentication enforced.** Authentication to production datastores uses authorized secure authentication mechanisms, such as a unique SSH key.
* **Encryption key access restricted.** Privileged access to encryption keys is restricted to authorized users with a business need.
* **Unique account authentication enforced.** Authentication to systems and applications uses a unique username and password or authorized Secure Socket Shell (SSH) keys.
* **Production application access restricted.** System access is restricted to authorized access only.
* **Access control procedures established.** The access control policy documents the requirements for adding new users, modifying users, and removing an existing user's access.
* **Production database access restricted.** Privileged access to databases is restricted to authorized users with a business need.
* **Firewall access restricted.** Privileged access to the firewall is restricted to authorized users with a business need.
* **Production OS access restricted.** Privileged access to the operating system is restricted to authorized users with a business need.
* **Production network access restricted.** Privileged access to the production network is restricted to authorized users with a business need.
* **Unique network system authentication enforced.** Authentication to the production network uses unique usernames and passwords or authorized SSH keys.
* **Remote access MFA enforced.** Production systems can only be remotely accessed by authorized employees using a valid multi-factor authentication (MFA) method.
* **Remote access encrypted enforced.** Production systems can only be remotely accessed by authorized employees via an approved encrypted connection.
* **Intrusion detection system utilized.** An intrusion detection system provides continuous monitoring of the network and early detection of potential security breaches.
* **Log management utilized.** A log management tool is used to identify events that may have a potential impact on the company's ability to achieve its security objectives.
* **Infrastructure performance monitored.** An infrastructure monitoring tool monitors systems, infrastructure, and performance, and generates alerts when predefined thresholds are met.
* **Network segmentation implemented.** The network is segmented to prevent unauthorized access to customer data.
* **Network firewalls reviewed.** Firewall rulesets are reviewed at least annually, and required changes are tracked to completion.
* **Network firewalls utilized.** Firewalls are used and configured to prevent unauthorized access.
* **Network and system hardening standards maintained.** Network and system hardening standards are documented, based on industry best practices, and reviewed at least annually.
* **Service infrastructure maintained.** Infrastructure supporting the service is patched as part of routine maintenance and as a result of identified vulnerabilities, helping ensure servers are hardened against security threats.

## 3. Organizational security

* **Asset disposal procedures utilized.** Electronic media containing confidential information is purged or destroyed in accordance with best practices, and certificates of destruction are issued for each device destroyed.
* **Production inventory maintained.** A formal inventory of production system assets is maintained.
* **Portable media encrypted.** Portable and removable media devices are encrypted when used.
* **Anti-malware technology utilized.** Anti-malware technology is deployed to environments commonly susceptible to malicious attacks and is configured to be updated routinely, logged, and installed on all relevant systems.
* **Employee background checks performed.** Background checks are performed on new employees.
* **Code of Conduct acknowledged by contractors.** Contractor agreements include a code of conduct or a reference to the company code of conduct.
* **Code of Conduct acknowledged by employees and enforced.** Employees acknowledge a code of conduct at the time of hire. Employees who violate the code of conduct are subject to disciplinary action in accordance with a disciplinary policy.
* **Confidentiality Agreement acknowledged by contractors.** Contractors sign a confidentiality agreement at the time of engagement.
* **Confidentiality Agreement acknowledged by employees.** Employees sign a confidentiality agreement during onboarding.
* **Password policy enforced.** Passwords for in-scope system components are configured according to the company's policy.
* **MDM system utilized.** A mobile device management (MDM) system is in place to centrally manage mobile devices supporting the service.
* **Visitor procedures enforced.** Visitors are required to sign in, wear a visitor badge, and be escorted by an authorized employee when accessing the data center or secure areas.
* **Security awareness training implemented.** Employees complete security awareness training within thirty days of hire and at least annually thereafter.

## 4. Product security

* **Data encryption utilized.** Datastores housing sensitive customer data are encrypted at rest.
* **Company uses Vanta for continuous security monitoring.** The company uses Vanta for continuous security monitoring.
* **Penetration testing performed.** Penetration testing is performed at least annually. A remediation plan is developed and changes are implemented to remediate vulnerabilities in accordance with SLAs.
* **Data transmission encrypted.** Secure data transmission protocols are used to encrypt confidential and sensitive data when transmitted over public networks.
* **Vulnerability and system monitoring procedures established.** Formal policies outline the requirements for vulnerability management and system monitoring functions related to IT / Engineering.

## 5. Internal security procedures

* **Continuity and Disaster Recovery plans established.** Business Continuity and Disaster Recovery plans are in place that outline communication plans to maintain information security continuity in the event of the unavailability of key personnel.
* **Continuity and Disaster Recovery plans tested.** A documented Business Continuity/Disaster Recovery (BC/DR) plan is maintained and tested at least annually.
* **Cybersecurity insurance maintained.** Cybersecurity insurance is maintained to mitigate the financial impact of business disruptions.
* **Configuration management system established.** A configuration management procedure is in place to ensure that system configurations are deployed consistently throughout the environment.
* **Change management procedures enforced.** Changes to software and infrastructure components of the service are authorized, formally documented, tested, reviewed, and approved prior to being implemented in the production environment.
* **Production deployment access restricted.** Access to migrate changes to production is restricted to authorized personnel.
* **Development lifecycle established.** A formal systems development life cycle (SDLC) methodology governs the development, acquisition, implementation, changes (including emergency changes), and maintenance of information systems and related technology requirements.
* **SOC 2 – System Description.** A system description is maintained for Section III of the SOC 2 audit report.
* **Whistleblower policy established.** A formalized whistleblower policy is established, and an anonymous communication channel is in place for users to report potential issues or fraud concerns.
* **Board oversight briefings conducted.** The board of directors or a relevant subcommittee is briefed by senior management at least annually on the state of the company's cybersecurity and privacy risk, and provides feedback and direction to management as needed.
* **Board charter documented.** The board of directors has a documented charter that outlines its oversight responsibilities for internal control.
* **Board expertise developed.** Board members have sufficient expertise to oversee management's ability to design, implement, and operate information security controls. The board engages third-party information security experts and consultants as needed.
* **Board meetings conducted.** The board of directors meets at least annually and maintains formal meeting minutes. The board includes directors that are independent of the company.
* **Backup processes established.** The data backup policy documents requirements for backup and recovery of customer data.
* **System changes externally communicated.** Customers are notified of critical system changes that may affect their processing.
* **Management roles and responsibilities defined.** Management has established defined roles and responsibilities to oversee the design and implementation of information security controls.
* **Organization structure documented.** An organizational chart is maintained that describes the organizational structure and reporting lines.
* **Roles and responsibilities specified.** Roles and responsibilities for the design, development, implementation, operation, maintenance, and monitoring of information security controls are formally assigned in job descriptions and/or the Roles and Responsibilities policy.
* **Security policies established and reviewed.** Information security policies and procedures are documented and reviewed at least annually.
* **Support system available.** An external-facing support system is in place that allows users to report system information on failures, incidents, concerns, and other complaints to appropriate personnel.
* **System changes communicated.** System changes are communicated to authorized internal users.
* **Access reviews conducted.** Access reviews are conducted at least quarterly for in-scope system components to help ensure that access is restricted appropriately. Required changes are tracked to completion.
* **Access requests required.** User access to in-scope system components is based on job role and function, or requires a documented access request form and manager approval prior to access being provisioned.
* **Incident response plan tested.** The incident response plan is tested at least annually.
* **Incident response policies established.** Security and privacy incident response policies and procedures are documented and communicated to authorized users.
* **Incident management procedures followed.** Security and privacy incidents are logged, tracked, resolved, and communicated to affected or relevant parties by management according to the company's security incident response policy and procedures.
* **Physical access processes established.** Processes are in place for granting, changing, and terminating physical access to company data centers based on an authorization from control owners.
* **Data center access reviewed.** Access to the data centers is reviewed at least annually.
* **External support resources available.** Guidelines and technical support resources relating to system operations are provided to customers.
* **Service description communicated.** A description of the company's products and services is provided to internal and external users.
* **Risk assessment objectives specified.** The company specifies its objectives to enable the identification and assessment of risk related to those objectives.
* **Risks assessments performed.** Risk assessments are performed at least annually. As part of this process, threats and changes (environmental, regulatory, and technological) to service commitments are identified and the risks are formally assessed. The risk assessment includes a consideration of the potential for fraud and how fraud may impact the achievement of objectives.
* **Risk management program established.** A documented risk management program is in place that includes guidance on the identification of potential threats, rating the significance of the risks associated with the identified threats, and mitigation strategies for those risks.
* **Third-party agreements established.** Written agreements are in place with vendors and related third parties. These agreements include confidentiality and privacy commitments applicable to that entity.
* **Vendor management program established.** A vendor management program is in place, including a critical third-party vendor inventory, vendor security and privacy requirements, and a review of critical third-party vendors at least annually.
* **Vulnerabilities scanned and remediated.** Host-based vulnerability scans are performed at least quarterly on all external-facing systems. Critical and high vulnerabilities are tracked to remediation.

## 6. Data and privacy

* **Data retention procedures established.** Formal retention and disposal procedures are in place to guide the secure retention and disposal of company and customer data.
* **Customer data deleted upon leaving.** Customer data containing confidential information is purged or removed from the application environment, in accordance with best practices, when customers leave the service.
* **Data classification policy established.** A data classification policy is in place to help ensure that confidential data is properly secured and restricted to authorized personnel.


# SDLC — governed delivery & SOX controls

KAWA ships a complete software-development lifecycle for analytics: isolated feature workspaces, Git-backed promotion, environment branches, and gated releases. Builders never touch Git — one CLI verb per lifecycle step drives everything — yet every change reaches production as a reviewed, approved, signed commit. This page describes the working model and the [SOX control framework](#7-sox-controls) it enforces.

## 1. Two operating models

**Not every use case needs a pipeline.** KAWA offers two operating models, and the choice is per use case, not per platform — most deployments run both side by side.

| Model         | How it works                                                                                                                                                                                               | Use it for                                                                                                         |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **Free mode** | One shared workspace. KAWA's native permission system and roles separate **builders** (create and edit) from **explorers** (consume, filter, drill). No branches, no gates — changes are live immediately. | Self-service analytics: internal dashboards, ad-hoc analysis, team reporting.                                      |
| **SDLC mode** | The lifecycle described on this page: Git-backed environments, feature workspaces, gated promotion, signed deploys.                                                                                        | Mission-critical and SOX-compliant processes: reconciliation, regulatory reporting (MiFID, EMIR), financial close. |

In Free mode, governance *is* the permission model — see [Sharing and permissions](/08_00_administration/08_01_permissions). In SDLC mode, governance is structural: nothing reaches a governed environment except through the promotion path. The rest of this page describes SDLC mode.

## 2. The model

**Git is the source of truth. Workspaces are materializations of branches.**

In SDLC mode, the definitive state of an environment is not the workspace — it is a Git branch holding the workspace's full definition as a file tree (one file per entity: datasources, sheets, views, workflows, scripts, dashboards). Workspaces are *deployed copies* of those branches:

<div data-with-frame="true"><figure><img src="/files/sv2ColeNB9B0SmfwllRY" alt="Git branches deploy to read-only environment workspaces; feature branches hydrate disposable feature workspaces"><figcaption></figcaption></figure></div>

**The tree is pure logic — KAWA as code.** It contains no data, no credentials, and no environment-specific identifiers: entities are addressed by immutable tags, data sources by abstract connection types. Each environment sits on top of **its own data and its own secrets** — development points at dev data with dev credentials, production at production data with production credentials — and supplies them when a tree is deployed (see [secrets & connection isolation](#73-secrets--connection-isolation)). This is what makes the definition fully portable: the *same commit* deploys unchanged to develop, staging, and production, and only the environment underneath it differs.

Environment workspaces are **read-only for humans**: their only write path is the CI deploy principal applying an approved commit. There is nothing to police by discipline — hand-editing a governed environment is structurally impossible.

Every KAWA workspace operates in one of two modes:

| Mode            | Who edits          | Purpose                                                                                                 |
| --------------- | ------------------ | ------------------------------------------------------------------------------------------------------- |
| **Exploration** | Anyone with access | Regular KAWA permissions. Ad-hoc analysis, prototyping, AI-assisted building. Outside the SDLC.         |
| **Governed**    | CI deploy only     | Environment workspaces of an SDLC pipeline. Refuses interactive mutation for everyone, admins included. |

Feature workspaces are exploration-mode by design — they are private and disposable, so builders keep full freedom, including AI assistance.

## 3. Pipelines

**A pipeline binds branches, workspaces, and policy under one name.** Pipelines are declared once, in the promotion repository's `pipelines.toml`:

```toml
[pipeline.mifid-reporting]
repo       = "git@gitlab.example.com:acme/mifid-promotion.git"
branches   = { develop = "mifid/develop", staging = "mifid/staging", production = "mifid/production" }
workspaces = { develop = 771, staging = 772, production = 773 }
policy     = "sox-strict"        # approval gates + evidence profile
ticketing  = { provider = "jira", project = "MIFID" }

[pipeline.sales-analytics]
repo       = "git@gitlab.example.com:acme/sales-promotion.git"
branches   = { develop = "sales/develop", production = "sales/production" }
workspaces = { develop = 810, production = 811 }
policy     = "light"             # peer review only, no ticket linkage
ticketing  = { provider = "linear", team = "SALES" }
```

Builders select a pipeline **by name** — branch names and workspace IDs never appear in the human vocabulary. The number of environments per pipeline is free: two for a light internal pipeline, three or more for regulated delivery.

The **policy profile** decides how much ceremony each merge gate carries: required approver roles, mandatory ticket linkage, and the evidence emitted at deploy time. The same lifecycle runs everywhere; only the gates differ.

### 3.1 Ticketing integration

**Every change is anchored to a ticket in your tracker.** A pipeline plugs into the team's existing ticketing system — Jira, Linear, GitHub Issues, or any provider with a REST API — through the `ticketing` block:

* **Validated at start** — `kawa feature start --ticket MIFID-123` checks the ticket *exists*, is *open*, and (under `sox-strict`) is assigned to the requesting builder. No ticket, no feature branch: the audit chain starts before the first edit.
* **Auto-created where needed** — `kawa release promote` creates the review/change ticket automatically (typed per hop: technical review for staging, change record for production), pre-filled with the plan diff and linked to the merge request. Reviewers work from their tracker, not from Git.
* **Stamped end to end** — the ticket ID travels through the branch name, every commit message, the merge request, and the signed deploy evidence. From a production deploy, the auditor walks back to the ticket in one hop — and from the ticket, forward to everything it changed.
* **Status sync** — ticket transitions follow the lifecycle: *In progress* at start, *In review* at request, *Done* when the change reaches its target environment.

## 4. The feature lifecycle

**One change = one ticket = one branch = one workspace = one merge request.**

<div data-with-frame="true"><figure><img src="/files/IYMtfPl2GcNUlPoBfbSp" alt="Feature lifecycle: start, build (with sync), request, merge gate, merged — then the feature workspace is retired"><figcaption></figcaption></figure></div>

### 4.1 Start

```
$ kawa feature start --pipeline mifid-reporting --ticket JIRA-123 --title "MiFID volume report"
  ✔ branch mifid/feat/JIRA-123 created off mifid/develop @ a1b2c3d
  ✔ feature workspace created and hydrated (round-trip verified)
  → https://kawa.example.com/workspaces/812
```

`start` performs, atomically:

1. Validates the ticket against the pipeline's tracker — it must exist and be open (see [Ticketing integration](#31-ticketing-integration)).
2. Creates the feature branch off the head of the pipeline's `develop` branch (the integration baseline is always the base; `--type hotfix` bases off `production` instead).
3. Creates a fresh KAWA workspace stamped with branch, ticket, and creator.
4. **Hydrates** it from the branch tree and verifies the round-trip: the hydrated workspace re-exports byte-identical to the branch. Data bindings resolve per environment — see [secrets & connection isolation](#73-secrets--connection-isolation).
5. Grants the builder full rights on that workspace, and registers the ticket ↔ branch ↔ workspace binding.

Omitting `--pipeline` lists the pipelines your identity has builder rights on. The KAWA principal who owns the workspace is stamped as the Git author of every subsequent commit — the audit chain has one identity across both systems.

### 4.2 Build

Work in the feature workspace through the normal KAWA UI — no-code, low-code, or AI-assisted. At any point:

```
$ kawa feature status JIRA-123
```

re-exports the workspace and prints a plan-style diff against the branch: *what have I changed so far*.

### 4.3 Sync

The develop baseline moves while your feature is open. Pull it in early:

```
$ kawa feature sync JIRA-123
```

merges the latest `develop` into your feature branch and re-hydrates the delta into your workspace. If another merged change touched the same entities, the conflict surfaces **here** — on entity files, reviewable — not silently at merge time.

### 4.4 Request

```
$ kawa feature request JIRA-123
  ✔ snapshot of workspace 812 → commit on mifid/feat/JIRA-123
  ✔ pushed — merge request !47 opened against mifid/develop
  → https://gitlab.example.com/.../merge_requests/47
```

Because the feature workspace only ever contained your branch plus your work, **the merge-request diff is exactly your change** — nothing from other builders leaks in. `request` is re-runnable: each snapshot adds a commit to the same branch and updates the same merge request.

### 4.5 The merge gate

CI validates every merge request before a human reviews it:

* **Consistency** — the tree is self-contained; every cross-entity reference resolves inside it.
* **Fidelity** — the tree hydrates and re-exports byte-identical.
* **Plan comment** — CI posts a human-readable diff on the merge request: *"merging this changes these entities in develop"* — reviewers read a change summary, not raw definitions.
* **Approvals** — per the pipeline's policy profile, enforced by Git branch protection: peer review (all profiles), ticket linkage and distinct approver roles (`sox-strict`). The author can never approve their own change.

On merge, CI deploys the new `develop` head into the develop workspace, byte-verifies it, and records evidence. The feature workspace and branch are then retired — explicitly via `kawa feature discard JIRA-123`, or by the reaper once the branch is merged or idle past its TTL.

## 5. Releases

**Promotion between environments is a merge request between environment branches**, driven by a different persona and verb:

<div data-with-frame="true"><figure><img src="/files/Ji3cQ5EaOPKTgMxpWU3M" alt="Feature branches merge into develop via peer review; develop promotes to staging and production through approver-gated merge requests; every environment is drift-checked"><figcaption></figcaption></figure></div>

```
$ kawa release promote --pipeline mifid-reporting --to staging
$ kawa release promote --pipeline mifid-reporting --to production
```

| Hop                  | Requested by    | Approved by        | Gate                                            |
| -------------------- | --------------- | ------------------ | ----------------------------------------------- |
| develop → staging    | Release manager | Technical approver | Green CI, validation runs on staging            |
| staging → production | Release manager | Business approver  | Ticket linkage, staging evidence, change record |

The production deploy itself is **commit-gated**: only a commit on the approved `production` branch history can be applied, and the deploy writes a signed evidence bundle (manifest fingerprint + attestation of who deployed what, when) — see [traceability & evidence](#72-traceability--evidence). Rollback is the same gated mechanism pointed at an earlier approved commit.

**Hotfixes** follow the same path with the same approvals, expedited: branch off `production`, merge request into `production`, then back-merge down through `staging` and `develop` so the tiers reconverge.

## 6. Detective controls

Every environment workspace is **drift-checked on schedule**: KAWA re-exports the workspace and byte-compares it against its branch. Any divergence — a failed deploy, a platform fault, an out-of-band change — raises an alert with the exact entity-level diff. Combined with governed mode (which prevents human drift structurally), the branch and the environment cannot silently disagree.

## 7. SOX controls

For pipelines running the `sox-strict` policy profile, the lifecycle above *is* the control framework — nothing sits beside it. This section maps the controls an auditor looks for onto the mechanics that enforce them.

### 7.1 Segregation of duties

**No single person builds, approves, and deploys.** The same person never fills two of these roles for the same change:

| Role                | Responsibility                                           | Enforced by                                                 |
| ------------------- | -------------------------------------------------------- | ----------------------------------------------------------- |
| Builder             | Builds in the feature workspace, requests promotion      | Merge-request authors can never approve their own change    |
| Technical approver  | Reviews the change; clears the develop and staging gates | Required approvals on protected branches                    |
| Business approver   | Confirms business impact; clears the production gate     | Protected branch + mandatory ticket linkage                 |
| CI deploy principal | The only writer on governed workspaces                   | Governed mode refuses interactive mutation, admins included |

> **Note:** enforced structurally — Git branch protection plus governed workspace mode — not by policy alone. AI assistance is available in feature workspaces only; governed environments are read-only for humans and agents alike.

### 7.2 Traceability & evidence

**Every deployment carries its own evidence**, written by the deploy itself and bound to the change — it cannot drift from what it describes. The signed bundle contains:

* Change ticket ID and merge request
* Git commit SHA and approver identities
* The plan diff and validation results
* A manifest fingerprint of every deployed file
* Deployment signature, timestamp, and rollback reference

An auditor re-exports the environment and compares it to the manifest, proving byte for byte that what runs is what was approved — and the scheduled [drift checks](#6-detective-controls) run that same proof continuously.

### 7.3 Secrets & connection isolation

**Promoted trees never contain credentials.** A data source is referenced by an abstract, portable type — "Postgres source", "SFTP source" — and each environment resolves it against its own secret store at deploy time. Development, staging, and production therefore run identical logic against different credentials and different data, with no secret ever present in Git, in a workspace definition, or in evidence.

## 8. Command reference

| Verb                                                | Persona         | What it does                                                       |
| --------------------------------------------------- | --------------- | ------------------------------------------------------------------ |
| `kawa feature start --pipeline P --ticket T`        | Builder         | Branch off develop, create + hydrate the feature workspace         |
| `kawa feature status T`                             | Builder         | Diff my workspace vs my branch                                     |
| `kawa feature sync T`                               | Builder         | Merge the latest develop baseline into my branch + workspace       |
| `kawa feature request T`                            | Builder         | Snapshot → commit → push → open/update the merge request           |
| `kawa feature discard T`                            | Builder         | Delete the feature workspace, branch, and registry entry           |
| `kawa release promote --to ENV`                     | Release manager | Open the promotion merge request to the next environment           |
| `kawa env plan --pipeline P --env E`                | CI / operator   | Dry-run diff: approved branch vs live environment workspace        |
| `kawa env deploy --pipeline P --env E --commit SHA` | CI              | Apply an approved commit to the environment, verify, emit evidence |
| `kawa env drift --pipeline P --env E`               | CI (scheduled)  | Detect divergence between an environment and its branch            |

> **Note:** builders only ever need the `feature` verbs. Git — branches, commits, pushes, merge requests — is fully mediated by the CLI; the only Git surface a builder sees is the merge-request page where their change is reviewed.


# Python integration

* [Python SDK](/09_00_python_integration/09_01_python_sdk)
* [Python Tools](/09_00_python_integration/09_02_python_tools)
* [Scenario 1 - Data transformation](/09_00_python_integration/09_03_python_tools_case1)


# Python SDK

KAWA offers a Python SDK that lets you perform various operations: Computations, Data loading and Advanced administration tasks.

You can find example workbooks and additional documentation here: [KAWA Python SDK Github Repository](https://github.com/kawa-analytics/kywy-documentation).

## 1. Getting started with the SDK

### 1.1 Installation

In order to install the SDK, run the following:

`pip install kywy`

> The SDK is hosted on [PyPI](https://pypi.org/project/kywy/).

### 1.2 Retrieve your API Key

The API Key can be retrieved from the KAWA GUI. Click on **Settings** > **API Key**.

Please set a date at which the key will expire and click on **Generate key**.

The key is of the following format:

```
kawa-........
```

> Once generated and copied, the key can no longer be retrieved. If you lost you key, you will need to generate a new one.

### 1.3 Connect and authenticate to KAWA

The recommended way to connect to KAWA with the Python SDK is by creating a `.env` file in your project root directory.

> The `.env` file will be located by searching upward from the current working directory until the file is found or the root directory is reached.

Here is what the content of your `.env` file should look like:

```bash
KAWA_URL=https://your-domain:your-port
KAWA_API_KEY=kawa-****
KAWA_WORKSPACE=1
```

Specify the following:

* *KAWA\_URL:* Enter your URL with the correct port
* *KAWA\_API\_KEY:* Fill in the API key that was generated at the previous step
* *KAWA\_WORKSPACE:* Specify in which workspace you want to be authenticated

When the file has been created, run the following:

```python
from kywy.client.kawa_client import KawaClient as K
kawa = K.load_client_from_environment()
```

Alternatively, you can authenticate without using the `.env` file (not recommended):

```python
from kywy.client.kawa_client import KawaClient as K
kawa = K(kawa_api_url='https://your-domain:your-port')
kawa.set_api_key(api_key='kawa-****')
kawa.set_active_workspace_id('1')
```

## 2. Upload data to KAWA using the Python SDK

In order to upload any pandas dataframe to KAWA:

```python
from kywy.client.kawa_client import KawaClient as K
kawa = K.load_client_from_environment()

# Define your dataframe
# df = ....

loader = kawa.new_data_loader(
    df=df, 
    datasource_name='Super Store',
)

loader.create_datasource()
loader.load_data()
```

Please have a look at this [Notebook](https://github.com/kawa-analytics/kywy-documentation/blob/main/notebooks/data-operations/01_load_data_notebook.ipynb) for a complete documentation of the data loading API.

> Note that you can also use arrow tables instead of pandas dataframe for improved performances. This is all detailed in the notebook mentioned above.

## 3. Run computations on KAWA from the Python SDK

This feature lets you query your data and download it as a pandas dataframe directly in your existing scripts. The execution of the query (Filtering, Aggregations, etc...) will be deported into the KAWA data warehouse to ensure low latency and a small memory footprint in your own Python runtime.

```python
from kywy.client.kawa_client import KawaClient as K
kawa = K.load_client_from_environment()

query = (kawa
         .sheet('Super Store')
         .select(K.col('Profit').sum())
         .group_by('State')
         .order_by('Profit', ascending=False)
         .limit(5))

df = query.compute()

# df is a regular Pandas dataframe that can be further manipulated.
```

Please have a look at this [Notebook](https://github.com/kawa-analytics/kywy-documentation/blob/main/notebooks/data-operations/02_compute_notebook.ipynb) for a complete documentation of the computing API.


# Python tools

In addition to proposing a Python SDK (`kywy`) to interact with your data in Python, KAWA also comes in with its own Python runtimes. These runtimes can be deployed in your infrastructure and scaled out to fit your use cases and load. See definitions in [Terminology](/13_00_terminology#python-tool) section.

In order to get started, please make sure that Python is healthy in our workspace. If it is not, please get in touch with the administrators of the system.

<div data-with-frame="true"><img src="/files/dJgatqFXofJqbNEHGfL6" alt=""></div>

*Make sure that you see a green dot on the python icon, like shown above.*

You can find example workbooks and additional documentation here: [KAWA Tools Github Repository](https://github.com/kawa-analytics/kawa-toolkits).

## 1. Configuring your Version Control System

You have two main options when configuring your Version Control System:

* Using KAWA's own store. This is a very simplistic setup that can be used when no governance or version control is needed on the script libraries that are connected to KAWA.
* Connecting to GIT using an SSH private key. With this option, KAWA will automatically synchronize with your repository. All your source code will then we entirely be handled by your organization, following the governance rules put in place.

> This configuration should be done when creating your workspace. It can be changed later on, but KAWA does not handle any migration. **If you are working on KAWA's store and decide to move to your GIT repo, you will need to reconnect your scripts manually.**

> This configuration is made *at the workspace level*, and can differ from one to the other. You can for instance have a sandbox workspace that is making use of the simple Kawa file store configuration. You can also work on a staging workspace connected to a staging branch and a production workspace connected to a production branch.

### 1.1 Using the KAWA file store

In the settings, go to (Source Control) and pick KAWA File Store. There is no further configuration to do. All your scripts can directly edited in the GUI and will be saved on KAWA.

<div data-with-frame="true"><img src="/files/wpFvrUrzCxRF1nYc1jRf" alt=""></div>

### 1.2 Connection to your GIT server using SSH

#### 1.2.1 Prepare your repository

Before you setup the connection, it is necessary to add the following files in your repository:

* A `requirements.txt` file at the root of the repository containing all the dependencies.
* At least one KAWA toolkit, described in a `kawa-toolkit.yaml` file.

Here is an example of a valid `kawa-toolkit.yaml`:

```yaml
name: basic-tools
tools:
  - name: Length of a Text
    file: length.py
```

And here is the valid `length.py` script:

```python
import pandas as pd
from kywy.client.kawa_decorators import kawa_tool

@kawa_tool(
    inputs={'text': str},
    outputs={'length': float},
)
def main(df: pd.DataFrame) -> pd.DataFrame:
    df['length'] = df['text'].apply(lambda x: len(x))
    return df
```

A very simplistic valid repository could look like this:

```txt
/
- requirements.txt
- kawa-toolkit.yaml
- length.py
```

> Please refer to [KAWA Tools Github Repository](https://github.com/kawa-analytics/kawa-toolkits) for more details about tools and toolkits. (This repository can be loaded in KAWA, it contains many examples).

#### 1.2.2 Configure the connection

In the settings, go to (Source Control) and pick Git (SSH). Please fill in:

* Your SSH remote URL
* The branch name (KAWA will only clone and pull source code from that branch)
* Your SSH private key.

> This private key will be encrypted in KAWA's secure store, and only decrypted at runtime to pull or clone the relevant branch.

<div data-with-frame="true"><img src="/files/mfxPVTxF2oLXGBQLgZLw" alt=""></div>

**Always use the SSH URL, the HTTPS one will not work.**

> KAWA Will only performs `git clone` and `git pull` commands. It is recommended to use a key associated with a git account with readonly permissions only.

A valid URL looks like that: `git@github.com:kawa-analytics/kawa-toolkits.git`. Do not include the protocol (like `ssh://`)

*On Gitlab, it can be found in the code section:*

<div data-with-frame="true"><img src="/files/qHdoFIvhsv49rGf1FZ0o" alt=""></div>

*On Github, it can be found in the code section, SSH tab:*

<div data-with-frame="true"><img src="/files/6MYdBJQeSkk0ucw6rZz2" alt=""></div>

*On Bitbucket, in the Clone section, (do not copy the `ssh://`):*

<div data-with-frame="true"><img src="/files/wdpTtV9magJh4886PUHT" alt=""></div>

> Please refer to the documentation of your platform to learn how to create a new SSH key pair. It will require you to paste the private key in KAWA and the public one in your version control system.

## 2. Adding new tools in KAWA

KAWA tools are regular python functions, decorated with the `@kawa_tool` decorator. (It requires `pip install kywy` and `from kywy.client.kawa_decorators import kawa_tool`).

The kywy library is available on [pypi](https://pypi.org/project/kywy/).

Please refer to this for details and examples: [KAWA Tools Github Repository](https://github.com/kawa-analytics/kawa-toolkits).

In order to access these features, make sure you have the two following permissions:

<div data-with-frame="true"><img src="/files/VACDWj40OlTmvq98jk3L" alt=""></div>

## 2.1 With the KAWA file store (no Version Control System)

In order to add a new tool to your workspace, go to the Script section and click on (+ Tool).

<div data-with-frame="true"><img src="/files/BccH1TfjYFm9Ga2zngDV" alt=""></div>

Type in the content of your script and click on save.

<div data-with-frame="true"><img src="/files/0Hbhh4gtXcDqHwxTvnsq" alt=""></div>

### 2.2 With a connected Version Control System

Like in the previous paragraph, navigate to the Script section and click on (+ Tool).

You will be presented with all your tools grouped by their corresponding toolkit:

For example:

<div data-with-frame="true"><img src="/files/IL3fF9qZzMVlBSdTpncw" alt=""></div>

This reflects the following repository structure:

```txt
/
- requirements.txt
- kawa-toolkit.yaml
- length1.py
- length2.py
```

With the following toolkit file:

```yaml
name: basic-tools
tools:
  - name: Length of a Text
    file: length1.py
 - name: Length of a Text 2
    file: length2.py
```

Once a tool is added from VCS, it cannot be edited in the GUI. You need to add commits to your branch for it to get picked up by KAWA. *There is a delay of a few seconds for the new commits to reach KAWA*.

<div data-with-frame="true"><img src="/files/cpiq6UQG29xSi2ORkop4" alt=""></div>


# Python tools — case 1

This scenario will demonstrate how to create a datasource from a python script, scheduled to run automatically.

This new datasource will be derived from an existing sheet.

This setup assumes that you are creating python tools directly in KAWA (Without any connection to a Version control system).

## 1. Preparing a sheet

Please create a sheet that has the following columns:

* `date` (a date column)
* `price` (a numeric column, can be integer or decimal)
* `quantity` (a numeric column, can be integer or decimal)
* `key` (a text column)

(If you have different names, you can adjust your script to match what you have)

## 2. Creating a new python tool

Here is an example script that you use to derive data from the prepared sheet: (please refer to \[Computation From Python notebook]

```python
import logging
import pandas as pd
import time
import datetime
from kywy.client.kawa_decorators import kawa_tool

log = logging.getLogger('script-logger')
NUM_DAYS_INCREMENT=3
NUM_DAYS_FULL=300

@kawa_tool(
    outputs={
        'key': str,
        'date': datetime.date,
        'avgPrice': float,
        'totalQuantity': float
    },
    parameters={
        'source_sheet_id': {'type':str}
    })
def sync_data(kawa, source_sheet_id, data_preview=False, append=False):
    start=time.time()
    log.info('Python etl example with computation')

    
    num_days = NUM_DAYS_INCREMENT if data_preview or append else NUM_DAYS_FULL
    from_date = datetime.date.today() - datetime.timedelta(days=num_days)

    #
    # This step loads data from the data warehouse.
    # Use filters and group_by to limit as much as possible the 
    # data that will be processed by your script.
    # This ensures that your process scales.
    #
    log.info(f'Loading data from:{from_date}')
    query = (kawa
            .sheet(sheet_id=source_sheet_id)
            .select( 
                # Adjust to the names of the columns of your sheet
                # (Make sure to adjust the filter as well)
                kawa.col('key').first().alias('key'),
                kawa.col('date').first().alias('date'),
                kawa.col('price').avg().alias('avgPrice'),
                kawa.col('quantity').sum().alias('totalQuantity'),
            )
            .filter(kawa.col('date').date_range(from_inclusive=from_date))
            #
            # Feel free to add stages to your query
            #
            .group_by('key')
            .no_limit())

    #
    # From this point on, you work with a regular pandas dataframe.
    # Feel free to add any more advanced transformation on it
    #
    df= query.compute()

    # 
    # The important thing is to return a dataframe with the columns
    # declared in the output section of the @kawa_tool decorator.
    #
    elapsed=time.time() - start
    log.info(f'Your transformation was performed in {elapsed}s', )
    return df
```

> (<https://github.com/kawa-analytics/kywy-documentation/blob/main/notebooks/data-operations/02\\_compute\\_notebook.ipynb>) to learn all the transformations you can apply to your data.

> 📚 Please note that we inject to the function two boolean parameters: `data_preview` and `append`. Those will be `True` when (respectively) we are previewing the data to configure the data source or when the load is incremental. Use them wisely in your script! in our case, it is read to change the range of the main date filter.

In order to save your script in KAWA, go to the script section and paste the script in a new tool.

<div data-with-frame="true"><img src="/files/fK6Tq7h0fpv1vzb0mMHh" alt=""></div>

## 3. Create your data source

Go to the datasource section, Click on \[+] or \[+ DataSource] and select: `Load with Python`.

Then, select your newly created script:

<div data-with-frame="true"><img src="/files/yZiKVapOHBMjzknUBEBV" alt=""></div>

> 🚨 Make sure to select the correct sheet ID and set the FEED TYPE to Incremental.

Click on `Preview Data` and then: `Next`.

<div data-with-frame="true"><img src="/files/reGISktE4E13svTwIDxi" alt=""></div>

> 🚨 In our example, the primary key is called: `key`. Make sure to select your primary keys consistently with the unicity criteria of your dataset. Each execution of the ETL in incremental mode will insert rows with new primary keys and replace rows with existing ones.

## 4. Configure scheduling

Make sure to configure scheduling in your datasource:

<div data-with-frame="true"><img src="/files/Sj3KYFAuXuB6gsezW6ri" alt=""></div>

You can also declare the parent datasource as *upstream*. In that way, each time the parent datasource is refreshed, the child one will be too.

<div data-with-frame="true"><img src="/files/b57BDbwtT5Msl0NDAEf3" alt=""></div>


# HTTP API Reference

KAWA exposes a REST API that lets you manage users, workspaces, datasources, workflows, and more programmatically. All examples use `curl`.

Base URL: `https://<your-kawa-instance>`

## 1. Authentication

KAWA supports two authentication methods:

| Method               | How                                                            |
| -------------------- | -------------------------------------------------------------- |
| **API Key**          | Pass `x-kawa-api-key` header on every request                  |
| **Email / Password** | `POST /authentication/login` → returns an `accessToken` cookie |

For programmatic access, **API key authentication is recommended**.

### 1.1 Required headers

Most endpoints require these headers:

Workspace-scoped endpoints typically include:

* `GET /backoffice/*`
* `POST /commands/secured/run`

Authentication and health endpoints typically do not require a workspace header:

* `POST /authentication/login`
* `GET /health`

### 1.2 Log in with email/password

`POST /authentication/login`

Use `-c` to store the returned `accessToken` cookie, then `-b` to reuse it in subsequent requests.

```bash
# 1) Login and store cookies
curl -X POST https://your-instance.kawa.ai/authentication/login \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{
    "credentialType": "LOGIN_AND_PASSWORD",
    "credentials": {
      "email": "john.doe@example.com",
      "password": "s3cr3t"
    }
  }'

# 2) Reuse the accessToken cookie
curl https://your-instance.kawa.ai/authentication/current-user \
  -b cookies.txt
```

Returns an `accessToken` cookie used in subsequent requests.

### 1.3 Get current user

`GET /authentication/current-user`

```bash
# API key auth
curl https://your-instance.kawa.ai/authentication/current-user \
  -H "x-kawa-api-key: <your-api-key>" \
  -H "x-kawa-workspace-id: <workspace-id>"

# Or cookie auth (see 1.2)
# curl https://your-instance.kawa.ai/authentication/current-user -b cookies.txt
```

## 2. Commands endpoint

Most write operations (create, update, delete) go through a single command bus:

`POST /commands/secured/run`

Request body:

```json
{
  "command": "<CommandName>",
  "parameters": {}
}
```

Example:

```bash
curl -X POST https://<your-kawa-instance>/commands/secured/run \
  -H "Content-Type: application/json" \
  -H "x-kawa-api-key: <your-api-key>" \
  -H "x-kawa-workspace-id: <workspace-id>" \
  -d '{
    "command": "CreateWorkspace",
    "parameters": {
      "displayInformation": {
        "displayName": "My Workspace",
        "description": ""
      }
    }
  }'
```

> Returns `200 OK` for most operations. Some operations may return `202 Accepted` when an async process is started. Returns `409 Conflict` when an entity already exists (depending on the command).

## 3. Users

### 3.1 List users

`GET /backoffice/principals`

```bash
curl https://your-instance.kawa.ai/backoffice/principals \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

### 3.2 Get user by ID

`GET /backoffice/principals/{id}`

```bash
curl https://your-instance.kawa.ai/backoffice/principals/p_01 \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

### 3.3 Create user

Command: `CreateActivePrincipalWithPassword`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "CreateActivePrincipalWithPassword",
    "parameters": {
      "email": "bob@example.com",
      "password": "B0bPassw0rd!",
      "firstName": "Bob",
      "lastName": "Jones"
    }
  }'
```

> If a user with this email already exists, the server may return `409 Conflict` (behavior depends on command implementation).

### 3.4 Change user status

Command: `ReplaceUserStatus`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "ReplaceUserStatus",
    "parameters": {
      "principalId": "p_01",
      "active": false
    }
  }'
```

Set `"active": true` to reactivate.

### 3.5 Change user role

Command: `ReplaceUserRole`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "ReplaceUserRole",
    "parameters": {
      "principalId": "p_01",
      "role": "ADMIN"
    }
  }'
```

### 3.6 Change user password

Command: `AdminChangeUserPassword`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "AdminChangeUserPassword",
    "parameters": {
      "principalId": "p_01",
      "newPassword": "N3wP@ssword!"
    }
  }'
```

### 3.7 Generate API key for a user

Command: `AdminGenerateApiKeyForPrincipal`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "AdminGenerateApiKeyForPrincipal",
    "parameters": {
      "principalId": "p_01",
      "name": "my-automation-key",
      "replaceIfExists": true
    }
  }'
```

Add `"expirationDate": 20000` (days since Unix epoch) to set an expiry.

> **Important:** The `clearTextApiKey` in the response is only returned once and cannot be recovered later.

### 3.8 Assign user to perimeters

Command: `AssignPrincipalToPerimeters`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "AssignPrincipalToPerimeters",
    "parameters": {
      "principalEntityId": "p_01",
      "memberOfPerimeterNames": ["EMEA", "Finance"]
    }
  }'
```

## 4. Workspaces

Most operations are scoped to a workspace. Include `x-kawa-workspace-id` for workspace-scoped endpoints.\
Some endpoints are global (for example `GET /backoffice/workspaces`) and work without this header.\
If you provide `x-kawa-workspace-id`, make sure your API key has access to that workspace — otherwise the server may return `403 Forbidden`.

### 4.1 List workspaces

`GET /backoffice/workspaces`

```bash
curl https://your-instance.kawa.ai/backoffice/workspaces \
  -H "x-kawa-api-key: kawa_abc123xyz"
```

> Note: This endpoint can be called without `x-kawa-workspace-id`. If you include `x-kawa-workspace-id`, use a workspace you have access to; otherwise you may receive `403 Forbidden`.

```bash
# Optional: scope the call to an accessible workspace
curl https://<your-kawa-instance>/backoffice/workspaces \
  -H "x-kawa-api-key: <your-api-key>" \
  -H "x-kawa-workspace-id: <accessible-workspace-id>"
```

### 4.2 Get workspace by ID

`GET /backoffice/workspaces/{id}`

```bash
curl https://your-instance.kawa.ai/backoffice/workspaces/ws_01 \
  -H "x-kawa-api-key: kawa_abc123xyz"
```

### 4.3 Create workspace

Command: `CreateWorkspace`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "CreateWorkspace",
    "parameters": {
      "displayInformation": {
        "displayName": "Q1 Analytics",
        "description": "Workspace for Q1 reporting"
      }
    }
  }'
```

> May return `409 Conflict` if a workspace with this name already exists (behavior depends on command implementation).

### 4.4 Add users to workspace

Command: `AddMembersToWorkspace`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "AddMembersToWorkspace",
    "parameters": {
      "workspaceId": "ws_01",
      "members": [
        { "principalId": "p_01", "permissions": [] },
        { "principalId": "p_02", "permissions": [] }
      ]
    }
  }'
```

To also set this as the users' active workspace:

Сommand: `SetCurrentWorkspaceForPrincipals`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "SetCurrentWorkspaceForPrincipals",
    "parameters": {
      "workspaceId": "ws_01",
      "principalIds": ["p_01", "p_02"]
    }
  }'
```

### 4.5 Remove users from workspace

Command: `RemoveWorkspaceMembers`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "RemoveWorkspaceMembers",
    "parameters": {
      "workspaceId": "ws_01",
      "principalIds": ["p_01"]
    }
  }'
```

### 4.6 Set workspace visibility

Command: `ToggleWorkspaceVisibility`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "ToggleWorkspaceVisibility",
    "parameters": {
      "workspaceId": "ws_01",
      "isPublic": false
    }
  }'
```

## 5. Datasources

### 5.1 List datasources

`GET /backoffice/datasources`

```bash
curl https://your-instance.kawa.ai/backoffice/datasources \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

### 5.2 Get datasource schema

`GET /backoffice/datasources/{id}/schema`

```bash
curl https://your-instance.kawa.ai/backoffice/datasources/ds_01/schema \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

### 5.3 Get datasource health report

`GET /backoffice/datasources/health-report/v2/{id}`

```bash
curl https://your-instance.kawa.ai/backoffice/datasources/health-report/v2/ds_01 \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

### 5.4 Archive datasource

Command: `ArchiveDataSourceAndDeleteAssociatedData`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "ArchiveDataSourceAndDeleteAssociatedData",
    "parameters": {
      "dataSourceId": "ds_01"
    }
  }'
```

> **Warning:** This permanently deletes the datasource and all its associated data.

### 5.5 Share datasource

Command: `UpdateDataSourceShareStatus`

`generalAccess` values: `RESTRICTED`, `READ`, `EDIT`.

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "UpdateDataSourceShareStatus",
    "parameters": {
      "dataSourceId": "ds_01",
      "shared": true,
      "advancedSharingConfiguration": {
        "sharedTeamIds": ["team_01"],
        "teamIdsWithWritePermission": [],
        "generalAccess": "RESTRICTED"
      }
    }
  }'
```

## 6. Workflows

### 6.1 List workflows

`GET /backoffice/workflows`

```bash
curl https://your-instance.kawa.ai/backoffice/workflows \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

### 6.2 Get workflow run history

`GET /backoffice/workflows/{workflow_id}/run-history`

```bash
curl https://your-instance.kawa.ai/backoffice/workflows/wf_01/run-history \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

### 6.3 Run a workflow

Command: `RunWorkflow`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "RunWorkflow",
    "parameters": {
      "workflowId": "wf_01",
      "parameterValues": {
        "taskVariable_startDate": "2024-01-01",
        "taskVariable_region": "EMEA"
      }
    }
  }'
```

> Workflow trigger parameters must be prefixed with `taskVariable_`.

### 6.4 Delete workflow

Command: `DeleteWorkflow`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "DeleteWorkflow",
    "parameters": {
      "workflowId": "wf_01"
    }
  }'
```

## 7. Sheets

### 7.1 List sheets

`GET /backoffice/sheets`

```bash
curl https://your-instance.kawa.ai/backoffice/sheets \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

### 7.2 Get sheet schema

`GET /backoffice/sheets/{id}/schema`

```bash
curl https://your-instance.kawa.ai/backoffice/sheets/sh_01/schema \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

### 7.3 Create sheet

Command: `CreateSimpleSheet`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "CreateSimpleSheet",
    "parameters": {
      "shared": false,
      "createDefaultLayout": true,
      "displayInformation": {
        "displayName": "Revenue Overview",
        "description": ""
      },
      "datasourceId": [
        {
          "targetDataSourceId": "ds_01",
          "foreignKeyNames": [],
          "defaultValueForAttributes": []
        }
      ]
    }
  }'
```

### 7.4 Delete sheet

Command: `DeleteSheet`

```bash
curl -X POST https://your-instance.kawa.ai/commands/secured/run \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "DeleteSheet",
    "parameters": {
      "sheetId": "sh_01"
    }
  }'
```

## 8. Server & Health

### 8.1 Health check

`GET /health`

```bash
curl https://your-instance.kawa.ai/health \
  -H "x-kawa-api-key: kawa_abc123xyz"
```

### 8.2 Server version

`GET /backoffice/application-environment`

```bash
curl https://your-instance.kawa.ai/backoffice/application-environment \
  -H "x-kawa-api-key: kawa_abc123xyz"
```

### 8.3 Usage statistics

`GET /backoffice/usage?from={from_date}&to={to_date}&zoneId={timezone}`

```bash
curl "https://your-instance.kawa.ai/backoffice/usage?from=2024-01-01&to=2024-12-31&zoneId=Europe/Paris" \
  -H "x-kawa-api-key: kawa_abc123xyz" \
  -H "x-kawa-workspace-id: ws_01"
```

## 9. Entity listing

All entity types follow the same URL pattern:

```
GET /backoffice/{entity_kind}
GET /backoffice/{entity_kind}?name={name}
GET /backoffice/{entity_kind}?tag={tag}
GET /backoffice/{entity_kind}/{id}
```

| Entity kind             | Description           |
| ----------------------- | --------------------- |
| `principals`            | Users                 |
| `workspaces`            | Workspaces            |
| `datasources`           | Datasources           |
| `sheets`                | Sheets                |
| `dashboards`            | Dashboards            |
| `applications`          | Applications          |
| `extended-applications` | Extended applications |
| `workflows`             | Workflows             |
| `data-providers`        | Data providers        |
| `layouts`               | Layouts / views       |
| `attributes`            | Attributes            |
| `scripts`               | Scripts               |
| `script-runners`        | Script runners        |
| `agents`                | Agents                |
| `teams`                 | Teams                 |
| `perimeters`            | Perimeters            |

**Example — search by name:**

```bash
# Search by name (server-side filter)
curl "https://your-instance.kawa.ai/backoffice/workspaces?name=Finance%20Team" \
  -H "x-kawa-api-key: <your-api-key>" \
  -H "x-kawa-workspace-id: <workspace-id>"
```

## 10. Error responses

| Status | Meaning                                                                                   |
| ------ | ----------------------------------------------------------------------------------------- |
| `200`  | Success                                                                                   |
| `202`  | Accepted (async operation started)                                                        |
| `403`  | Forbidden — API key does not have access to the workspace specified `x-kawa-workspace-id` |
| `409`  | Conflict — entity already exists                                                          |
| `4xx`  | Client error — body contains reason                                                       |
| `5xx`  | Server error                                                                              |

Error body format:

```bash
# Inspect status code + response body
curl -i "https://your-instance.kawa.ai/backoffice/principals/does-not-exist" \
  -H "x-kawa-api-key: <your-api-key>" \
  -H "x-kawa-workspace-id: <workspace-id>"
```

> The error response body is returned by the server and may vary depending on the endpoint. Use curl with -i to inspect the HTTP status code and response body.


# Architecture

This section describes the KAWA platform architecture — its core components, how they work together, and how KAWA connects to your data warehouse, storage, and source systems.

KAWA runs as a high-availability, horizontally scalable platform: an active-active backend control plane, a BPMN 2.0 workflow engine, sandboxed Python runners, and a pluggable warehouse and object-storage layer — encrypted in transit and at rest.

<div data-with-frame="true"><figure><img src="/files/CNeVV4BHAlQozDHk7Oe4" alt=""><figcaption></figcaption></figure></div>

*KAWA platform architecture — high-availability components, sandboxed execution, and end-to-end encryption.*

***

* [Data Lakehouse](/10_00_architecture/10_01_lakehouse)
* [Snowflake native connection](/10_00_architecture/10_02_snowflake_native_connection)
* [BigQuery native connection](/10_00_architecture/bigquery-native-connection)
* [KAWA Query cache](/10_00_architecture/10_03_query_cache)


# Data Lakehouse integration

This section illustrates how KAWA can be integrated in your data lakehouse.

## 1. Overview

KAWA integrates seamlessly into the consumption layer of your data lakehouse, providing an intuitive interface for data exploration and analytics while directly leveraging the Iceberg API to manage KAWA tables and ingest user data—all within a dedicated S3 bucket. It bridges to the processing layer by utilizing existing execution engines like Trino to perform scalable analytical queries using standard SQL. This integration allows users to harness the full suite of KAWA’s features—such as Python ETL, dynamic columns, and rich visualizations—natively within the lakehouse, combining performance, flexibility, and usability in a unified environment.

<div data-with-frame="true"><img src="/files/2IYGyM2FdBNv1KFnnMQo" alt=""></div>

## 2. Configuration guide

This configuration guide outlines how to integrate KAWA into a data lakehouse architecture using Trino as the execution engine, S3 as the object storage layer, and Hive Metastore as the metadata layer.

This guide supposes the following prerequisites:

* Working instance of a hive metastore
* An existing S3 bucket with a R/W account for KAWA
* A Trino instance with a Read only account for KAWA + CREATE and DROP views in a catalogue managed by KAWA.

### 2.1 Configuring Trino

> If you do not wish to activate the write back feature, you can skip this paragraph.

#### 2.1.1 Creating an Iceberg catalog

In Trino, you have to configure a new catalogue using the iceberg connector.

> Example for the Trino catalog kawa (content of the file: `kawa.properties`)

```ini
# Connector
connector.name=iceberg

# Catalog
iceberg.catalog.type=hive_metastore
hive.metastore.uri=thrift://hive-metastore:9083

# Name of you hive catalog if you support multi catalogs
# (Not necessarily the same as the trino catalogue...)
hive.metastore.thrift.catalog-name=hive

# S3 Configuration
fs.native-s3.enabled=true
s3.endpoint=http://my-bucket:9000
s3.aws-access-key=KAWA
s3.aws-secret-key=***********
s3.path-style-access=true
s3.region=us-east-1
```

#### 2.1.2 Create a new schema via Trino

In the new catalog (Here, we are working in the `kawa` catalogue), create a new schema via Trino.

```sql
CREATE SCHEMA IF NOT EXISTS kawa.kawa_sandbox
```

## 2.2 Configuring KAWA

#### 2.2.1 Readonly configuration

In order for KAWA to function in read only mode on Trino, you need the following four environment variables:

```bash
export KAWA_TRINO_USER=....
export KAWA_TRINO_PASSWORD=....
export KAWA_TRINO_JDBC_URL=jdbc:trino://host:port
export KAWA_WAREHOUSE_TYPE=TRINO
```

#### 2.2.2 Read+Write configuration

To add the write back capability, **in addition to the four variables described above**, three more variables are necessary

*Environment variables:*

```bash
# Use the TRINO catalog name (kawa in the previous paragraph)
export KAWA_TRINO_WRITER_CATALOG=....

# Use the schema name you created in paragraph 2.1.2
export KAWA_TRINO_WRITER_SCHEMA=....

# Any string that will serve as a prefix for all tables created by KAWA
export KAWA_TRINO_WRITER_TABLE_PREFIX=__KAWA__
```

Once those four variables are set, use the Python SDK to configure the S3 access as well as the Hive URL. This will allow KAWA to initialize the Iceberg API with the correct information and credentials.

*Server configuration:*

```python

from kywy.client.kawa_client import KawaClient as K
kawa = K(kawa_api_url='https://your-domain:your-port')
kawa.set_api_key(api_key='kawa-****')

kawa.commands.replace_configuration('IcebergConfiguration', {
  # Must match hive.metastore.thrift.catalog-name 
  # in Trino catalogue
  "hiveCatalog": "hive",

  # Options for the iceberg API when loading the hive catalogue.
  # These options will be passed when the Iceberg API
  # initializes the Hive catalogue.
  # cf: https://iceberg.apache.org/docs/1.8.0/
  "options": {
        # Hive metastore URI
        "uri": "thrift://localhost:9083",
       
        # S3 configuration, must match the ones specified in the 
        # Trino catalogue
        "s3.region": "us-east-1",
        "s3.endpoint": "http://localhost:9999",
        "s3.path-style-access": "true",
        "s3.access-key-id": "KAWA",
        "s3.secret-access-key": "**********",      
  }
})
```

For these to be taken in account, the KAWA server must be restarted.

### 2.3 Recap of the configured components

<div data-with-frame="true"><img src="/files/htXkWA35ORP1EYyY4vZQ" alt=""></div>

This diagram shows all the different configuration elements that were covered in the previous paragraphs.

Note that `KAWA_TRINO_WRITER_CATALOG` (In red) must match the name of the catalog in Trino ( `$(KAWA_TRINO_WRITER_CATALOG).config`).


# Snowflake native connection

This section illustrates how KAWA can leverage Snowflake as its data warehouse.

## 1. Overview

KAWA integrates natively with Snowflake as a primary data warehouse, enabling seamless connectivity to your centralized enterprise datasets. By leveraging Snowflake’s scalable compute and elastic storage architecture, KAWA empowers users to explore, transform, and visualize data through its intuitive analytics interface—while maintaining full SQL compatibility and direct access to live Snowflake tables. This integration unlocks powerful capabilities such as Python-based ETL, dynamic schema augmentation, and real-time insights, all orchestrated within the Snowflake ecosystem. With KAWA, organizations can unify interactive analytics and advanced data workflows on Snowflake, combining performance, governance, and usability in one streamlined platform.

<div data-with-frame="true"><img src="/files/mLBHbIWKbnt66a6q41VJ" alt=""></div>

## 2. Configuration guide

This configuration guide outlines how to connect KAWA to a snowflake data warehouse.

### 2.1 Configuring Snowflake

Connect to your snowflake instance with a user that can create databases, roles and users.

#### a. Creating a database and schema for KAWA to write in

KAWA will create tables containing user data such as CSV uploads, pandas data frames etc.. in this database and schema.

```sql
CREATE DATABASE KAWA_ANALYTICS_DB 
  COMMENT = 'Database for KAWA to write back its objects';

CREATE SCHEMA KAWA_ANALYTICS_DB.KAWA;
```

#### b. Create a role and a user with all permissions in the new schema

This creates a role with Write Back capabilities in the kawa database and schema.

```sql
CREATE ROLE kawa_write_back_role;

-- Grand access to a warehouse
GRANT USAGE ON WAREHOUSE compute_wh TO ROLE kawa_write_back_role;

-- Access to the DB and schema
GRANT USAGE ON DATABASE KAWA_ANALYTICS_DB TO ROLE KAWA_WRITE_BACK_ROLE;
GRANT ALL PRIVILEGES ON SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;

-- Allow to create objects
GRANT CREATE TABLE ON SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;
GRANT CREATE VIEW  ON SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;
GRANT CREATE STAGE ON SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;

-- All privileges on current objects
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;
GRANT ALL PRIVILEGES ON ALL VIEWS IN SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;

-- All privileges on future objects
GRANT ALL PRIVILEGES ON FUTURE TABLES IN SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;
GRANT ALL PRIVILEGES ON FUTURE VIEWS IN SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;
GRANT ALL PRIVILEGES ON FUTURE SEQUENCES IN SCHEMA kawa_analytics_db.kawa TO ROLE kawa_write_back_role;
```

Create a user associated with that role:

```sql
-- KAWA Write back user

CREATE USER kawa_write_back_user
  PASSWORD = '@StrongPassword@'
  DEFAULT_ROLE = kawa_write_back_role
  DEFAULT_WAREHOUSE = compute_wh
  COMMENT = 'User with full access to kawa_analytics_db only';

GRANT ROLE kawa_write_back_role TO USER kawa_write_back_user;
```

#### c. Create a role / user with readonly access on selected databases

The readonly user must have permission to read the `kawa_analytics_db` created in the previous steps. It must also be able to access all the schemas and databases (**SELECT only**) that you want to connect to KAWA.

Make also sure that this user can create views in the KAWA database and schema.

```sql
CREATE ROLE kawa_readonly_role;

-- Grand access to a warehouse
GRANT USAGE ON WAREHOUSE compute_wh TO ROLE kawa_readonly_role;

-- 🚨 Specific for the kawa schema
GRANT CREATE VIEW  ON SCHEMA kawa_analytics_db.kawa TO ROLE kawa_readonly_role;


-- Grant read on kawa_analytics_db.kawa for all tables and views
GRANT USAGE ON DATABASE kawa_analytics_db TO ROLE kawa_readonly_role;
GRANT USAGE ON SCHEMA kawa_analytics_db.kawa TO ROLE kawa_readonly_role;
GRANT SELECT ON ALL TABLES IN SCHEMA kawa_analytics_db.kawa  TO ROLE kawa_readonly_role;
GRANT SELECT ON ALL VIEWS IN SCHEMA kawa_analytics_db.kawa  TO ROLE kawa_readonly_role;
GRANT SELECT ON FUTURE TABLES IN SCHEMA kawa_analytics_db.kawa  TO ROLE kawa_readonly_role;
GRANT SELECT ON FUTURE VIEWS IN SCHEMA kawa_analytics_db.kawa  TO ROLE kawa_readonly_role;

-- Add all the necessary USAGE and SELECT grants to all the 
-- databases and schemas that you want to connect to KAWA.
-- 🚨 DO NOT GRANT ANY OTHER PERMISSIONS BESIDES SELECT and USAGE to this role.
```

Now, create a user with the new readonly role.

```sql
-- KAWA readonly user
CREATE USER kawa_readonly_user
  PASSWORD = '@StrongPassword@'
  DEFAULT_ROLE = kawa_readonly_role
  DEFAULT_WAREHOUSE = compute_wh
  COMMENT = 'Readonly KAWA user';

GRANT ROLE kawa_readonly_role TO USER kawa_readonly_user;
```

> Alternatively, you can opt to configure a RSA keypair to authenticate your users. In oder to do so, for each user:

1. Generate a private key with:

```bash
# Key has to be in the PKCS8 format (Starting with: -----BEGIN PRIVATE KEY-----)
openssl genrsa -out rsa_key.pem 2048
```

2. Derive your public key from the private key:

```bash
openssl rsa -in rsa_key.pem -pubout -out rsa_key.pub
```

3. Configure your users

```sql
-- Extract the base64 part from your public key
ALTER USER kawa_readonly_user 
SET RSA_PUBLIC_KEY='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkm.....9QIDAQA';
```

### 2.2 Configuring KAWA

KAWA needs the following environment variables to work with this setup:

```bash
# Common: Warehouse and account
KAWA_SNOWFLAKE_ACCOUNT=ACCOUNT1
KAWA_SNOWFLAKE_WAREHOUSE=compute_wh

# configure the readonly user (do not use password if using a private key)
KAWA_SNOWFLAKE_PASSWORD=@StrongPassword@
KAWA_SNOWFLAKE_USER=kawa_readonly_user

# Configure the write back user (do not use password if using a private key)
KAWA_SNOWFLAKE_WRITER_CATALOG=KAWA_ANALYTICS_DB
KAWA_SNOWFLAKE_WRITER_PASSWORD=@StrongPassword@
KAWA_SNOWFLAKE_WRITER_SCHEMA=KAWA
KAWA_SNOWFLAKE_WRITER_TABLE_PREFIX=KW__
KAWA_SNOWFLAKE_WRITER_USER=kawa_write_back_user
```

If you opted for the keypair authentication method, set the following two variables instead of the passwords:

```bash
# The private key files should be accessible from the Java instance
KAWA_SNOWFLAKE_PATH_TO_PRIVATE_KEY="/path/to/private/key/for/readonly/user"
KAWA_SNOWFLAKE_WRITER_PATH_TO_PRIVATE_KEY="/path/to/private/key/for/writeback/user"
```

*In the snowflake GUI, you can easily retrieve your account identifier*.

<div data-with-frame="true"><img src="/files/lkZhVwUngiG46NQn7AAu" alt=""></div>

<div data-with-frame="true"><img src="/files/JiMMaEDLALvLfFEXog6Q" alt=""></div>


# BigQuery native connection

## 1. Overview

KAWA's native connection to **Google BigQuery** allows organizations to leverage BigQuery as KAWA's data warehouse. KAWA's computation engine sits directly on top of a live BigQuery deployment — every query KAWA generates runs in BigQuery, against the customer's actual datasets. There is no extraction, no copy, and no separate analytics store.

This unlocks the full set of KAWA capabilities — Python-based ETL, dynamic schema augmentation, and real-time insights — orchestrated entirely within the customer's BigQuery environment. KAWA inherits BigQuery's elastic compute and storage, IAM model, and data residency.

KAWA connects to BigQuery using **Google's official `google-cloud-bigquery` Java client** (the first-party native SDK). The only secret KAWA ever needs is a **GCP service account JSON key**.

> If instead you want to use BigQuery only as an ingestion **source** (KAWA extracts data from BQ and loads it into a different KAWA warehouse such as ClickHouse), configure it as a regular datasource through the KAWA UI rather than following this guide.

## 2. Configuration guide

### 2.1 Configuring BigQuery

KAWA uses two distinct service accounts:

* A **write-back** account, used by KAWA to create and write its internal tables (user uploads, pandas DataFrame outputs, materializations, etc.) inside a dataset dedicated to KAWA.
* A **read-only** account, used by KAWA to query the customer's existing datasets across one or more GCP projects.

The two can be the same account with broader IAM, but separating them is recommended.

#### **a. Creating a project and dataset for KAWA to write in**

KAWA needs a dedicated **dataset** (the BigQuery equivalent of a Snowflake schema) where it will store its own tables. This dataset can live in any GCP project — typically the same project as the rest of the customer's BigQuery footprint.

```bash
PROJECT_ID=<your-project-id>

bq --project_id=${PROJECT_ID} mk \
  --dataset \
  --description="KAWA internal write-back dataset" \
  ${PROJECT_ID}:kawa_analytics
```

#### **b. Create a service account and role for KAWA's write-back account**

This account writes KAWA-managed tables into the `kawa_analytics` dataset created above.

```bash
PROJECT_ID=<your-project-id>
WRITER_SA=kawa-write-back@${PROJECT_ID}.iam.gserviceaccount.com

# Create the service account
gcloud iam service-accounts create kawa-write-back \
  --display-name="KAWA write-back" \
  --project=${PROJECT_ID}

# Allow it to run jobs in the project
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member="serviceAccount:${WRITER_SA}" \
  --role="roles/bigquery.jobUser"

# Allow it to create / read / write tables in the kawa_analytics dataset
bq --project_id=${PROJECT_ID} add-iam-policy-binding \
  --member="serviceAccount:${WRITER_SA}" \
  --role="roles/bigquery.dataEditor" \
  ${PROJECT_ID}:kawa_analytics

# Generate the JSON key
gcloud iam service-accounts keys create kawa-write-back-key.json \
  --iam-account=${WRITER_SA}
```

The resulting `kawa-write-back-key.json` is the only secret KAWA needs for write-back.

#### **c. Create a service account and role with read-only access to the customer's data**

This account is used by KAWA to query the customer's datasets. Grant it `roles/bigquery.dataViewer` on each dataset KAWA users should be able to query, and `roles/bigquery.jobUser` on each project KAWA will run queries in.

> **DO NOT GRANT ANY OTHER PERMISSIONS BESIDES `dataViewer` AND `jobUser`** to this service account. The read-only account should only be able to query — never write or modify data outside the dedicated `kawa_analytics` dataset.

```bash
PROJECT_ID=<your-project-id>
READER_SA=kawa-readonly@${PROJECT_ID}.iam.gserviceaccount.com

# Create the service account
gcloud iam service-accounts create kawa-readonly \
  --display-name="KAWA read-only" \
  --project=${PROJECT_ID}

# Allow it to run queries in each project KAWA should query
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member="serviceAccount:${READER_SA}" \
  --role="roles/bigquery.jobUser"

# Grant dataViewer on each dataset KAWA users should query
bq --project_id=${PROJECT_ID} add-iam-policy-binding \
  --member="serviceAccount:${READER_SA}" \
  --role="roles/bigquery.dataViewer" \
  ${PROJECT_ID}:<dataset_to_expose>

# Generate the JSON key
gcloud iam service-accounts keys create kawa-readonly-key.json \
  --iam-account=${READER_SA}
```

If KAWA should query datasets across **multiple GCP projects**, repeat the `jobUser` and `dataViewer` bindings for each project, and list every project in `KAWA_BIGQUERY_READER_PROJECT_LIST` (see below).

### 2.2 Configuring KAWA

KAWA reads its BigQuery configuration from the secret store at startup. The following keys must be set as environment variables on the KAWA backend container, **or pulled from a managed secret store** — KAWA supports:

* **Google Secret Manager** (recommended when KAWA is deployed on GCP — keeps the BigQuery JSON keys inside the same trust boundary as BigQuery itself)
* **AWS Secrets Manager** (for KAWA on AWS)
* **Azure Key Vault** (for KAWA on Azure)
* **HashiCorp Vault** (cloud-agnostic — recommended for on-prem and multi-cloud deployments)
* Plain Kubernetes / environment variables for POC and development setups

In every case the secret **names** (e.g. `KAWA_BIGQUERY_READER_JSON_KEY`) and **values** are the same — only the storage backend changes.

**Read-only account:**

* `KAWA_BIGQUERY_READER_JSON_KEY` — the full contents of `kawa-readonly-key.json` as a single-line JSON string.
* `KAWA_BIGQUERY_READER_PROJECT_LIST` — *optional* — comma-separated list of GCP project IDs the reader is allowed to query. Defaults to the project named in the JSON key.

**Write-back account:**

* `KAWA_BIGQUERY_WRITER_JSON_KEY` — the full contents of `kawa-write-back-key.json` as a single-line JSON string.
* `KAWA_BIGQUERY_WRITER_DATASET` = `kawa_analytics`
* `KAWA_BIGQUERY_WRITER_TABLE_PREFIX` = `KW__`

The writer account always operates on the **project named in its own JSON key** — it does not need a project list.

#### **Example — environment variables**

```bash
KAWA_BIGQUERY_READER_JSON_KEY='{"type":"service_account","project_id":"...","client_email":"...","private_key":"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"}'
KAWA_BIGQUERY_READER_PROJECT_LIST='analytics-prod,analytics-staging'

KAWA_BIGQUERY_WRITER_JSON_KEY='{"type":"service_account","project_id":"...","client_email":"...","private_key":"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"}'
KAWA_BIGQUERY_WRITER_DATASET='kawa_analytics'
KAWA_BIGQUERY_WRITER_TABLE_PREFIX='KW__'
```

JSON keys are passed as **single-line JSON strings**. The GCP-issued JSON already escapes newlines in `private_key` with `\n`, so no manual escaping is required.

#### **Example — Google Secret Manager**

When KAWA runs on GCP, the JSON keys can be stored in **Google Secret Manager** instead of being passed as plain env vars:

```bash
PROJECT_ID=<your-project-id>

# Store each JSON key as a secret
gcloud secrets create KAWA_BIGQUERY_READER_JSON_KEY \
  --data-file=kawa-readonly-key.json \
  --project=${PROJECT_ID}

gcloud secrets create KAWA_BIGQUERY_WRITER_JSON_KEY \
  --data-file=kawa-write-back-key.json \
  --project=${PROJECT_ID}

# Grant the KAWA backend's service account access to read them
gcloud secrets add-iam-policy-binding KAWA_BIGQUERY_READER_JSON_KEY \
  --member="serviceAccount:kawa-backend@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"

gcloud secrets add-iam-policy-binding KAWA_BIGQUERY_WRITER_JSON_KEY \
  --member="serviceAccount:kawa-backend@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"
```

KAWA fetches the secrets by name at startup and uses them exactly as if they had been provided via environment variables.

#### **Other backends**

The same pattern applies to **AWS Secrets Manager**, **Azure Key Vault**, and **HashiCorp Vault** — store each `KAWA_BIGQUERY_*` key as a named secret, grant the KAWA backend's identity access to read it, and KAWA will resolve it transparently. HashiCorp Vault is the recommended option for on-prem and multi-cloud deployments where a single secret store needs to serve KAWA regardless of where it runs.

> Workload Identity Federation (using GCP-side identity instead of a long-lived JSON key) is **not currently supported** — KAWA expects a JSON key.


# KAWA Query Cache

This section explains how to work with KAWA query cache.

## 1. Default query cache

KAWA uses a query cache on all datasources that are managed by KAWA. This does not require any particular configuration.

<div data-with-frame="true"><img src="/files/aiwZlgOlCEfEfLMmYs8K" alt=""></div>

However, for data sources using the Live Connection option, no query cache is provided by default. This is because KAWA does not know when the data has changed and cannot invalidate the cache when needed.

> The cached queries are stored in KAWA's postgres database. The cached data is encrypted by KAWA (no clear text query results are stored in the cache).

## 2. Configuring a query cache for Live connections

For live connections, you can decide to enable a query cache from the datasource section.

<div data-with-frame="true"><img src="/files/lkwGUtJuMIUrTeE4A4tL" alt=""></div>

Here are the settings of the query cache:

* *Query cache is enabled:* Determines whether a query cache is activated for this datasource.
* *Time to live:* Defines how long will each query stay in the cache for, in seconds.
* *Cache disabled time window:* During that time window, the cache will not be used. (The times are indicated in UTC). For example, if you set a time window from 10:00 to 11:00, the query cache will be bypassed everyday from 10 to 11. (This can be used in case you know when the data will be refreshed, but have no control over it or do not have the possibility to add cache invalidation logic in your ETLs).

## 3. Cache eviction mechanisms

### 3.1 Using the Clear Cache button from the GUI

Anyone with access (in readonly or read write) to the datasource, can decide to manually invalidate the cache for this datasource.

<div data-with-frame="true"><img src="/files/hndioH6j9rLiLc2kE6VL" alt=""></div>

### 3.2 Eviction based on TTL (time to live)

Each time a query is added to the cache, it will be naturally evicted once its time to live has elapsed. The TTL is computed taking in account the configured TTL on the GUI, but will also take in account the *Cache disabled time window* parameter.

For example, a query that is made during the *Cache disabled time window* will have a TTL of 0.

### 3.3 Programmatic evictions (Recommended method)

In order to evict entries from the query cache directly from your ETL processes, you can use the Python SDK:

```python
from kywy.client.kawa_client import KawaClient
kawa = KawaClient.load_client_from_environment()

kawa.commands.run_command(
    command_name='ClearQueryCacheForTable',
    command_parameters={
        'catalog':'pristine',
        'schema': '',
        'table': 'C_19'
    } 
)
```

This command outputs the ids of the datasources that were invalidated. Please consider checking the `catalog`, `schema` and `table` if you see empty arrays (which means that no data sources has been found connected to that table).

You can check here for the correct definition of tables:

<div data-with-frame="true"><img src="/files/L2Cn0SQgDrzdJzQUjIA1" alt=""></div>

> In clickhouse, the schema should be left empty.


# Exploitation

* [Backup and Restore](/11_00_exploitation/11_01_backup_restore)
* [Create Monitoring views](/11_00_exploitation/11_02_create_monitoring_datasources)
* [OAuth2 / OIDC configuration](/11_00_exploitation/11_03_oauth2_configuration)
* [ClickHouse Users Configuration](/11_00_exploitation/clickhouse-users-configuration)
* [Deployment Options](/11_00_exploitation/deployment-options)
* [Disaster Recovery Architecture](/11_00_exploitation/disaster-recovery-architecture)


# Backup restore

This section contains scripts to backup and restore your postgres and clickhouse databases. It is compatible with a docker based installation, but can be adapted to work with bare metal setups.

## 1. Backup and Restore the postgres Database

> Run these scripts from directory that contains the `docker-compose.yml`file.

### 1.1 Backup postgres

KAWA Postgres database contains all the state of the application - dashboards, applications, sheets, datasources, views, etc... It also contains user accounts if using KAWA's internal authentication mechanism.

```bash
#!/bin/bash

DOCKER_CONTAINER_NAME="postgres"
DUMP_DIR="/tmp"
KAWA_COMPOSE_DIR="$PWD"
EXCLUDED_TABLE="application_event"
DB_USER="kawa"
DB_NAME="postgres"

TIMESTAMP=$(date +%Y-%m-%d)
DUMP_FILE="$DUMP_DIR/kawa-db-$TIMESTAMP.sql"
TAR_FILE="$DUMP_DIR/kawa-db-$TIMESTAMP.tar.gz"

mkdir -p "$DUMP_DIR"

echo "1️⃣ Starting database dump to $DUMP_FILE..."
if sudo docker compose exec "$DOCKER_CONTAINER_NAME" \
    pg_dump -U "$DB_USER" "$DB_NAME" --exclude-table-data="$EXCLUDED_TABLE" > "$DUMP_FILE"; then
  echo "✅ Database dump completed successfully."
else
  echo "❌ Database dump failed." >&2
  exit 1
fi

echo "2️⃣ Compressing dump to $TAR_FILE..."
if tar -czf "$TAR_FILE" -C "$DUMP_DIR" "$(basename "$DUMP_FILE")"; then
  echo "✅ Compression successful: $TAR_FILE"
  rm -f "$DUMP_FILE"
else
  echo "❌ Compression failed." >&2
  exit 1
fi
```

### 1.2 Restore the postgres database

```bash
#!/bin/bash

DOCKER_CONTAINER_NAME="postgres"
TAR_FILE_PATH="/tmp/kawa-db-$(date +%Y-%m-%d).tar.gz"
DB_USER="kawa"
DB_NAME="postgres"
TARGET_SCHEMA='kawa'
BACKUP_SCHEMA="kawa_backup_$(date +%Y%m%d)"


echo "1️⃣ Extracting SQL dump from $TAR_FILE_PATH..."
TEMP_DIR=$(mktemp -d)

if tar -xzf "$TAR_FILE_PATH" -C "$TEMP_DIR"; then
  DUMP_FILE=$(find "$TEMP_DIR" -name "*.sql")
  echo "✅ Extracted dump file: $DUMP_FILE"
else
  echo "❌ Failed to extract $TAR_FILE_PATH" >&2
  exit 1
fi

echo "2️⃣ Checking for existing '$TARGET_SCHEMA' schema..."
RENAME_SQL="
DO \$\$
BEGIN
  IF EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = '$TARGET_SCHEMA') THEN
    EXECUTE 'DROP SCHEMA IF EXISTS $BACKUP_SCHEMA CASCADE';
    EXECUTE 'ALTER SCHEMA $TARGET_SCHEMA RENAME TO $BACKUP_SCHEMA';
  END IF;
END
\$\$;
"

echo "$RENAME_SQL" | sudo docker compose exec -T "$DOCKER_CONTAINER_NAME" \
  psql -U "$DB_USER" -d "$DB_NAME" || {
    echo "❌ Failed to rename existing schema." >&2
    rm -rf "$TEMP_DIR"
    exit 1
}


echo "3️⃣ Restoring dump into Docker container database..."
if cat "$DUMP_FILE" | sudo docker compose exec -T "$DOCKER_CONTAINER_NAME" psql -U "$DB_USER" -d "$DB_NAME"; then
  echo "✅ Database restored successfully."
else
  echo "❌ Database restore failed." >&2
  rm -rf "$TEMP_DIR"
  exit 1
fi


rm -rf "$TEMP_DIR"
```

## 2. Backup and Restore the clickhouse Database

In order to backup and restore a clickhouse database, we use the native BACKUP and RESTORE utilities from Clickhouse.

Connect to your clickhouse database with a SQL client:

```sql
BACKUP DATABASE default TO Disk('backups', 'default.zip')
```

To restore a database:

```sql
 RESTORE DATABASE default FROM Disk('backups', 'default.zip')
```

The backups will be generated in the `assets/backup` directory of your docker compose install folder. Please refer to <https://clickhouse.com/docs/operations/backup> for additional configuration options and details.


# Create monitoring views

## 1. Monitor user events

### 1.1 Setup a connection to KAWA's database from the KAWA GUI

From the datasource section, create a new data source: (+ Data Source) > (Connect to an external system) > (+ Create new connection)

Pick the postgres connector:

<div data-with-frame="true"><img src="/files/Qomq7GTEnaNvUDS6TA1A" alt=""></div>

Then, configure the connection to the KAWA store (The configuration is present in KAWA server configuration, in the *POSTGRES JDBC URL*). Click on Test Connection and then Create.

<div data-with-frame="true"><img src="/files/ufFfIxGdnGmWK57TO4ij" alt=""></div>

*The above screenshot contains some example parameters*.

### 1.2 Create a new datasource to load application events

Making sure that you selected your new connection (here KAWA), click on the Custom SQL query option.

<div data-with-frame="true"><img src="/files/YVva2Rx5ekgmbSCSXX0E" alt=""></div>

Then, copy paste the following query in the text area (feel free to adapt it to your needs).

```sql
-- Query to load events and associated user information
SELECT
    E.event_id,
    upper(E.event_name) as event_name,
    upper(E.event_group) as event_group,
    upper(E.event_context_id) as event_context_id,
    E.event_date,
    E.event_correlated_entity_id,
    E.event_principal_id,
    E.event_correlated_entity_type,
    -- E.event_payload::text as payload, (OPTIONAL for more detailed information)
    P.display_name,
    P.payload->>'email' as email,
    P.payload->>'uniqueId' as uniqueId,
    P.payload->>'role' as role


FROM kawa.application_event as E
    LEFT JOIN kawa.entity_principal as P 
    ON E.event_principal_id = P.entity_id::text

```

CLick on (Preview data) > (Next) > (Save).

Input a name for your datasource: **Application Events** then click on (Save and Run).

<div data-with-frame="true"><img src="/files/NldZqCoPdi89QtKrW5Zo" alt=""></div>

Once your data is ready, feel free to schedule it:

<div data-with-frame="true"><img src="/files/kyvn4eXQiDzDIucAIMeL" alt=""></div>

*On this example, we schedule a full refresh of this data every 10 minutes. Feel free to adjust the frequency to your requirements.*

### 1.3 Create some charts and views to visualize this data

Below are a few examples of views that you can do to visualize the data:

*Chart to show number of interactions per user and command name:*

<div data-with-frame="true"><img src="/files/vM9vznmtmpysWqk2NQcz" alt=""></div>

*Chart to show when users interact with the platform:*

<div data-with-frame="true"><img src="/files/P5Sdlpof6PkUN4f1JMst" alt=""></div>

*Chart that shows which show the evolution of events per type*:

<div data-with-frame="true"><img src="/files/mnDmaIJqZEOGAhsvaiDc" alt=""></div>

## 2. Monitor script execution

In order to monitor script execution, please follow the steps described in 1.1 to connect to KAWA's database.

### 2.1 Create a new datasource to load the script monitoring table

Making sure that you selected your new connection (here KAWA), click on the Custom SQL query option.

<div data-with-frame="true"><img src="/files/YVva2Rx5ekgmbSCSXX0E" alt=""></div>

Then, copy paste the following query in the text area (feel free to adapt it to your needs).

```sql
SELECT
    JOB.job_id,
    JOB.status,
    JOB.date_created,
    JOB.date_updated,
    ROUND(EXTRACT(EPOCH FROM (JOB.date_updated - JOB.date_created)))::int AS duration_seconds,
    SCRIPT.display_name,
    SCRIPT.workspace_id
FROM kawa.python_jobs AS JOB
    LEFT JOIN kawa.entity_script as SCRIPT ON JOB.script_id = SCRIPT.entity_id
```

CLick on (Preview data) > (Next) > (Save).

Input a name for your datasource: Application Events then click on (Save and Run).

<div data-with-frame="true"><img src="/files/AF1smY39I01EaU0adSVn" alt=""></div>

Like for the events, feel free to schedule the ETL to run automatically.

### 2.2 Create some charts and views to visualize this data

*Boxplot chart to show script execution time*:

<div data-with-frame="true"><img src="/files/2J4F2aCxaQH5gQPeG3pb" alt=""></div>

*Boxplot chart to show script status over time*:

<div data-with-frame="true"><img src="/files/5gPVLFIOaEJ0cSUwySic" alt=""></div>


# Oauth2 configuration

## 1. General flow

When OAuth2 with OIDC is configured on KAWA, a "Login with SSO" button appears on the login page.

When users click on that button, here is what happens:

* **Step 1:** The user is redirected to the authorization URL of the Authentication server/IdP. This URL can either be retrieved from `openIdIssuer` or can be manually specified in the configuration (`authenticationDomain` and `loginApiPath`). If the authentication is successful, the authentication server will send a code back to KAWA which will use it to retrieve a token pair (Access and Refresh tokens).

> ℹ️ It is important to configure the application integration to provide both refresh and access tokens to allow safe and transparent token renewals.

* **Step 2:** Once the token pair is obtained, KAWA will use the access token to retrieve the user information from the user info url. This is usually discovered via the `openIdIssuer` but can also be overridden with `userInfoApiPath`.

> ℹ️ Make sure to allow the correct scopes: KAWA will read the following from the user info endpoint: `email`, `name`, `sub` (for user id, can be configured) and `email_verified` (You can decide to turn off email verification if that scope cannot be granted).

* **Step 3:** Once the user identity was extracted, we attempt to load the corresponding user on KAWA. Two behaviors are available:

  * The users in KAWA are created on the fly if they are not found
  * The Authentication blocks at that point (Users MUST be created by an admin).

  This is controlled via `blockIfPrincipalDoesNotExist` in the OAuth2 configuration.
* **Step 4:** Once the user is authenticated, a short lived access token is generated and sent to the client along with the refresh token sent by the Authentication server in step 2. Once the access token expires, KAWA will use the refresh token to generate a new access token (transparent for the end user).

## 2. Configuration

In order for the OAuth2/OIDC mechanism to be used, make sure that:

* You have configured your client Secret and set up a `OAuth2ClientConfiguration` (all is detailed below):

```python
cmd.replace_configuration('OAuth2ClientConfiguration', {
    'clientId': 'fdamnkj$@W%sgrwgtrwrtw', 
    'openidIssuer': 'https://some.idp.com/oauth2/default',
    'clientHost': 'https://wayne.kawa.ai'
})
```

* Your GlobalAuthenticationConfiguration is set to `JWT`.

```python
cmd.replace_configuration('GlobalAuthenticationConfiguration', {
        "setupAdminUniqueId": "setup-admin@kawa.io",
         "authenticationMethod": "JWT"
})
```

cf [This notebook to configure](https://github.com/kawa-analytics/kywy-documentation/blob/main/notebooks/administration/02_initial_instance_configuration.ipynb) the required objects.

> ⚠️ Make sure to RESTART your KAWA instances after configuration has been changed to take it in account.

> ℹ️ If you want to completely remove the OAuth mechanism, use the following:

```python
cmd.run_command(
        'DeleteConfiguration', 
        {'configurationClassSimpleName': 'OAuth2ClientConfiguration'}
)
```

### 2.1 Client Secret

The client secret is NOT stored in the main OAuth2 configuration object. It must be injected as an environment variable or stored in your secret store (for example: Vault): `KAWA_OAUTH2_CLIENT_SECRET`.

### 2.2 General configuration

Here are the various parameters that are made available to configure OAuth2 with OIDC:

* (REQUIRED) `clientId`: the client ID is a unique identifier assigned to KAWA (the “client” or “relying party”) by the identity provider (IdP) when you register the app.
* (REQUIRED) `clientHost`: This is KAWA's base URL. It is used by the IdP to send KAWA the code used to retrieve tokens (cf step 1 in the flow). Example: `https://wayne.kawa.ai:222`
* (RECOMMENDED) `openidIssuer`: The issuer URL is used for discovery: Will fetch the OpenID Provider Configuration from https\://{issuer}/.well-known/openid-configuration to learn endpoints (authorization endpoint, token endpoint, JWKS URI, userinfo endpoint, etc.). These can be manually overridden if needed.

> If the `openIdIssuer` is not present, you must specify: `authenticationDomain`, `tokenApiPath`, `userInfoApiPath` and `loginApiPath`. These will let KAWA know about the three necessary URLs. Each of them will be built like so:

```bash
    ${authenticationDomain}/${tokenApiPath}
    ${authenticationDomain}/${userInfoApiPath}
    ${authenticationDomain}/${loginApiPath}
```

Additional parameters:

* `userIdField` (defaults to `sub`): The field to extract the user id from.
* `stateValidationEnabled` (defaults to `true`): If turned on, the state will be validated. State validation protects against Cross-Site Request Forgery (CSRF) attacks by ensuring that the authorization response corresponds to an actual request your app initiated.
* `emailMustBeVerified` (defaults to `true`): This ensures that the emails of principals have been verified with the IdP. If you turn it on, make sure that the user info contains the `email_verified` claim.
* `offlineAccessScope` (default to `true`): This usually ensures that a refresh token will be emitted along the access token. It is important to automatically let users re authenticate when the short lived access token expires. This is STRONGLY RECOMMENDED.
* `blockIfPrincipalDoesNotExist` (defaults to `false`): Set to true if you want to manually create all your users before they are able to authenticate.


# ClickHouse Users Configuration

This section explains how to configure per-user ClickHouse connections in KAWA. By default, all queries are executed using a single default ClickHouse user defined in the JDBC URL. KAWA supports assigning dedicated ClickHouse users to specific KAWA users, enabling fine-grained access control at the warehouse level (quotas, row policies, read-only access, etc.).

> **Terminology:** In this document, "principal" refers to a KAWA user account. The terms are used interchangeably.

> **Important:** The default user should not have restrictive limitations, as it is used by KAWA for administrative operations (listing tables, managing schemas, etc.). When you need to impose limits on a specific user, create a separate ClickHouse user and assign it to that user's KAWA account.

## 1. Environment Variables

### 1.1 ClickHouse Environment Variables

The two key variables for this feature are highlighted below. The remaining variables are listed for reference.

| Variable                                         | Description                                            |
| ------------------------------------------------ | ------------------------------------------------------ |
| **`KAWA_CLICKHOUSE_JDBC_URL`**                   | **Default JDBC connection URL**                        |
| **`KAWA_CLICKHOUSE_ADDITIONAL_USERS`**           | **JSON array of additional ClickHouse users**          |
| `KAWA_CLICKHOUSE_IS_POST_23_DOT_8`               | Whether ClickHouse version is >= 23.8 (`true`/`false`) |
| `KAWA_CLICKHOUSE_ALLOW_LIVE_CONNECT`             | Allow live connect (`true`/`false`)                    |
| `KAWA_CLICKHOUSE_CLUSTER`                        | Cluster name (optional)                                |
| `KAWA_CLICKHOUSE_STORAGE_POLICY`                 | Storage policy (optional)                              |
| `KAWA_CLICKHOUSE_VALIDATION_ALLOW_DATA_DELETION` | Allow data deletion during schema validation           |

### 1.2 JDBC URL Format

```
jdbc:clickhouse://hostname:port/database?user=username&password=password
```

Example:

```bash
export KAWA_CLICKHOUSE_JDBC_URL="jdbc:clickhouse://localhost:8123/kawa?user=kawa&password=secretpass"
```

### 1.3 Additional Users Format

The value of `KAWA_CLICKHOUSE_ADDITIONAL_USERS` is a JSON array of objects, each containing a `user` and `password` field:

```bash
export KAWA_CLICKHOUSE_ADDITIONAL_USERS='[{"user":"analyst_readonly","password":"pass_1"},{"user":"trader_limited","password":"pass_2"}]'
```

Each entry creates a separate connection pool to ClickHouse. The `user` value must correspond to an existing ClickHouse user.

> **Important:** Changes to `KAWA_CLICKHOUSE_ADDITIONAL_USERS` take effect only after restarting the KAWA server, as the variable is read at startup and used to create connection pools.

## 2. Assigning a ClickHouse User to a KAWA User

Once additional users are defined in the environment variable, you need to specify which KAWA user (principal) should use which ClickHouse user. This is done via the Python SDK using the `ReplacePrincipalDbUser` command.

### 2.1 Running the Command

```python
from kywy.client.kawa_client import KawaClient as K

kawa = K(kawa_api_url='https://your-domain:your-port')
kawa.set_api_key(api_key='kawa-****')

kawa.commands.run_command('ReplacePrincipalDbUser', {
    'principalId': '3',
    'dbUser': 'analyst_readonly'
})
```

**Parameters:**

* `principalId` — The numeric ID of the KAWA user (principal).
* `dbUser` — The name of the ClickHouse user from the `KAWA_CLICKHOUSE_ADDITIONAL_USERS` array.

**Restrictions:** This command is available to KAWA administrators only.

> **Note:** Calling `ReplacePrincipalDbUser` does not require a KAWA restart — the mapping is updated in the database and takes effect immediately.

### 2.2 Removing the Assignment

To revert a user to the default connection, pass `None` as the `dbUser` value:

```python
kawa.commands.run_command('ReplacePrincipalDbUser', {
    'principalId': '3',
    'dbUser': None
})
```

> **Note:** The backend parameter type is `Optional<String>`. Passing `None` from the Python SDK sends an empty optional, which clears the ClickHouse user assignment for that principal.

## 3. How It Works

```
KAWA_CLICKHOUSE_JDBC_URL ──────────► default connection
                                     (admin tasks & users without dbUser)

KAWA_CLICKHOUSE_ADDITIONAL_USERS ──► per-user connection pools:
  [{"user":"analyst_readonly",...}]     ├── analyst_readonly → pool 1
  [{"user":"trader_limited",...}]       └── trader_limited  → pool 2

ReplacePrincipalDbUser(principalId=3, dbUser="analyst_readonly")
  → stores dbUser on the KAWA user (principal)
```

When a user makes a request:

1. KAWA checks the user's `dbUser` field.
2. If set (e.g., `analyst_readonly`) → uses the corresponding connection pool.
3. If the user is not found in the additional users list → falls back to the default connection.
4. If `dbUser` is not set → uses the default connection.

## 4. Bulk Assignment via CSV

For assigning ClickHouse users to multiple principals at once, you can use a CSV file with a Python script.

**File `user_mapping.csv`:**

```csv
principalId,dbUser
3,analyst_readonly
5,trader_limited
8,analyst_readonly
12,trader_limited
```

**Script `assign_ch_users.py`:**

```python
import csv
from kywy.client.kawa_client import KawaClient as K

kawa = K(kawa_api_url='https://your-domain:your-port')
kawa.set_api_key(api_key='kawa-****')

with open('user_mapping.csv', 'r') as f:
    reader = csv.DictReader(f)
    for row in reader:
        kawa.commands.run_command('ReplacePrincipalDbUser', {
            'principalId': row['principalId'],
            'dbUser': row['dbUser']
        })
        print(f"Principal {row['principalId']} → CH user: {row['dbUser']}")
```

## 5. ClickHouse Configuration Examples

### 5.1 Creating the Default KAWA User

```sql
CREATE USER kawa IDENTIFIED WITH sha256_password BY 'your_secure_password';
CREATE DATABASE kawa;
GRANT ALL ON kawa.* TO kawa;
GRANT CLUSTER ON *.* TO kawa;
```

### 5.2 Read-Only User

```sql
CREATE USER analyst_readonly IDENTIFIED WITH sha256_password BY 'pass_1';
GRANT SELECT ON kawa.* TO analyst_readonly;
```

This user can only read data and cannot create or modify tables.

### 5.3 User with Row-Level Security (Row Policy)

```sql
CREATE USER trader_limited IDENTIFIED WITH sha256_password BY 'pass_2';
GRANT SELECT ON kawa.* TO trader_limited;

CREATE ROW POLICY region_filter ON kawa.trades
    FOR SELECT
    USING region = 'EMEA'
    TO trader_limited;
```

This user will only see rows where `region = 'EMEA'`.

### 5.4 User with Quotas

```sql
CREATE USER limited_user IDENTIFIED WITH sha256_password BY 'pass_3';
GRANT SELECT ON kawa.* TO limited_user;

CREATE QUOTA limited_quota
    FOR INTERVAL 1 hour MAX queries = 100, result_rows = 1000000
    TO limited_user;
```

### 5.5 Settings Profiles

```sql
CREATE SETTINGS PROFILE readonly_profile SETTINGS
    max_memory_usage = 1000000000,
    max_execution_time = 30,
    readonly = 1;

ALTER USER analyst_readonly SETTINGS PROFILE readonly_profile;
```

## 6. End-to-End Example

**Step 1.** Create ClickHouse users (no KAWA restart needed):

```sql
CREATE USER analyst IDENTIFIED WITH sha256_password BY 'analyst_pass';
GRANT SELECT ON kawa.* TO analyst;

CREATE USER power_user IDENTIFIED WITH sha256_password BY 'power_pass';
GRANT SELECT, INSERT ON kawa.* TO power_user;
```

**Step 2.** Add them to the environment variable and **restart KAWA** (required for new connection pools to be created):

```bash
export KAWA_CLICKHOUSE_ADDITIONAL_USERS='[{"user":"analyst","password":"analyst_pass"},{"user":"power_user","password":"power_pass"}]'
```

**Step 3.** Assign to KAWA users via Python SDK (no restart needed, takes effect immediately):

```python
from kywy.client.kawa_client import KawaClient as K

kawa = K(kawa_api_url='https://your-domain:your-port')
kawa.set_api_key(api_key='kawa-****')

# Assign CH user "analyst" to KAWA user #5
kawa.commands.run_command('ReplacePrincipalDbUser', {
    'principalId': '5',
    'dbUser': 'analyst'
})

# Assign CH user "power_user" to KAWA user #8
kawa.commands.run_command('ReplacePrincipalDbUser', {
    'principalId': '8',
    'dbUser': 'power_user'
})
```

**Step 4.** Verify: when KAWA user #5 executes a query, it will use the `analyst` connection, and ClickHouse will enforce the `SELECT`-only restriction.


# Deployment Options

KAWA is packaged as a Docker image and can be deployed across all major cloud providers. For POCs, KAWA can run as a single `docker compose` stack; for production, it runs on Kubernetes (EKS / GKE / AKS) with a stateless, horizontally scalable backend.

## 1. AWS

| Category             | Details                                                                                                                                                                                                                                                                                                                                        |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Installation**     | Distributed as a Docker image. POC: single-node `docker compose` on EC2. Production: Kubernetes on **EKS** (Helm chart). Deployable inside the customer's own VPC/account. *Not currently listed on AWS Marketplace.*                                                                                                                          |
| **AI compatibility** | Native integration with **Amazon Bedrock** (bearer-token auth, VPC endpoints, and inference-profile ARNs supported). Additional providers available out of the box: Anthropic, OpenAI, OpenRouter, xAI, Mistral, Groq, Cerebras, Cohere, Together AI, Perplexity. Any OpenAI-compatible endpoint can also be configured via base-URL override. |
| **Data warehousing** | Native connectors to **BigQuery**, **Snowflake**, **Starburst**, **StarRocks**, **Trino**, and **ClickHouse**.                                                                                                                                                                                                                                 |
| **Database**         | Requires an actual PostgreSQL database (not a Postgres-compatible engine). Recommended: **Amazon RDS for PostgreSQL**, or self-managed Postgres on EC2 / EKS. *Aurora PostgreSQL is **not** supported.*                                                                                                                                        |
| **Security**         | Deployed in customer VPC; secrets managed via **AWS Secrets Manager** / **AWS KMS**. SOC 2 Type II certified.                                                                                                                                                                                                                                  |
| **Scalability**      | Stateless backend — scales horizontally on EKS. A single instance comfortably handles hundreds of concurrent users.                                                                                                                                                                                                                            |
| **Authentication**   | OIDC / OAuth2. Integrates with **AWS IAM Identity Center (SSO)** and any OIDC-compliant IdP (Okta, Auth0, etc.).                                                                                                                                                                                                                               |
| **SaaS enabled**     | Yes — KAWA can be offered as a managed SaaS on AWS, or self-hosted in the customer's account.                                                                                                                                                                                                                                                  |

## 2. GCP

| Category             | Details                                                                                                                                                                                                                                                                                                                                     |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Installation**     | Docker image. POC: single-node `docker compose` on Compute Engine. Production: Kubernetes on **GKE** (Helm chart). Deployable inside the customer's own GCP project / VPC. *Not currently listed on Google Cloud Marketplace.*                                                                                                              |
| **AI compatibility** | Native integration with **Google Vertex AI** (including **Claude on Vertex**) and **Google Gemini** (direct API). Additional providers available out of the box: Anthropic, OpenAI, OpenRouter, xAI, Mistral, Groq, Cerebras, Cohere, Together AI, Perplexity. Any OpenAI-compatible endpoint can also be configured via base-URL override. |
| **Data warehousing** | Native connectors to **BigQuery**, **Snowflake**, **Starburst**, **StarRocks**, **Trino**, and **ClickHouse**.                                                                                                                                                                                                                              |
| **Database**         | Requires an actual PostgreSQL database (not a Postgres-compatible engine). Recommended: **Cloud SQL for PostgreSQL**, or self-managed Postgres on Compute Engine / GKE. *AlloyDB is **not** supported.*                                                                                                                                     |
| **Security**         | Deployed in customer VPC; secrets managed via **Google Secret Manager** / **Cloud KMS**. SOC 2 Type II certified.                                                                                                                                                                                                                           |
| **Scalability**      | Stateless backend — scales horizontally on GKE. A single instance comfortably handles hundreds of concurrent users.                                                                                                                                                                                                                         |
| **Authentication**   | OIDC / OAuth2. Integrates with **Google Cloud Identity** and any OIDC-compliant IdP.                                                                                                                                                                                                                                                        |
| **SaaS enabled**     | Yes — KAWA can be offered as a managed SaaS on GCP, or self-hosted in the customer's project.                                                                                                                                                                                                                                               |

## 3. Azure

| Category             | Details                                                                                                                                                                                                                                                                                                    |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Installation**     | Docker image. POC: single-node `docker compose` on an Azure VM. Production: Kubernetes on **AKS** (Helm chart). Deployable inside the customer's own Azure subscription / VNet. *Not currently listed on Azure Marketplace.*                                                                               |
| **AI compatibility** | Native integration with **Azure OpenAI Service** `(https://.openai.azure.com/)`. Additional providers available out of the box: Anthropic, OpenAI, OpenRouter, xAI, Mistral, Groq, Cerebras, Cohere, Together AI, Perplexity. Any OpenAI-compatible endpoint can also be configured via base-URL override. |
| **Data warehousing** | Native connectors to **BigQuery**, **Snowflake**, **Starburst**, **StarRocks**, **Trino**, and **ClickHouse**.                                                                                                                                                                                             |
| **Database**         | Requires an actual PostgreSQL database (not a Postgres-compatible engine). Recommended: **Azure Database for PostgreSQL — Flexible Server**, or self-managed Postgres on Azure VM / AKS. *Cosmos DB for PostgreSQL is **not** supported.*                                                                  |
| **Security**         | Deployed in customer VNet; secrets managed via **Azure Key Vault**. SOC 2 Type II certified.                                                                                                                                                                                                               |
| **Scalability**      | Stateless backend — scales horizontally on AKS. A single instance comfortably handles hundreds of concurrent users.                                                                                                                                                                                        |
| **Authentication**   | OIDC / OAuth2. Integrates natively with **Microsoft Entra ID** (formerly Azure AD) and any OIDC-compliant IdP.                                                                                                                                                                                             |
| **SaaS enabled**     | Yes — KAWA can be offered as a managed SaaS on Azure, or self-hosted in the customer's subscription.                                                                                                                                                                                                       |

## 4. On-Premise

| Category             | Details                                                                                                                                                                                                                                                                                                                                                  |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Installation**     | Three options: (1) Docker image with single-node `docker compose` for POC, (2) Kubernetes via Helm chart on any CNCF-conformant distribution (OpenShift, Rancher, vanilla k8s), or (3) **non-containerized install** — JAR for the backend + pip-installable Python packages — for environments where containers are not allowed.                        |
| **AI compatibility** | Supports fully self-hosted inference for air-gapped deployments: native integration with **Ollama** and **llama.cpp**, plus **vLLM**, **NVIDIA NIM**, and any other server exposing an OpenAI-compatible endpoint. Outbound managed providers (Anthropic, OpenAI, Bedrock, Vertex, Azure OpenAI, etc.) are also available where customer policy permits. |
| **Data warehousing** | Native connectors to **BigQuery,** **Snowflake**, **Starburst**, **StarRocks**, **Trino**, and **ClickHouse**.                                                                                                                                                                                                                                           |
| **Database**         | Requires an actual PostgreSQL database (not a Postgres-compatible engine). Customer-managed Postgres cluster — bare-metal, VM, or containerized (e.g. CloudNativePG, Crunchy Postgres, Patroni).                                                                                                                                                         |
| **Security**         | Fully air-gap capable. Secrets via **HashiCorp Vault** or Kubernetes secrets. SOC 2 Type II certified.                                                                                                                                                                                                                                                   |
| **Scalability**      | Stateless backend — scales horizontally on Kubernetes. Single instance handles hundreds of concurrent users.                                                                                                                                                                                                                                             |
| **Authentication**   | OIDC / OAuth2. Integrates with any OIDC/SAML-compliant IdP (Entra ID, Okta, Keycloak, Ping).                                                                                                                                                                                                                                                             |
| **SaaS enabled**     | N/A — on-prem is customer-managed.                                                                                                                                                                                                                                                                                                                       |


# Deployment Architectures

KAWA can be deployed in several ways. This page shows the reference architecture for each option, illustrated with a diagram. For a side-by-side comparison of what each cloud provider supports, see **Deployment Options**.

## 1. AWS (EC2 / ECS)

A single-host **Docker Compose** deployment: KAWA runs as one OCI artifact on a single **EC2 / ECS** host. This is one of several supported options — for the Kubernetes (**EKS**) topology, see **Deployment Options**. The same pinned artifact is promoted unchanged across environments, and all network links are TLS-encrypted in transit.

<div data-with-frame="true"><figure><img src="/files/nLAO3mmD6LRS3B6rvoHH" alt=""><figcaption></figcaption></figure></div>

<table data-search="false"><thead><tr><th>Category</th><th>Details</th></tr></thead><tbody><tr><td>Model</td><td>Single-host <strong>Docker Compose</strong> deployment — one OCI artifact on a single <strong>EC2 / ECS</strong> host.</td></tr><tr><td>Artifact</td><td>One OCI release bundling four images: <strong>KAWA Java</strong> (JDK 21 — core backend and platform APIs), <strong>KAWA AI</strong> (Python — inference via <strong>Amazon Bedrock</strong>, isolated in a filesystem sandbox), <strong>KAWA Workflow</strong> (<strong>BPMN 2.0</strong> workflow engine), <strong>KAWA Python</strong> (runs user Python code, packages from <strong>PyPI</strong>).</td></tr><tr><td>Host</td><td><code>m7a.2xlarge</code> — 8 vCPU, 32 GB RAM, <strong>AMD64 / x86_64</strong>, standard Linux. Runtime: <strong>Docker Compose</strong> (OCI).</td></tr><tr><td>Data &#x26; storage</td><td><strong>PostgreSQL</strong> — back-office and entity store; <strong>Amazon S3</strong> — user files, one bucket per environment; <strong>Databricks</strong> — SQL warehouse / lake.</td></tr><tr><td>AI</td><td><strong>Amazon Bedrock</strong> — model inference.</td></tr><tr><td>Authentication</td><td>OIDC / OAuth2 — <strong>Auth0</strong> or any OIDC-compliant IdP.</td></tr><tr><td>Integrations &#x26; email</td><td><strong>SMTP</strong> — outbound email; <strong>Jira Cloud</strong> — issue sync; <strong>Bitbucket</strong> — source repositories; external / internal REST APIs.</td></tr><tr><td>Environments</td><td>Same pinned artifact promoted <strong>DEV → STAGING → PROD</strong>. Production runs active with an optional passive warm-standby host — see <strong>Disaster Recovery Architecture</strong>. Each environment has its own <strong>S3</strong> bucket.</td></tr></tbody></table>

### 1.1 Deploy or upgrade

Deploy or upgrade an environment with a single command:

```bash
docker compose \
  -f oci://<registry>/kawa-platform:<tag> up -d
```

Replace `<registry>` and `<tag>` with the values provided by KAWA during onboarding. To upgrade, pull a new tag — there is no rebuild. The same pinned artifact moves unchanged from non-production to production.


# Disaster Recovery Architecture

## 1. Overview

This page covers the disaster recovery (DR) reference architecture for self-hosted and on-premise KAWA deployments. Use it to plan a resilient installation that meets your recovery objectives.

You will find:

* The recommended active/active topology and the role of each component.
* Achievable RTO / RPO targets per component.
* Backup strategy, failover procedures, and testing cadence.

All recommendations can be adapted to your own infrastructure, compliance requirements, and risk appetite.

## 2. Scope

This guidance applies to production deployments of the KAWA platform operated by a customer in their own environment, whether on-premise, in a private cloud, or in a public cloud account managed by the customer. The components covered are:

* **KAWA Backend Server** (Java application, JDK 21), deployable in an active/active topology across two datacenters.
* **KAWA Python Runners** (script execution containers), deployable in each datacenter.
* **BMPN2.0 Workflow Engine** (CMMN and DMN execution), deployable in an active/active topology backed by the shared PostgreSQL database.
* **PostgreSQL** — the entity store for KAWA server state and the persistence layer for the BMPN2.0 workflow engine. The recommended deployment is Amazon Aurora PostgreSQL Global Database, or an equivalent managed multi-region PostgreSQL service in non-AWS environments.
* **ClickHouse** — the analytical data warehouse, deployable as a replicated set with replicas across both datacenters, coordinated by ClickHouse Keeper.
* **Object storage** for user-uploaded files, internal CSV exchanges and platform artifacts (Amazon S3 or S3-compatible storage with versioning and cross-region replication).
* **Configuration, secrets and infrastructure-as-code repositories** required to rebuild the platform from a known-good state.

> This guidance does not cover the KAWA SaaS environment operated by KAWA Analytics Corp.; that service is governed by KAWA's own internal policies and the commitments made in its customer contracts and SOC 2 report.

## 3. Definitions

**Disaster Recovery (DR):** The set of processes, tools, and procedures used to recover the KAWA platform after a service-affecting failure of one or more system components.

**Recovery Time Objective (RTO):** The maximum tolerable elapsed time between a disaster declaration and the restoration of service.

**Recovery Point Objective (RPO):** The maximum tolerable amount of data loss measured in time, between the most recent recoverable state and the point of failure.

**Active/Active:** A deployment topology in which two or more instances of a component serve production traffic concurrently. The KAWA Backend Server supports this through its master/master deployment mode.

**Replicated Set:** A clustered deployment of a stateful component (ClickHouse, PostgreSQL) where data is replicated across multiple nodes or availability zones.

## 4. Recommended Operational Roles

We recommend that the customer team operating the KAWA platform identify owners for the following responsibilities. These do not need to be distinct individuals; in smaller teams a single engineer may cover several of them.

**Platform Owner:** Owns the KAWA deployment end-to-end, approves the recovery objectives the deployment commits to, and is the escalation point during a disaster.

**Infrastructure / SRE Team:** Implements and operates the DR architecture, executes failover procedures, maintains backup tooling and runs DR tests.

**Incident Commander (on-call):** Declares a disaster, coordinates the response and decides when to invoke failover procedures.

**Compliance / Risk Owner:** Confirms the DR posture meets the customer’s internal compliance and regulatory obligations, and retains evidence of testing.

**KAWA Support (escalation):** KAWA Analytics Corp. provides product-level support for the platform during recovery; contact details are provided in the customer’s support agreement.

## 5. Recommended Reference Architecture

The KAWA platform is designed to be deployed in a fully symmetric, active/active topology across two independent datacenters (referred to here as **DC-A** and **DC-B**). Each stateful and stateless component runs as a master/master pair, with one instance in each datacenter serving production traffic concurrently. For the relational store, we recommend Amazon Aurora PostgreSQL Global Database, with a primary cluster in one region and a secondary cluster in the other for cross-region failover.

<div data-with-frame="true"><figure><img src="/files/RGUfq5BbEPotHTLlM7lp" alt=""><figcaption></figcaption></figure></div>

<p align="center"><em>Figure 1. KAWA platform reference architecture for self-hosted deployments.</em></p>

The components of the reference architecture are:

* **KAWA Backend Server:** master/master, one active instance in DC-A and one in DC-B.
* **KAWA Python Runners:** master/master, one active runner in DC-A and one in DC-B.
* **BMPN2.0 Workflow Engine** (BPMN, CMMN and DMN execution), deployable in an active/active topology backed by the shared PostgreSQL database. **Workflow Engine:** master/master, one active instance in each datacenter, coordinated through the shared Aurora database.
* **ClickHouse:** deployed as a replicated set with replicas in both DC-A and DC-B (master/master replication). Writes are synchronously acknowledged by the replication quorum so committed data is present in both datacenters.
* **PostgreSQL:** Amazon Aurora PostgreSQL Global Database with primary cluster in the DC-A region and secondary cluster in the DC-B region. Typical cross-region replication lag is under one second; managed promotion of the secondary is achievable in approximately one minute.
* **S3 object storage:** versioning enabled, with cross-region replication between the two paired regions.
* **Traffic routing:** global load balancer / DNS layer with health-check-based failover, distributing traffic across both datacenters during normal operation.

### 6. Achievable Recovery Objectives

The following recovery objectives are achievable with the recommended reference architecture for the loss of a single datacenter. Operators may target tighter or looser objectives by adjusting the topology (for example, additional ClickHouse replicas, more aggressive backup cadence, or a warm DR region for the both-datacenters-down scenario). The simultaneous loss of both datacenters — a separate scenario covered in §9.4 — has its own recovery objectives shown in the final row.

| Component                  | RTO       | RPO      | Failover Mode                   |
| -------------------------- | --------- | -------- | ------------------------------- |
| KAWA Backend Server        | ≤ 10 min  | 0        | Master/Master                   |
| KAWA Python Runners        | ≤ 10 min  | 0        | Master/Master                   |
| BMPN2.0 Workflow Engine    | ≤ 10 min  | 0        | Master/Master (shared DB)       |
| ClickHouse                 | ≤ 10 min  | 0        | Master/Master across DCs        |
| PostgreSQL (Aurora Global) | ≤ 5 min   | ≤ 1 min  | Cross-region promotion          |
| S3 object storage          | ≤ 30 min  | ≤ 15 min | Cross-region replication        |
| Loss of one datacenter     | ≤ 10 min  | 0        | Traffic re-route, both DCs live |
| Loss of both datacenters   | ≤ 4 hours | ≤ 15 min | Rebuild from backups            |

## 7. Recommended Deployment Pattern by Component

### 7.1 KAWA Backend Server

The KAWA Backend Server is stateless and supports a master/master deployment. One active instance is operated in each datacenter (DC-A and DC-B), and both serve production traffic concurrently behind a global load balancer with HTTP-level health checks. The loss of either instance, or of an entire datacenter, results in traffic shifting automatically to the surviving instance.

* Each instance is sized to handle 100 % of production load to absorb the loss of its peer.
* Container images are pulled from an internal registry; images are pinned by digest in deployment manifests so a restored instance is always reproducible.
* Configuration and license files are stored in a version-controlled repository and rendered at deploy time.
* RTO: ≤ 10 minutes (bounded by health-check detection and load-balancer convergence). RPO: 0.

### 7.2 KAWA Python Runners

Python Runners are stateless script execution containers deployed master/master, with one active runner in DC-A and one in DC-B. Job scheduling is performed by the KAWA Backend Server and is idempotent at the script execution layer; if a runner fails mid-execution, the job is rescheduled to the surviving runner.

* Runners interact with the KAWA server through HTTP APIs and retrieve script artifacts from source control / the KAWA file store.
* RTO: ≤ 10 minutes. RPO: 0.

### 7.3 BMPN2.0 Workflow Engine

The KAWA platform uses the BMPN2.0 engine for CMMN and DMN execution. The BMPN2.0 engine is deployed master/master with one active instance in DC-A and one in DC-B. Both instances share the Aurora PostgreSQL Global Database described in §7.4, which acts as the synchronization anchor for the cluster. No additional clustering middleware is required.

This topology is supported natively by BMPN2.0: the engine is stateless and any number of nodes pointed at the same database operate as a cluster, with idempotent service calls regardless of which node served the request. Asynchronous and timer jobs are coordinated through the database: a job is first attempted on the originating node and, if not picked up, becomes available for any node in the cluster to acquire via a database-backed lock. This guarantees exactly-once execution of each job without external coordination.

* Both nodes run the same BMPN2.0 engine version and use a digest-pinned container image.
* The `workflow.properties` configuration file is identical on both nodes and is managed in version control.
* **Auto-deployment locking is enabled** (`flowable.auto-deployment.use-lock=true`) so that only one node performs process-definition deployments on startup.
* **Sticky sessions are configured on the load balancer** for any flow that relies on long-polling (SockJS) or the OpenID Connect authorization code exchange, as those flows hold node-affine state.
* All durable workflow state (process instances, tasks, timers, history) is held in Aurora PostgreSQL and inherits the recovery objectives of that component.
* On loss of a BMPN2.0 node, jobs locked by the failed node are released after the lock timeout and picked up by the surviving node. In-flight job interruption is bounded by the configured lock timeout (typically 5 minutes).
* RTO: ≤ 10 minutes. RPO: 0 (no local state on the engine nodes).

### 7.4 PostgreSQL (Aurora Global Database)

PostgreSQL holds the KAWA entity store (users, workspaces, dashboards, permissions, configuration, metadata) and the BMPN2.0 workflow engine's durable state (deployments, process and case instances, tasks, timers, history). It is operated as Amazon Aurora PostgreSQL Global Database, with the primary cluster in the DC-A region and a secondary cluster in the DC-B region. Aurora provides typical cross-region replication lag under one second and supports managed promotion of the secondary cluster in approximately one minute when the primary region is lost.

* Within each region, the Aurora cluster runs across multiple availability zones with synchronous replication, providing transparent failover within the region in under 60 seconds.
* Cross-region replication is asynchronous but typically sub-second; RPO under normal operation is well under one minute.
* Automated daily snapshots are retained for **30 days**; point-in-time recovery (PITR) is enabled with a **7-day** recovery window.
* Snapshots are copied to a third region at least once every **24 hours** to protect against simultaneous loss of both Aurora regions.
* Connection strings resolve to the Aurora cluster endpoints; promotion of the secondary cluster is transparent to KAWA Backend and BMPN2.0 nodes after connection reset.
* RTO: ≤ 5 minutes for cross-region failover. RPO: ≤ 1 minute.

### 7.5 ClickHouse (Analytical Warehouse)

ClickHouse stores customer analytical data and is the execution engine for all queries prepared by the KAWA Backend. It is deployed master/master across the two datacenters, with replicas in both DC-A and DC-B coordinated by ClickHouse Keeper. Writes are synchronously acknowledged by the replication quorum, so committed data is present in both datacenters before the write returns to the client.

* Each shard has replicas placed in both DC-A and DC-B, providing immediate read availability after the loss of a datacenter.
* ClickHouse Keeper is deployed as an odd-numbered ensemble (minimum three nodes) spread across both datacenters and at least one additional availability zone to avoid split-brain.
* On the loss of a replica or an entire datacenter, the surviving replica continues to serve reads and accept writes immediately; no promotion step is required.
* Full backups are taken daily using `clickhouse-backup` (or equivalent) and stored in S3 with cross-region replication, providing an independent recovery path against logical corruption or simultaneous loss of both datacenters.
* Incremental backups are taken every **6 hours**.
* Backup restorability is validated by automated test restores at least monthly.
* RTO: ≤ 10 minutes for the loss of a single replica or datacenter. RPO: 0 within quorum.

### 7.6 Object Storage (S3)

S3 stores user-uploaded files, internal CSV exchanges, query result caches, and platform artifacts. It is configured with object versioning to protect against accidental deletion and overwrite, and with cross-region replication to a secondary region for regional resilience.

* Versioning is enabled on all production buckets; lifecycle policies retain prior versions for at least **90 days**.
* Cross-region replication targets a paired region with a different blast radius (e.g., us-east-1 → us-west-2).
* Bucket access is restricted by least-privilege IAM policies; bucket policies block public access by default.
* MFA Delete is enabled on buckets containing customer data, where supported.
* RTO: ≤ 1 hour (regional). RPO: ≤ 15 minutes via replication.

#### 7.7 Configuration, Secrets, and Infrastructure

All infrastructure is defined as code (Terraform / equivalent) and stored in version control. Application configuration is stored alongside the deployment manifests. Secrets are stored in a dedicated secret manager (AWS Secrets Manager / Vault) with replication enabled to the secondary region.

* Infrastructure code is retained indefinitely in Git with off-platform mirroring.
* Secrets are versioned; rotation policies are documented separately in the Access Control Policy.

## 8. Recommended Backup Strategy

Backups should provide an independent recovery path from the live replication topology, protecting against logical corruption, accidental deletion, and ransomware in addition to infrastructure failure. The frequencies and retention periods below are starting points; operators should adjust them to their own data volumes, regulatory requirements and risk appetite.

| Component              | Frequency     | Retention  | Location                     |
| ---------------------- | ------------- | ---------- | ---------------------------- |
| Aurora snapshots       | Daily         | 30 days    | Primary + tertiary region    |
| Aurora PITR (WAL)      | Continuous    | 7 days     | Primary cluster              |
| ClickHouse full backup | Daily         | 30 days    | S3, cross-region             |
| ClickHouse incremental | Every 6 hours | 7 days     | S3, cross-region             |
| S3 versioned objects   | On change     | 90 days    | Same bucket + replica region |
| Configuration / IaC    | On change     | Indefinite | Git + off-platform mirror    |

> We recommend that backup integrity be verified through automated restore tests performed at least monthly, with outcomes logged and retained as evidence of backup viability.

## 9. Disaster Scenarios and Response

### 9.1 Loss of a Single Node or Container

* Detection: automated health checks at the load balancer and container orchestrator.
* Response: traffic is automatically routed to the surviving master in the same datacenter or its peer in the other datacenter; the failed instance is replaced by the orchestrator. No manual intervention is required.
* Service impact: typically none observable to end users.

### 9.2 Loss of an Availability Zone

* Detection: simultaneous health-check failures of multiple components in the same AZ, confirmed by the cloud provider status page.
* Response: Aurora PostgreSQL fails over within the region in under 60 seconds; ClickHouse continues to serve from surviving replicas; the affected datacenter's KAWA Backend, Python Runner and BMPN2.0 Workflow Engine instances are rescheduled in the surviving AZ. BMPN2.0 jobs locked by the failed node are released automatically and picked up by the surviving node after the lock timeout expires.
* RTO: ≤ 5 minutes for the affected components.

### 9.3 Loss of an Entire Datacenter (DC-A or DC-B)

This is the primary disaster scenario the KAWA topology is designed for. Because every component runs master/master across DC-A and DC-B, the loss of one datacenter is a traffic-routing event rather than a recovery event.

* Detection: complete loss of connectivity to all services in one datacenter, confirmed by independent health checks and the cloud provider status page.
* KAWA Backend, Python Runners and BMPN2.0 Workflow Engine: the global load balancer detects the failed datacenter and shifts all traffic to the surviving instances in the other datacenter, which are already live and sized to handle full production load.
* ClickHouse: surviving replicas in the other datacenter continue to serve reads and writes immediately; no promotion is required.
* PostgreSQL (Aurora Global): if the lost datacenter was the primary Aurora region, the secondary cluster is promoted (managed promotion, approximately one minute). If it was the secondary, no action is required.
* BMPN2.0: jobs locked by the failed datacenter's engine instance are released after the lock timeout and reacquired by the surviving instance.
* S3: requests are redirected to the replica bucket if the lost datacenter's region was the primary.
* RTO: ≤ 10 minutes (bounded by health-check detection, load-balancer convergence, and Aurora promotion if required). RPO: 0 to 1 minute (Aurora cross-region replication lag).

### 9.4 Simultaneous Loss of Both Datacenters

This scenario covers the rare catastrophic event in which both DC-A and DC-B are lost simultaneously: a multi-region cloud provider outage, a coordinated cyber incident, or a geographic disaster affecting both regions. It is the only scenario in which the platform must be rebuilt rather than failed over.

* Detection: confirmed loss of both regions from the cloud provider and loss of all production endpoints.
* The Incident Commander formally declares a disaster and initiates the recovery runbook.
* PostgreSQL is restored from the most recent cross-region snapshot (stored in a third region for this scenario).
* ClickHouse is restored from cross-region backups stored in S3.
* S3 traffic is redirected to a tertiary region replica or restored from versioned backups.
* Application services (Backend, Python Runners, BMPN2.0) are deployed from the IaC repository to a recovery region.
* RTO: ≤ 4 hours. RPO: ≤ 15 minutes.

#### 9.5 Logical Corruption or Ransomware

* Detection: integrity checks, anomalous activity in audit logs, customer reports.
* Response: affected services are isolated; restore from the most recent clean backup taken before the corruption window. S3 object versioning is used to restore overwritten or deleted objects. Forensic copies are preserved before restoration.

## 10. Recommended Testing and Validation

We recommend that operators test the DR design on a defined cadence to ensure procedures remain effective and runbooks reflect the current deployment. The cadence below is a starting recommendation.

* **Backup restore tests:** Monthly, automated, for PostgreSQL and ClickHouse.
* **Component failover drills:** Quarterly. Includes forced failover of a KAWA Backend instance, a Python Runner, a BMPN2.0 Workflow Engine instance, and a ClickHouse replica.
* **Datacenter failover exercise:** Annually. Simulates the loss of one datacenter and validates that the surviving datacenter sustains full production load.
* **Tabletop exercise:** Annually. Walk-through of the disaster declaration, communication and decision-making process with the operator's key stakeholders.

Each test should produce a written report capturing scope, participants, observations, RTO and RPO actually achieved, and remediation items. Operators with regulatory or audit obligations should retain these reports for the period required by their own programmes.

## 11. Communication Plan (Recommended)

During a declared disaster, internal and external communication should be coordinated by the operator's Incident Commander. We recommend that operators define, in advance, the channels and timelines below.

* **Internal:** a dedicated incident channel for the response team and a paging mechanism (PagerDuty or equivalent) for the on-call rotation.
* **End users:** status updates published through whatever channel the operator's users normally consume (status page, email, in-product banner). For customer-facing deployments, an initial update within 30 minutes of disaster declaration and updates at least every 60 minutes thereafter until resolution is a reasonable starting point.
* **Regulators / contractually-bound parties:** notifications issued as required by the operator's customer contracts and applicable regulations.
* **KAWA Support:** for product-level issues during recovery, the operator should contact KAWA Support through the channel defined in their support agreement.

### 12. Document Maintenance

This reference architecture is maintained by KAWA Analytics Corp. and is reviewed at least annually. Material changes to the platform architecture, supported components or recommended deployment patterns trigger an out-of-cycle update. The version, issue date and next review date are recorded in the document control table at the top of this page.

Operators are encouraged to share feedback on this guide with KAWA through their support channel so that real-world operational experience can be reflected in subsequent revisions.


# Release notes

* [Release note - KAWA 1.32](/12_00_release_notes/12_01_release_note_1.32)
* [Release note - KAWA 1.33](/12_00_release_notes/12_02_release_note_1.33)
* [Release note - KAWA 1.34](/12_00_release_notes/12_03_release_note_1.34)
* [Release note - KAWA 1.35](/12_00_release_notes/release-note-kawa-1.35)
* [Release note - KAWA 1.36](/12_00_release_notes/release-note-kawa-1.36)


# Release note - KAWA 1.32

## 1. New Features

### 1.1 Cross Filters — on dashboards, filter multiple widgets at once with a single selection

* Set it up in the **Config panel**.

<div data-with-frame="true"><img src="/files/gCSCUECSwM1gs5C39DpE" alt=""></div>

* Active filters show as chips at the top.

<div data-with-frame="true"><img src="/files/PZz5uRs8NMLLvErSDrwC" alt=""></div>

* Each widget has a filter status icon that lists active filters (cross + dashboard + control panel) and displays whether they are enabled/disabled for this widget.

<div data-with-frame="true"><figure><img src="/files/TFD80oOVz22NKLYosIyI" alt=""><figcaption></figcaption></figure></div>

Benefits: fewer duplicate filters, faster analysis, shared context across widgets.

### 1.2 Widget in full screen mode in application and dashboard

All type of widgets ( charts , pivot and grid) will have an new icon full screen in dashboard and application.

<div data-with-frame="true"><img src="/files/LbrlndQsRFL1MleAVCtI" alt=""></div>

### 1.3 The query cache on external DataSources

Query cache configuration (per data source). Turn caching on/off, set TTL in seconds, and define an optional daily UTC window when cache is bypassed. Existing Clear query cache action remains available. For complete documentation and setup details, please visit: [KAWA Query Cache](https://github.com/kawa-analytics/kawa-documentation/blob/gitbook-migration/10_03_query_cache/README.md) section.

### 1.4 Python scripts: File upload parameters

Add a file-upload field to a script by setting 'fileExtensions'. Users can upload only the allowed formats, and Apply stays disabled until all required files are uploaded.

<div data-with-frame="true"><img src="/files/4jU5NkrQ6xClpfrbATNK" alt=""></div>

### 1.5 Run Python tool for scripts without inputs/outputs

Triggers a script with no inputs/outputs (scriptType: "RUNNABLE"). Pick the script in the control settings (FE stores the scriptId; no Python private join/mapping needed). On click the script runs and the view auto-refreshes when it’s done.

<div data-with-frame="true"><img src="/files/V0479WVn9BV70FZW4xQq" alt=""></div>

### 1.6 Create input table

Create an editable data source in two ways:

<div data-with-frame="true"><img src="/files/S9YdYiAT9Zg0lhh3dtax" alt=""></div>

Start from scratch — same flow as before, with a richer creation modal for custom columns.

<div data-with-frame="true"><img src="/files/lnF03YIfUbiQTHmr5VaL" alt=""></div>

Quick upload — upload a CSV and start editing right away (uses the existing CSV upload component).

## 2. Improvements & Bugs fixes

### 2.1 Python

* Applications API: The extended application endpoint now returns the app’s current sheets alongside existing metadata. This lets clients fetch everything in one call.
* Further optimisation of Python server

### 2.2 Data edit

* Data constraints in datasource model
* Copy / PAste ranges of data

### 2.3 Pivot table & charts

* Improved pivot table functionality (Modify Pivot behaviour)
* Charting improvements
* Add stacked line chart
* Changed legend behaviour

### 2.4 Other

* Security mapping for Live connections. Map source-side row/column permissions to KAWA roles when the data source uses Live connection.
* UI updates (Dashbords, Applications, Control panel)
* Add filter null in CP filter
* Fix BUG in last date filter (on date time)
* Improved exploration mode in apps
* Improved all home pages grids


# Release note - KAWA 1.33

## 1. New Features

### 1.1 Workflows — design, schedule, and run multi‑step automations

> You can read more about this in the [The Workflows section](/07_00_workflows).

* Set it up in **Home** → **Workflows** (click **+ Workflow**).

<div data-with-frame="true"><img src="/files/rwYODb4biT6hUFLRsRkf" alt=""></div>

* Trigger types (**WHEN**): Daily / Intraday / Weekly / Monthly / Manual only + Timezone and Only on business days.

<div data-with-frame="true"><img src="/files/reV30xXqjAe0Jhf4QAH3" alt=""></div>

* Actions (**THEN**): Transform data (Sheets), Run python script (from the library), Send email, AI prompt.

<div data-with-frame="true"><img src="/files/x62Gdc2ujaUWNDa2qPVK" alt=""></div>

* **Bindings** link fields from the current step to outputs from previous steps, passing data between actions in a workflow.

<div data-with-frame="true"><img src="/files/vnWqfzEIa1UkyvQp9Mba" alt=""></div>

* **Save & Update**: Click **Create workflow** to save a new workflow; after any edits, click **Update workflow**.
* **Run & monitor**: open **Run history** (top‑right) → Run for ad‑hoc execution; review Start / End / Status / Error with counters (Total / Success / Failed) and date filters.

<div data-with-frame="true"><img src="/files/xxXSKJptExwAzUU712QF" alt=""></div>

**Workflows** make it easier than ever to turn your data into action—automate routine tasks, deliver consistent email/AI outputs, and stay safe with built‑in guardrails and run history.

### 1.2 Doc — a free-length document page that combines text and interactive widgets.

> You can read more about this in the [The Reports section](https://docs.kawa.ai/12_00_release_notes/pages/bInymCODQr52n2UD9ouA#id-4.-doc).

* Set it up in **Home** → **+ Report** → **Doc** (next to Dashboard and Slides).
* Quick **block** insert via + or /: Text, H1–H3, Bulleted/Numbered list, Checklist, Сode, Image, Add widget.

<div data-with-frame="true"><img src="/files/3dxmXX0UNCBGLh049w0j" alt=""></div>

* **Block handle** (six dots) with context menu: Convert to, Move up/Move down, Delete.

<div data-with-frame="true"><img src="/files/90uO2hLWOI7PhTnhxdeE" alt=""></div>

* **Add widget** in a Doc: choose a Sheet → Existing or New widget (chart, grid, pivot).

<div data-with-frame="true"><img src="/files/zlSZL7VCGsW6WcfWORUQ" alt=""></div>

* **Filters & exploration**: work the same as in dashboards (Control panel, Exploration mode).
* **Widget actions** (Refresh, Summary, Edit, Full screen, Rename, Disable/Enable all filters, Duplicate, Synchronize, Go to sheet, Delete) — identical to Dashboard.

**Doc** turns insights into stories—write your narrative, drop in live widgets, and share a single scrollable page that stays in sync with your data.

### 1.3 Synchronize widgets for all types of Reports

All types of Reports will have a new **Synchronize** option; it pulls the latest configuration and schema from the linked Sheet/View so the widget reflects recent changes while keeping its size and position.

<div data-with-frame="true"><img src="/files/j7xmC0Un5aXyR4hjlr7n" alt=""></div>

### 1.4 Exploration mode for all types of Reports

All types of Reports have a new Exploration mode (via the Explore binoculars) to quickly adjust how a widget shows data and changes affect only this widget.

<div data-with-frame="true"><img src="/files/6kRtu7MbAdH7gd3W9BP2" alt=""></div>

### 1.5 AI Chat — New section type: Code (streaming)

AI Chat now includes a new Code section that streams code in real time via Server-Sent Events with a pending loader, using the same UI as Analysis.

<div data-with-frame="true"><img src="/files/ZmDpytQSAYadstrvYJJB" alt=""></div>

### 1.6 Create with AI in Reports

A new Create with AI option in the + Report menus opens a prompt-based modal to generate a Dashboard/Slides/Docs, with multi-select Sheets and Knowledge, and returns the created object.

<div data-with-frame="true"><img src="/files/VCtUX1oLn1N3gwMNJz8u" alt=""></div>

<div data-with-frame="true"><img src="/files/JnK8Ce2LAJQy02QbtiRA" alt=""></div>

### 1.7 New sheet creation options in Sheets

The Create sheet menu is expanded: alongside the existing From Data Source option, there are now two new choices — From CSV, which uploads a CSV, initializes a new “from-scratch” dataset with that CSV, auto-creates a sheet, and redirects you to the sheet’s default grid; and From Scratch, which does the same creation and redirect but without a CSV upload.

<div data-with-frame="true"><img src="/files/mzyaPtHrHtyPj1yeDxWM" alt=""></div>

### 1.8 Advanced pasting in Edit Sheet

Users editing sheets now have Advanced pasting: a single value fills the selection, N vertical values repeat across it, and any N×M block tiles to cover the range (Google Sheets–style).

### 1.9 Documentation page for Sheets & Data Sources

Introduced a new **Documentation** tab on **Sheets** and **Data Sources** that opens an auto-published report for documenting the dataset.

<div data-with-frame="true"><img src="/files/AdC5QO5pOydbYsxUDWTX" alt="Release"></div>

![](/files/jZCRj3LuC5ILLZEAgyeT)

* **Who can edit**: only users with WRITE access to the sheet/data source.
* **Auto-publish**: edits are saved immediately.
* **Creation flow**: the page is created on first open, linked to the corresponding sheetId or dataSourceId.
* **Visibility**: these docs do not appear in Dashboards.

## 2. Improvements & Bugs fixes

### 2.1 Data sources

* Improved the Feed Type: Being able to give a name to snapshot ETL runs

### 2.2 Sheets

* View settings: hiding the name of the aggregation in the grid

### 2.3 Pivot table & charts

* Added a new Reset every parameter for Show data as: Cumulated.

  When the user selects a column of type DATE or DATETIME, a new option appears (default: NEVER; options: YEAR / SEMESTER / QUARTER / MONTH / WEEK, and for datetime also DAY). It resets the running total at the start of the selected period.
* View settings: hiding the empty fields in the pivot table
* Added duplicate formula in pivot and chart
* Improved the Export pivot
* Improved copying for Pivot

### 2.4 Reports

* Improved the full screen widget

### 2.5 Other

* Upgraded Date and Date time filters
* New format in number formatting


# Release note - KAWA 1.34

## 1. New Features

### 1.1 Workflows — more triggers, actions, and control flow

**Workflows** (introduced in [Release note - KAWA 1.33](/12_00_release_notes/12_02_release_note_1.33)) are KAWA’s automation builder: define a trigger (WHEN), chain actions (THEN), pass outputs between steps, and monitor every run in Run history. In 1.34, **Workflows** expand with an On data refresh trigger, more built-in actions (Enrich data with AI, Reports, Build a chart, Export to data source, Generate output, User task), and control-flow blocks (If/Else, Routing, Interrupt workflow) for real-world orchestration.

> You can read more about this in the [The Workflows section](/07_00_workflows).

Workflows now support richer automation from end to end:

* **New trigger: On data refresh** — run a Workflow automatically right after a selected data source refreshes (supports multiple sources with **OR** logic).

<div data-with-frame="true"><img src="/files/ZvE0kg4krSAt3Gas2EKw" alt=""></div>

* **New actions** to build pipelines without leaving the editor
  * **Enrich data with AI** (generate output columns per row)
  * **Report** (select a report from the workspace and use it later in the Workflow as an artifact)
  * **Build a chart** (from previous steps or any Sheet)
  * **Export to data source** (export a table result with export mode + access policy)
  * **Generate output** (produce a text result using variables from prior steps)
  * **User task** (assign a task to a user and collect form inputs before the Workflow continues)

<div data-with-frame="true"><img src="/files/48sl6BBW44uPhEsmQinS" alt=""></div>

* **New logic blocks** for advanced orchestration
  * **If / Else** with multiple rules (**AND** logic), comparing values from prior steps (grid, aggregates, properties)
  * **Routing** to split one input table into multiple routes (R1/R2/R3…), each with its own slice and actions
  * **Interrupt workflow** to immediately stop execution (useful as an “emergency stop” inside branches/routes)
* **More ways to run workflows**: from the **Controls panel** (button → Run workflow) and from **AI Chat** as Agent commands (add workflows to an agent and run them directly from chat)

### 1.2 Create Multi-sheets — support Pivot tables and Charts across multiple sheets

Now user can create a **Multi-sheet** (a combined sheet) that consists of several child sheets. A Multi-sheet supports working with multiple primary data sources in one object.

<div data-with-frame="true"><img src="/files/uAiFLIwT82d7A1IXY1iF" alt=""></div>

* Multi-sheet supports **Pivot Table** and **Chart** as the main modes (Grid Flat is disabled), so user can build visualizations using columns from different child sheets.

<div data-with-frame="true"><img src="/files/8uoODC7fEkKJS5nHiFT7" alt=""></div>

* **Filters** show all Multi-sheet columns (from all child sheets).
* A **2-level hierarchy** was added to the column picker: **Data sources** → **Columns**, so sources don’t get mixed into one list and the setup is easier to understand.

<div data-with-frame="true"><img src="/files/qa9dMwycJT7DcL7TojgK" alt=""></div>

* For Multi-sheet, the “Join a new data source” option was removed.
* For Multi-sheet, AI Chat and Automations are disabled; the Model is still available, but read-only.
* Editing actions that don’t make sense for Multi-sheets were removed: Edit linked columns and mapping edit.

## 1.3 Views — cancel a computation

Long-running computations can now be interrupted directly from the loading state using a **Cancel** action. A new backend command, InterruptComputation, stops the current computation and returns a **CANCELLED** status (with metadata and empty records).

<div data-with-frame="true"><img src="/files/Fflz3ja8gBKZC1CZ6iE9" alt=""></div>

## 1.4 Reports — Read-only (View) mode

Reports now have a clearer **View** (read-only) mode designed for presenting and consuming content:

<div data-with-frame="true"><img src="/files/OmDQIiWZ0apQ1HOu4wic" alt=""></div>

* A dedicated **View** mode toggle is available.
* When a user has **read-only** access, Reports open in View mode by default.
* The Control panel works in View mode: if at least one control exists, it is shown so viewers can apply filters without editing the report.
* Export to PDF is available in View mode.

## 2. Improvements & Bugs fixes

### 2.1 Data sources

* Changed data deletion permissions

### 2.2 Pivot table & charts

* Improved legend behaviour

### 2.3 Filters

* Improved Text filters: User can now paste a list of values copied from Excel into a text filter. KAWA converts line breaks into ; and automatically selects all pasted values (even if they are not currently in the list). The same behavior is supported in the **Control** panel.
* Improved Date filter

### 2.4 Reports

* Improved AI widgets
* Visual polish and UI fixes
* Added button to duplicate dashboards

### 2.4 Applications

* Added Export as CSV content of views in Applications

### 2.5 Python

* Scripts — built-in script library: Scripts can now be marked as built-in (builtIn: true) and surfaced as a dedicated “built-in” set in the Scripts list (with a filter). Built-in scripts are read-only for users: they can’t be deleted, renamed, edited (description), or shared — only Add to favorites is available.

### 2.6 Authentication

* Improved API key management

### 2.7 Other

* Improved Conditional formatting

## 3. Patch releases (1.34.x)

### Patch 1.34.2

* Fixed Python step timeout behavior to ensure workflows fail gracefully instead of hanging the workflow engine

### Patch 1.34.3

* Fixed workflow Date filters not resolving LAST DATE
* Fixed workflow computations incorrectly using the server default timezone: scheduled runs now use the workflow creator’s timezone, and all other runs use the triggering user’s timezone

### Patch 1.34.4

* Added environment variable `KAWA_EMAIL_IS_ENABLED` to control email related features. It is FALSE by default
* Improved Workflow engine stability when jobs get stuck
* Improved stability of drag and drop columns on the Grid

### Patch 1.34.5

* Added “Send email” toggle to User Input Task configuration — defaults to ON when emails are enabled and is hidden when emails are disabled
* Added an Abort action in Run history → Run details to interrupt running workflow executions, updating the run status to Interrupted
* Show who triggered a workflow run (“Triggered by”) in the Workflow run history dialog
* Speeded up the linked column sheet/view selector by loading a lightweight (shallow) sheets list instead of full sheet data, while preserving server-side suggestions and default view selection
* Added a KYWY endpoint to list workspace teams and their members (team contents) for a given workspace
* Added a KYWY reporting endpoint to retrieve who viewed what (which user opened which views/dashboards, and when)
* Improved Lookup columns UX by displaying the source sheet name in the Model panel, and Lookup column field information
* Added a new “X” button in dashboards to clear all active cross filters at once (shown when 2+ cross filters are applied, including in the overflow menu), and fixed the “N more…” dropdown to display the correct overflow items
* Added a new “Last run date” column on the Workflows homepage list to show when each workflow was last executed
* Added a new “User tasks” (TODOs) section in Workflows, with a badge showing the number of pending tasks and a searchable task list that lets users open and complete task validation forms
* Added the ability to duplicate conditional formatting rules (both Single and Scale) via a new clone action, which creates a copy and opens it in the rule editor for quick adjustments
* Added a new “Trigger type” column to the Workflows list grid, displaying whether each workflow is Manual, Scheduled, or On refresh (with corresponding icons)
* Improved Workflow task naming in the editor: Transform data tasks now show clearer dynamic titles (e.g., “Load data from sheet/task”), and Python tasks display the selected script’s name instead of the generic “Run python script”
* Added a “Download widget” action for dashboard Sheet widgets (grid/pivot/chart) to export the widget’s current data as CSV — available in view mode via the widget header button and in edit mode via the widget actions dropdown
* Improved Sheets list loading by lazy-loading `defaultLayoutId` (no longer fetched upfront for every sheet), while keeping sheet opening/navigation, view CRUD, and sharing behavior unchanged
* Improved performance of sheet selector dropdowns by switching several dialogs to a lightweight “shallow sheets” list (full sheet details load only after selection), speeding up lookup/link/widget configuration flows—especially in workspaces with many sheets
* Improved the “Link columns” flow by auto-selecting the target column when a matching column display name is found in the targets list (users can still change the selection)
* Updated Workflow execution to stop using ApiKey and instead run actions as the initiator (PrincipalId) for more accurate permissions and auditing
* Updated Workflow permissions so users can run a workflow shared in read-only, as long as they have the workspace “Run Workflows” permission (previously it required the workflow to be shared with Edit access)
* Fixed ETL file authorization so users with Edit access can update datasource files (e.g., replace a CSV) and successfully run the ETL, resolving previous permission-related failures
* Added Entity Audit (Audit Log) support across entity pages, providing a change history modal with date/user/entity filters, an “only master” toggle, and version comparison for audited entities (e.g., sheets, data sources, reports, workflows, scripts)
* Fixed missing user timezone on first login by resolving it from the browser during workspace initialization and performing a one-time sync to persist it on the backend (without blocking workspace load)
* Enhanced Indicator charts to support text outputs

### Patch 1.34.6

* Added “Transpose view” option in the grid context menu (Sheets & dashboard grid widgets) to switch between transposed and standard table layouts (swap rows/columns)
* Improved Pivot view CSV export to use client-side export with flattened headers, so the exported values/rows/columns match what users see
* Fixed dashboard full-screen widget mode so it preserves Cross Filters when opening/closing a widget in full screen (previously the cross-filter state was lost and data was reloaded from scratch)
* Added clickable “Link” properties for Workflow URLs (e.g., View Link, Image Link, Report Link) rendered as \<a>
* Added “Apply format” option in the Grid column menu, with “To other fields” and “From another field” actions to copy column formatting

### Patch 1.34.7

* Added a new “Join datasets” workflow task to combine two datasets using INNER / LEFT / RIGHT / FULL OUTER joins, with support for selecting datasets, configuring one or more join keys, and choosing which output columns to include
* Added a new “Stack datasets” workflow task to stack (union) multiple datasets into a single output, with support for adding/removing dataset inputs and manual column mapping to define the resulting output columns
* Fixed OutOfMemoryError (Java heap space) / 504 Gateway Timeout when loading Backoffice → Computation history
* Added a “Value source” setting for text-type dashboard column filters, allowing users to choose which bound sheet(s) are used to load filter value suggestions (defaulting to all bound sheets) to improve performance on multi-sheet dashboards
* Improved ClickHouse (CH) connections to support multi-user access (handling multiple user credentials for the same connection)
* Added entity history tracking and a new API endpoint to retrieve previously stored entity history
* Added COUNT\_TRUE and COUNT\_FALSE aggregations for boolean columns (available across dashboards, views, and workflows), and updated SUM on booleans to behave as COUNT\_TRUE (also exposed in the formula editor)
* Added a “Cancel all computations” control to dashboards: the toolbar refresh button now switches to a stop action while any widget is computing, allowing users to cancel all running dashboard computations in one click
* Added a “Free-form placement” toggle in Dashboard settings, allowing widgets to be dragged and placed anywhere on the grid without automatic re-packing (default behavior remains auto-packing to fill gaps)
* Added Y-axis range controls to view charts, letting users set custom min/max bounds (with an optional log scale toggle for numeric axes)

### Patch 1.34.8

* Added a “Value source” setting for text-type dashboard column filters, allowing users to choose which bound sheet(s) are used to load filter value suggestions (defaulting to all bound sheets) to improve performance on multi-sheet dashboards
* Updated ClickHouse installation scripts and CI for compatibility with the latest LTS version
* Added a collapsible left sidebar for application pages with a persistent icon-only collapsed state
* Added dashboard size controls to dashboards embedded in applications, matching the same Small / Medium / Large / Full options available in standalone dashboards
* Standardized indicator card font sizes by using fixed size steps for titles and values, with improved truncation for long text and progressive display of comparison rows as card height increases
* Improved workflow editor interaction for If/Else and routing blocks by making block backgrounds non-selectable, while allowing branch tabs and route/condition rows to be clicked to select the corresponding task
* Added read-only mode for mapping column editors and definition view dialogs
* Added Y-axis range and log scale controls for numeric chart axes, allowing users to set custom min/max bounds
* Added table support to dashboard text and report editor widgets, with a new “Table” block in the editor toolbox and controls for inserting tables, managing rows and columns

### Patch 1.34.9

* Fixed linked column layout permission checks so users with edit access can save linked column layouts correctly instead of seeing a false permission error
* Improved the line chart series configuration panel by grouping settings into labeled sections for easier navigation, with no functional changes
* Added “Apply format” support for pivot table and chart fields, allowing users to copy formatting between compatible fields
* Improved workflow save validation to catch invalid input bindings, incomplete Routing/IF branches, and missing “On data refresh” data sources with clear UI errors

### Patch 1.34.10

* Added an API command to transfer workflow ownership between users
* Fixed a workflow engine issue where executions could hang on the Join datasets task instead of completing correctly

### Patch 1.34.11

* Fixed SSO redirection so users are correctly returned to KAWA after authentication
* Fixed workflow authentication to correctly load the user’s team memberships for permission and sharing checks
* Fixed report usage behavior by filtering out editable layouts and technical sheets to prevent missing columns in reported data


# Release note - KAWA 1.35

## 1. New Features

### 1.1 New design for home pages

All **home pages** have been restyled — **Data Sources**, **Sheets**, **Workflows,** **Applications**, **Dashboards**, **Scripts**, **Agents**, and **Knowledge** now share a consistent, updated layout.

<div data-with-frame="true"><figure><img src="/files/7WcrBWxIwqbkPrpLK50N" alt=""><figcaption></figcaption></figure></div>

### 1.2 Workflows — Loop and Run workflow

In 1.35, Workflows gain two new capabilities — **Loop** for row-level iteration and **Run workflow** for composing workflows together.

> You can read more about this in the [Workflows section](/07_00_workflows).

* **New logic block: Loop** — iterate over the rows of an input table, repeating a set of actions for each row.
* **New action: Run workflow** — run an existing workflow as a sub-workflow.

### 1.3 Workflows — new format for Run history

**Run history** is now rendered as an expandable tree. Loops, If / Else, Routing, and Sub-workflows appear as container rows with a count pill (e.g. **2 iterations**, **2 routes**, **Branch: Then**). Click the chevron to expand and inspect nested tasks.

<div data-with-frame="true"><figure><img src="/files/r2MDkhVnW1RiQDvoJBX2" alt=""><figcaption></figcaption></figure></div>

### 1.4 Formula history

The formula editor now keeps a **Formula history**. Click the **clock icon** next to Input / Blockly / Ask AI to open a side panel listing saved formula versions with timestamps and authors. Click any version to load it back into the editor.

<div data-with-frame="true"><figure><img src="/files/HQUeunrZvws2bbwlPc3X" alt=""><figcaption></figcaption></figure></div>

### 1.5 Sheets — Edit mode

Sheets based on an editable data source now support a dedicated **Edit mode**. Click **Edit data** in the action bar to open an edit session. Changes are no longer sent to the backend after each action — they are collected as pending edits and submitted in one pass when you click **Save**.

<div data-with-frame="true"><figure><img src="/files/tIPKEwqEC6VlNtqgYKjG" alt=""><figcaption></figcaption></figure></div>

### 1.6 Sheets — Tab grouping

Views in a sheet can now be organized into groups. Each group is displayed as a colored tab with an icon in the view strip.

<div data-with-frame="true"><figure><img src="/files/BWQKkJ0zg8gTHpH6bbLB" alt=""><figcaption></figcaption></figure></div>

Open the group dropdown: pick a Style (color and icon), rename the group with an optional title, or Ungroup.

<div data-with-frame="true"><figure><img src="/files/k98ucj9ibeUHiqZbuDnU" alt=""><figcaption></figcaption></figure></div>

### 1.7 Sheets — Change data source

A new **Change data source** action in the **Model** tab lets you replace the primary data source of a sheet without losing its structure. KAWA guides you through a 4-step wizard: **Select source → Map columns → New columns → Review**.

<div data-with-frame="true"><figure><img src="/files/aBqwPxDqllcj0bfWcWEd" alt=""><figcaption></figcaption></figure></div>

* Columns are auto-matched by name and type. Key columns must remain mapped; non-key columns can be left unmapped.
* If the new source has extra columns, choose which ones to add to the sheet in the **New columns** step.
* Unmapped columns stay in the sheet but receive no data — they can be reconnected in a later replacement.
* Existing charts, pivot tables, filters, formulas, and lookup columns are preserved.

> You can read more about this in the [Change data source section](/02_00_modeling/change-data-source).

## 2. Improvements & Bugs fixes

### 2.1 Workflows

* Improved Stack datasets — the column mapping panel now offers three Column matching methods to auto-populate the mapped columns table: By column name, By column order, Auto-mapping.
* When a task's input comes from a previous task's output (not a data source), the editor now shows an info banner: "Preview data is a sample. Actual values will be computed when the workflow runs."

### 2.3 Pivot table & charts

* Pivot CSV export now offers two modes: Visible data (exports what's currently rendered, with formatting) and All data (raw) (full unformatted export from the backend).

### 2.2 ClickHouse

* Migrated from the JDBC driver to the native ClickHouse Java client. The new client reuses connection pools across requests and is backward compatible with ClickHouse LTS versions 24, 25, and 26.

## 3. Patch releases (1.35.x)

### Patch 1.35.1

* Splited datasources into Editable (created from scratch or via the Python client — full edit mode with add/remove rows and columns) and Patchable (backed by an external system — in-place cell patching only)
* Adapted the Print screen feature to the new backend, migrating its capabilities to Playwright on Java


# Release note - KAWA 1.36

## 1. New Features

### 1.1 Artifacts — a workspace library for your files

The **Artifacts** tab is a workspace-level library for the files you bring into KAWA: a single place to upload, store, preview, version, publish, and download PDFs, images, presentations, spreadsheets, text, application files, and other binaries without leaving the platform. Where sheets hold your modeled data and dashboards hold your live views, **Artifacts** holds your files — the documents and deliverables you want to keep, version, and share with your team.

> You can read more about this in the [Artifacts section](/artifacts).

* **Create an artifact** — click (+ Artifact) at the top right, or the (+) at the top of the left panel, enter a Name, and choose a Type (`Application`, `PDF`, `Image`, `PowerPoint`, `Excel`, `Word`, `Binary`, or `Text`). An artifact is created as an empty container ("No versions yet") and populated by adding a file as its first version.

<div data-with-frame="true"><figure><img src="/files/3g8rEO2hTTmjoce9x2gr" alt=""><figcaption></figcaption></figure></div>

* **Version every file** — each artifact keeps a full history (v1, v2, …), each version showing its timestamp and author, with the current one marked. Add a version from the (+) in the VERSIONS panel; the file chooser only lists files matching the artifact's type, and an optional Description can be added per version. Updating a file — a revised report, an updated deck, a corrected dataset — never loses the version it replaces.

<div data-with-frame="true"><figure><img src="/files/o4j9HE6oBJc913gDAjBU" alt=""><figcaption></figcaption></figure></div>

* **Preview and act on a version** — opening an artifact shows a detail page with a Details panel (Creator, Access, Created/Updated), a Versions panel, and a Preview that renders supported types directly in the browser (a text file as a table, a presentation slide by slide) with zoom controls. Header actions: **Download** (save the version in its original format), **Publish**, **Open** (open in a new tab), and **Details** (show/hide the panels).

<div data-with-frame="true"><figure><img src="/files/sQ5OMx2oFTG7Opi7B6vY" alt=""><figcaption></figcaption></figure></div>

* **Publish with a visibility level** — choose a version and who can open it:

  * **Restricted** — no shareable link; reachable only inside the workspace.
  * **Signed-in users** — anyone signed in who has the link can open it.
  * **Anyone with the link** — no sign-in required.

  The current state is shown in the **Published** column.

<div data-with-frame="true"><figure><img src="/files/awJkxQZFV7JcB2n8xKqE" alt=""><figcaption></figcaption></figure></div>

* **Govern anonymous access** — a new **Anonymous** tab under Workspace → Permissions controls which actions anonymous artifacts may perform in the workspace, grouped into READ (Query, Get workflow status, Get workflow definition, List workflow run tasks, Get ETL status, Download file), WRITE (Patch data, Upload file), EXECUTION (Run workflow, Run ETL), and a WARNING group of sensitive actions (Generate by AI). Any action left off is disallowed.

<div data-with-frame="true"><figure><img src="/files/usshHfgu9zdg5Dz1KPBw" alt=""><figcaption></figcaption></figure></div>

* **Use artifacts in workflows** — two new KAWA actions connect artifacts to automations:
  * **Load artifact** — load a stored artifact (Latest or a pinned version) so its properties (Author, Version, Description) are available to later steps via bindings (e.g., Send email, AI prompt, Generate output, Run python script).
  * **Save artifact** — persist a workflow's file output as a new version of an existing or newly created artifact; earlier versions are kept, and the result stays available in the Artifacts tab and to later Load artifact steps.

### 1.2 Workflows — Approval step for User Input tasks

The workflow form builder now includes an **Approval** input for **User Input** tasks. It comes seeded with editable "Approve" / "Reject" options, and KAWA automatically generates an **If / Else** branch for each option immediately after the task — so an approver's decision routes the run without any manual branch setup.

<div data-with-frame="true"><figure><img src="/files/0o19nnVDOe7le8lBGmzc" alt=""><figcaption></figcaption></figure></div>

### 1.3 Workflows — File input

Workflows can now collect a file from the user. A new **File** input is available in two places:

* As a **data input** in the **Task Input Form** of a **User** task (**+ Add data input → File**), so an assignee can upload a file as part of completing the task.
* As a **workflow input** on the **Manual run** trigger (**+ Add data input → File**), so a user starting the run manually is prompted to upload a file.

Either way, the uploaded file is passed into the run and can be used by downstream tasks.

### 1.4 Python — Workflow tools with the `@kawa_workflow_tool` decorator

Python workflow scripts gain a first-class way to declare their interface. The new **`@kawa_workflow_tool`** decorator lets a script declare its **input files**, **output files**, **output scalars**, and **named output data frames**, which downstream tasks can bind to individually. An **overview panel** summarizes each tool's inputs and outputs.

* Scripts can now produce **multiple named output data frames** in a single run, and each downstream task selects and binds to the specific output it needs.

<div data-with-frame="true"><figure><img src="/files/V0UZxvBxpfY7PUUtL4xv" alt=""><figcaption></figcaption></figure></div>

### 1.5 Dashboards — Edit sheet data in place

Editable sheet views embedded in dashboards can now be edited without opening them full-screen. A new **"Edit data"** toolbar on dashboard sheet widgets opens a buffered edit session: changes are collected as pending edits and either committed on **Save** or discarded on **Cancel**. This extends the sheet **Edit mode** introduced in 1.35 to dashboards.

<div data-with-frame="true"><figure><img src="/files/4Ywh5ud746sYmM3Kt3w6" alt=""><figcaption></figcaption></figure></div>

### 1.6 Data sources — Feed type switcher

The data source overview now shows a **"Feed type"** section with the current loading mode. Users can switch between **Incremental** and **Reset before insert** directly from the overview — with an inline warning about the impact of the change — instead of going through a separate toolbar action.

<div data-with-frame="true"><figure><img src="/files/KglZVRFGPn7N0oFdo5FL" alt=""><figcaption></figcaption></figure></div>

### 1.7 Sharing — Share with individual users

Entities can now be shared with **individual users**, not only teams. The **Share** dialog uses a single combined picker with a **Users / Teams** toggle, and each user or team gets its own **Viewer** / **Editor** access level.

<div data-with-frame="true"><figure><img src="/files/iEwYJHPOTeajnknLxFaT" alt=""><figcaption></figcaption></figure></div>

### 1.8 Permissions — Reworked workspace roles

The workspace permissions UI has been reworked for clarity:

* Clearer role names — **Explorer**, **Builder**, and **Admin** — with a **role** field on the **User Profile** tab.
* Share and write permissions are combined into a single **"Share and Write"** group.
* Admin-level permissions are grouped into a highlighted **"danger zone"**.

<div data-with-frame="true"><figure><img src="/files/ps34GhQYaLqt9nr1rNVq" alt=""><figcaption></figcaption></figure></div>

## 2. Improvements & Bugs fixes

### 2.1 Workflows

* Added a **"Left Anti Join"** type to the **Join** task, returning rows from Dataset A with no match in Dataset B.
* Added **auto-matching of join columns by name**, pre-selecting columns that exist on both sides of the join.
* Added a **"Preview workflow"** button to the sub-process task, opening a read-only preview of the selected sub-workflow.
* Updated the workflow header to a **breadcrumb-style header** matching other entity pages (inline rename, favourite star, share, and **Workflow** / **Run history** tabs), and improved the creation flow to prompt for a name and description.
* Fixed workflow updates to share the layouts backing **COMPUTE** / **CHART** tasks, so adding or rebuilding a task no longer leaves those layouts private and breaks workspace mounts for non-admin members.
* Fixed workflow filters so an invalid or missing variable binding is surfaced and blocks saving, instead of failing silently until deploy; the collapsed filter card now shows the binding's real name and turns red when broken.

### 2.2 Sheets & Computations

* Added the ability to **duplicate a lookup (linked) column** — like duplicating a formula — keeping the same target view, source field, aggregation, and join keys.
* Added **view / edit** for formula, lookup, and mapping columns in the **Sheet model** section.
* Added editable **multi-line descriptions** to formula, lookup, and mapping columns, accessible from an info icon in their editors.
* Added support for **integer and date columns as mapping keys**, so mapping columns can key off numeric and date values in addition to text.
* Added a distinct **icon for editable sheets** wherever sheets are listed.
* Added a **computed-columns count** to each sheet in the home page catalog, updating automatically as computed columns are added or removed in the grid view.
* Fixed formula creation in grid view so users **without formula edit permission** can create a new formula, instead of being blocked by a permission check that was mistakenly applied in create mode.

### 2.3 Charts & segmentation

* Added a per-series **Bar / Line type toggle** to grid-based charts, so a single chart can mix bar and line series.
* Added a **3M (three-month) granularity** option to date and date-time segmentation across the app.


# Terminology

## A

### Agent

**In KAWA:** A workspace-wide AI assistant available in AI chats. Each agent is configured with instructions, connected knowledge sources (including unstructured data), a set of commands, and defined capabilities to help with tasks and queries. Every workspace includes a default agent (Paul) that can be edited but not removed; users with the Edit Agent flag can create, update, or remove other agents.

**How it relates to data modelling and standard terminology:** Not a data object; an application component that augments analysis and automation; comparable to BI assistants or workflow bots that sit outside the relational model but can read model metadata and execute queries/commands.

**Used in:** [AI Integration](/06_00_ai_integration) section.

#### Artifact

**In KAWA:** A file stored in the workspace-level **Artifacts** library — PDFs, images, presentations, spreadsheets, text, application files, and other binaries — that you can upload, preview, version, download, publish, and share without leaving the platform. You choose the artifact's type when you create it (for example, `TEXT`, `PDF`, `BINARY`) and then add the file as its first version, and KAWA keeps a full version history (v1, v2, ...). An artifact is **Private** (Access: *Restricted*) by default and becomes visible to others only once it is shared.

**How it relates to data modelling and standard terminology:** An artifact is not a data object — it does not define a schema, a granularity, or queryable columns the way a Data Source or Sheet does. It belongs to the content/asset-management layer of the platform: a versioned file store with access control (comparable to object storage or a document library) that sits alongside the data model rather than inside it. Workflows can also produce artifacts as outputs (for example, exports or generated documents).

**Used in:** [Artifacts](/artifacts) section.

***

## C

### Column

**In KAWA:** A Column is an attribute in a Sheet that’s part of the sheet’s model and is used in queries, grouping, aggregations, and visualizations. Columns can come from the Data Source or be derived (formulas, mappings, Python). They define what the model captures and how the data can be queried, grouped, and visualized; values can be displayed as-is or aggregated/formatted for analysis. Columns can be:

* **Dimension** — descriptive, categorical data used to group, slice, or label metrics (e.g., names, dates, categories, regions).
* **Measure** — numeric indicator from the Data Source.
* **Metrics** — numeric values that are derived from expressions/formulas or other columns saved in the Sheet.

> Columns from Data Source are always shared (non-toggleable). Promoted columns can be shared/unshared according to Sheet settings.

**How it relates to data modelling and standard terminology:**

* In standard terms, a column is a table attribute (a field).
* In KAWA, a Sheet’s granularity (level of detail) is set by the primary key of the main Data Source. The set of columns only defines how data can be queried, grouped, and aggregated in the Sheet.

**Example**

<div data-with-frame="true"><img src="/files/z7JbmlG4slqYGAnnD2o2" alt=""></div>

<div data-with-frame="true"><img src="/files/qHERS3tU4l1T1VFlDj9x" alt=""></div>

**Used in:** [Data Modeling](/02_00_modeling) section.

### Control Panel

**In KAWA:** A configurable set of user-facing controls that manages both filters (applied to one or many Views/Sheets/Reports) and variables used to configure scenarios and referenced in formulas/metrics. It defines scope, default values, and propagation rules for governed self-service.a configurable set of user-facing controls that manages both filters (applied to one or many Views/Sheets/Reports) and variables used to configure scenarios and referenced in formulas/metrics. It defines scope, default values, and propagation rules for governed self-service.

**How it relates to data modelling and standard terminology:** Parameters (similar to parameters in BI) that shape queries and visuals. Scope defines where a setting applies; propagation governs inheritance.

**Example**

<div data-with-frame="true"><img src="/files/jXKYC3uCHlNZmxbqA5EM" alt=""></div>

**Used in:** [Control Panel](/02_00_modeling/02_01_control_panel) section.

***

## D

### Data Source

**In KAWA:** A single data table in KAWA. It contains Indicators (columns) with defined types (e.g., date, datetime, text, boolean, integer, decimal; lists of texts/numbers are supported) and must have a primary key (single or composite) that uniquely identifies each row. Data can come through Importing files, Connecting to external systems such as CRMs, databases, APIS etc, Linking to existing tables in the main warehouse, Importing unstructured data, Importing data from Python scripts, creating a data source from input table.

**How it relates to data modelling and standard terminology:**

* Data Source in KAWA corresponds to a single data table or tabular representation.
* Column in modeling = table column; in KAWA it is represented as Indicator.
* Primary key in relational database meaning: uniquely identifies a row; can be composite.

**Example**

<div data-with-frame="true"><img src="/files/93TKzCfYmvneJhe3WmI1" alt=""></div>

<div data-with-frame="true"><img src="/files/9PyPTJfihxQiYYe6dmZM" alt=""></div>

**Used in:** [Data Integration](/01_00_data_integration) section.

***

## F

### Field

**In KAWA:** The contextual use of a Column within a View, specifying role (axis, series, grouping, value) and aggregation behavior. Fields control how data is rendered and summarized, without changing the underlying column definition.

**How it relates to data modelling and standard terminology:** In standard terms, this is the presentation layer: a field in a view (a visualization field). It’s not a physical column in the data model, but a projection within a specific View with a chosen role and aggregation. The field controls how data is rendered and summarized, but it doesn’t change the schema or the definition of the Column.

**Example**

<div data-with-frame="true"><img src="/files/X7ymJUK6PmnfWJqfMtEt" alt=""></div>

**Used in:** [Grid views](/04_00_visualization/04_01_grid_views) section.

### Filter

**In KAWA:** A condition that limits the records returned and shown by KAWA when a View, Sheet, or Report is evaluated. A filter specifies fields, operators, and values; it can be saved with a view, defined on a sheet, or applied report-wide. Filters change what is queried and displayed; they do not modify stored data.

**How it relates to data modelling and standard terminology:** The behavior of filters in KAWA follows the common practice of BI and relational systems: a filter returns a subset of rows that meet the conditions.

**Example**

<div data-with-frame="true"><img src="/files/87ip6cNGbYprmYxuwa3y" alt=""></div>

**Used in:** [Filtering data](/04_00_visualization/04_04_filtering) section.

***

## I

### Indicator

**In KAWA:** A Data Source column (one field of the source table). Indicators have a defined type (e.g., date, datetime, text, boolean, integer, decimal; lists are also supported) and may be part of the primary key (single or composite) that uniquely identifies rows. After clicking **+ Create new sheet** on this Data Source, KAWA creates a new Sheet where the initial columns are automatically populated from the Indicators of that source.

**How it relates to data modelling and standard terminology:** An Indicator is a column (attribute) in a Data Source table with a defined data type; it can be part of the primary key. Analytical roles are assigned later within the Sheet.

**Example**

***See:** Data Source — Example*

**Used in:** [Data Integration](/01_00_data_integration) section.

***

## K

### Knowledge

**In KAWA:** A workspace section for connecting and integrating unstructured data (documents, files) into KAWA to support data-driven work. This feature requires a connection to OCR (Optical Character Recognition) and to a completion API.

**How it relates to data modelling and standard terminology:** In the classic data classification, unstructured data is information in the form of files or documents that is not stored in tables. To convert this kind of content into text suitable for further processing, OCR is used. Then, text operations are performed using the completion API. So, Knowledge in KAWA follows the standard chain: unstructured data source → OCR → text → working with text via completion API.

**Used in:**

***

## P

### Python Tool

**In KAWA:** A Python function registered in KAWA with the @kawa\_tool decorator (from kywy.client.kawa\_decorators). Tools run on KAWA’s Python runtimes configured at the workspace level. You can add tools in two ways:

1. KAWA File Store — write/edit the script directly in the GUI;
2. Connected VCS (Git over SSH) — KAWA clones/pulls your repo; each repo includes requirements.txt and a kawa-toolkit.yaml that groups tools into toolkits. When a tool is added from VCS, it is not editable in the GUI (update via commits). Tools declare inputs/outputs in the decorator, appear in the Tools section, and require the appropriate workspace permissions and a healthy Python setup.

**How it relates to data modelling and standard terminology:** In standard terms, a Python Tool is a regular Python function with the @kawa\_tool decorator, which declares inputs and outputs for use in the GUI and runs in managed Python runtimes at the Workspace level. In other words, a Python Tool belongs to the execution/extension layer of the platform, not to data modelling.

**Used in:** [Python Tools](/09_00_python_integration/09_02_python_tools) section.

***

## R

### Reports

**In KAWA:** Published collections of Views arranged with layout, shared Filters, and optional Control Panels for interactivity. Reports define audience, permissions, and refresh behavior, and serve as the primary surface for analyses. User can create three types of reports: Dashboard (infinite canvas), Slides (fixed-size pages), and Doc (rich text with embedded widgets).

**How it relates to data modelling and standard terminology:**

* The Presentation layer, not the modeling layer. Reports don’t define schemas or modify stored data. Instead, they organize Views into a layout, apply shared Filters, and optionally include Control Panels for user interaction. They also manage the audience, permissions, and refresh behavior.
* Standard BI alignment: In typical BI terms, Reports correspond to the report/dashboard layer, which visualizes already modeled data through charts, grids, and pivot tables (i.e., widgets/views).

**Example**

<div data-with-frame="true"><img src="/files/RyAmceJRXGTYB0OK8hbs" alt=""></div>

**Used in:** [Reports](/05_00_publishing/05_01_reports) section.

***

## S

### Sheet

**In KAWA:** A data object built on one main data source (defines the sheet grain and primary keys) with optional linked data sources joined by LEFT JOIN to add columns. A sheet contains views (at least a Grid), columns (dimensions, measures, calculated, metadata), and metadata (descriptions, data types, relationships, aggregation/formatting), and also provides a control panel and global sheet filters. Sheets are used to explore/visualize data and to build your data model.

Metadata (Sheet) — descriptive info about the sheet’s model: a global description (for AI), column names and descriptions, data types, links between sources, and rules for aggregation and formatting. It ensures consistency, easy data discovery, and correct analytics in Views/visualizations.

**How it relates to data modelling and standard terminology:**

* The main Data Source in the Sheet defines the level of detail (granularity) and primary keys — this aligns with the “fact/detail” concept in relational or BI modeling.
* Related data sources are joined using LEFT JOIN — the classic approach of “enriching facts with attributes” without changing the number of rows in the fact table.

**Example**

***See:** Column — Example*

**Used in:** [Data Modeling](/02_00_modeling) section.

***

## T

### Team

**In KAWA:** A managed group of users with shared roles, permissions, and ownership within a Workspace. Teams control access to data sources, sheets, views, and reports, and can be targeted by workflows and sharing policies.

**How it relates to data modelling and standard terminology:**

* According to standard access control practices, these are user groups in an RBAC (role-based access control) model.
* This is not directly part of data modeling. Teams use already modeled objects for sharing and security purposes.

**Used in:** [Sharing and permissions](/08_00_administration/08_01_permissions) section.

***

## V

### View

**In KAWA:** A saved representation of data—grid, chart, or pivot—configured with Fields, Filters, and formatting. Views govern how data is queried and displayed and act as reusable building blocks inside Reports and applications.

**How it relates to data modelling and standard terminology:**

* In standard BI terms, a View corresponds to the visualization layer (grid/chart/pivot) built on top of already modeled data from the Sheet.
* A View does not perform data modeling. It consumes the Sheet model and defines how data is queried and displayed.

**Example**

![Terminology](/files/jN4fr0dJfwpVpVrycMRK)

***See also:** Field — Example*

**Used in:** [Visualization](/04_00_visualization) section.

***

## W

### Workspace

**In KAWA:** The top-level environment that contains data sources, sheets, views, reports, automations, knowledge, and teams. A workspace defines governance (roles, policies), resources, and collaboration boundaries for a project or organization.

**How it relates to data modelling and standard terminology:**

* In standard terms, this is the workspace scope: the level of governance and access control (roles, policies, RLS/CLS, sharing rules, resources, and collaboration boundaries).
* It does not perform data modeling; instead, it defines the security, visibility, and collaboration framework for all platform objects.

**Used in:** [Sharing and permissions](/08_00_administration/08_01_permissions) section.

### Workflow

**In KAWA:** A Workflow is a saved automation built in the Workflows builder. It has a trigger (WHEN) and a set of steps/actions (THEN). A workflow can start on a schedule, manually, or after a data source is successfully refreshed. Steps usually run in sequence, but you can also branch the flow using Logic steps (for example, If/Else or Routing). Typical actions include transforming data, running Python tools, running AI prompts, exporting data, creating/using reports, and sending emails (including attachments). KAWA Reference Documentation

**How it relates to data modelling and standard terminology:** A workflow is an automation layer (similar to a pipeline or a “workflow” in tools like Zapier/Airflow). It uses existing KAWA objects (data sources, sheets, reports) and produces results (exports, emails, artifacts), but it does not define the data model itself.

**Used in:** [Workflows](/07_00_workflows) section.

***

## Key “vs” comparisons

### Column vs Field vs Indicator

* Indicator (Data Source level) - the source’s original columns; may be part of the primary key. After Create new sheet, these Indicators automatically become the Sheet’s initial Columns. They do not define an analytic role.
* Column (Sheet level) - built on top of Indicators or computed; used in queries and aggregations. The Sheet’s grain is set by the primary key of the main Data Source (not by the set of Columns).
* Field (View level) - contextual use of a Column inside a specific View (role: axis/series/group/value, plus aggregation). Affects display only; does not change the data schema.

Consequences of changes:

* Changes to a Field do not change the Column/Indicator.
* Changes to a Column apply to all Views that use it.
* Changes to the Data Source schema affect Indicators and, in turn, any derived Columns in Sheets.

### View vs Reports

* View — one saved visualization (grid/chart/pivot).
* Reports — a collection of views with shared filters and layout.

### Data Source vs Sheet

* Data Source — data imported or synchronized from underlying systems (files, databases, APIs, SaaS еtс).
* Sheet — the data model built on top of one or more data sources, where you define metrics for downstream use.

### Filter vs Control Panel

* Filter — a rule that restricts the dataset for a View/Sheet/Report; sets conditions and scope (local/global) and affects the query context without changing stored data.
* Control Panel — a configurable set of user controls that manages filters and variables across Views/Sheets/Reports; defines scope, default values, and propagation, and exposes variables reusable in formulas/metrics.


