Authentication
Manage API keys and verify identity.
All auth endpoints support session authentication (browser cookie) with API key fallback. This means you can call these endpoints from the browser (using your Meteor login session) or from scripts and external clients (using a valid API key in the X-API-Key header).
POST /api/v1/auth/api-keys
Create a new API key
Returns the raw key value once — store it securely.
Authentication: Session (browser cookie) or API key
Request Body:
{
"name": "My CI/CD Key",
"scopes": ["apps:read", "apps:deploy", "workflows:execute"],
"expiresInDays": 90
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable key name (non-empty) |
scopes | string[] | Yes | Permission scopes (non-empty array; see Scopes) |
expiresInDays | integer | No | Days until expiration. Omit for no expiration |
Response: 201 Created
{
"data": {
"id": "67a1b2c3d4e5f6a7b8c9d0e1",
"key": "sk-prod-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"name": "My CI/CD Key",
"keyPrefix": "sk-prod-a1b2",
"scopes": ["apps:read", "apps:deploy", "workflows:execute"],
"expiresAt": "2025-05-07T00:00:00.000Z",
"createdAt": "2025-02-07T00:00:00.000Z"
},
"meta": { "requestId": "req_abc123" }
}
| Field | Type | Description |
|---|---|---|
id | string | MongoDB document ID |
key | string | The raw API key (only returned once) |
name | string | Key name |
keyPrefix | string | First 12 characters of the key |
scopes | string[] | Assigned permission scopes |
expiresAt | string | null | ISO-8601 expiration date, or null if no expiration |
createdAt | string | ISO-8601 creation date |
The key field is only returned once at creation time. Store it securely — it cannot be retrieved later.
GET /api/v1/auth/api-keys
List API keys for the current user
Authentication: Session (browser cookie) or API key
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
status | query | string | No | Filter by status: active, revoked, expired |
limit | query | integer | No | Max results (default: 50, max: 200) |
offset | query | integer | No | Pagination offset |
sort | query | string | No | Sort field (default: -createdAt) |
Response: 200 OK (paginated)
{
"data": [
{
"_id": "67a1b2c3d4e5f6a7b8c9d0e1",
"keyPrefix": "sk-prod-a1b2",
"name": "My CI/CD Key",
"userId": "user123",
"organizationId": "org-abc",
"scopes": ["apps:read", "apps:deploy"],
"status": "active",
"expiresAt": "2025-05-07T00:00:00.000Z",
"lastUsedAt": "2025-02-06T10:30:00.000Z",
"lastUsedIp": "192.168.1.1",
"createdAt": "2025-02-07T00:00:00.000Z",
"revokedAt": null,
"revokedBy": null,
"metadata": null
}
],
"meta": { "total": 3, "limit": 50, "offset": 0, "hasMore": false, "requestId": "req_abc123" }
}
| Field | Type | Description |
|---|---|---|
_id | string | MongoDB document ID |
keyPrefix | string | First 12 characters of the key |
name | string | Key name |
userId | string | Owning user ID |
organizationId | string | null | Bound organization |
scopes | string[] | Permission scopes |
status | string | active, revoked, or expired |
expiresAt | string | null | Expiration date |
lastUsedAt | string | null | Last request timestamp |
lastUsedIp | string | null | Last request IP address |
createdAt | string | Creation timestamp |
revokedAt | string | null | Revocation timestamp (if revoked) |
revokedBy | string | null | User who revoked (if revoked) |
isDefaultPlatformKey | boolean | true if this is the auto-generated platform key |
metadata | object | null | Optional metadata (userAgent, description) |
GET /api/v1/auth/api-keys/:id
Get a single API key
Authentication: Session (browser cookie) or API key
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | API key ID (_id from list response) |
Response: 200 OK
{
"data": {
"_id": "67a1b2c3d4e5f6a7b8c9d0e1",
"keyPrefix": "sk-prod-a1b2",
"name": "My CI/CD Key",
"userId": "user123",
"organizationId": "org-abc",
"scopes": ["apps:read", "apps:deploy"],
"status": "active",
"expiresAt": "2025-05-07T00:00:00.000Z",
"lastUsedAt": "2025-02-06T10:30:00.000Z",
"lastUsedIp": "192.168.1.1",
"createdAt": "2025-02-07T00:00:00.000Z",
"revokedAt": null,
"revokedBy": null,
"metadata": null
},
"meta": { "requestId": "req_abc123" }
}
Returns the same fields as the list endpoint for a single key.
DELETE /api/v1/auth/api-keys/:id
Revoke an API key
Permanently revokes the key. This cannot be undone. Default Platform Keys cannot be revoked — use rotate instead.
Authentication: Session (browser cookie) or API key
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | API key ID |
Response: 204 No Content
POST /api/v1/auth/api-keys/:id/rotate
Rotate an API key
Revokes the old key and creates a new one with the same name, scopes, and expiration. For Default Platform Keys, also updates profile.stronglyApiKey on the user document.
Authentication: Session (browser cookie) or API key
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | API key ID |
Response: 201 Created
{
"data": {
"id": "67b2c3d4e5f6a7b8c9d0e1f2",
"key": "sk-prod-q1w2e3r4t5y6u7i8o9p0a1s2d3f4g5h6",
"name": "My CI/CD Key",
"keyPrefix": "sk-prod-q1w2",
"scopes": ["apps:read", "apps:deploy"],
"expiresAt": "2025-05-07T00:00:00.000Z",
"createdAt": "2025-02-08T00:00:00.000Z"
},
"meta": { "requestId": "req_abc123" }
}
Returns the same fields as the create endpoint, including the new raw key.
The new key is only returned once. Store it securely — the old key is immediately revoked and cannot be used.
GET /api/v1/auth/whoami
Get current user and organization info
Authentication: Session (browser cookie) or API key (X-API-Key header)
Response: 200 OK
{
"data": {
"userId": "user_abc123",
"email": "user@example.com",
"name": "John Doe",
"roles": ["admin"],
"organization": {
"id": "org_abc123",
"role": "owner",
"isMultiTenant": true,
"isSolo": false
},
"apiKey": {
"id": "67a1b2c3d4e5f6a7b8c9d0e1",
"name": "My Key",
"keyPrefix": "sk-prod-a1b2",
"scopes": ["apps:read"],
"expiresAt": "2025-05-07T00:00:00.000Z"
}
},
"meta": { "requestId": "req_abc123" }
}
The apiKey field is only present when authenticating with an API key (not session auth). Use this endpoint to verify your API key is working correctly.
Troubleshooting 401 Errors
If you receive a 401 Unauthorized response when using an API key, check the following:
1. Verify the header format
The key must be sent in the X-API-Key header:
curl -H "X-API-Key: sk-prod-your-key-here" \
https://<your-instance>/api/v1/auth/whoami
2. Verify the key prefix
All API keys start with sk-prod-. If your key does not start with this prefix, it is not a valid Strongly API key.
3. Check key status
Your key may have been revoked or expired. List your keys via the UI (Profile > Security > API Keys) or via session auth:
# List keys using session auth (from browser dev tools, copy your meteor_login_token cookie)
curl -b "meteor_login_token=your-session-token" \
https://<your-instance>/api/v1/auth/api-keys?status=active
4. Confirm there are no extra characters
Ensure no whitespace, newlines, or quotes are included in the key value. Copy-paste errors are common.
5. Test with the whoami endpoint
The /api/v1/auth/whoami endpoint is the simplest way to verify your key works:
curl -v -H "X-API-Key: sk-prod-your-key-here" \
https://<your-instance>/api/v1/auth/whoami
A successful response returns your user info and key details. A 401 response means the key is invalid, expired, or revoked.
Default Platform Key
When a new user is created, a Default Platform Key is automatically provisioned with * (wildcard) scopes. This key is:
- Stored in
profile.stronglyApiKeyon the user document for workspace injection - Visible in the API Keys list with an
isDefaultPlatformKeyflag - Cannot be revoked — use rotate instead to replace it
- Automatically rotated when you rotate it via the API or UI, keeping
profile.stronglyApiKeyin sync
Workspaces use this key as their STRONGLY_API_KEY environment variable, so rotating it will require restarting running workspaces to pick up the new key.