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.

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"