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. For Terraform HTTP backend, use HTTP Basic Auth with username session and the API key as password.
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 states for a tenant.
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"
}
]
}
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 |
Backend Endpoints
These endpoints implement the Terraform HTTP backend protocol.
Get State
GET /api/v1/states/backend/{group_id}
GET /api/v1/states/backend/{group_id}/{workspace}
Retrieves the current state for Terraform.
Response: Raw Terraform state JSON
Set State
POST /api/v1/states/backend/{group_id}
POST /api/v1/states/backend/{group_id}/{workspace}
Updates state from Terraform.
Request Body: Raw Terraform state JSON
Response: 200 OK
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": []
}
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"