# Dataroom

URL: https://docs.hanzo.ai/docs/openapi/dataroom

> A secure document room you share by link and watch page by page.

Also for this capability: **API** · [CLI](https://docs.hanzo.ai/docs/cli/dataroom) · [MCP](https://docs.hanzo.ai/docs/mcp-tools/dataroom) · [SDKs](https://docs.hanzo.ai/docs/sdks)

A secure document room you share by link and watch page by page.

> [All API references →](https://docs.hanzo.ai/docs/openapi) · [Get started →](https://docs.hanzo.ai/docs/guides)

|                |                                        |
| -------------- | -------------------------------------- |
| **Base URL**   | `https://api.hanzo.ai`                 |
| **Operations** | 26                                     |
| **Auth**       | `Authorization: Bearer $HANZO_API_KEY` |

## Specification

> **HIP-1121 · Dataroom — Documents Shared by Link** — Draft · [read the specification →](https://github.com/hanzoai/hips/blob/7ed737180339cd05b5f2935eafe7f7b83d2449a2/HIPs/hip-1121-dataroom-shared-documents.md)

`/v1/dataroom` is a secure document room: upload documents, group them into
rooms, share them by access-controlled link, and watch who read what, page by
page. It is implemented in `hanzoai/cloud` at `apps/dataroom`, which runs the
ported Papermark business logic in-process rather than as a separate service.
This HIP states where the bytes live, where the rows live, and how an
unauthenticated viewer is routed to exactly one tenant.

### Motivation

The upstream product was a Next.js + Prisma + Postgres deployment — a pod, a
database and a framework for what is, to the cloud, one subsystem. The fold
(`apps/dataroom/dataroom.go:4-9`) retires that deployment: cloud serves the
surface itself, on the same per-tenant storage every folded application uses,
and the standalone pod holds nothing to migrate.

### Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted
as in RFC 2119.

#### §1 Two stores, one owner

Rows — documents, rooms, links, viewers, page-view events — live in one SQLite
file per tenant, opened by the shared goja host with one transaction per
request (`apps/dataroom/dataroom.go:12-20`). Document bytes never touch the
bundle or local disk: they go through the object-storage seam (`deps.VFS`)
under an org-scoped opaque key, and the tenant DB persists only the key
(`apps/dataroom/dataroom.go:30-35`). Beside the tenant files sits one
system-namespace index, `link_index` (`apps/dataroom/index.go:26`) — the link
id → org routing table, the single deliberately cross-tenant piece.

#### §2 The addresses

Every route is under `/v1/dataroom` (`manifest/apps.go:336`). Ten routes are
typed operations. Seven stay raw, each for a reason in the wire
(`apps/dataroom/dataroom.go:159-163`): the upload takes the file itself as the
raw body, the two `/file` routes answer a byte stream, and the four
`/view/{linkId}` routes carry no validated org a typed op could read — they are
the visitor's surface. Each raw route declares its prose beside the wire fact
(`apps/dataroom/dataroom.go:212-236`).

#### §3 Tenancy

Admin routes require a validated principal and resolve the org from it
(`apps/dataroom/dataroom.go:344-346`, HIP-0026); refusal is `principal.Refused`,
never the unscoped store. Viewer routes carry no principal: the link id is
resolved through the link index to the owning org before any per-tenant store
opens, and a link with a password checks it through a bcrypt host function.
Isolation is therefore a host property — the bundle is handed a database already
pinned to one tenant and cannot name another.

#### §4 Money, events, telemetry

dataroom is free, in those words (`plugin/dataroom/main.go:21`,
`cloud.Free`; not in `spend.go:275`). It publishes no events on the bus —
page views are rows in the tenant DB read back through the analytics routes,
not bus events — and it emits nothing to observability beyond the request span
every route gets.

#### §5 Stage

dataroom is `beta`: a vertical application, not the agentic-OS core. The
manifest row declares it (`manifest/apps.go:336`, `Stage: Beta`), so the
capability is reached by flag (HIP-0139 §8).

#### §6 Upstream

dataroom embeds `github.com/hanzoai/dataroom` v1.1.7 — the ESM-free port of the
Papermark API handlers, pinned and checksummed rather than copied in. The
upstream is Papermark, AGPL-3.0 outside its `ee/` directories (the module's
LICENSE carries the split), and what survives in HEAD is the domain logic as a
goja bundle: documents, rooms, links, viewers, analytics. The Go leaf adds only
the tenant schema, the object-storage seam, the bcrypt host function and the
link index; zero domain logic lives in Go (`apps/dataroom/dataroom.go:17-20`).

### Rationale

The alternative to the link index is scanning tenant files for a link id, which
turns every anonymous view into a walk of every tenant's store — slow, and a
cross-tenant read performed on every request instead of never. One small
routing table that maps id → org keeps the cross-tenant surface to a single
lookup whose answer is an org name, not data.

### Security Considerations

The viewer path is an unauthenticated door into tenant data by design, so the
whole exposure concentrates in the link: a guessable id is a readable dataroom.
Link ids are crypto-random, a passworded link verifies through bcrypt before
any page is served, and the index answers only the owning org — never rows. The
other exposure is the bytes: they are keyed by org-scoped opaque keys on the
object store, so a copy of one tenant's SQLite file contains no document
content, only keys the store will not honour for another caller.

## Four surfaces

| Surface  | Reaches this capability as                       | Coverage                                                                   |
| -------- | ------------------------------------------------ | -------------------------------------------------------------------------- |
| **REST** | `dataroom` at its own prefix                     | 26 operations                                                              |
| **CLI**  | `hanzo dataroom …`                               | 26 of 26                                                                   |
| **SDK**  | `DataroomApi` in every published client          | 26 methods                                                                 |
| **MCP**  | tool `dataroom` on `https://api.hanzo.ai/v1/mcp` | 19 operations, 2 under the document's own id — ask `describe` for the rest |

## Quickstart

```bash
export HANZO_API_KEY=sk-...   # console.hanzo.ai → API keys
```

Then the first call — a read that needs nothing but the key. `GET /v1/dataroom/links`, operation `get_dataroom_links`:

**CLI**

```bash
hanzo dataroom links get
```

**SDK**

**.ts**

```ts
import { Configuration, DataroomApi } from 'hanzoai';

const api = new DataroomApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getDataroomLinks();
```

**.py**

```python
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import DataroomApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = DataroomApi(client).get_dataroom_links()
```

**.go**

```go
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.DataroomAPI.GetDataroomLinks(context.Background()).Execute()
if err != nil {
	return err
}
```

**.rs**

```rust
use hanzo_client::apis::{configuration::Configuration, dataroom_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = dataroom_api::get_dataroom_links(&cfg, Default::default()).await?;
```

**.java**

```java
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.DataroomApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new DataroomApi(client).getDataroomLinks();
```

**HTTP**

```bash
curl https://api.hanzo.ai/v1/dataroom/links \
  -H "Authorization: Bearer $HANZO_API_KEY"
```

**MCP**

MCP reaches **dataroom** through the `dataroom` tool, which names its 19 operations with its own verbs — this one among them, under a name only MCP declares. `describe` explains any of them:

```bash
curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "describe",
         "arguments": {
           "op": "get_dataroom_analytic_dataroom"
         }
       }
     }'
```

Answers `200` with `object` — ok.

## Endpoints

| Endpoint                                                                                                                                                           | What it does                                                                                                                                                                                                                                                     |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`GET /v1/dataroom/analytics/dataroom/{dataroomId}`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-analytics-dataroom-by-dataroomid)                    | Rolls up every share link pointing at one data room: session and page-view totals for the room, plus the per-page breakdown for each link beneath it.                                                                                                            |
| [`GET /v1/dataroom/analytics/link/{linkId}`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-analytics-link-by-linkid)                                    | Reports how one share link was actually read: total viewing sessions, total page views, and per page the view count, the summed dwell measure and its average.                                                                                                   |
| [`POST /v1/dataroom/datarooms/{id}/documents`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-datarooms-by-id-documents)                                | Puts an already-uploaded document into one of the caller org's data rooms and answers with the new membership id.                                                                                                                                                |
| [`GET /v1/dataroom/datarooms/{id}`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-datarooms-by-id)                                                      | Reads one of the caller org's data rooms together with every document in it, each carrying its membership id and order index.                                                                                                                                    |
| [`GET /v1/dataroom/datarooms`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-datarooms)                                                                 | Returns every data room in the caller org's own store, newest first, with its short public id, name, description and timestamps.                                                                                                                                 |
| [`POST /v1/dataroom/datarooms`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-datarooms)                                                               | Opens a new data room for the caller org and answers with it, including the short public id it is addressed by.                                                                                                                                                  |
| [`GET /v1/dataroom/documents/{id}/file`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-documents-by-id-file)                                            | Download a document's bytes as its owner                                                                                                                                                                                                                         |
| [`GET /v1/dataroom/documents/{id}`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-documents-by-id)                                                      | Reads one of the caller org's documents — its name, opaque storage key, content type, page count, size and timestamps.                                                                                                                                           |
| [`GET /v1/dataroom/documents`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-documents)                                                                 | Returns every document in the caller org's own store, newest first — name, opaque storage key, content type, page count, size and timestamps.                                                                                                                    |
| [`POST /v1/dataroom/documents`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-documents)                                                               | Upload a document's bytes and record it                                                                                                                                                                                                                          |
| [`GET /v1/dataroom/health`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-health)                                                                       | Reports that the data room subsystem is up.                                                                                                                                                                                                                      |
| [`GET /v1/dataroom/links`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-links)                                                                         | Returns every live share link in the caller org's own store, newest first, with the controls a visitor will meet: whether an address is required, whether a password is set, the allow and deny lists, whether download is permitted, and when the link expires. |
| [`POST /v1/dataroom/links`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-links)                                                                       | Grants access: it mints a public share link over one data room (`dataroomId`) or one document (`documentId`) — one of the two is required — and answers with the link, whose `id` is the token a visitor opens it with.                                          |
| [`PATCH /v1/dataroom/trust/artifacts/{id}`](https://docs.hanzo.ai/docs/openapi/dataroom/patch-dataroom-trust-artifacts-by-id)                                      | Changes an item on the caller org's trust centre — replace its file with a newer edition, move it between public and gated, rewrite what it says, or retire it — and answers with the item as it now stands.                                                     |
| [`POST /v1/dataroom/trust/artifacts`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-trust-artifacts)                                                   | Puts an item on the caller org's trust centre and answers with it.                                                                                                                                                                                               |
| [`GET /v1/dataroom/trust/center/{slug}/file/{item}`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-trust-center-by-slug-file-by-item)                   | Read a public trust-centre item's bytes                                                                                                                                                                                                                          |
| [`POST /v1/dataroom/trust/center/{slug}/requests`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-trust-center-by-slug-requests)                        | Records a request to read what an independent auditor signed, and answers with its id.                                                                                                                                                                           |
| [`GET /v1/dataroom/trust/center/{slug}`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-trust-center-by-slug)                                            | Answers an org's public trust centre: its name, the text a party must accept to ask for a document, and every item it publishes.                                                                                                                                 |
| [`POST /v1/dataroom/trust/requests/{id}/grant`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-trust-requests-by-id-grant)                              | Answers a request by opening access: it mints a share link over what was asked for, addressed to the address that asked and closing at expiry, records the decision, and mails the asker.                                                                        |
| [`POST /v1/dataroom/trust/requests/{id}/refuse`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-trust-requests-by-id-refuse)                            | Answers a request by declining it, recording who declined and why.                                                                                                                                                                                               |
| [`GET /v1/dataroom/trust`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-trust)                                                                         | Answers the caller org's OWN trust centre: its settings, every item it holds in both tiers, the requests waiting on it, and the grants it has made.                                                                                                              |
| [`PUT /v1/dataroom/trust`](https://docs.hanzo.ai/docs/openapi/dataroom/put-dataroom-trust)                                                                         | Opens, publishes or withdraws the caller org's trust centre and answers with the centre as it now stands.                                                                                                                                                        |
| [`POST /v1/dataroom/view/{linkId}/authenticate`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-view-by-linkid-authenticate)                            | Pass a share link's gates and open a viewing session                                                                                                                                                                                                             |
| [`GET /v1/dataroom/view/{linkId}/document/{documentId}/file`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-view-by-linkid-document-by-documentid-file) | Read a document's bytes as an authorised link visitor                                                                                                                                                                                                            |
| [`POST /v1/dataroom/view/{linkId}/pageview`](https://docs.hanzo.ai/docs/openapi/dataroom/post-dataroom-view-by-linkid-pageview)                                    | Record one page-view against an open viewing session                                                                                                                                                                                                             |
| [`GET /v1/dataroom/view/{linkId}`](https://docs.hanzo.ai/docs/openapi/dataroom/get-dataroom-view-by-linkid)                                                        | What a share link's visitor sees before authenticating                                                                                                                                                                                                           |

---

[All Hanzo APIs](https://docs.hanzo.ai/docs/openapi) · [Interactive reference](https://docs.hanzo.ai/docs/openapi/reference)
