Skip to content

Ask AI

Ask anything about Destesi — setup, products, APIs.

Powered by Claude. Answers may be wrong — always verify against the docs.

Drive reference

The Drive API is served at api.drive.destesi.io and is fully workspace-scoped. Every route is under /v1.

Send one of the following with each request:

  • API keyAuthorization: Bearer drive_<token>. Mint one in the Drive web app under API keys. The token addresses the workspace it was created in.
  • Personal access tokenAuthorization: Bearer idn_pat_<token> plus an X-Destesi-Workspace: <workspace-slug> header to choose the workspace.
  • Browser session — the drive_session cookie (set automatically after single sign-on). Used by the Drive web app.

An Authorization header with an unrecognized scheme or token prefix is rejected with 401 — Drive never silently falls back to the cookie when a bearer token is present.

Method & path Purpose
GET /v1/folders List folders. Optional ?parent=<id> to list one folder’s children (omit for root).
POST /v1/folders Create a folder. Body: { "name": "...", "parent_id": "<id>"? }.
GET /v1/folders/{id} Fetch one folder (used to walk the breadcrumb chain).
PATCH /v1/folders/{id} Rename ({ "name": "..." }) and/or move ({ "parent_id": "<id>" }; null moves to root).
DELETE /v1/folders/{id} Soft-delete the folder.

A folder response looks like:

{
"id": "…",
"parent_id": null,
"name": "Reports",
"created_at": "2026-05-31T12:00:00.000Z",
"created_by": "…",
"is_system": false
}

Folder names are 1–255 characters and unique (case-insensitive) among live siblings. A name collision returns 409 name_conflict; an unknown or cross-workspace parent_id returns 404.

Method & path Purpose
GET /v1/files List files. Filters: ?folder=<id>, ?q=<name-prefix>, ?limit=<n>, ?cursor=<token>.
POST /v1/files/upload Upload a file (multipart). See below.
GET /v1/files/{id} File metadata.
GET /v1/files/{id}/download Redirect (302) to a short-lived signed content URL.
GET /v1/files/{id}/content Stream the file bytes inline.
PATCH /v1/files/{id} Rename ({ "name": "..." }) and/or move ({ "folder_id": "<id>" }; null moves to root).
DELETE /v1/files/{id} Soft-delete the file.

A file response:

{
"id": "…",
"folder_id": "…",
"name": "report.pdf",
"mime_type": "application/pdf",
"size_bytes": 184320,
"uploaded_by": "…",
"uploaded_at": "2026-05-31T12:00:00Z",
"created_at": "2026-05-31T12:00:00Z"
}

List responses are paginated: when more results exist, the response includes a next_cursor you pass back as ?cursor=.

{ "items": [ /* … */ ], "next_cursor": "…" }

System folders and their files are excluded from listings by default. Pass ?include_system=true (on an authenticated request) to include them.

POST /v1/files/upload takes a multipart/form-data body:

Field Required Notes
file yes The file bytes. Streamed straight to storage.
name no Display name. Defaults to the uploaded filename.
mime no MIME type. Defaults to the file part’s Content-Type.
folder_id no Destination folder. Validated against your workspace.
Terminal window
curl https://api.drive.destesi.io/v1/files/upload \
-H "Authorization: Bearer drive_xxxxxxxxxxxx..." \
-F "file=@report.pdf" \
-F "name=Q3 report.pdf" \
-F "folder_id=2f1c…"

The response carries the new id and a signed URL to fetch the bytes back:

{
"drive_file_id": "…",
"name": "Q3 report.pdf",
"mime": "application/pdf",
"size": 184320,
"signed_content_url": "https://drive.destesi.io/v1/files/…/content?ws=…&exp=…&sig=…",
"signed_content_expires_at": "2026-05-31T13:00:00Z"
}

The signed content URL is valid for one hour and is bound to your workspace — anyone with the link can fetch that one file until it expires, without a session.

Method & path Purpose
POST /v1/api-keys Create a key. Body: { "name": "..." }. Returns the plaintext once.
GET /v1/api-keys List your workspace’s keys (prefix + metadata only; never the secret).
DELETE /v1/api-keys/{id} Revoke a key.

Creating a key requires a browser session (cookie auth) — a bearer token cannot mint a key. The create response is the only time the full token is returned:

{
"id": "…",
"name": "ci-uploads",
"display_prefix": "drive_ab12",
"created_at": "2026-05-31T12:00:00Z",
"token": "drive_ab12cd34…"
}

Store the token securely. Afterward only the display_prefix (the first 10 characters) is shown, so you can tell keys apart without revealing the secret.

Method & path Purpose
GET /v1/auth/me Current user (id, email, name).
POST /v1/auth/logout Clear the session cookie. Always succeeds (idempotent).
GET /v1/me/products The product list powering the 9-dot launcher.
GET /healthz, GET /readyz Health checks.
Limit Value
Maximum upload size 5 GiB per file
Folder / file name length 1–255 characters
Signed content URL lifetime 1 hour
API-key token format drive_ + 43 base64url characters

Errors are returned as JSON { "error": "<code>" } with a matching HTTP status. Common codes:

Status Example codes
400 invalid_json, missing_name, name_too_long, bad_limit, bad_cursor, folder_not_found
401 missing_session, session_invalid, api_key_invalid, api_key_revoked, unknown_bearer_scheme
403 cookie_required (minting a key over bearer auth)
404 not_found, parent_not_found
409 name_conflict
503 store_unavailable (database briefly unreachable), storage_unavailable (file storage briefly unreachable)

A concrete trigger for each family:

  • 400 name_too_longPOST /v1/folders (or a PATCH rename) with a name over 255 characters. missing_name is the empty-name version of the same check; invalid_json is a malformed request body.
  • 400 bad_limit / bad_cursor — listing files with a non-numeric or < 1 ?limit= (bad_limit), or replaying a ?cursor= value that isn’t a token this API issued (bad_cursor).
  • 400 folder_not_found — uploading with a folder_id that doesn’t exist in your workspace (a cross-workspace id surfaces the same way, so one workspace can’t probe another’s folders).
  • 401 unknown_bearer_scheme — sending Authorization: Basic …, or a Bearer token whose prefix is neither drive_ nor idn_pat_. A drive_ token in the wrong format gives api_key_invalid; a revoked key gives api_key_revoked; no credentials at all gives missing_session.
  • 403 cookie_required — calling POST /v1/api-keys with a bearer token instead of a browser session. A key can never mint another key.
  • 404 parent_not_found — creating a folder under a parent_id that doesn’t exist (or belongs to another workspace). not_found is the equivalent for a missing file or folder you address by id.
  • 409 name_conflict — uploading a file, or creating/renaming a folder, whose name already exists (case-insensitive) among the live siblings in that folder. Rename or move the existing item first, or pick a different name.
  • 503 store_unavailable / storage_unavailable — the database (store_unavailable) or object storage (storage_unavailable) is briefly unreachable. Both are transient; retry with backoff.