> ## Documentation Index
> Fetch the complete documentation index at: https://wordsmithai.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Power BI

> Load Wordsmith reports, usage and tasks into Power BI with the OData feed.

> [!NOTE]
>
> The Power BI feed is enabled per organisation. To turn it on, contact your Wordsmith account manager.

Wordsmith publishes an **OData v4 feed** that Power BI reads with **Get Data → OData feed**. It gives you three kinds of table:

* **Reports**: one row per document, with one column per report column, typed as numbers, dates, yes/no or text.
* **Usage and value**: sessions, interactions, active users, hours saved and cost saved, per day or per user, team, agent or skill.
* **Tasks**: one row per task, and tasks created and completed per day with their average turnaround.

The feed is read-only. It returns what the API key's user can see in Wordsmith. A table appears only when the key can read it and its feature is on for your organisation. A key limited to some scopes sees only the tables for those scopes. Of the features, `Tasks` needs Tasks, and `TaskDaily` and `TaskDailyCoverage` also need the Tasks view of **Usage and value**.

## Connect

1. In Wordsmith, create an [API key](https://wordsmithai.mintlify.site/authentication.md) for the user whose view Power BI should load. A key limited to some scopes needs `reports:read`, `usage:read` and `tasks:read` for the tables it loads.
2. In Power BI Desktop, choose **Get Data → OData feed**.
3. Enter the feed URL for your region:

   ```
   https://api.wordsmith.ai/api/v1/odata
   ```

   In the US region, use `https://api.us.wordsmith.ai/api/v1/odata`.
4. When Power BI asks how to connect, choose **Basic**. Enter any user name, and the API key as the password.
5. Pick the tables in the navigator, then **Load**.

> [!NOTE]
>
> Choose **Basic** yourself. Power BI may suggest **Anonymous**, and the feed's sign-in prompt does not reach Power BI, so it will not switch to Basic on its own. An anonymous request is refused unless the key is in the URL (see [If the feed key is in the URL](#if-the-feed-key-is-in-the-url)).

Scheduled refresh in the Power BI service uses the same Basic credential. You set it once under the semantic model's **Data source credentials**.

## Tables

| Table | One row per | Key columns |
| - | - | - |
| `Reports` | Report you can read | `Name`, `RowCount`, `Status`, `FeedUrl` |
| `UsageDaily` | Day | `Sessions`, `Interactions`, `ActiveUsers`, `HoursSaved`, `CostSavedExternal` |
| `UsageByUser`, `UsageByTeam`, `UsageByAgent`, `UsageBySkill` | User, team, agent or skill | The same figures for the period, plus `CostSavedInHouse` |
| `Tasks` | Task | `Status`, `Priority`, `AssigneeName`, `SourceChannel`, `Tags`, `CompleteBy`, `CreatedAt` |
| `TaskDaily` | Day | `Created`, `Completed`, `AverageTurnaroundHours` |
| `TaskDailyCoverage` | Period (one row) | `PeriodStart`, `PeriodEnd`, `UntrackedTasks`, `LateHistoryTasks` |

`TaskDaily` counts completions from each task's recorded status history. A task created before Wordsmith recorded that history has none, so its completion is missing from `Completed` and `AverageTurnaroundHours`. `TaskDailyCoverage` gives the period's `UntrackedTasks` (tasks with no recorded history) and `LateHistoryTasks` (tasks whose history starts after the day they were created). When either is above zero, completions are undercounted. Load it with the same period options as `TaskDaily`.

Hours and cost saved are estimates priced with your organisation's rates. Organisation figures include your organisation's calibration, as on the **Usage and value** dashboard.

### Period and scope

The usage tables and `TaskDaily` cover the **last 30 days** unless the URL says otherwise. Add options to the feed URL to change it:

| Option | Values |
| - | - |
| `period` | `this_month`, `last_month`, `last_7_days`, `last_30_days`, `last_quarter`, `lifetime`, `custom` |
| `start`, `end` | ISO dates, for example `2026-01-01`. Required with `period=custom` |
| `scope` | `company` (the default) or `me`. Only `UsageDaily`, `TaskDaily` and `TaskDailyCoverage` take it |

The options on the feed URL carry into each table that takes them. For example, to load the whole of 2026: `https://api.wordsmith.ai/api/v1/odata?period=custom&start=2026-01-01&end=2026-12-31`.

Organisation figures need permission to view company analytics. Per-user rows need your organisation to show named users on the dashboard.

## A report as a table

Each report has its own feed. Copy it from the `FeedUrl` column of the `Reports` table, or build it from the report ID:

```
https://api.wordsmith.ai/api/v1/odata/reports/{report_id}
```

Connect to it the same way. It has three tables:

| Table | One row per | Columns |
| - | - | - |
| `Rows` | Document | `RowId`, `DocumentName`, `FileId`, `PendingCells`, `FailedCells`, `UntypedCells`, then one column per report column |
| `Cells` | Answer | `RowId`, `ColumnId`, `ColumnName`, `Value`, `Status`, `Reviewed`, `UpdatedAt` |
| `Columns` | Report column | `Id`, `Name`, `PropertyName`, `UnitPropertyName`, `Type`, `Prompt`, `Options` |

In `Rows`, a column's name is the report column's name, with spaces and punctuation replaced by `_`. A name that is also a filter keyword, such as `null` or `not`, gets `_2` added. A number column is followed by a `_unit` column, such as `Rent_unit`, holding the answer's unit or currency. Check it before you add amounts together. The `Columns` table maps each report column to its names in `Rows`.

A cell in `Rows` is empty when Wordsmith has not answered it yet, could not answer it, or gave an answer that is not the column's type. Each row counts these:

* `PendingCells`: cells still being computed.
* `FailedCells`: cells that failed.
* `UntypedCells`: answers that do not fit the column's type, such as "not stated" in a date column.

The `Cells` table keeps every answer as text, with its status.

## Filters and limits

Power BI sends filters to the feed as `$filter`, and the feed applies them. A comparison with an empty value follows OData: a row is kept only when the whole filter is true, so `not contains(Title,'x')` leaves out rows with no `Title`. A filter that compares values of different types, such as a number column with text, is refused with `400`. It supports comparisons (`eq`, `ne`, `gt`, `ge`, `lt`, `le`), `and`, `or`, `not`, and the functions `contains`, `startswith`, `endswith`, `tolower`, `toupper`, `trim`, `length`, `year`, `month`, `day` and `date`.

The feed refuses with `501` what it cannot apply, rather than returning rows Power BI would trust as filtered:

* Sorting (`$orderby`) and counting (`$count`). Sort and count in Power BI after the table loads.
* Other `$filter` functions and arithmetic operators. Apply that filter in Power BI after the table loads.

`Tasks` pages in the same order as the app's task list. A task whose priority or position changes while Power BI reads the table can be read twice or missed until the next refresh.

Each request counts towards your organisation's [rate limit](https://wordsmithai.mintlify.site/rate-limits.md). Power BI reads a table in pages, so a large report takes one request per page.

## If the feed key is in the URL

If you cannot use Basic authentication, add the key to the URL as `?api_key=` and connect as **Anonymous**. The key then appears in the URL and in your Power BI file, so prefer Basic.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.