Resource API

Configuration is written through the resource API, under https://app.usespotlight.ai/api/resources, or under /api/resources on your deployment's address on a self-hosted Spotlight. The Terraform provider calls the same endpoints. The web app shows configuration read-only.

Request and response bodies are JSON with camelCase field names. Errors are application/problem+json.

Credentials

CredentialSent asActs
A person's sessionthe session cookie the web app sets at sign-inas that person, within their role in each workspace
A workspace tokenAuthorization: Bearer <token>only within its own workspace, and only as its scopes allow

A request with neither is refused with 401. See Roles and permissions for what each role and scope allows.

Operations

Every resource kind has these six operations, except where a rule below says otherwise. <plural> is the kind's collection path, from the endpoint catalogue. <holder> is workspaces/{workspaceId} or teams/{teamId}, depending on what holds the kind.

OperationMethod and pathSuccessRequired header
createPOST /api/resources/<holder>/<plural>201Idempotency-Key
readGET /api/resources/<plural>/{id}200
updatePUT /api/resources/<plural>/{id}200If-Match
deleteDELETE /api/resources/<plural>/{id}204If-Match
importGET /api/resources/workspaces/{workspaceId}/<plural>/import?address=<address>200
listGET /api/resources/workspaces/{workspaceId}/<plural>200
  • A read returns every field, with defaults filled in. Create and update return exactly what a read would.
  • An update replaces the whole resource. There is no partial update.
  • A list returns { "items": [...] }, each item as a read would return it, in address order. A list of a team-held kind covers every team in the workspace.
  • An import returns the resource at an address, as a read would.

Headers

HeaderDirectionDescription
AuthorizationrequestBearer <token>, for a workspace token.
Idempotency-Keyrequest, createRequired. 1 to 255 visible characters. A retry with the same key and the same body within 24 hours returns the resource the first attempt created and creates nothing more.
If-Matchrequest, update and deleteRequired. The ETag of the version the write is based on, as in "3".
Spotlight-Api-Versionrequest and responseOptional on a request. Every response carries the version it was served in.
Spotlight-ClientrequestOptional. The client making the request, recorded with every configuration change: terraform-provider/<version> is recorded as the Terraform provider at that version, web as the web app, and anything else, or nothing, as unknown.
ETagresponseThe resource's version as a strong entity tag, as in "3".
Idempotent-Replayedresponsetrue when a create returns an earlier attempt with the same key.
Spotlight-Workspace-TokenresponseThe new token, on creating a workspace token. Returned once.
Spotlight-Pool-TokenresponseThe token Spotlight issued, on creating a pull pool without one. Returned once.

Common fields

Every resource representation has these fields besides its own.

FieldTypeDefaultDescription
idstringread-onlyIssued by Spotlight on create. Never changes.
versionintegerread-onlyRises with every change. Carried as the ETag.
addressstringread-onlyNames the resource within its workspace. The import operation takes it.
warningsarray of objectsread-onlyEach { path, message, severity }, with severity warning. Each describes a setting Spotlight accepts but cannot honour as written.

Addresses

KindAddressExample
workspaceslugacme
teamkeycore
line<team key>/<key>core/delivery
skill<team key>/<key>core/grafana
harness-profile<team key>/<key>core/careful
integration<team key>/<integration>core/acme
hook<team key>/<key>core/migrations-need-a-dba
secretnameACME_API_KEY
workspace-tokennameci
workspace-token-trustkeygithub-main
runner-poolkeyephemeral
runner-trustkeyproduction-cluster
workspace-runnersthe workspace's slugacme
replay-candidate<team key>/<line key>/<station key>/<name>core/delivery/implementation/Higher effort

Errors

An error body has these fields.

FieldTypeDescription
typestringurn:spotlight:problem:<slug>. Stable.
titlestringA short summary.
statusintegerThe HTTP status.
detailstringWhat went wrong. For invalid, each error as <path>: <message>, one per line.
errorsarray of objectsPresent when the request was invalid. Each { path, message, severity }.
currentVersionintegerPresent on moved-on. The version the resource is at now.
permissionstringPresent on forbidden. The permission the caller lacks.
StatusTypeWhen
400bad-requestThe request is malformed.
400idempotency-key-requiredA create has no Idempotency-Key.
400malformed-if-matchIf-Match is not one entity tag such as "3".
400unsupported-api-versionSpotlight-Api-Version names a version this server does not serve.
401unauthenticatedNo valid credentials.
403forbiddenThe caller's role or token scopes do not allow this request. permission names what it lacks.
404not-foundNo such resource that the caller may see. Anything the caller may not see returns 404.
409already-existsA create names an address already taken. The detail says to import it.
409moved-onIf-Match names a version that is no longer current.
409in-useThe write or delete would remove something another resource or a task still uses.
409conflictAnother conflict with the resource's state.
422invalidOne or more fields are wrong.
422idempotency-key-reusedThe Idempotency-Key was used for a different request body.
428precondition-requiredAn update or delete has no If-Match.
502refused-upstreamAn upstream service refused.

permission is one of configure, manage-secrets, manage-runners, manage-token-trusts, administer, open-workspaces or plan.

Error paths

An error's path names the attribute it is about, as the Terraform provider addresses one. An empty path is about the whole resource.

FormExample
a fieldname
a list elementstations[2].check
a map key that is a plain namemcpServers[0].env.LINEAR_API_KEY
a map key that is not a plain name, quotedon["task.opened"].to.thread

Paths use the API's camelCase field names.

API version

The version served is 2026-09-23. Within a version, fields and operations are only added, never removed, renamed or given a new meaning.

Endpoint catalogue

All paths are under /api/resources.

KindHeld byCreateRead, update, deleteImport and list
workspacenonePOST /workspaces/workspaces/{id}/workspaces/import, /workspaces
teamworkspacePOST /workspaces/{workspaceId}/teams/teams/{id}/workspaces/{workspaceId}/teams
lineteamPOST /teams/{teamId}/lines/lines/{id}/workspaces/{workspaceId}/lines
skillteamPOST /teams/{teamId}/skills/skills/{id}/workspaces/{workspaceId}/skills
harness-profileteamPOST /teams/{teamId}/harness-profiles/harness-profiles/{id}/workspaces/{workspaceId}/harness-profiles
integrationteamPOST /teams/{teamId}/integrations/integrations/{id}/workspaces/{workspaceId}/integrations
hookteamPOST /teams/{teamId}/hooks/hooks/{id}/workspaces/{workspaceId}/hooks
secretworkspacePOST /workspaces/{workspaceId}/secrets/secrets/{id}/workspaces/{workspaceId}/secrets
workspace-tokenworkspacePOST /workspaces/{workspaceId}/tokens/tokens/{id}/workspaces/{workspaceId}/tokens
workspace-token-trustworkspacePOST /workspaces/{workspaceId}/token-trusts/token-trusts/{id}/workspaces/{workspaceId}/token-trusts
runner-poolworkspacePOST /workspaces/{workspaceId}/runner-pools/runner-pools/{id}/workspaces/{workspaceId}/runner-pools
runner-trustworkspacePOST /workspaces/{workspaceId}/runner-trusts/runner-trusts/{id}/workspaces/{workspaceId}/runner-trusts
workspace-runnersworkspacePOST /workspaces/{workspaceId}/workspace-runners/workspace-runners/{id}/workspaces/{workspaceId}/workspace-runners
replay-candidatestationPOST /lines/{lineId}/stations/{stationKey}/replay-candidates/replay-candidates/{id}, read and delete only/workspaces/{workspaceId}/replay-candidates

Import is the list path followed by /import, except for workspaces.

Rules particular to a kind:

  • workspace and team: see the rules of spotlight_workspace and spotlight_team.
  • workspace-token: only a person with the administer permission creates, updates or deletes one. An update changes only its description. A delete revokes it.
  • workspace-runners: a workspace has at most one, so its list returns zero or one item.
  • replay-candidate: has no update. Proposing and withdrawing one need plan, and only someone who may read the station's team sees one. It is not exported.

Each kind's fields are on its own page:

Export

Method and pathReturns
GET /api/resources/workspaces/{id}/export{ "files": [{ "path", "content" }] }: the whole workspace as a Terraform module.
GET /api/resources/{collection}/{id}/hcl{ "hcl", "files": [{ "path", "content" }] }: one resource as HCL.

Both need only permission to read.

The export's files are ordered by path and are the same on every export:

FileContents
main.tfEvery resource, each with an import block beside it, referring to one another.
variables.tfA sensitive variable for each value Spotlight never returns, such as secret values and runner pool tokens.
prompts/<team>/<line>/<station>.mdEach station prompt, read by main.tf with file().
judges/<team>/<line>/<station>.mdEach station judge's rubric, read by main.tf with file(). A judge with claims alone has none.
skills/<team>/<key>/<path>Each skill file, read by main.tf with file().

Workspace tokens are not exported.

The hcl string holds, in order, any variable the resource reads, an import block that adopts it, and the resource. files holds the prompt, rubric and skill files it reads. collection is one of workspaces, teams, lines, skills, harness-profiles, integrations, hooks, secrets, token-trusts, runner-trusts, runner-pools or workspace-runners.

Configuration changes

GET /api/workspaces/{slug}/configuration-changes

Returns every create, update and delete of the workspace's configuration, newest first. Only a person with the administer permission may read it; for anyone else the workspace is 404.

Query parameterDescription
beforeOnly changes with a lower seq.
limitAt most this many. Default 50, at most 200.
FieldTypeDescription
seqintegerRises with every change. Pass it as before to page back.
resourcestringThe kind, as in line or secret. replay is a replay started or stopped, replay-set a replay set's guard of its station's changes turned on or off, replay-search a search started or stopped, replay-check a replay check started, and config-pull-request a search's candidate picked, keyed by its branch.
resourceIdstringThe resource's id.
keystringThe key or name it had within its team or workspace.
actionstringcreated, updated or deleted.
by.kindstringperson, token, or spotlight for a change Spotlight made following another.
by.userIdstring or nullThe person, or for a token the person who issued it. For a token minted through a workspace token trust, the person who defined the trust.
by.tokenIdstring or nullThe workspace token.
by.truststring or nullThe workspace token trust the token was minted through.
clientstring or nullThe client, from Spotlight-Client.
detailsobjectWhat the kind records beyond the action. A secret records its value version and bound hosts, never its value.
atstringWhen, as an ISO 8601 time.

Expression checks

POST /api/expressions/check and GET /api/expressions/positions take the same credentials and return the same problems as the resource API. See Check API.

On this page