Skip to content

Overview

Files, folders, sharing and synchronisation for the Optiwork suite.

Optiwork Drive is the file service of the Optiwork suite: company folders, files and folders (nodes), content upload and download, versioning, locks, approvals, sharing and audit — plus a change journal that lets an integration keep an external copy of the drive up to date.

Drive shares its host and its authentication key with the rest of the suite. The account layer — users, groups, notifications, multi-factor authentication — is documented in the Optiwork reference.

Base URL

All endpoints are served under the fixed Optiwork host: https://work.optidata.cloud/api.

Authentication

Every endpoint (except GET /ping) requires a Bearer JWT passport. Your personal passport is available in Optiwork at https://work.optidata.cloud/user/settings/security (My account > Profile > Security), where it is shown as the authentication key. Send it on every request:

Authorization: Bearer opk_xxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json

Passports are short-lived; when a request returns 401, get a fresh authentication key from the same settings page and retry.

Drives and nodes

Files and folders are nodes. A node is either a folder (type: 1) or a file (type: 2), and every node lives in a drive — the tenant’s file space. Most installations have a single drive, whose numeric id you can read from GET /drives/config.json in the Optiwork reference; it is 1 unless your tenant was configured otherwise.

Nodes form a tree. A node with parent_id: null is a company folder: the top-level shared spaces of the tenant. GET /drives/{drive}/nodes/company/folders lists the company folders the caller can reach, and everything below them is navigated with GET /drives/{drive}/nodes/{node}.

Permissions

Access to a node is governed by the share permissions granted on it or on one of its ancestors. A permission carries an actions level: none < view < edit < manage. Operations note the level they require; requests by users without it return 403. share and owner are legacy aliases of manage and may still appear on older permissions.

Concurrent edits of a file

The four operations that replace a file’s content — POST .../content/file, POST .../content/base64, and the prepare and finish of a chunked replacement — accept an optional base_content_hash: the hash_sha256 of the content the caller edited, exactly as the server returned it on node.content.hash_sha256. With it, the write behaves like an If-Match:

  • The write is accepted only when base_content_hash is equal to the hash_sha256 stored on the node’s current content. That includes the SHA-256 of empty content when the current version is an empty file.
  • When the node’s current content has no stored hash — the file has no content yet, or its current version has a null or empty hash — no base can match, and any base_content_hash is refused.
  • A refused write answers 409 Conflict and writes nothing: no new content version, no history entry, no journal entry. Read the node again and keep your version as a copy instead of overwriting.
  • Two base-carrying writes, or chunk finishes, of the same file run one at a time, and the second is checked against the hash the first one left. It is refused when that hash differs from its base; a first write that stored the same bytes leaves the hash unchanged, so the second still passes. The check compares content hashes: it does not detect every intervening version.
  • A write that carries a base, and every chunk finish, waits for another such write of the same file to finish, 30 seconds by default. Past that, or when the write could not keep its turn, it answers 423 Locked with a Retry-After header (5 seconds by default; read the header rather than assume the value) and writes nothing. Its base was not checked, so retry the same request after the delay; do not keep a copy as for a 409.
  • A write whose bytes equal the current content creates no new version: the answer’s node.content.base_content_hash is the one already stored on that version, not the one the request sent.
  • Without base_content_hash the write is unconditional, as before. For a chunked replacement that means neither step sent one: a base sent at prepare is still checked at finish.

The accepted base is stored on the new version as node.content.base_content_hash, returned on the node and on every journal row of GET /drives/{drive}/wal/nodes/since/{nodeWalEntryId}. A sync client compares it with the hash it last committed: equal means the other side edited its version (a sequential edit); different, or null, means it cannot tell, so it keeps a conflict copy.

Conventions

  • Identifiers in paths ({drive}, {node}, …) are the numeric id of the resource. Nested resources are scope-checked: referencing a node that does not belong to the given drive returns 404.
  • Timestamps are ISO-8601 UTC strings (e.g. 2026-03-10T17:09:05.000000Z).
  • Validation failures return 422 with a per-field errors map.
  • List endpoints are paginated with Laravel’s paginator envelope. Endpoints that use the simple paginator omit total and last_page.
  • Requests are rate limited per authenticated user; exceeding the limit returns 429.

The authentication key (JWT passport) from My account > Profile > Security at work.optidata.cloud/user/settings/security.

Security scheme type: http

Bearer format: JWT