---
title: "API Tokens"
description: "Personal and organization API tokens in Shiplight Cloud — scopes, creation, revocation, and using a token for the Cloud API and the Shiplight LLM proxy."
---

# API Tokens

<div class="view-markdown-wrapper">
<ViewMarkdown />
</div>

API tokens authenticate everything outside the browser: uploading test results, reading them back from the `/shiplight cloud` skill, and calling LLMs through the Shiplight [LLM proxy](#llm-proxy). Manage them at [app.shiplight.ai/api-tokens](https://app.shiplight.ai/api-tokens).

A token carries **scopes**, so one token can be limited to exactly the job it does.

## Two kinds of token

|                              | Personal                     | Organization                                           |
| ---------------------------- | ---------------------------- | ------------------------------------------------------ |
| Prefix                       | `shp_pat_`                   | `shp_org_`                                             |
| Acts as                      | You, within one organization | The organization, with no user attached                |
| Who can create               | Any member                   | **Owners only**                                        |
| Visible to                   | Only you                     | All members; only owners can create, revoke, or reveal |
| Survives you leaving the org | No                           | Yes                                                    |
| Limit                        | 20 active per organization   | 20 active (configurable per org)                       |

Use a **personal** token for local development and anything that should stop working when you leave. Use an **organization** token for CI and shared automation, so it does not break when someone changes teams.

## Scopes

Scopes are a **ceiling, not a grant**. What a token can actually do is your live organization role _intersected with_ its scopes — so a member's token can never do owner-only things just by checking more boxes, and a token loses access immediately if your role changes or you're removed.

| Scope                                                          | Grants                                                             |
| -------------------------------------------------------------- | ------------------------------------------------------------------ |
| `llm:invoke`                                                   | Calling the [LLM proxy](#llm-proxy)                                |
| `analytics:read`                                               | The whole `/v1/analytics/*` API                                    |
| `action-cache:read` / `action-cache:write`                     | Reading and updating the self-healing locator cache                |
| `recordings:write`                                             | Uploading recorder sessions (personal tokens only)                 |
| `proxy:egress`                                                 | Authenticating the browser egress proxy (organization tokens only) |
| `secrets:*`, `organizations:*`, `instances:*`, `accounts:read` | Platform management APIs (personal tokens only)                    |

::: warning An empty scope list means full access
If you tick **every** box in the create form, the token is stored with no scope restriction at all — which is _broader_ than the boxes you ticked, because it also grants scopes the form doesn't show. The token list displays this as **Full access**. To get a genuinely limited token, leave at least one box unticked.
:::

::: warning Some scopes can't be selected in the UI
The create form doesn't render `analytics:read` or `recordings:write` for personal tokens. Unticking any box therefore silently drops them. If you need one of those as part of a _restricted_ personal token, create the token through the API rather than the UI, or use a full-access token.

`proxy:egress` is also special: it is off by default, and a full-access token cannot perform egress — the scope must be granted explicitly.
:::

## Managing tokens

Create tokens on the **Personal** or **Organization** tab at [app.shiplight.ai/api-tokens](https://app.shiplight.ai/api-tokens), giving a name, an expiration (90 days by default), and the scopes above. Unlike most platforms, Shiplight can show you a token again later — **Reveal & copy** works for as long as the token is active, and only owners can reveal organization tokens. There is no rotate action: to rotate, create a replacement, deploy it, then revoke the old one. Revocation takes effect everywhere within about a minute.

## Reading results with a token

The result endpoints — test runs, test results, failing and flaky tests, and artifact downloads — require only a valid, active token. **No particular scope is needed**, so even a tightly scoped token can read results. This is what the `/shiplight cloud` skill uses:

```bash
export SHIPLIGHT_API_TOKEN=shp_pat_…
```

The analytics endpoints are the exception: `/v1/analytics/*` requires `analytics:read`, or a full-access token.

The organization is always taken from the token itself, never from the request, so a token can only ever see its own org's data.

## LLM proxy

The Shiplight LLM proxy is a metered pass-through in front of Anthropic, OpenAI, and Google Gemini. You send provider-native requests and get provider-native responses — it is not a separate API, so existing SDKs work unchanged. It lets you run AI-powered test steps without holding your own provider keys, and bills the usage to your organization alongside compute and storage.

Prompts and responses are **never stored**. Only token counts, model, provider, and request metadata are recorded, which is what powers per-token cost attribution in [Usage](/cloud_v2/billing#usage).

### Pointing a tool at the proxy

Base URL is `https://nova-api.shiplight.ai/llm`, and your Shiplight token replaces the provider key:

```bash
# Anthropic
ANTHROPIC_BASE_URL=https://nova-api.shiplight.ai/llm
ANTHROPIC_AUTH_TOKEN=shp_pat_…

# OpenAI
OPENAI_BASE_URL=https://nova-api.shiplight.ai/llm/v1
OPENAI_API_KEY=shp_pat_…

# Gemini
GOOGLE_GEMINI_BASE_URL=https://nova-api.shiplight.ai/llm
GEMINI_API_KEY=shp_pat_…
```

For the Shiplight CLI you don't set these at all — setting `SHIPLIGHT_API_TOKEN` is what puts it in proxy mode. See [LLM Providers](/local/local-testing/llm-providers).

### Requirements

The token needs **`llm:invoke`** (or full access). Without it you get a 403 naming the missing scope. This doesn't apply on Shiplight-managed compute, where credentials are injected automatically.

### Models

The proxy only forwards models in the Shiplight catalog. Requesting anything else is rejected before the request reaches the provider, with a 400 naming the unsupported model — so a typo or an unlisted model fails immediately rather than running up cost.

| Model                                                               | Provider  | Tier     |
| ------------------------------------------------------------------- | --------- | -------- |
| `claude-opus-4-8`, `claude-opus-4-7`                                | Anthropic | Pro      |
| `claude-sonnet-5`, `claude-sonnet-4-6`                              | Anthropic | Standard |
| `claude-haiku-4-5`                                                  | Anthropic | Lite     |
| `gemini-3.1-pro-preview`, `gemini-3.5-flash`                        | Google    | Standard |
| `gemini-3-flash-preview`                                            | Google    | Lite     |
| `gpt-5.6-sol`                                                       | OpenAI    | Pro      |
| `gpt-5.6-terra`, `text-embedding-3-large`, `text-embedding-3-small` | OpenAI    | Standard |
| `gpt-5.6-luna`                                                      | OpenAI    | Lite     |

Shiplight can also retire a model from the catalog, after which new requests for it are rejected the same way.

### What you're billed for

Usage is metered in millions of tokens (MTok) at your plan's rate per tier. The billed total is **every** token the provider reports — input, output, thinking, cache reads, and cache writes.

Shiplight checks your balance **before** forwarding the request, so an exhausted allowance fails fast without spending anything upstream. A request already in flight is always allowed to finish.

Retries are safe: send an `Idempotency-Key` header and a replayed request returns the cached response without billing twice.

## Related

- [LLM Providers](/local/local-testing/llm-providers) — the proxy versus your own provider keys
- [Billing](/cloud_v2/billing) — how token usage is priced
- [Test Results](/cloud_v2/test-results) — uploading and reading results
- [CI Runners](/cloud_v2/ci-runners) — automatic credentials on hosted runners
