Skip to content

Authentication ​

Every request to the TOSE API carries exactly one credential. The API checks them in this order:

OrderCredentialHow to send itUsed by
1API keyX-API-Key: tose_sk_... headerScripts, CI, the CLI
2Session tokenAuthorization: Bearer <token> headerApps acting for a signed-in user
3Session tokentose_token cookieBrowser sessions

Limit what a key can do

A key can be limited with scopes, and always belongs to one workspace. See API Keys and Scopes. Every change made with a key is recorded in the audit log.

API keys ​

bash
curl -sS https://api.tose.sh/api/workspaces \
  -H "X-API-Key: $TOSE_API_KEY"

An API key belongs to one workspace. On any route that contains {slug} the key only works for its own workspace, and any other slug returns 403 forbidden. Revoked or expired keys return 401 unauthenticated.

Create a key ​

  1. Sign in to the TOSE dashboard.
  2. Open Settings > API Keys.
  3. Choose Create New API Key and copy the key. It starts with tose_sk_.
  4. Store it in a secret manager or an environment variable.
bash
export TOSE_API_KEY=tose_sk_xxxxxxxxxxxx

WARNING

Never commit an API key. Never put it in a URL, a query string, a log line or client-side code.

Session tokens ​

An app that acts for a signed-in user sends the session token as Authorization: Bearer <token>. Browsers use the tose_token cookie, which holds the same token.

Never put credentials in the URL ​

URLs end up in proxy logs, browser history and analytics, so the API refuses credentials there. A request is rejected with 400 and code credentials_in_url, before authentication runs, when its query string has either of these:

  • an api_key or access_token parameter (the check also covers apikey and x-api-key, in any case);
  • any value that starts with tose_sk_.
bash
# Rejected: 400 credentials_in_url
curl "https://api.tose.sh/api/workspaces?api_key=..."

# Correct
curl https://api.tose.sh/api/workspaces -H "X-API-Key: $TOSE_API_KEY"
json
{
  "error": "Credentials must not be sent in the URL. Send the API key in the X-API-Key header.",
  "code": "credentials_in_url",
  "message": "Credentials must not be sent in the URL. Send the API key in the X-API-Key header.",
  "request_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
  "retryable": false,
  "doc_url": "https://docs.tose.sh/api/errors#credentials_in_url"
}

The secret in a rejected request is never used, but it already travelled in a URL. Rotate any key that was sent that way.

The API's access log records the request path and not the query string, so short-lived tokens that some flows still carry in a URL do not reach the logs.

Scopes and automatic deploys ​

A workspace API key may carry scopes. A key without scopes, and every session, keeps full access to its workspace. Connecting Git to a project, and creating a project with a git block, are projects:write operations that can also queue a first deployment. The API starts that deployment only when the caller also has deployments:execute. Each automatic first deploy writes a deployment.execute audit event with the deployment ID.

For a key with projects:write but not deployments:execute:

ActionResult
Connect Git to a projectSucceeds. No first deploy is queued and the connection is created with auto_deploy off.
Create a project with a git blockSucceeds and returns deploy_skipped instead of deployment_id and build_logs_url.
Turn auto_deploy on, or move the branch of an auto-deploying connectionFails with 403 insufficient_scope and required_scope: deployments:execute.

Add deployments:execute to the key, or use a session, when Git pushes should deploy automatically.

WebSockets ​

WebSocket endpoints authenticate on the HTTP upgrade request with the same header or cookie.

The desktop gateway reads its session token from the Sec-WebSocket-Protocol list (tose.desktop.token.{token}). The ?token= query parameter is a legacy path that the API still accepts for older clients. Do not use it in new code. See the Cloud Desktops API.

Authentication errors ​

StatusCodeCauseFix
400credentials_in_urlCredential in the query stringMove it to a header
401unauthenticatedNo credential, or it is invalid, revoked or expiredSend a valid key or token
403forbiddenThe credential cannot access this workspace or actionUse a key for that workspace
403insufficient_scopeThe key lacks a required scopeCreate a key with the scope

See Errors for the envelope.

CI/CD ​

yaml
- name: Deploy
  env:
    TOSE_API_KEY: ${{ secrets.TOSE_API_KEY }}
  run: tose up

The CLI reads TOSE_API_KEY from the environment. See Configuration.

Released under the ISC License.