API keys API reference
Create, list and revoke API keys.
List API keys
GET /api-keys
Auth: API key with scope keys:manage, or access token.
Requires keys:manage. Non-revoked keys, newest first (max 100). Secrets are never returned. An API key caller sees only keys of its own environment.
Credits. Free: no credits.
Responses
200 Keys.
Schema ApiKeyList (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
{
"data": [
{
"id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
"name": "string",
"prefix": "ss_live_AbCd",
"environment": "live",
"scopes": [],
"status": "active",
"createdAt": "2026-10-21T12:00:00.000Z",
"lastUsedAt": "2026-10-21T12:00:00.000Z",
"expiresAt": "2026-10-21T12:00:00.000Z"
}
]
} 400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id
403 FORBIDDEN: see details.reason.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After
default Error (see ErrorEnvelope and /v1/docs/errors).
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
Create an API key
POST /api-keys
Auth: API key with scope keys:manage, or access token.
Requires keys:manage. The full key is in this response only. An API key acting as the manager can only create keys for its own environment with a subset of its own scopes.
Credits. Free: no credits.
Request body application/json
Required. Schema CreateApiKeyRequest.
| Field | Type | Description |
|---|---|---|
namerequired | string | 1–64 characters after trimming; need not be unique. |
environmentrequired | ApiKeyEnvironment | |
scopes | array of ApiKeyScope | Defaults to every scope except keys:manage, billing:read, billing:write, webhooks:read and webhooks:write. |
expiresAt | string | Optional RFC 3339 expiry, in the future and at most 365 days ahead.Format: date-time |
{
"name": "string",
"environment": "live",
"scopes": [
"generations:write"
],
"expiresAt": "2026-10-29T12:00:00.000Z"
}Responses
201 Created. Store key now; it is never shown again.
Schema CreatedApiKey (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
{
"id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
"name": "string",
"prefix": "ss_live_AbCd",
"environment": "live",
"scopes": [
"generations:write"
],
"status": "active",
"createdAt": "2026-10-21T12:00:00.000Z",
"lastUsedAt": "2026-10-21T12:00:00.000Z",
"expiresAt": "2026-10-21T12:00:00.000Z",
"key": "string"
} 400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id
403 FORBIDDEN: see details.reason.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
413 PAYLOAD_TOO_LARGE.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id
429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After
default Error (see ErrorEnvelope and /v1/docs/errors).
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
Revoke an API key
DELETE /api-keys/{id}
Auth: API key with scope keys:manage, or access token.
Requires keys:manage. Idempotent: revoking an already-revoked key returns 204. Takes effect on the next request.
Credits. Free: no credits.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | Format: uuid |
Responses
204 Revoked.
Headers: X-Request-Id , RateLimit , RateLimit-Policy
400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id
403 FORBIDDEN: see details.reason.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
404 NOT_FOUND: no such resource for this caller.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After
default Error (see ErrorEnvelope and /v1/docs/errors).
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , RateLimit , RateLimit-Policy
Start device authorization
POST /auth/device
No authentication.
No authentication. For an agent or tool without an API key (RFC 8628): returns a user_code and a console link for its human, and a secret device_code to poll POST /auth/device/token with every interval seconds. The human signs in to the developer console, checks the request and approves or denies it; on approval the next poll returns a new API key. Codes expire after expires_in seconds (10 minutes) and work once. Accepts JSON or application/x-www-form-urlencoded. Device authorization may be disabled on a server: then this answers 404 NOT_FOUND. Limited per IP address (429 RATE_LIMITED with Retry-After).
When to use. When STORESHOT_API_KEY is not set: start here, show your human verification_uri_complete and user_code, then poll POST /auth/device/token.
Credits. Free: no credits.
Request body application/json
Optional. Schema DeviceAuthorizationRequest.
| Field | Type | Description |
|---|---|---|
client_name | string | Your tool’s name, shown to the human as an unverified label. 1–64 printable ASCII characters. When absent, client_id is used instead (generic OAuth libraries send one). |
scope | string | Space-separated scopes for the new key. Default generations:write generations:read uploads:write usage:read. keys:manage, billing:write and webhooks:write cannot be requested. The human may narrow the list, or add billing:read and webhooks:read. |
environment | ApiKeyEnvironment | Environment of the new key. Default live. |
{
"client_name": "Claude Code",
"scope": "generations:write generations:read uploads:write usage:read",
"environment": "live"
}
Request body application/x-www-form-urlencoded
Optional. Schema DeviceAuthorizationRequest.
| Field | Type | Description |
|---|---|---|
client_name | string | Your tool’s name, shown to the human as an unverified label. 1–64 printable ASCII characters. When absent, client_id is used instead (generic OAuth libraries send one). |
scope | string | Space-separated scopes for the new key. Default generations:write generations:read uploads:write usage:read. keys:manage, billing:write and webhooks:write cannot be requested. The human may narrow the list, or add billing:read and webhooks:read. |
environment | ApiKeyEnvironment | Environment of the new key. Default live. |
Responses
200 Started. Show verification_uri_complete and user_code to the human.
Schema DeviceAuthorization (application/json).
Headers: X-Request-Id , Cache-Control
{
"device_code": "string",
"user_code": "BCDF-GHJK",
"verification_uri": "https://console.storeshot.art/device",
"verification_uri_complete": "https://console.storeshot.art/device?code=BCDF-GHJK",
"expires_in": 600,
"interval": 5
} 400 INVALID_INPUT: see details.issues.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id
404 NOT_FOUND: device authorization may be disabled on this server, and then both operations answer the /v1 404 envelope.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id
429 RATE_LIMITED: too many requests from this IP address, or too many pending ones (details.reason too_many_pending). Retry after Retry-After seconds.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id , Retry-After
default Error (see ErrorEnvelope and /v1/docs/errors).
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id
Poll for the device authorization result
POST /auth/device/token
No authentication.
No authentication. Send grant_type=urn:ietf:params:oauth:grant-type:device_code and the device_code from POST /auth/device, as application/x-www-form-urlencoded (RFC 8628 §3.4) or JSON. Poll every interval seconds until the human decides. Approved: 200 with the new API key in access_token, returned once; the code then answers invalid_grant. Errors here are the one deliberate exception to ErrorEnvelope: refusals are HTTP 400 { "error", "error_description" } (RFC 6749 §5.2) with error authorization_pending (keep polling), slow_down (add 5 seconds to your interval; Retry-After gives the new one), access_denied, expired_token (start again), invalid_grant, invalid_request or unsupported_grant_type. The X-Request-Id header is on every response: quote it to support. Other failures (404 while device authorization is disabled on this server, 413, 5xx) use ErrorEnvelope.
When to use. Every interval seconds after POST /auth/device, until it returns the key; add 5 s on slow_down; stop on access_denied or expired_token.
Credits. Free: no credits.
Request body application/x-www-form-urlencoded
Required. Schema DeviceTokenRequest.
| Field | Type | Description |
|---|---|---|
grant_typerequired | string | One of: "urn:ietf:params:oauth:grant-type:device_code" |
device_coderequired | string |
Request body application/json
Required. Schema DeviceTokenRequest.
| Field | Type | Description |
|---|---|---|
grant_typerequired | string | One of: "urn:ietf:params:oauth:grant-type:device_code" |
device_coderequired | string |
{
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": "string"
}Responses
200 Approved: the new API key. Store access_token now; it is never shown again.
Schema DeviceToken (application/json).
Headers: X-Request-Id , Cache-Control , Pragma
{
"access_token": "string",
"token_type": "Bearer",
"scope": "string",
"key_id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
"environment": "live"
} 400 OAuth error (RFC 6749 §5.2), not ErrorEnvelope. slow_down also carries Retry-After.
Schema DeviceTokenError (application/json).
Headers: X-Request-Id , Retry-After
404 NOT_FOUND: device authorization may be disabled on this server, and then both operations answer the /v1 404 envelope.
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id
default Error (see ErrorEnvelope and /v1/docs/errors).
Schema ErrorEnvelope (application/json).
Headers: X-Request-Id