API Keys
Creating a Key
Section titled “Creating a Key”Create keys interactively in the Forgecroft console under Settings → API keys.
To create a key programmatically:
POST /api-keys{ "name": "CI/CD Pipeline", "scope_type": "project", "scope_id": "project-uuid", "action_scope": "write"}Scope Type
Section titled “Scope Type”Controls which resources the key can access:
| Scope Type | Access |
|---|---|
org | All resources in the organization |
project | The specified project and its Systems |
Project-scoped keys require scope_id (the project UUID).
Action Scope
Section titled “Action Scope”Controls the level of access:
| Action Scope | Capabilities |
|---|---|
read | Read-only access within scope |
write | Read + write (create and update resources, ingest evidence, evaluate changes) |
admin | Full access including member and key management |
Capability Scopes
Section titled “Capability Scopes”Fine-grained permissions for specific API operations:
| Capability | Allows |
|---|---|
api_keys:read | List and view API keys |
api_keys:write | Create and manage API keys |
members:read | View org members |
members:write | Manage org members |
billing:read | View billing status |
evaluate:read | Evaluate a change against Controls (the merge gate) |
resolve:read / query:read | Read the system map |
controls:read | Read Controls |
evidence:read | Read Evidence |
propose:write | Ingest evidence and propose graph changes |
If no capability scopes are specified, the key is a legacy key with unrestricted capability access. Scoped keys must explicitly list each capability they need. Capability scopes go in the scopes array:
{ "name": "CI Gate", "scope_type": "project", "scope_id": "project-uuid", "action_scope": "read", "scopes": ["evaluate:read", "resolve:read"]}This key can evaluate changes and read the map, but cannot manage members, keys, or billing.
Recommended scopes by use case
Section titled “Recommended scopes by use case”| Use Case | Scopes |
|---|---|
| CI merge gate (evaluate changes) | ["evaluate:read", "resolve:read"] |
| Evidence ingest pipeline | ["propose:write", "resolve:read"] |
| Read-only dashboard | ["resolve:read", "query:read", "controls:read"] |
| Key-management automation | ["api_keys:read", "api_keys:write"] |
How Scopes Interact
Section titled “How Scopes Interact”When a key has both an action scope and capability scopes, both must pass for a request to succeed:
action_scope: "admin"+scopes: ["api_keys:read"]= can only read API keys. The capability scopes restrict it despite the admin action scope.action_scope: "read"+scopes: ["propose:write"]= cannot ingest. The action scope blocks write operations.
If scopes is empty (legacy key), only the action scope is checked.
Anti-Escalation
Section titled “Anti-Escalation”API keys cannot create child keys with more permissions than they hold:
- A scoped key can only create keys whose capability scopes are a subset of its own.
- A legacy key (empty scopes) can create any type of child key.
- A scoped key can never create a legacy (empty-scope) key, even if it holds broad scopes.
Lifecycle
Section titled “Lifecycle”| Operation | Endpoint | Notes |
|---|---|---|
| Create | POST /api-keys | Returns raw key once |
| Authenticate | Authorization: Bearer fc_live_... | |
| Suspend | PATCH /api-keys/{id} with {"suspended": true} | Immediate rejection; reversible |
| Resume | PATCH /api-keys/{id} with {"suspended": false} | |
| Delete | DELETE /api-keys/{id} | Permanent; cannot be undone |
Suspended keys return a clear error message. Use suspension for temporary disabling without losing the key.
Expired keys return "invalid api key" (identical to a revoked key). Expiration is checked at request time, not proactively. Track expires_at in your key inventory and rotate before expiry.
Expiration
Section titled “Expiration”Set expires_in_days at creation time. If omitted, the key never expires.
Principal Types
Section titled “Principal Types”| Principal Type | Use Case |
|---|---|
org | Service accounts owned by the organization (CI/CD, bots). Outlives any individual. |
user | Keys attributed to a specific person. Use when audit attribution matters. |
team | Keys shared by a team for team-scoped tooling. |
Non-admin callers can only create user-principal keys for themselves. Admins can create keys for any principal type.
Auditing Key Usage
Section titled “Auditing Key Usage”Every key tracks last_used_at — the timestamp of the last successful request.
GET /api-keysReview keys where last_used_at is null or older than 90 days. The 90-day threshold is a guideline: keys unused that long are likely orphaned from decommissioned pipelines or departed team members. Suspend them to confirm nothing breaks, then delete.
Only admins or the key creator can view a key’s full metadata via GET /api-keys/{id}.
Key Rotation
Section titled “Key Rotation”To rotate a key without downtime:
- Create a new key with the same scopes
- Deploy the new key to your clients
- Monitor
last_used_aton the old key until it goes stale - Delete the old key
Incident Response: Leaked Key
Section titled “Incident Response: Leaked Key”- Suspend immediately —
PATCH /api-keys/{id}with{"suspended": true}. Blocks the key within seconds. - Audit usage — Check
last_used_atand review recent activity for the affected key in the audit trail. - Create a replacement — Issue a new key with the same scopes.
- Delete the leaked key — Once the replacement is deployed, permanently delete the old key.
Suspend first, investigate second. Suspension is instant and reversible; deletion is permanent.
API Endpoints
Section titled “API Endpoints”| Endpoint | Description |
|---|---|
POST /api-keys | Create an API key |
GET /api-keys | List all keys for the org (requires api_keys:read) |
GET /api-keys/{id} | Get a key’s metadata (admin or creator only) |
PATCH /api-keys/{id} | Suspend or unsuspend (requires api_keys:write) |
DELETE /api-keys/{id} | Revoke permanently (requires api_keys:write) |
GET /whoami | Verify which key is currently authenticated |