Keep the personal half of the desktop in one place, and ask before installing it

Agent instructions, skills, SSH host aliases and expansion triggers are worth
having identical on every machine one person owns, and belong in none of the
shared configuration. They live in user/ now, with a manifest saying where each
piece goes and a link-user stage that puts it there.

That stage does nothing unless the machine said yes. Somebody who clones Panama
to try the desktop keeps their own ~/.claude/CLAUDE.md exactly where it was;
the question names the destinations and defaults to no. Anything displaced goes
to config/old rather than being deleted.

~/.claude/CLAUDE.md and ~/.codex/AGENTS.md were byte-identical copies of one
file, which is the drift this exists to prevent.

Also adds the vitals toggles for the battery and Claude usage readouts, which
had preferences and no way to reach them.
This commit is contained in:
Gabriel Brown
2026-08-22 08:54:43 -04:00
parent 8b96d907a1
commit 89761a7da3
156 changed files with 16439 additions and 6 deletions
@@ -0,0 +1,180 @@
# Authentication
Infisical supports multiple authentication methods. Machine identity Universal Auth is the recommended approach for production use.
## Universal Auth (Recommended)
Universal Auth is the preferred machine identity authentication method for all use cases.
### Login Endpoint
```
POST /api/v1/auth/universal-auth/login
```
### Request Body
```json
{
"clientId": "string",
"clientSecret": "string"
}
```
### Response
```json
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 3600,
"accessTokenMaxTTL": 86400,
"tokenType": "Bearer"
}
```
### Example cURL
```bash
curl -X POST https://us.infisical.com/api/v1/auth/universal-auth/login \
-H "Content-Type: application/json" \
-d '{
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET"
}'
```
### Using the Token
Include the token in all subsequent requests as a Bearer token:
```bash
curl -X GET https://us.infisical.com/api/v4/secrets?projectId=PROJECT_ID&environment=dev \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Alternative Auth Methods
Infisical supports additional authentication methods for machine identities:
### AWS Auth
```
POST /api/v1/auth/aws-auth/login
```
Login with AWS IAM credentials. Useful for AWS-hosted applications.
### Azure Auth
```
POST /api/v1/auth/azure-auth/login
```
Login with Azure managed identity. Ideal for Azure-hosted applications.
### GCP Auth
```
POST /api/v1/auth/gcp-auth/login
```
Login with GCP service account. Recommended for Google Cloud deployments.
### Kubernetes Auth
```
POST /api/v1/auth/kubernetes-auth/login
```
Login with Kubernetes service account token. Perfect for containerized workloads.
### OIDC Auth
```
POST /api/v1/auth/oidc-auth/login
```
Login via OpenID Connect provider. Supports any OIDC-compliant provider.
### JWT Auth
```
POST /api/v1/auth/jwt-auth/login
```
Login with custom JWT. Useful for custom authentication systems.
### LDAP Auth
```
POST /api/v1/auth/ldap-auth/login
```
Login with LDAP credentials. Enterprise directory integration.
## Token Refresh
Access tokens expire after the `expiresIn` seconds returned in the login response. For long-lived integrations, implement token refresh logic:
```javascript
// Pseudocode for token refresh
let tokenExpiresAt = Date.now() + (expiresIn * 1000);
async function getValidToken() {
if (Date.now() >= tokenExpiresAt - 60000) {
// Refresh within 1 minute of expiry
const response = await login(clientId, clientSecret);
token = response.accessToken;
tokenExpiresAt = Date.now() + (response.expiresIn * 1000);
}
return token;
}
```
The `accessTokenMaxTTL` value indicates the maximum lifetime of the token from issuance (typically 24 hours), which may be shorter than the server's token validity window.
## Deprecated: Service Tokens
Service tokens (prefixed with `st.`) are deprecated and should not be used in new code. They lack:
- Fine-grained permission controls
- Machine identity features
- Audit logging capabilities
- Rotation enforcement
Migrate all service token usage to machine identities with Universal Auth.
## Region Selection
Choose the appropriate Infisical region endpoint:
- **US Region**: `https://us.infisical.com`
- **EU Region**: `https://eu.infisical.com`
- **Self-Hosted**: Use your custom domain (e.g., `https://secrets.mycompany.com`)
## Headers
All authentication requests must include:
```
Content-Type: application/json
```
## Common Issues
### 401 Unauthorized
- Verify clientId and clientSecret are correct
- Confirm the token hasn't expired
- Check that the Bearer token is included in the Authorization header
### 403 Forbidden
- Machine identity may not have permission for the requested resource
- Verify identity auth method is configured for the project
- Check role-based access controls (RBAC) in the project
### 404 Not Found
- Confirm you're using the correct endpoint URL
- Verify the projectId exists and is accessible
- Check that the region (us.infisical.com vs eu.infisical.com) matches your deployment
@@ -0,0 +1,287 @@
# Pagination and Rate Limits
## Pagination
Infisical uses offset-based pagination for list endpoints. All responses include pagination metadata.
### Pagination Parameters
| Parameter | Type | Default | Max | Description |
|-----------|------|---------|-----|-------------|
| offset | integer | 0 | - | Number of items to skip from the beginning |
| limit | integer | 20 | 100 | Maximum number of items to return in this request |
### Pagination Response
```json
{
"items": [...],
"total": 150,
"offset": 0,
"limit": 20
}
```
- **total**: Total count of all available items (ignoring pagination)
- **offset**: Requested offset
- **limit**: Requested limit (may be less if fewer items available)
- **items**: Array of results for this page
### Example: Paginating Through All Results
```bash
#!/bin/bash
# Retrieve all secrets in batches of 20
offset=0
limit=20
total=-1
while [ $offset -lt $total ] || [ $total -eq -1 ]; do
response=$(curl -s "https://us.infisical.com/api/v4/secrets?projectId=abc123&environment=dev&offset=$offset&limit=$limit" \
-H "Authorization: Bearer TOKEN")
# Extract items and total from response
total=$(echo $response | jq '.total')
items=$(echo $response | jq '.secrets[]')
# Process items
echo "Processing items $offset to $((offset + limit))..."
offset=$((offset + limit))
done
```
### Pagination Best Practices
1. **Start with offset=0**: Always begin pagination at offset 0
2. **Use maximum limit**: Set `limit=100` for faster retrieval (unless you need fewer items)
3. **Check total**: Use the `total` value to determine if more pages exist: `hasMore = (offset + limit) < total`
4. **Handle edge cases**: Always check if `limit` in response is less than requested (indicates fewer items available)
5. **Respect rate limits**: Add delays between requests if hitting rate limits
## Rate Limits (Cloud Only)
Infisical Cloud deployments have rate limits. Self-hosted deployments have no rate limits.
### Rate Limit Types
#### Read Operations (GET, LIST)
- **Free Tier**: 200 reads per minute
- **Pro Tier**: 350 reads per minute
- **Enterprise**: Custom limits
#### Write Operations (CREATE, UPDATE, DELETE)
- **Free Tier**: 90 writes per minute
- **Pro Tier**: 200 writes per minute
- **Enterprise**: Custom limits
#### Secret Operations (All /api/v4/secrets/* endpoints)
- **Free Tier**: 120 secret ops per minute
- **Pro Tier**: 300 secret ops per minute
- **Enterprise**: Custom limits
### Rate Limit Response Headers
When you hit a rate limit, the API returns HTTP 429 (Too Many Requests):
```
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1713350400
Content-Type: application/json
{
"statusCode": 429,
"message": "Too many requests, please try again later."
}
```
- **X-RateLimit-Limit**: Maximum requests allowed in the window
- **X-RateLimit-Remaining**: Requests remaining in the current window
- **X-RateLimit-Reset**: Unix timestamp when the limit resets
### Handling Rate Limits
#### Implement Exponential Backoff
```javascript
async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const resetTime = parseInt(response.headers.get('X-RateLimit-Reset')) * 1000;
const delayMs = Math.max(resetTime - Date.now(), 1000 * Math.pow(2, attempt - 1));
console.log(`Rate limited. Waiting ${delayMs}ms before retry...`);
await new Promise(resolve => setTimeout(resolve, delayMs));
continue;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
throw new Error('Max retries exceeded');
}
```
#### Monitor Rate Limit Usage
```bash
curl -s 'https://us.infisical.com/api/v4/secrets?projectId=abc123&environment=dev&limit=1' \
-H "Authorization: Bearer TOKEN" \
-w "\nRate Limit Remaining: %{http_header{X-RateLimit-Remaining}}\n"
```
#### Batch Operations
Group multiple operations to reduce request count:
```bash
# Instead of 100 DELETE requests, use one batch delete
curl -X DELETE 'https://us.infisical.com/api/v4/secrets/batch' \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "abc123",
"environment": "dev",
"secretPath": "/",
"secretIds": ["id1", "id2", "id3", ...]
}'
```
#### Request Queuing
Implement a request queue to spread requests over time:
```python
import asyncio
import aiohttp
from collections import deque
class RateLimitedClient:
def __init__(self, requests_per_minute=200):
self.requests_per_minute = requests_per_minute
self.min_interval = 60 / requests_per_minute
self.last_request_time = 0
self.queue = deque()
async def request(self, session, method, url, **kwargs):
# Wait if necessary to maintain rate limit
elapsed = asyncio.get_event_loop().time() - self.last_request_time
if elapsed < self.min_interval:
await asyncio.sleep(self.min_interval - elapsed)
async with session.request(method, url, **kwargs) as response:
self.last_request_time = asyncio.get_event_loop().time()
return await response.json()
```
## Required Headers
All API requests must include:
```
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN
```
### Example: Complete Request with Headers
```bash
curl -X GET 'https://us.infisical.com/api/v4/secrets?projectId=abc123&environment=dev&offset=0&limit=20' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc..."
```
## HTTP Status Codes
| Code | Meaning | When It Occurs |
|------|---------|----------------|
| 200 | OK | Successful GET, PATCH, DELETE |
| 201 | Created | Successful POST |
| 400 | Bad Request | Invalid parameters or request body |
| 401 | Unauthorized | Missing or invalid token |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Duplicate secret name or resource conflict |
| 429 | Too Many Requests | Rate limit exceeded (cloud only) |
| 500 | Internal Error | Server error |
## Performance Tips
1. **Use pagination**: Limit each request to 100 items maximum
2. **Cache responses**: Store secret values locally to reduce API calls
3. **Use appropriate timeouts**: Set 30-second timeouts for API calls
4. **Batch operations**: Combine multiple operations into single requests where possible
5. **Monitor headers**: Check X-RateLimit-Remaining to anticipate throttling
6. **Implement exponential backoff**: Automatically retry failed requests with increasing delays
7. **Use webhooks**: Subscribe to changes instead of polling for updates (if available)
## Example: Comprehensive Pagination with Error Handling
```bash
#!/bin/bash
PROJECT_ID="abc123"
ENVIRONMENT="dev"
API_BASE="https://us.infisical.com"
TOKEN="your_access_token"
BATCH_SIZE=100
offset=0
total_processed=0
while true; do
# Make request with error handling
response=$(curl -s -w "\n%{http_code}" \
"$API_BASE/api/v4/secrets?projectId=$PROJECT_ID&environment=$ENVIRONMENT&offset=$offset&limit=$BATCH_SIZE" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json")
# Extract body and status code
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
# Check for errors
if [ "$http_code" = "429" ]; then
reset_time=$(curl -s -I "$API_BASE/api/v4/secrets?projectId=$PROJECT_ID&environment=$ENVIRONMENT" \
-H "Authorization: Bearer $TOKEN" | grep X-RateLimit-Reset | awk '{print $2}')
echo "Rate limited. Waiting until $reset_time..."
sleep 60
continue
elif [ "$http_code" != "200" ]; then
echo "Error: HTTP $http_code"
echo "$body" | jq .
exit 1
fi
# Process response
total=$(echo "$body" | jq '.total')
count=$(echo "$body" | jq '.secrets | length')
echo "Processing items $offset-$((offset + count)) of $total..."
# Do something with the secrets
echo "$body" | jq '.secrets[] | .secretName'
total_processed=$((total_processed + count))
# Check if we've retrieved all items
if [ $total_processed -ge $total ]; then
break
fi
offset=$((offset + BATCH_SIZE))
done
echo "Processed $total_processed items total"
```
@@ -0,0 +1,496 @@
# Projects and Identities
## Projects
Projects are containers for secrets, environments, and team members. Always use `/api/v1/projects` (not the deprecated `/api/v1/workspace`).
### List Projects
#### Endpoint
```
GET /api/v1/projects
```
#### Query Parameters
| Parameter | Type | Default | Max | Description |
|-----------|------|---------|-----|-------------|
| offset | integer | 0 | - | Number of items to skip |
| limit | integer | 20 | 100 | Number of items to return |
#### Response
```json
{
"projects": [
{
"id": "project-id-uuid",
"name": "My Project",
"slug": "my-project",
"createdAt": "2026-04-01T10:00:00.000Z",
"updatedAt": "2026-04-16T10:30:00.000Z",
"version": 1
}
],
"total": 5
}
```
#### Example
```bash
curl -X GET 'https://us.infisical.com/api/v1/projects?offset=0&limit=20' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
### Get Project
#### Endpoint
```
GET /api/v1/projects/{projectId}
```
#### Response
```json
{
"project": {
"id": "project-id-uuid",
"name": "My Project",
"slug": "my-project",
"createdAt": "2026-04-01T10:00:00.000Z",
"updatedAt": "2026-04-16T10:30:00.000Z",
"version": 1,
"environments": [
{
"id": "env-id",
"name": "Development",
"slug": "dev",
"version": 1
},
{
"id": "env-id-2",
"name": "Production",
"slug": "prod",
"version": 1
}
]
}
}
```
#### Example
```bash
curl -X GET 'https://us.infisical.com/api/v1/projects/abc123' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
### Create Project
#### Endpoint
```
POST /api/v1/projects
```
#### Request Body
```json
{
"name": "string",
"slug": "string (optional)"
}
```
#### Response
Returns the created project object.
#### Example
```bash
curl -X POST 'https://us.infisical.com/api/v1/projects' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "New Project",
"slug": "new-project"
}'
```
### Update Project
#### Endpoint
```
PATCH /api/v1/projects/{projectId}
```
#### Request Body
```json
{
"name": "string (optional)",
"slug": "string (optional)"
}
```
#### Example
```bash
curl -X PATCH 'https://us.infisical.com/api/v1/projects/abc123' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Project Name"
}'
```
### Delete Project
#### Endpoint
```
DELETE /api/v1/projects/{projectId}
```
#### Example
```bash
curl -X DELETE 'https://us.infisical.com/api/v1/projects/abc123' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Environments
Environments (dev, staging, prod) organize secrets by deployment target.
### List Project Environments
#### Endpoint
```
GET /api/v1/projects/{projectId}/environments
```
#### Response
```json
{
"environments": [
{
"id": "env-id-uuid",
"name": "Development",
"slug": "dev",
"version": 1
},
{
"id": "env-id-2",
"name": "Production",
"slug": "prod",
"version": 1
}
]
}
```
#### Example
```bash
curl -X GET 'https://us.infisical.com/api/v1/projects/abc123/environments' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Project Members
Manage who has access to a project and their role.
### List Project Members
#### Endpoint
```
GET /api/v1/projects/{projectId}/memberships
```
#### Query Parameters
| Parameter | Type | Default | Max |
|-----------|------|---------|-----|
| offset | integer | 0 | - |
| limit | integer | 20 | 100 |
#### Response
```json
{
"memberships": [
{
"id": "membership-id",
"projectId": "project-id",
"userId": "user-id",
"user": {
"id": "user-id",
"email": "[email protected]"
},
"role": "admin"
}
],
"total": 3
}
```
#### Example
```bash
curl -X GET 'https://us.infisical.com/api/v1/projects/abc123/memberships' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Machine Identities
Machine identities allow non-human accounts to authenticate and access secrets.
### List Identities
#### Endpoint
```
GET /api/v1/identities
```
#### Query Parameters
| Parameter | Type | Default | Max | Description |
|-----------|------|---------|-----|-------------|
| offset | integer | 0 | - | Number to skip |
| limit | integer | 20 | 100 | Number to return |
#### Response
```json
{
"identities": [
{
"id": "identity-id-uuid",
"name": "Production API",
"createdAt": "2026-04-01T10:00:00.000Z",
"updatedAt": "2026-04-16T10:30:00.000Z"
}
],
"total": 1
}
```
#### Example
```bash
curl -X GET 'https://us.infisical.com/api/v1/identities?offset=0&limit=20' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
### Get Identity
#### Endpoint
```
GET /api/v1/identities/{identityId}
```
#### Response
```json
{
"identity": {
"id": "identity-id-uuid",
"name": "Production API",
"universalAuthClientId": "machine-identity-uuid",
"createdAt": "2026-04-01T10:00:00.000Z",
"updatedAt": "2026-04-16T10:30:00.000Z"
}
}
```
### Create Identity
#### Endpoint
```
POST /api/v1/identities
```
#### Request Body
```json
{
"name": "string"
}
```
### Update Identity
#### Endpoint
```
PATCH /api/v1/identities/{identityId}
```
#### Request Body
```json
{
"name": "string (optional)"
}
```
### Delete Identity
#### Endpoint
```
DELETE /api/v1/identities/{identityId}
```
## Identity Auth Methods
Configure how machine identities authenticate.
### Universal Auth (Recommended)
#### Endpoint
```
GET /api/v1/auth/universal-auth/identities/{identityId}
POST /api/v1/auth/universal-auth/identities/{identityId}
DELETE /api/v1/auth/universal-auth/identities/{identityId}
```
#### Example: Get Universal Auth Config
```bash
curl -X GET 'https://us.infisical.com/api/v1/auth/universal-auth/identities/identity-id' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
Response includes `clientId` and regenerated `clientSecret`.
#### AWS Auth
```
POST /api/v1/auth/aws-auth/identities/{identityId}
PATCH /api/v1/auth/aws-auth/identities/{identityId}
```
#### Azure Auth
```
POST /api/v1/auth/azure-auth/identities/{identityId}
PATCH /api/v1/auth/azure-auth/identities/{identityId}
```
#### GCP Auth
```
POST /api/v1/auth/gcp-auth/identities/{identityId}
PATCH /api/v1/auth/gcp-auth/identities/{identityId}
```
#### Kubernetes Auth
```
POST /api/v1/auth/kubernetes-auth/identities/{identityId}
PATCH /api/v1/auth/kubernetes-auth/identities/{identityId}
```
## Groups
Organize machine identities and manage permissions at scale.
### Endpoint
```
GET /api/v1/groups
POST /api/v1/groups
GET /api/v1/groups/{groupId}
PATCH /api/v1/groups/{groupId}
DELETE /api/v1/groups/{groupId}
```
### Example: List Groups
```bash
curl -X GET 'https://us.infisical.com/api/v1/groups' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Folders
Organize secrets into hierarchical folder structures.
### Endpoint
```
GET /api/v2/folders
POST /api/v2/folders
PATCH /api/v2/folders/{folderId}
DELETE /api/v2/folders/{folderId}
```
### List Folders
```bash
curl -X GET 'https://us.infisical.com/api/v2/folders?projectId=abc123&environment=dev' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Secret Imports
Import secrets from one environment into another.
### Endpoint
```
GET /api/v2/secret-imports
POST /api/v2/secret-imports
PATCH /api/v2/secret-imports/{importId}
DELETE /api/v2/secret-imports/{importId}
```
### Example: List Secret Imports
```bash
curl -X GET 'https://us.infisical.com/api/v2/secret-imports?projectId=abc123&environment=dev' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Deprecated Endpoints
**Do not use these endpoints in new code:**
- `/api/v1/workspace/*` — Use `/api/v1/projects` instead
- Service token endpoints — Use machine identities with Universal Auth instead
## Common Workflows
### Set Up a New Machine Identity
1. Create identity: `POST /api/v1/identities` → get `identityId`
2. Configure auth: `POST /api/v1/auth/universal-auth/identities/{identityId}`
3. Login: `POST /api/v1/auth/universal-auth/login` with `clientId` and `clientSecret`
4. Use returned `accessToken` for all subsequent API calls
### Add Identity to Project
1. Create identity and auth method (see above)
2. Create a folder/path in the target project
3. Create project membership or use RBAC rules to grant access
4. Test login with the new credentials
### Organize Secrets with Folders
1. Create folder: `POST /api/v2/folders` with `projectId`, `environment`, `folderName`
2. Create secrets under folder: `POST /api/v4/secrets/SECRET_NAME` with `secretPath: "/folder-name"`
3. List secrets in folder: `GET /api/v4/secrets?secretPath=/folder-name`
@@ -0,0 +1,338 @@
# Secrets Endpoints
All secret operations use `/api/v4/secrets`. Previous API versions (v1, v2, v3) are deprecated.
## List Secrets
### Endpoint
```
GET /api/v4/secrets
```
### Query Parameters
| Parameter | Type | Required | Default | Max | Description |
|-----------|------|----------|---------|-----|-------------|
| projectId | string | Yes | - | - | ID of the project |
| environment | string | Yes | - | - | Environment slug (e.g., "dev", "prod") |
| secretPath | string | No | "/" | - | Secret folder path (e.g., "/database", "/") |
| offset | integer | No | 0 | - | Number of items to skip for pagination |
| limit | integer | No | 20 | 100 | Number of items to return per page |
| viewSecretValue | boolean | No | false | - | Include plaintext secret values in response |
| expandSecretReferences | boolean | No | false | - | Expand secret references (e.g., `${OTHER_SECRET}`) |
| recursive | boolean | No | false | - | Include secrets from all subdirectories |
| includeImports | boolean | No | false | - | Include secrets from imported secret environments |
| tagSlugs | string | No | - | - | Comma-separated tag slugs to filter by |
| metadataFilter | string | No | - | - | JSON filter for metadata-based search |
### Response
```json
{
"secrets": [
{
"id": "secret-id-uuid",
"version": 1,
"workspace": "workspace-id",
"project": "project-id",
"environment": "dev",
"secretPath": "/",
"secretName": "DATABASE_URL",
"secretValue": "postgres://user:pass@localhost/db",
"secretComment": "Production database connection",
"type": "shared",
"tags": [
{
"id": "tag-id",
"slug": "database",
"name": "Database",
"color": "#3b82f6"
}
],
"createdAt": "2026-04-16T10:30:00.000Z",
"updatedAt": "2026-04-16T10:30:00.000Z",
"createdBy": "user-id"
}
],
"total": 42,
"offset": 0,
"limit": 20
}
```
### Example
```bash
curl -X GET 'https://us.infisical.com/api/v4/secrets?projectId=abc123&environment=dev&offset=0&limit=20&viewSecretValue=true' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Get Secret
### Endpoint
```
GET /api/v4/secrets/{secretName}
```
### Query Parameters
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| projectId | string | Yes | - | ID of the project |
| environment | string | Yes | - | Environment slug |
| secretPath | string | No | "/" | Secret folder path |
### Response
```json
{
"secret": {
"id": "secret-id-uuid",
"version": 1,
"workspace": "workspace-id",
"project": "project-id",
"environment": "dev",
"secretPath": "/",
"secretName": "API_KEY",
"secretValue": "sk_live_abc123def456ghi789",
"secretComment": "Third-party API key",
"type": "shared",
"tags": [],
"createdAt": "2026-04-16T10:30:00.000Z",
"updatedAt": "2026-04-16T10:30:00.000Z",
"createdBy": "user-id"
}
}
```
### Example
```bash
curl -X GET 'https://us.infisical.com/api/v4/secrets/API_KEY?projectId=abc123&environment=dev&secretPath=/' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Create Secret
### Endpoint
```
POST /api/v4/secrets/{secretName}
```
### Request Body
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| projectId | string | Yes | ID of the project |
| environment | string | Yes | Environment slug |
| secretPath | string | No | Secret folder path (default: "/") |
| secretValue | string | Yes | The secret value (plaintext) |
| type | string | No | "shared" or "personal" (default: "shared") |
| tagIds | array | No | List of tag IDs to attach |
| secretComment | string | No | Comment/description for the secret |
### Response
```json
{
"secret": {
"id": "secret-id-uuid",
"version": 1,
"workspace": "workspace-id",
"project": "project-id",
"environment": "dev",
"secretPath": "/",
"secretName": "NEW_SECRET",
"secretValue": "super-secret-value",
"secretComment": "My new secret",
"type": "shared",
"tags": [],
"createdAt": "2026-04-16T10:30:00.000Z",
"updatedAt": "2026-04-16T10:30:00.000Z",
"createdBy": "user-id"
}
}
```
### Example
```bash
curl -X POST 'https://us.infisical.com/api/v4/secrets/DATABASE_PASSWORD' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "abc123",
"environment": "dev",
"secretPath": "/",
"secretValue": "my-secure-password",
"type": "shared",
"secretComment": "Database password for dev environment"
}'
```
## Update Secret
### Endpoint
```
PATCH /api/v4/secrets/{secretName}
```
### Request Body
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| projectId | string | Yes | ID of the project |
| environment | string | Yes | Environment slug |
| secretPath | string | No | Secret folder path |
| secretValue | string | No | New secret value |
| secretComment | string | No | Updated comment/description |
| tagIds | array | No | Updated list of tag IDs |
### Response
Same as Create Secret response.
### Example
```bash
curl -X PATCH 'https://us.infisical.com/api/v4/secrets/DATABASE_PASSWORD' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "abc123",
"environment": "dev",
"secretPath": "/",
"secretValue": "new-secure-password"
}'
```
## Delete Secret
### Endpoint
```
DELETE /api/v4/secrets/{secretName}
```
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| projectId | string | Yes | ID of the project |
| environment | string | Yes | Environment slug |
| secretPath | string | No | Secret folder path (default: "/") |
### Response
```json
{
"secret": {
"id": "secret-id-uuid",
"secretName": "DELETED_SECRET"
}
}
```
### Example
```bash
curl -X DELETE 'https://us.infisical.com/api/v4/secrets/OLD_SECRET?projectId=abc123&environment=dev&secretPath=/' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
## Batch Delete Secrets
### Endpoint
```
DELETE /api/v4/secrets/batch
```
### Request Body
```json
{
"projectId": "string",
"environment": "string",
"secretPath": "string",
"secretIds": ["uuid1", "uuid2", "uuid3"]
}
```
### Response
```json
{
"deletedSecrets": [
{
"id": "uuid1",
"secretName": "SECRET_1"
},
{
"id": "uuid2",
"secretName": "SECRET_2"
}
]
}
```
### Example
```bash
curl -X DELETE 'https://us.infisical.com/api/v4/secrets/batch' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "abc123",
"environment": "dev",
"secretPath": "/",
"secretIds": ["id1-uuid", "id2-uuid"]
}'
```
## Important Notes
### API Version
- Use `/api/v4/secrets` for all new code
- `/api/v1/secrets`, `/api/v2/secrets`, and `/api/v3/secrets` are deprecated
- Migrate existing integrations to v4 endpoints
### Secret Names
- Must be unique within the environment and secret path
- Use uppercase with underscores (e.g., `DATABASE_PASSWORD`)
- Cannot contain spaces or special characters
### Secret Types
- **shared**: Visible to all project members with appropriate permissions
- **personal**: Only visible to the user who created it
### Secret Values
- Plaintext strings only
- For large values, base64-encode before creating
- References using `${SECRET_NAME}` syntax are supported when `expandSecretReferences=true`
### Tags
- Secrets can have multiple tags
- Tags are organization-wide but applied per secret
- Use `tagSlugs` parameter to filter list results by tag
### Pagination
- Always specify `offset` and `limit` for predictable results
- Default limit is 20; maximum is 100
- Use `total` to determine remaining items: `hasMore = (offset + limit) < total`
### Performance
- For listing many secrets (>1000), use pagination with `limit=100`
- Avoid `viewSecretValue=true` on large lists unless values are needed
- Use `recursive=false` by default for better performance