API Reference
Stategraph provides a REST API for programmatic access to states, queries, and management operations.
Authentication
Stategraph supports two authentication methods:
Bearer Token (API Key)
For CLI, Terraform, and programmatic access, use Bearer authentication with API keys:
curl -H "Authorization: Bearer $STATEGRAPH_API_KEY" http://localhost:8080/api/v1/...
API keys are created via the Settings page in the UI.
Session Cookie
For browser-based access, authentication uses session cookies set after login (local authentication or OAuth). This is handled automatically by the browser.
Authentication Modes
Stategraph supports three authentication modes:
- Local Authentication - Email/password with user management (default)
- Google OAuth - SSO via Google Workspace
- Generic OIDC - Any OIDC-compatible provider
Base URL
http://localhost:8080/api/v1
Replace localhost:8080 with your Stategraph server address if different.
User Endpoints
Get Current User
GET /api/v1/whoami
Returns information about the authenticated user.
Response
{
"id": "f30ed1f9-be44-46a3-9050-03e9561e94f0",
"name": "User Name",
"email": "user@example.com",
"is_admin": false,
"type": "user"
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique user ID (UUID) |
name |
string | Display name |
email |
string | User email address |
is_admin |
boolean | Whether user has admin privileges |
type |
enum | user, api, or system |
List User Tenants
GET /api/v1/user/tenants
Returns tenants the user has access to.
Response
{
"results": [
{
"id": "tenant-123",
"name": "My Organization"
}
]
}
Authentication Endpoints
Get Login Options
GET /api/v1/login/options
Returns available authentication methods.
Response
When local authentication is the only method configured:
{
"options": []
}
When OAuth is configured:
{
"options": [
{
"type": "google",
"display_name": "Sign in with Google"
}
]
}
Login with Password
POST /api/v1/login/password
Authenticates user with email and password (local authentication only).
Request Body
{
"email": "user@example.com",
"password": "your-password"
}
Response
{
"session_token": "eyJhbGciOiJIUzI1NiIs...",
"success": true
}
The session_token can be used for Bearer authentication. A session cookie is also set for browser-based access.
Check Setup Status
GET /api/v1/setup/status
Returns whether initial setup is needed (no users exist).
Response
{
"needs_setup": false
}
Create First Admin User
POST /api/v1/setup/admin
Creates the first admin user during initial setup. Only works when no users exist.
Request Body
{
"email": "admin@example.com",
"password": "secure-password",
"name": "Admin User",
"organization": "My Organization"
}
Response
{
"session_token": "eyJhbGciOiJIUzI1NiIs...",
"user_id": "f30ed1f9-be44-46a3-9050-03e9561e94f0"
}
The session_token can be used for Bearer authentication.
Complete Setup
POST /api/v1/setup/complete
Marks the initial setup as complete. Called after creating the first admin user.
Response: 200 OK
Logout
GET /api/v1/logout
Clears session cookie and logs out the user.
Response: 200 OK with redirect
User Management Endpoints
All user management endpoints require admin authentication.
List Users
GET /api/v1/users
Returns list of users with cursor-based pagination and filtering.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
type |
query | No | Filter by user type: user or api |
limit |
query | No | Max results (default: 25) |
cursor |
query | No | Pagination cursor from previous response |
search |
query | No | Search by name or email |
Response
{
"users": [
{
"id": "f30ed1f9-be44-46a3-9050-03e9561e94f0",
"name": "John Doe",
"email": "john@example.com",
"is_admin": false,
"type": "user",
"auth_origin": "local",
"created_at": "2024-01-15T10:30:00Z"
}
],
"total_count": 10,
"limit": 25,
"has_more": false,
"next_cursor": null
}
| Field | Type | Description |
|---|---|---|
users |
array | List of user objects |
total_count |
integer | Total number of matching users |
limit |
integer | Limit used for this request |
has_more |
boolean | Whether more results are available |
next_cursor |
string | Cursor for next page (null if no more) |
auth_origin |
string | Authentication origin (e.g., local, google, oidc) |
Get User Details
GET /api/v1/users/detail?user_id={user_id}
Returns detailed information about a specific user.
Response
{
"id": "f30ed1f9-be44-46a3-9050-03e9561e94f0",
"name": "John Doe",
"email": "john@example.com",
"is_admin": false,
"type": "user",
"created_at": "2024-01-15T10:30:00Z",
"last_login": "2024-01-20T14:30:00Z"
}
Create User
POST /api/v1/users
Creates a new user account.
Request Body
{
"name": "Jane Doe",
"email": "jane@example.com",
"password": "secure-password",
"is_admin": false
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Display name |
email |
string | Yes | Email address (used for login) |
password |
string | Yes | Password (8-128 characters) |
is_admin |
boolean | No | Grant admin privileges (default: false) |
Response
{
"id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"name": "Jane Doe",
"email": "jane@example.com",
"is_admin": false,
"type": "user"
}
Update User
PUT /api/v1/users/update?user_id={user_id}
Updates user information.
Request Body
{
"name": "Jane Smith",
"email": "jane.smith@example.com"
}
Response: 200 OK with updated user object
Delete User
DELETE /api/v1/users/delete?user_id={user_id}
Soft-deletes a user (marks as deleted, invalidates API keys).
Response: 204 No Content
Change User Password
POST /api/v1/users/change-password?user_id={user_id}
Changes a user's password.
Request Body
{
"new_password": "new-secure-password",
"current_password": "old-password"
}
| Field | Type | Required | Description |
|---|---|---|---|
new_password |
string | Yes | New password (8-128 characters) |
current_password |
string | Conditional | Required when changing own password |
Response: 200 OK
Note: Admins can change any user's password without providing current_password. Users changing their own password must provide current_password.
Toggle Admin Status
POST /api/v1/users/toggle-admin?user_id={user_id}
Grants or revokes admin privileges.
Request Body
{
"is_admin": true
}
Response: 200 OK with updated user object
Note: Cannot remove admin privileges from the last admin user.
Create Service Account
POST /api/v1/api-users
Creates a service account (API user) for programmatic access.
Request Body
{
"name": "ci-production",
"tenant_id": "550e8400-e29b-41d4-a716-446655440000"
}
Response
{
"user_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"token": "eyJhbGciOiJIUzI1NiIs..."
}
Important: The token is only returned once. Save it immediately.
State Endpoints
List States
GET /api/v1/tenants/{tenant_id}/states
Returns all active (non-deleted) states for a tenant. Soft-deleted states are excluded from results.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tenant_id |
path | Yes | Tenant ID |
page |
query | No | Pagination cursor |
limit |
query | No | Max results (default: 100) |
q |
query | No | MQL filter query |
Response
{
"results": [
{
"id": "state-123",
"name": "networking",
"group_id": "networking",
"workspace": "production",
"created_at": "2024-01-15T10:30:00Z",
"deleted_at": null,
"deleted_by": null
}
]
}
Create State
POST /api/v1/tenants/{tenant_id}/states
Creates a new state.
Request Body
{
"name": "networking",
"group_id": "networking",
"workspace": "production"
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | State name |
group_id |
string | No | Group identifier (defaults to name) |
workspace |
string | No | Workspace name (default: default) |
Response: 201 Created with state object
Import State
POST /api/v1/tenants/{tenant_id}/states/import
Imports an existing Terraform state file.
Request Body
{
"name": "networking",
"group_id": "networking",
"workspace": "production",
"state": { ... },
"tags": { "source": "migration" }
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | State name |
state |
object | Yes | Terraform state JSON |
group_id |
string | No | Group identifier |
workspace |
string | No | Workspace name |
tags |
object | No | Metadata tags |
Delete State
DELETE /api/v1/states/{state_id}
Deletes a state. Supports both soft delete (default) and hard delete modes.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
state_id |
path | Yes | State ID (UUID) |
hard_delete |
query | No | If true, performs hard delete (default: false) |
Deletion Modes
Soft Delete (default)
- Sets deleted_at timestamp and deleted_by user ID
- State remains in database but is marked as deleted
- Preserves historical data for audit purposes
Hard Delete (hard_delete=true)
- Permanently removes state and all related data:
- Resources
- Instances
- Providers
- Outputs
- Raw states
- Transaction logs
- Check entries and results
- Cannot be undone
Authorization
Requires admin privileges.
Response Codes
| Code | Description |
|---|---|
200 OK |
State successfully deleted |
401 Unauthorized |
Not authenticated |
403 Forbidden |
User is not an admin |
404 Not Found |
State does not exist |
500 Internal Server Error |
Server error occurred |
Examples
Soft delete:
curl -X DELETE "http://localhost:8080/api/v1/states/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer $STATEGRAPH_API_KEY"
Hard delete:
curl -X DELETE "http://localhost:8080/api/v1/states/550e8400-e29b-41d4-a716-446655440000?hard_delete=true" \
-H "Authorization: Bearer $STATEGRAPH_API_KEY"
Error Response (403 Forbidden)
{
"id": "ADMIN_REQUIRED",
"data": "Admin privileges required"
}
Error Response (404 Not Found)
{
"id": "STATE_NOT_FOUND",
"data": "State not found"
}
Instance Endpoints
List Instances
GET /api/v1/states/{state_id}/instances
Returns resource instances for a state.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
state_id |
path | Yes | State ID |
page |
query | No | Pagination cursor |
limit |
query | No | Max results |
q |
query | No | MQL filter |
Response
{
"results": [
{
"address": "aws_instance.web",
"type": "aws_instance",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"module": null,
"attributes": { ... },
"dependencies": ["aws_subnet.main", "aws_security_group.web"]
}
]
}
Get Blast Radius
GET /api/v1/states/{state_id}/instances/{instance_address}/blast-radius
Returns resources affected by changes to the specified instance.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
state_id |
path | Yes | State ID |
instance_address |
path | Yes | URL-encoded instance address |
Response
{
"results": [
{
"address": "aws_eip.web",
"resource_address": "aws_eip.web",
"distance": 1
}
]
}
Module Endpoints
List Modules
GET /api/v1/states/{state_id}/modules
Returns modules in a state.
Response
{
"results": [
{
"name": "module.vpc",
"instance_count": 25,
"resource_count": 10
}
]
}
Summary Endpoints
State Summary
GET /api/v1/states/{state_id}/summary
Returns aggregate statistics for a state.
Response
{
"instances": 150,
"resources": 45,
"modules": 8,
"providers": 3,
"edges": 234
}
Resources Summary
GET /api/v1/states/{state_id}/resources/summary
Returns instance counts by resource type.
Response
{
"aws_instance": { "instances": 20 },
"aws_security_group": { "instances": 15 },
"aws_subnet": { "instances": 6 }
}
Query Endpoints
Execute MQL Query
GET /api/v1/mql
Executes an MQL query across all states.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q |
query | Yes | MQL query string |
page |
query | No | Pagination cursor |
tz |
query | No | Timezone for date formatting |
Example
curl "http://localhost:8080/api/v1/mql?q=SELECT%20*%20FROM%20instances%20WHERE%20type%20%3D%20%27aws_instance%27" \
-H "Authorization: Bearer $STATEGRAPH_API_KEY"
Response
[
{
"address": "aws_instance.web",
"type": "aws_instance",
...
}
]
Get MQL Schema
GET /api/v1/mql/schema
Returns the MQL schema for autocomplete.
Transaction Endpoints
List Transactions
GET /api/v1/tenants/{tenant_id}/tx
Returns transactions for a tenant.
Response
{
"results": [
{
"id": "455fe705-f27f-4335-9355-dbe8f14098df",
"created_at": "2024-01-15T10:30:00Z",
"created_by": "f30ed1f9-be44-46a3-9050-03e9561e94f0",
"completed_at": "2024-01-15T10:30:05Z",
"completed_by": "f30ed1f9-be44-46a3-9050-03e9561e94f0",
"state": "completed",
"tags": {"desc": "Backend update"}
}
]
}
Create Transaction
POST /api/v1/tenants/{tenant_id}/tx/create
Creates a new transaction.
Request Body
{
"tags": {
"pipeline": "github-actions",
"commit": "abc123"
}
}
Get Transaction Logs
GET /api/v1/tx/{tx_id}/logs
Returns logs for a transaction.
Response
{
"results": [
{
"id": "log-123",
"action": "state_set",
"object_type": "instance",
"created_at": "2024-01-15T10:30:00Z",
"state_id": "state-123",
"data": { ... }
}
]
}
Abort Transaction
POST /api/v1/tx/{tx_id}/abort
Aborts an active transaction.
API Key Endpoints
Create API Key
POST /api/v1/tx/session/create
Creates an API key for CLI, Terraform, and programmatic access. API keys do not expire.
Request Body
{
"tags": {
"name": "my-api-key"
}
}
Response
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}
Gap Analysis Endpoints
Get Gap Analysis Config
GET /api/v1/tenants/{tenant_id}/gaps/config
Returns gap analysis configuration status.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
provider |
query | Yes | Cloud provider (e.g., aws) |
Response
{
"status": "ready",
"ready_for_gap_analysis": true,
"has_aggregator": true,
"aggregator_region": "us-east-1",
"indexed_regions": ["us-east-1", "us-west-2"],
"index_count": 1500,
"warnings": []
}
Run Gap Analysis
GET /api/v1/tenants/{tenant_id}/gaps
Returns unmanaged resources.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
provider |
query | Yes | Cloud provider |
source |
query | No | cache (default) or no-cache |
Response
{
"summary": {
"total_aws_resources": 1500,
"managed_by_stategraph": 1200,
"unmanaged": 300
},
"unmanaged_resources": [ ... ],
"fetched_at": 1705312800,
"from_cache": true
}
Generate Import
POST /api/v1/tenants/{tenant_id}/gaps/import
Generates Terraform import blocks.
Request Body
{
"provider": "aws",
"resources": [
{
"arn": "arn:aws:s3:::bucket-name",
"service": "S3",
"resource_type": "AWS::S3::Bucket",
"region": "us-east-1",
"owning_account_id": "123456789012"
}
]
}
Response
{
"import_blocks": "import { ... }",
"provider_hcl": "provider \"aws\" { ... }",
"generated_hcl": "resource \"aws_s3_bucket\" { ... }",
"supported_count": 1,
"unsupported_count": 0,
"unsupported_resources": []
}
OpenAPI Schema
Get OpenAPI Schema
GET /api/v1/openapi
Returns the full OpenAPI schema for the Stategraph API as JSON. This endpoint is unauthenticated and can be used to discover available endpoints, request/response types, and integrate with tools that consume OpenAPI specifications.
Example
curl http://localhost:8080/api/v1/openapi
Response: 200 OK with the complete OpenAPI JSON schema.
The schema is also available as a downloadable file from each release.
Error Responses
400 Bad Request
{
"id": "bad-request",
"data": "Error description"
}
401 Unauthorized
No body. Authentication required.
404 Not Found
Resource not found.
500 Internal Server Error
Server error occurred.
Pagination
List endpoints support cursor-based pagination:
# First page
curl "http://localhost:8080/api/v1/tenants/$TENANT_ID/states?limit=50" \
-H "Authorization: Bearer $STATEGRAPH_API_KEY"
# Next page (use cursor from previous response)
curl "http://localhost:8080/api/v1/tenants/$TENANT_ID/states?limit=50&page=cursor..." \
-H "Authorization: Bearer $STATEGRAPH_API_KEY"