Hanzo

Dataroom

Package dataroom is a secure document room you share by link and watch page by page.

Package dataroom is a secure document room you share by link and watch page by page.

Base URLhttps://api.hanzo.ai
Operations17
AuthAuthorization: Bearer $HANZO_API_KEY

dataroom

GET /v1/dataroom/analytics/dataroom/{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.

A room id outside the caller's own tenant store is not found. Only links that NAME the room are counted — a link created over a single document contributes nothing here, even when that document also sits in the room.

ParameterInTypeRequiredDescription
dataroomIdpathstringyesDataroomID is the room to report on.

GET /v1/dataroom/analytics/link/{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.

The link is resolved in the caller's OWN tenant store, so another org's link id is not found — knowing a link id is enough to OPEN the room it shares, and never enough to read who has been reading it.

ParameterInTypeRequiredDescription
linkIdpathstringyesLinkID is the link to report on.

POST /v1/dataroom/datarooms/{id}/documents

Puts an already-uploaded document into one of the caller org's data rooms and answers with the new membership id.

It ATTACHES, it never uploads: the bytes must already be stored, so the usual order is upload the document, then add it to the room. Both the room and the document must exist in the caller's own store — either missing is not found — and a document already in the room is refused as a conflict rather than duplicated.

ParameterInTypeRequiredDescription
idpathstringyesID is the room to add to. It is the path segment: the URL is the addressing authority, and the org it is resolved in…

Request bodyapplication/json (required)

FieldTypeRequiredDescription
documentIdDocumentId is the document to attach. Required, and it must already exist in the caller's own store — this route…
idstringID is the room to add to. It is the path segment: the URL is the addressing authority, and the org it is resolved in…
orderIndexOrderIndex fixes the document's place in the viewer's list.

GET /v1/dataroom/datarooms/{id}

Reads one of the caller org's data rooms together with every document in it, each carrying its membership id and order index.

The documents are sorted by that index with unordered ones last and creation time breaking ties — the SAME order a link's visitor sees, so this is what the room looks like from the outside. A room id outside the caller's own tenant store is not found.

ParameterInTypeRequiredDescription
idpathstringyesID is the room to read. It is the path segment: the URL is the addressing authority, and the org it is resolved in…

GET /v1/dataroom/datarooms

Returns every data room in the caller org's own store, newest first, with its short public id, name, description and timestamps.

Documents are not included — a room's contents come from reading the single room.

POST /v1/dataroom/datarooms

Opens a new data room for the caller org and answers with it, including the short public id it is addressed by.

name is required; without it the call is refused and the tenant store is untouched, because a dispatch answering 4xx rolls its transaction back. A new room holds no documents and is reachable by NOBODY until a share link is created over it — opening a room and granting access are two separate acts, so a room cannot leak by existing.

Request bodyapplication/json (required)

FieldTypeRequiredDescription
descriptionDescription is the room's description. Optional; any JSON scalar is accepted and stored as its text, and omitting it…
nameName is the room's display name.

GET /v1/dataroom/documents/{id}/file

Download a document's bytes as its owner

Streams the stored file back under its recorded content type, falling back to application/octet-stream when none was recorded.

Requires a validated principal; 403 without one, and the document is resolved in the caller's own tenant store, so another org's id is a 404. This is the OWNER's path and applies no link gate at all — the per-link password, email and download controls live on the viewer surface, not here. Bytes that cannot be fetched from object storage are 502, never a truncated or empty file.

ParameterInTypeRequiredDescription
idpathstringyes

GET /v1/dataroom/documents/{id}

Reads one of the caller org's documents — its name, opaque storage key, content type, page count, size and timestamps.

The lookup runs in the caller's own tenant store, so an id belonging to another org is not found exactly like one that never existed. Metadata only: the bytes are a separate read.

ParameterInTypeRequiredDescription
idpathstringyesID is the document to read. It is the path segment: the URL is the addressing authority, and the org it is resolved in…

GET /v1/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.

Tenant isolation is the per-org store itself: there is one SQLite file per org and the org is never a parameter, so no input the caller controls can address another tenant's documents. Metadata only — the bytes come from the file route.

POST /v1/dataroom/documents

Upload a document's bytes and record it

Takes the file ITSELF as the raw request body — not a JSON envelope, not multipart — stores it on the object-storage seam, and records the metadata row, answering with the new document. ?name= names it (default "document"), the request's Content-Type becomes the recorded mime type, and ?numPages= is optional.

Requires a validated principal; 403 without one. An empty body is 400 and anything over 64 MiB is 413 — a data room holds decks and PDFs, not a media library.

The storage key is 128 random bits under the tenant's own key prefix, minted before the bytes are written: if the system's randomness is unavailable the upload fails 500 rather than fall back to a predictable key that could overwrite another document's bytes. A storage write that fails is 502 and no metadata row is recorded, so a document never exists without its file.

GET /v1/dataroom/health

Liveness of the dataroom subsystem

Answers {service, status} unconditionally — no principal, no tenant. It is registered BEFORE the bundle, the link index and the object-storage seam are wired, so it keeps answering when any of those fail and the subsystem degrades to health-only. That is the point, and the limit: a 200 here says the process is alive, never that a data room can be read or written.

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.

Archived links are omitted entirely. A link reports only THAT a password is set — the stored form is a bcrypt hash and no route returns it.

POST /v1/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.

This is how a party is let in. The controls are declared HERE and enforced on the viewer surface: password is hashed with bcrypt before storage and is never readable back, emailProtected (on by default) makes a visitor state an address, allowList/denyList narrow which addresses pass, allowDownload (off by default) governs downloads, and expiresAt closes the link. The target room or document must exist in the caller's own store or it is not found.

Creating a link also writes dataroom's ONE cross-tenant row: the link id to owning org mapping an anonymous visitor is routed through. That write is part of the operation — if it fails the call is 500 — so a link that no visitor could open is never handed back as usable.

The address a visitor later states is recorded UNVERIFIED, so a link gated only by email is openable by anyone the link reaches. Use a password for a link that must not travel.

Request bodyapplication/json (required)

FieldTypeRequiredDescription
allowDownloadAllowDownload permits downloading rather than viewing only.
allowListany[]AllowList narrows which addresses pass the email gate.
dataroomIdDataroomId is the room to share. One of dataroomId or documentId is required, and the target must exist in the caller's…
denyListany[]DenyList rejects addresses, in the same three forms as the allow list. Optional.
documentIdDocumentId shares a SINGLE document instead of a room.
emailProtectedEmailProtected makes a visitor state an address before entering.
expiresAtExpiresAt closes the link, in unix milliseconds.
nameName labels the link.
passwordPassword gates the link.

POST /v1/dataroom/view/{linkId}/authenticate

Pass a share link's gates and open a viewing session

Clears the link's access controls and answers with the viewing session — a viewId, whether download is permitted, and the documents behind the link — which every later viewer call is authorised by.

No principal: the visitor is whoever holds the link id, and the org is resolved from it. The gates run in a fixed order and each is a flat refusal, never a hint. An archived or unknown link is 404 and an expired one 403. A missing address on an email-protected link is 401. An address on the deny list is 403, checked BEFORE the allow list so deny always wins. An address the allow list does not admit is 403 — an EMPTY allow list admits everyone, so a link with no list enforces the email gate alone. A wrong or absent password is 401, decided against the stored bcrypt hash.

The address is taken as stated and recorded UNVERIFIED: it names a viewer for analytics and repeat visits from it reuse one viewer record, but it proves nothing about who is on the other end. A link gated only by email is openable by anyone the link reaches.

ParameterInTypeRequiredDescription
linkIdpathstringyes

GET /v1/dataroom/view/{linkId}/document/{documentId}/file

Read a document's bytes as an authorised link visitor

Streams a document's bytes under its recorded content type to a visitor holding an open viewing session.

No principal: ?viewId= from the authenticate step is the authorisation and must belong to this link, or the call is 403 — holding the link id alone gets no bytes. The document must be reachable THROUGH this link (a member of the room the link opens, or the single document the link names), so a visitor cannot walk to an unrelated document by guessing an id; anything else is a 404, as is an unknown or archived link. Bytes that cannot be fetched from object storage are 502.

?download=1 additionally requires the link's allowDownload and is 403 when the owner did not permit it. Read that flag precisely: it gates the DOWNLOAD intent, not access to the bytes — without the parameter an authorised visitor is served the file for in-place viewing whether or not downloads are allowed.

ParameterInTypeRequiredDescription
linkIdpathstringyes
documentIdpathstringyes

POST /v1/dataroom/view/{linkId}/pageview

Record one page-view against an open viewing session

Appends a single per-page analytics event — {viewId, pageNumber, documentId, versionNumber, duration} — and answers with its id. These events are what the owner's analytics count.

No principal: the viewId from the authenticate step IS the authorisation, and it must belong to THIS link or the call is 404, so a session opened on one link cannot write events onto another. pageNumber is required (400 without it); documentId falls back to the document the session was opened on, and duration is the caller's own dwell measure, summed per page by analytics.

Events are additive: the same page reported twice is two views, which is the metric's whole point.

ParameterInTypeRequiredDescription
linkIdpathstringyes

GET /v1/dataroom/view/{linkId}

What a share link's visitor sees before authenticating

Answers the pre-auth face of a link to anyone holding its id: name and type, which gates apply (whether an address is required, whether a password is set), whether download is permitted, whether it has expired, and the name and description of the room behind it — or, for a single-document link, that document's name and page count.

No principal is involved: the owning org is resolved from the link id through dataroom's one cross-tenant routing table, and an unknown or archived link is a 404.

It is metadata only — a room's document list and every file stay behind the authenticate step. An expired link is REPORTED as expired here rather than refused, so a visitor learns why the next step will fail; nothing about the password beyond its existence is disclosed.

ParameterInTypeRequiredDescription
linkIdpathstringyes

Dataroom guide · All Hanzo APIs · Interactive reference

How is this guide?

On this page