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
| Field | Type | Description |
|---|
id | string | Credential ID |
name | string | Display name |
description | string? | Description |
type | string | Credential type |
provider | string | Provider identifier |
status | string | Current status |
scope | string | WORKSPACE or CREW |
crew_id | string? | Legacy single crew ID |
crew_ids | string[] | All associated crew IDs |
account_label | string? | Account label |
account_email | string? | Account email |
token_expires_at | string? | Token expiry timestamp |
last_checked_at | string? | Last health check timestamp |
last_error | string? | Last error message |
_count_agent_credentials | integer | Number of agent assignments |
agent_names | string[] | Names of assigned agents |
mcp_used | boolean | Whether used by MCP bindings |
username | string? | USERPASS cleartext identifier (null for all other types) |
last_used_at | string? | Latest USE event recorded by the audit timeline |
last_used_ips | string[] | Ring-buffer (max 5) of recent caller IPs |
tags | string[] | Free-form tag labels (always non-null in JSON) |
created_at | string | ISO 8601 timestamp |
updated_at | string | ISO 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:
| Field | Type | Required | Default | Description |
|---|
name | string | Yes | — | Display name (1-255 characters) |
value | string | Conditional | — | Secret value (required unless type is OAUTH2, or pending = true) |
description | string | No | null | Description |
type | string | No | "SECRET" | Credential type |
provider | string | No | "NONE" | Provider identifier |
scope | string | No | "WORKSPACE" | "WORKSPACE" or "CREW" |
crew_id | string | No | null | Legacy single crew ID |
crew_ids | string[] | No | [] | Associated crew IDs (auto-sets scope to CREW) |
tags | string[] | No | [] | Free-form tag labels |
account_label | string | No | null | Account label |
account_email | string | No | null | Account email |
username | string | No | null | Cleartext identifier half of USERPASS credentials (required when type is USERPASS) |
token_expires_at | string | No | null | Token expiry timestamp |
security_level | integer | No | 1 | Security level (1-3) |
refresh_token | string | No | null | OAuth refresh token |
oauth_client_id | string | No | null | OAuth client ID (OAUTH2 type only) |
oauth_client_secret | string | No | null | OAuth client secret |
oauth_auth_url | string | No | null | OAuth authorization URL |
oauth_token_url | string | No | null | OAuth token URL |
oauth_scopes | string | No | null | OAuth scopes (space-separated) |
pending | boolean | No | false | Create 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).
| Status | Condition |
|---|
400 | Missing name, missing value (non-OAuth2), invalid crew_id, invalid type/USERPASS without username |
403 | Insufficient role (requires MANAGER, ADMIN, or OWNER) |
409 | Credential 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.
| Status | Condition |
|---|
404 | Credential 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:
| Field | Type | Description |
|---|
name | string | Display name |
description | string | Description |
value | string | New secret value (re-encrypted; resets status to ACTIVE and records an inline-rotate audit event) |
type | string | Credential type |
provider | string | Provider identifier |
scope | string | WORKSPACE or CREW |
crew_id | string | Legacy single crew ID |
crew_ids | string[] | Associated crew IDs (replaces existing) |
tags | string[] | Free-form tag labels (empty array / null clears the column) |
account_label | string | Account label |
account_email | string | Account email |
username | string | USERPASS cleartext identifier |
token_expires_at | string | Token expiry timestamp |
security_level | integer | Security 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.
| Status | Condition |
|---|
400 | No fields to update, invalid crew_id |
403 | Insufficient role |
404 | Credential 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
| Status | Condition |
|---|
403 | Insufficient role |
404 | Credential 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)
| Field | Type | Required | Description |
|---|
provider | string | No | Provider to test against (drives which API is probed) |
type | string | No | Credential type (used to special-case Anthropic OAuth tokens) |
value | string | Yes | Value 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 }.
| Status | Condition |
|---|
403 | Insufficient role |
404 | Credential 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:
| Parameter | Type | Default | Description |
|---|
limit | integer | 50 | 1-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"
}
]
| Status | Condition |
|---|
403 | Insufficient role |
404 | Credential 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:
| Field | Type | Required | Default | Description |
|---|
value | string | Yes | — | New secret value (will be encrypted with AES-256-GCM) |
grace_seconds | integer | No | 86400 | Grace 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
}
| Status | Condition |
|---|
400 | Missing value, or grace_seconds outside 0..604800 |
401 | No authenticated user on the request |
403 | Insufficient role |
404 | Credential 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).
| Status | Condition |
|---|
404 | Credential not found |
Rotation Statuses
| Status | Description |
|---|
ACTIVE | Grace window open; sidecar may fall back to old_value on 401 |
EXPIRED | Grace window elapsed; old_value scrubbed by the hourly expiry worker |
CANCELLED | Operator 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" }
| Status | Condition |
|---|
403 | Insufficient role |
404 | Rotation 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:
| Endpoint | Method | Description |
|---|
GET /api/v1/agents/{agentId}/credentials | GET | List assigned credentials |
POST /api/v1/agents/{agentId}/credentials | POST | Assign credential to agent |
DELETE /api/v1/agents/{agentId}/credentials/{assignmentId} | DELETE | Remove assignment |
Credential Types
| Type | Description |
|---|
AI_CLI_TOKEN | LLM API key for agent CLI adapters |
API_KEY | External service API key |
SECRET | Arbitrary secret value |
OAUTH2 | OAuth 2.0 token (managed via OAuth flow) |
Providers
| Provider | Description |
|---|
ANTHROPIC | Anthropic (Claude) |
OPENAI | OpenAI (GPT) |
GOOGLE | Google (Gemini) |
GITHUB | GitHub |
SLACK | Slack |
NONE | Generic / custom |
Statuses
| Status | Description |
|---|
ACTIVE | Ready to use |
PENDING | Awaiting OAuth completion |
RATE_LIMITED | Temporarily rate-limited |
EXPIRED | Needs renewal |
REVOKED | Manually revoked |
ERROR | Validation/connection test failed |