Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.crewship.ai/llms.txt

Use this file to discover all available pages before exploring further.

All credential endpoints require authentication and workspace context unless otherwise noted. Credential values are encrypted with AES-256-GCM and never returned in API responses.

List Credentials

GET /api/v1/credentials?workspace_id={workspaceId}
Returns all credentials in the workspace with agent assignment info. Auth: Session or CLI token + workspace membership Response: 200 OK
[
  {
    "id": "cred_abc",
    "name": "anthropic-primary",
    "description": "Main Anthropic API key",
    "type": "AI_CLI_TOKEN",
    "provider": "ANTHROPIC",
    "status": "ACTIVE",
    "scope": "WORKSPACE",
    "crew_id": null,
    "crew_ids": [],
    "account_label": "Production",
    "account_email": "team@company.com",
    "token_expires_at": "2025-12-31T00:00:00Z",
    "last_checked_at": "2024-01-15T10:00:00Z",
    "last_error": null,
    "created_at": "2024-01-01T00:00:00Z",
    "updated_at": "2024-01-15T10:00:00Z",
    "_count_agent_credentials": 5,
    "agent_names": ["Backend Dev", "Frontend Dev", "QA Engineer"],
    "mcp_used": false
  }
]

Response Fields

FieldTypeDescription
idstringCredential ID
namestringDisplay name
descriptionstring?Description
typestringCredential type
providerstringProvider identifier
statusstringCurrent status
scopestringWORKSPACE or CREW
crew_idstring?Legacy single crew ID
crew_idsstring[]All associated crew IDs
account_labelstring?Account label
account_emailstring?Account email
token_expires_atstring?Token expiry timestamp
last_checked_atstring?Last health check timestamp
last_errorstring?Last error message
_count_agent_credentialsintegerNumber of agent assignments
agent_namesstring[]Names of assigned agents
mcp_usedbooleanWhether used by MCP bindings
usernamestring?USERPASS cleartext identifier (null for all other types)
last_used_atstring?Latest USE event recorded by the audit timeline
last_used_ipsstring[]Ring-buffer (max 5) of recent caller IPs
tagsstring[]Free-form tag labels (always non-null in JSON)
created_atstringISO 8601 timestamp
updated_atstringISO 8601 timestamp

Create Credential

POST /api/v1/credentials?workspace_id={workspaceId}
Auth: OWNER, ADMIN, or MANAGER role (canRole(role, "create") in internal/api/credentials_mutate.go:66) Request Body:
FieldTypeRequiredDefaultDescription
namestringYesDisplay name (1-255 characters)
valuestringConditionalSecret value (required unless type is OAUTH2, or pending = true)
descriptionstringNonullDescription
typestringNo"SECRET"Credential type
providerstringNo"NONE"Provider identifier
scopestringNo"WORKSPACE""WORKSPACE" or "CREW"
crew_idstringNonullLegacy single crew ID
crew_idsstring[]No[]Associated crew IDs (auto-sets scope to CREW)
tagsstring[]No[]Free-form tag labels
account_labelstringNonullAccount label
account_emailstringNonullAccount email
usernamestringNonullCleartext identifier half of USERPASS credentials (required when type is USERPASS)
token_expires_atstringNonullToken expiry timestamp
security_levelintegerNo1Security level (1-3)
refresh_tokenstringNonullOAuth refresh token
oauth_client_idstringNonullOAuth client ID (OAUTH2 type only)
oauth_client_secretstringNonullOAuth client secret
oauth_auth_urlstringNonullOAuth authorization URL
oauth_token_urlstringNonullOAuth token URL
oauth_scopesstringNonullOAuth scopes (space-separated)
pendingbooleanNofalseCreate a placeholder credential (status PENDING, no real value) used by crewship apply -f manifest slots
{
  "name": "anthropic-primary",
  "type": "AI_CLI_TOKEN",
  "provider": "ANTHROPIC",
  "value": "sk-ant-api03-xxxxx",
  "scope": "WORKSPACE",
  "description": "Main Anthropic API key"
}
The value is encrypted with AES-256-GCM before storage using the format v1:{base64(IV||AuthTag||Ciphertext)}. Response: 201 Created — credential object (without the encrypted value).
StatusCondition
400Missing name, missing value (non-OAuth2), invalid crew_id, invalid type/USERPASS without username
403Insufficient role (requires MANAGER, ADMIN, or OWNER)
409Credential with this name already exists in the workspace

Get Credential

GET /api/v1/credentials/{credentialId}?workspace_id={workspaceId}
Returns credential metadata (never the encrypted value). Response: 200 OK — credential object with crew_ids, agent_names, and mcp_used.
StatusCondition
404Credential not found

Update Credential

PATCH /api/v1/credentials/{credentialId}?workspace_id={workspaceId}
PUT /api/v1/credentials/{credentialId}?workspace_id={workspaceId}
Both methods behave as partial update. Auth: OWNER, ADMIN, or MANAGER role (canRole(role, "update") in internal/api/credentials_mutate.go:319) Updatable Fields:
FieldTypeDescription
namestringDisplay name
descriptionstringDescription
valuestringNew secret value (re-encrypted; resets status to ACTIVE and records an inline-rotate audit event)
typestringCredential type
providerstringProvider identifier
scopestringWORKSPACE or CREW
crew_idstringLegacy single crew ID
crew_idsstring[]Associated crew IDs (replaces existing)
tagsstring[]Free-form tag labels (empty array / null clears the column)
account_labelstringAccount label
account_emailstringAccount email
usernamestringUSERPASS cleartext identifier
token_expires_atstringToken expiry timestamp
security_levelintegerSecurity level (1-3)
Note: The status field cannot be updated via this endpoint. Status changes are managed by the credential monitor and OAuth refresh worker. Response: 200 OK — updated credential object.
StatusCondition
400No fields to update, invalid crew_id
403Insufficient role
404Credential not found

Delete Credential

DELETE /api/v1/credentials/{credentialId}?workspace_id={workspaceId}
Soft-deletes the credential and clears it from all MCP bindings. Auth: OWNER or ADMIN role Response: 200 OK
{
  "success": true
}
StatusCondition
403Insufficient role
404Credential not found

Test Credential

POST /api/v1/credentials/test
Validates a credential value against the provider’s API without storing it. Useful for checking a key before saving. Rate-limited to 60 requests/minute per IP. Auth: Session or CLI token (no workspace context needed) Request Body: (internal/api/credentials_test_endpoint.go:229)
FieldTypeRequiredDescription
providerstringNoProvider to test against (drives which API is probed)
typestringNoCredential type (used to special-case Anthropic OAuth tokens)
valuestringYesValue to test
{
  "provider": "ANTHROPIC",
  "type": "AI_CLI_TOKEN",
  "value": "sk-ant-api03-xxxxx"
}
Response: 200 OK with { "valid": bool, "status": int, "error": string }.

Test Stored Credential

POST /api/v1/credentials/{credentialId}/test
Re-tests an already-stored credential by decrypting its value server-side and probing the provider. Records an audit event so the detail-sheet timeline reflects the manual check. (internal/api/credentials_test_endpoint.go:253) Auth: OWNER, ADMIN, or MANAGER role (canRole(role, "update")). Rate-limited to 60 requests/minute per IP. Response: 200 OK with { "valid": bool, "status": int, "error": string }.
StatusCondition
403Insufficient role
404Credential not found

Audit Timeline

GET /api/v1/credentials/{credentialId}/audit?limit=50
Returns the credential’s append-only audit timeline (USE, ROTATE, TEST, REVOKE, DETECTED, CREATED events). Backs the Audit tab in the detail Sheet. (internal/api/credential_audit.go:276) Auth: OWNER, ADMIN, or MANAGER role (canRole(role, "update")). Audit reveals admin-action IPs, so VIEWER/MEMBER are blocked. Query Parameters:
ParameterTypeDefaultDescription
limitinteger501-500. Out-of-range values fall back to the default.
Response: 200 OK — JSON array (newest first).
[
  {
    "id": "ca_01h9z7k0",
    "event_type": "ROTATE",
    "agent_id": null,
    "ip_address": "10.0.4.21",
    "metadata": { "rotation_id": "rot_abc", "grace_seconds": 86400, "rotated_by": "user_5" },
    "occurred_at": "2026-05-14T09:12:44Z"
  },
  {
    "id": "ca_01h9z6jp",
    "event_type": "USE",
    "agent_id": "agent_backend",
    "ip_address": "10.0.4.21",
    "metadata": null,
    "occurred_at": "2026-05-14T09:11:02Z"
  }
]
StatusCondition
403Insufficient role
404Credential not found (or in another workspace)

Rotate Credential

POST /api/v1/credentials/{credentialId}/rotate
Issues a new value and starts a configurable grace overlap window. The previous encrypted value is preserved on a new credential_rotations row for sidecar 401-fallback during the grace window, then scrubbed when status transitions to EXPIRED or CANCELLED. (internal/api/credential_rotation.go:77) Auth: OWNER or ADMIN role (canRole(role, "manage")) Request Body:
FieldTypeRequiredDefaultDescription
valuestringYesNew secret value (will be encrypted with AES-256-GCM)
grace_secondsintegerNo86400Grace window in seconds. Range 0..604800 (7 days).
{
  "value": "sk-ant-api03-newvalue",
  "grace_seconds": 86400
}
Response: 200 OK
{
  "id": "rot_01h9z7k0abc",
  "credential_id": "cred_abc",
  "grace_seconds": 86400,
  "rotated_at": "2026-05-14T09:12:44Z",
  "expires_at": "2026-05-15T09:12:44Z",
  "rotated_by": "user_5",
  "status": "ACTIVE",
  "old_value_gone": false
}
StatusCondition
400Missing value, or grace_seconds outside 0..604800
401No authenticated user on the request
403Insufficient role
404Credential not found

List Rotations

GET /api/v1/credentials/{credentialId}/rotations
Returns the rotation history for a credential, newest first. Powers the Settings tab in the detail Sheet. (internal/api/credential_rotation.go:209) Auth: Session or CLI token + workspace membership Response: 200 OK — JSON array.
[
  {
    "id": "rot_01h9z7k0abc",
    "credential_id": "cred_abc",
    "grace_seconds": 86400,
    "rotated_at": "2026-05-14T09:12:44Z",
    "expires_at": "2026-05-15T09:12:44Z",
    "rotated_by": "user_5",
    "status": "ACTIVE",
    "old_value_gone": false
  }
]
old_value_gone is true once status is EXPIRED or CANCELLED (the old encrypted value has been scrubbed from the row).
StatusCondition
404Credential not found

Rotation Statuses

StatusDescription
ACTIVEGrace window open; sidecar may fall back to old_value on 401
EXPIREDGrace window elapsed; old_value scrubbed by the hourly expiry worker
CANCELLEDOperator ended grace early via DELETE /credential-rotations/{rotationId}; old_value scrubbed

Cancel Rotation (End Grace Early)

DELETE /api/v1/credential-rotations/{rotationId}
Ends an ACTIVE grace overlap immediately, scrubbing the stored old value. Idempotent: already-terminal rotations return 200 with the existing status. (internal/api/credential_rotation.go:258) Auth: OWNER or ADMIN role (canRole(role, "manage")) Response: 200 OK
{ "status": "CANCELLED" }
For an already-terminal rotation:
{ "status": "EXPIRED", "message": "rotation already terminal" }
StatusCondition
403Insufficient role
404Rotation not found, or its credential lives in another workspace

Default Environment Variable

GET /api/v1/credentials/default-env-var?provider={provider}
Returns the default environment variable name for a given provider (e.g., GH_TOKEN for GITHUB). The handler reads only provider; the type query parameter is not consulted (internal/api/credentials.go:261). Auth: Session or CLI token (no workspace context needed) Response: 200 OK
{ "env_var": "GH_TOKEN" }

Agent Credential Assignment

See the Agents page for credential assignment endpoints:
EndpointMethodDescription
GET /api/v1/agents/{agentId}/credentialsGETList assigned credentials
POST /api/v1/agents/{agentId}/credentialsPOSTAssign credential to agent
DELETE /api/v1/agents/{agentId}/credentials/{assignmentId}DELETERemove assignment

Credential Types

TypeDescription
AI_CLI_TOKENLLM API key for agent CLI adapters
API_KEYExternal service API key
SECRETArbitrary secret value
OAUTH2OAuth 2.0 token (managed via OAuth flow)

Providers

ProviderDescription
ANTHROPICAnthropic (Claude)
OPENAIOpenAI (GPT)
GOOGLEGoogle (Gemini)
GITHUBGitHub
SLACKSlack
NONEGeneric / custom

Statuses

StatusDescription
ACTIVEReady to use
PENDINGAwaiting OAuth completion
RATE_LIMITEDTemporarily rate-limited
EXPIREDNeeds renewal
REVOKEDManually revoked
ERRORValidation/connection test failed