# 2026.20 Public API / SDK Changes

5 min read

**Heads Up – New API Version Coming Soon**

We want to inform you that a new version of the Public API is currently in development. No action is required on your end at this time - we will provide full documentation and a clear adoption timeline ahead of any changes.

**What's changing?**

- Enforced role-based access control (RBAC)
- Stricter request validation

## Public API

### **New Endpoints**

**Analytics**  
**Kong Plugin Upgrade Required**  
The analytics endpoints are only available for clients who have upgraded to the latest Kong plugin ( >1.3.0). This is already the case for Single-Tenant (ST) and Multi-Tenant (MT) deployments. For Customer-Managed-Tenant (CMT) deployments this must be verified — and it will only work if the CMT avoids hair-pinning. CMTs that use hair-pinning will not be able to use these endpoints, because the endpoints are now protected by roles retrieved from Zitadel.

**Required Role**: `chat.feedback.read` or `chat.data.admin`

**Supported analytics types**: `ACTIVE_USER`, `CHAT_INTERACTION`, `CHAT_INTERACTION_DETAILED`, `REFERENCE_STAT`, `INGESTION_STAT`, `PRODUCT_METRICS`, `MODEL_USAGE`, `NPS`, `USER_CHAT_EXPORT`

**Create Order** — create and start an analytics report order.

**Body**

- `type` (AnalyticsType enum, required) — report type (see supported types above)
- `start_date` (string, required) — report start date in ISO 8601 format (e.g. `"2024-01-01"`)
- `end_date` (string, required) — report end date in ISO 8601 format (e.g. `"2024-12-31"`)
- `assistant_id` (string, optional) — filter the report to a specific assistant

**Example**

```
curl --location 'https://api.uat1.unique.app/public/analytics/orders' \
  --header 'x-user-id: <user-id>' \
  --header 'x-company-id: <company-id>' \
  --header 'Authorization: Bearer <api-key>' \
  --header 'x-app-id: <app-id>' \
  --header 'x-api-version: 2026-03-01' \
  --header 'Content-Type: application/json' \
  --data '{
    "type": "CHAT_INTERACTION",
    "start_date": "2024-01-01",
    "end_date": "2024-12-31"
  }'
```

**List Orders** **—** list all analytics orders for the company.

**Query**

- `skip` (number, optional, default `0`) — number of orders to skip
- `take` (number, optional, default `20`, max `100`) — number of orders to return

**Example**

```
curl --location 'https://api.uat1.unique.app/public/analytics/orders?skip=0&take=20' \
  --header 'x-user-id: <user-id>' \
  --header 'x-company-id: <company-id>' \
  --header 'Authorization: Bearer <api-key>' \
  --header 'x-app-id: <app-id>' \
  --header 'x-api-version: 2026-03-01'
```

**Get Order** — fetch a single order by ID; use this to poll for completion.

**Example**

```
curl --location 'https://api.uat1.unique.app/public/analytics/orders/<order-id>' \
  --header 'x-user-id: <user-id>' \
  --header 'x-company-id: <company-id>' \
  --header 'Authorization: Bearer <api-key>' \
  --header 'x-app-id: <app-id>' \
  --header 'x-api-version: 2026-03-01'
```

**Download Results** — download the completed analytics report as a CSV file. The order must be in `DONE` state. Returns `422` if the order is still processing.

**Example**

```
curl --location 'https://api.uat1.unique.app/public/analytics/orders/<order-id>/download' \
  --header 'x-user-id: <user-id>' \
  --header 'x-company-id: <company-id>' \
  --header 'Authorization: Bearer <api-key>' \
  --header 'x-app-id: <app-id>' \
  --header 'x-api-version: 2026-03-01' \
  --output analytics_report.csv
```

**Delete Order** — delete an analytics order by ID

**Example**

```
curl --location --request DELETE 'https://api.uat1.unique.app/public/analytics/orders/<order-id>' \
  --header 'x-user-id: <user-id>' \
  --header 'x-company-id: <company-id>' \
  --header 'Authorization: Bearer <api-key>' \
  --header 'x-app-id: <app-id>' \
  --header 'x-api-version: 2026-03-01'
```

**Briefings**

Briefings let integration partners attach a markdown narrative and a set of suggested prompts to an assistant. When the `FEATURE_FLAG_ENABLE_NARRATIVE_SUGGESTED_PROMPTS_UN_19396` flag is enabled, the chat web app renders the briefing on the chat welcome view of any assistant that has one attached.

**Required Role**: `chat.admin` (briefing `READ`, `WRITE`, `DELETE`, `MANAGE`). `chat.user` and space admins have `READ` only.

**Upsert Briefing** — create or update the briefing attached to an assistant. Sending a new `prompts` array fully replaces the previous one; the array order becomes the displayed order.

**Path**

- `assistantId` (string, required, max 255 chars) — identifier of the assistant the briefing should be attached to. Used as the briefing's external id.

**Body**

- `title` (string, optional, max 100 chars) — display title shown above the briefing in the chat UI. Omit to use the default copy ("Today's Briefing").
- `text` (string, required, max 4,000 chars) — markdown briefing body.
- `generatedAt` (string, required, ISO-8601) — timestamp when the briefing was generated by the integration partner.
- `prompts` (array, required, max 200 items) — full replacement set of suggested prompts. Each item has:
  - `title` (string, required, max 100 chars)
  - `body` (string, required, max 4,000 chars)

**Example**

```
curl --location --request DELETE 'https://api.uat1.unique.app/public/briefings/' \
  --header 'x-user-id: <user-id>' \
  --header 'x-company-id: <company-id>' \
  --header 'Authorization: Bearer <api-key>' \
  --header 'x-app-id: <app-id>' \
  --header 'x-api-version: 2026-03-01'
```

**Get Briefing** — fetch the briefing currently attached to an assistant. Returns `404` if no briefing is attached.

**Path**

- `assistantId` (string, required) — identifier of the assistant whose briefing should be returned.

**Example**

```
curl --location 'https://api.uat1.unique.app/public/briefings/' \
  --header 'x-user-id: <user-id>' \
  --header 'x-company-id: <company-id>' \
  --header 'Authorization: Bearer <api-key>' \
  --header 'x-app-id: <app-id>' \
  --header 'x-api-version: 2026-03-01'
```

**Detach Briefing** — detach the briefing from the assistant. The underlying briefing record is preserved when other assistants in the same tenant still reference it; the response confirms the per-assistant detach. Returns `404` if no briefing is attached.

**Path**

- `assistantId` (string, required) — identifier of the assistant whose briefing should be detached.

**Example**

## SDK

### New Functions

**unique_sdk.AnalyticsOrder.create** — create and start an analytics order (see supported types above in the beginning of the Public API section)

**Example**

```
order = unique_sdk.AnalyticsOrder.create(
    user_id=user_id,
    company_id=company_id,
    type="CHAT_INTERACTION",
    startDate="2024-01-01",
    endDate="2024-12-31",
)
```

**unique_sdk.AnalyticsOrder.list** — list analytics orders for the company, sorted by creation date descending

**Example**

```
orders = unique_sdk.AnalyticsOrder.list(
    user_id=user_id,
    company_id=company_id,
    skip=0,
    take=20,
)
```

**unique_sdk.AnalyticsOrder.retrieve** — fetch a single order by ID; use this to poll for status changes.

**Example**

```
order = unique_sdk.AnalyticsOrder.retrieve(
    user_id=user_id,
    company_id=company_id,
    order_id="ord_abc123",
)
```

**unique_sdk.AnalyticsOrder.delete** — delete an analytics order by ID.

**Example**

```
deleted = unique_sdk.AnalyticsOrder.delete(
    user_id=user_id,
    company_id=company_id,
    order_id="ord_abc123",
)
```

**unique_sdk.AnalyticsOrder.download** — download the CSV content of a completed order; order must be in `DONE` state.

**Example**

```
csv_content = unique_sdk.AnalyticsOrder.download(
    user_id=user_id,
    company_id=company_id,
    order_id="ord_abc123",
)
```

**Analytics Order Run Utility** — `run_analytics_order` async helper handles the full create → poll → download workflow in one call

**Example**

```
import asyncio
from unique_sdk.utils.analytics_order_run import run_analytics_order

async def main():
    result = await run_analytics_order(
        user_id=user_id,
        company_id=company_id,
        type="CHAT_INTERACTION",
        start_date="2024-01-01",
        end_date="2024-12-31",
        save_csv_to="./report.csv",
    )
    print(result["order"]["state"])   # "DONE"
    print(result.get("csv_path"))     # "./report.csv"

asyncio.run(main())
```
