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

Each instance serves an OpenAPI specification for its installed version. Use it as the authoritative API reference. Windshift publishes two formats 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 API explorer at either URL. It can then read the complete API surface for the version you run. This page explains authentication, scopes, common parameters, and selected endpoint groups.

Authentication

The v1 API 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 use the crw_ prefix. The web UI uses the cookie-authenticated /api/* surface, which rejects bearer tokens. The REST API rejects cookies. The two authentication surfaces stay separate.

The REST API limits requests to 1,000 per minute. Each request includes a request ID for tracing.

Getting a token

For CLI and agent workflows, use the ws CLI. On first use, it mints a token through a browser flow:

ws init

See CLI Configuration for the full flow. It also explains how to pass a token with --token or WS_TOKEN. You can also create tokens in the Windshift UI and select their scopes. A token can access only the resources its scopes allow. The system-admin bypass for the web UI does not apply to token-scope checks. A token for one bot never inherits owner privileges by accident.

Token scopes

Scopes use resource:action strings. Windshift rejects a request unless its token has the required route scope. Destructive access is opt-in. :delete is separate from :write, so a token can create and edit without hard-delete access.

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 the scope on the token and the system-admin role on the user. Windshift rejects a request that lacks either one. Workspace-content routes also check the user's per-workspace view or edit permission. A token scope alone never grants access to a workspace the user cannot see.

Common parameters

exclude_personal

Several item endpoints accept an exclude_personal query parameter. Set it to true or 1 to exclude items from the caller's personal workspaces. Integrations that republish items in shared contexts should set it. This prevents a private personal item from appearing on a page that other people read.

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

Item lookup (GET /items/{id}), item search (GET /search/items), and collection items (GET /collections/{key}/items) support this parameter. For a single-item lookup, Windshift returns 404 instead of a personal item.

Assets

Asset entities belong to an asset set. Read endpoints require assets:read. Mutations require assets:write, and hard-delete requires 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 in v1. This includes GET /asset-sets, GET /asset-sets/{setId}/types, and similar routes. All require assets:read. Route scope is only the first gate. The handler also checks the per-set asset role: Viewer, Editor, or Administrator. On any permission failure, it returns 404 instead of 403. This prevents a response from exposing whether a set or asset exists.

Attachments

Upload item attachments to an item. Delete them 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

Windshift sanitizes request input before it stores it. If sanitization changes input, such as by stripping disallowed markup from a comment or description, the response includes the cleanup in a warnings array. A client can then show the change instead of silently diverging from the stored value. The stored row always contains the clean version. An empty or absent warnings array means Windshift made no change.