REST API Overview

Windshift ships a versioned REST API for automation, integrations, and embedding. Every endpoint lives under a single version prefix:

https://windshift.example.com/rest/api/v1

The route table changes as features land, so the OpenAPI specification served by your own instance is the authoritative, always-current reference. Two formats are published without authentication so a client can fetch them before it has a token:

GET /rest/api/v1/openapi.json
GET /rest/api/v1/openapi.yaml

Point a generator or an API explorer at one of those URLs to get the complete, exact surface for the version you are running. This page covers the cross-cutting rules: authentication, scopes, common parameters, and a few endpoint groups worth calling out.

Authentication

The v1 surface accepts API bearer tokens only. Send the token in the Authorization header:

curl https://windshift.example.com/rest/api/v1/users/me \
  -H "Authorization: Bearer crw_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Tokens are prefixed with crw_. The cookie-authenticated surface (/api/*) used by the web UI explicitly rejects bearer tokens, and the bearer surface here rejects cookies, so the two never cross over.

The bearer surface is rate limited (1000 requests per minute) and every request carries a request ID for tracing.

Getting a token

The quickest path for CLI and agent workflows is the ws CLI, which mints a token through a browser flow on first use:

ws init

See CLI Configuration for the full flow and for passing a token explicitly with --token or WS_TOKEN. You can also create tokens in the Windshift UI, choosing the scopes the token should carry at creation time. Scope selection is the security boundary: a token only reaches what its scopes allow, and the system-admin bypass that applies to the web UI intentionally does not apply to token-scope checks, so a token issued for one bot never inherits owner privileges by accident.

Token scopes

Scopes are resource:action strings. A request is rejected unless the token carries the scope the route requires. Destructive access is opt-in: :delete is a separate scope from :write, so a token can be allowed to create and edit without ever being able to hard-delete.

Scope Grants
items:read / items:write / items:delete Items, including comments, attachments, history, transitions, links, labels, and diagrams.
workspaces:read / workspaces:write / workspaces:delete Workspaces and their read-only config (statuses, item types, priorities).
collections:read Read-only access to collections and their items. Ideal for report and embed clients that should never mutate anything.
milestones:read / milestones:write / milestones:delete Global milestone endpoints.
iterations:read / iterations:write / iterations:delete Global iteration endpoints.
projects:read / projects:write / projects:delete Projects.
pages:read / pages:write / pages:delete Workspace pages (wiki), labels, history, permissions. :delete is required to archive a page.
tests:read / tests:write Test management: folders, cases, labels, sets, plans, run templates, runs, results, and report summaries.
assets:read / assets:write / assets:delete Asset sets, types, categories, statuses, and asset entities. :write covers mutations and CSV import; :delete is required to hard-delete an asset.
time:read / time:write / time:delete Worklogs, timers, and time projects. :delete is required to delete a worklog.
actions:read / actions:write Automation graphs: node catalog discovery plus action CRUD (:write for create/update).
statuses:read, workflows:read, item-types:read, priorities:read, custom-fields:read Global, read-only configuration resources.
users:read User directory and GET /users/me.
agent-skills:read Read-only access to a workspace's agent skill library.
mcp:access Access to the MCP server. A single binary scope (the MCP server exposes both read and write tools).
admin:users:read / admin:users:write Admin user management. Requires the system-admin role in addition to the scope.
admin:groups:read / admin:groups:write Admin group management.
admin:audit-logs:read Read the audit log.
admin:api-tokens:read / admin:api-tokens:write List and revoke API tokens.

Admin scopes require both the scope on the token and the system-admin role on the user; one without the other is rejected. Workspace-content routes apply a second check in the handler (the user's per-workspace view/edit permission), so a token scope alone never grants access to a workspace the user cannot see.

Common parameters

exclude_personal

Several item-facing endpoints accept an exclude_personal query parameter. Set it to true (or 1) so the response never surfaces items from the caller's personal workspaces. Integrations that republish items into shared contexts (document embeds, dashboards, reports) should set it so a private personal item never leaks into a page other people read.

curl "https://windshift.example.com/rest/api/v1/search/items?q=login&exclude_personal=true" \
  -H "Authorization: Bearer crw_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"

It is supported on item lookup (GET /items/{id}), item search (GET /search/items), and collection items (GET /collections/{key}/items). When set on a single-item lookup, a personal item is treated as not found (404) rather than returned.

Assets

Asset entities live under their owning set. Read endpoints need assets:read, mutations need assets:write, and hard-delete needs assets:delete.

Method and path Scope
GET /asset-sets/{setId}/assets assets:read
POST /asset-sets/{setId}/assets assets:write
POST /asset-sets/{setId}/assets/import assets:write (CSV import)
GET /assets/{id} assets:read
PUT /assets/{id} assets:write
DELETE /assets/{id} assets:delete

Sets, types, categories, and statuses are read-only on v1 (GET /asset-sets, GET /asset-sets/{setId}/types, and similar), all gated by assets:read. The route scope is only the first gate: the handler still asks the per-set asset role (Viewer, Editor, Administrator) before serving, and returns 404 (not 403) on any permission failure so set and asset existence is never leaked.

Attachments

Item attachments are uploaded against the item and deleted by attachment ID:

Method and path Scope
GET /items/{id}/attachments items:read
POST /items/{id}/attachments items:write
GET /attachments/{id}/download items:read
GET /attachments/{id}/thumbnail items:read
DELETE /attachments/{id} items:write

Page attachments use a parallel route, POST /workspaces/{id}/pages/{pageId}/attachments, gated by pages:write.

Input sanitization

Request input is sanitized server-side before it is stored. When sanitization changes what you sent (stripping disallowed markup from a comment or description, for example), the response reports the cleanup in a warnings array so a client can surface it rather than silently diverging from the stored value. The stored row is always the clean version; an empty or absent warnings array means nothing was changed.