Skip to main content

API Reference

Complete reference for the Arcan HTTP API. All endpoints are served over HTTPS at /api/v1/.

Authentication

Authenticated endpoints require a Bearer token in the Authorization header. Tokens can be:

  • API tokens (persistent) -- created with arcan token create, prefixed with arc_
  • JWT tokens (session) -- returned by POST /api/v1/auth/login, valid for 24 hours
  • SSO tokens -- returned by OIDC/SAML/LDAP callback flows
Authorization: Bearer arc_a1b2c3d4e5f6...

Common Error Format

All errors return a JSON object:

{
"error": {
"code": "not_found",
"message": "realm not found — list realms with: arcan realm list",
"status": 404
}
}

Health

GET /api/v1/health

Health check endpoint. Returns server status, version, and component health.

Authentication: None (public)

Response:

{
"status": "healthy",
"version": "0.1.0",
"commit": "abc1234",
"built": "2026-04-01T12:00:00Z",
"mode": "standalone",
"checks": {
"database": "healthy",
"encryption": "healthy"
}
}

Status codes:

CodeDescription
200All components healthy
503One or more components unhealthy

Example:

curl -k https://localhost:8081/api/v1/health

Auth

POST /api/v1/auth/register

Create a new user account.

Authentication: None (public, subject to registration policy)

Request body:

FieldTypeRequiredDescription
emailstringYesUser email address
passwordstringYesPassword (minimum 12 characters)
namestringNoDisplay name (defaults to email)

Response (201 Created):

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"name": "User",
"created_at": "2026-04-01T12:00:00Z"
}

Status codes:

CodeDescription
201User created
400Validation error (missing fields, password too short)
403Registration disabled or admin-only
409Email already registered

Example:

curl -k -X POST https://localhost:8081/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "supersecure123", "name": "Admin"}'

POST /api/v1/auth/login

Authenticate with email and password. Returns a JWT token.

Authentication: None (public)

Request body:

FieldTypeRequiredDescription
emailstringYesUser email address
passwordstringYesUser password

Response (200 OK):

{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": 1743523200,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"name": "Admin"
}
}

Status codes:

CodeDescription
200Login successful
400Validation error (missing fields)
401Invalid email or password

Example:

curl -k -X POST https://localhost:8081/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "supersecure123"}'

GET /api/v1/auth/providers

List configured SSO providers.

Authentication: None (public)

Response (200 OK):

{
"providers": [
{
"name": "okta",
"type": "oidc",
"display_name": "Okta"
},
{
"name": "corporate",
"type": "saml",
"display_name": "Corporate SAML"
}
]
}

Example:

curl -k https://localhost:8081/api/v1/auth/providers

SSO

SSO endpoints handle the login flows for OIDC, SAML, and LDAP providers. These are public endpoints that initiate or complete authentication.

GET /api/v1/auth/oidc/{provider}/login

Initiate an OIDC login flow. Redirects the user to the identity provider.

Authentication: None (public)

Path parameters:

ParameterDescription
providerProvider name (e.g., okta, google)

Response: 302 Redirect to the identity provider's authorization endpoint.

Example:

# Browser redirect (not typically called via curl)
curl -k -L https://localhost:8081/api/v1/auth/oidc/okta/login

GET /api/v1/auth/oidc/{provider}/callback

OIDC callback endpoint. The identity provider redirects here after authentication. Returns a JWT token on success.

Authentication: None (public -- called by identity provider)

Path parameters:

ParameterDescription
providerProvider name

Query parameters:

ParameterDescription
codeAuthorization code from the IdP
stateCSRF state parameter

Response (200 OK):

{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": 1743523200,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"name": "User"
}
}

GET /api/v1/auth/saml/{provider}/login

Initiate a SAML login flow. Redirects to the identity provider's SSO URL.

Authentication: None (public)

Path parameters:

ParameterDescription
providerProvider name (e.g., corporate)

Response: 302 Redirect to the SAML IdP.


POST /api/v1/auth/saml/{provider}/acs

SAML Assertion Consumer Service endpoint. Receives the SAML response from the IdP.

Authentication: None (public -- called by identity provider)

Path parameters:

ParameterDescription
providerProvider name

Request body: application/x-www-form-urlencoded with SAMLResponse field.

Response (200 OK):

{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": 1743523200,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"name": "User"
}
}

GET /api/v1/auth/saml/{provider}/metadata

Return the SAML Service Provider metadata XML. Provide this URL to your IdP during SAML configuration.

Authentication: None (public)

Path parameters:

ParameterDescription
providerProvider name

Response: 200 OK with Content-Type: application/xml

Example:

curl -k https://localhost:8081/api/v1/auth/saml/corporate/metadata

POST /api/v1/auth/ldap/{provider}/login

Authenticate via LDAP. Binds to the directory with the provided credentials.

Authentication: None (public)

Path parameters:

ParameterDescription
providerProvider name (e.g., active-directory)

Request body:

FieldTypeRequiredDescription
usernamestringYesLDAP username or DN
passwordstringYesLDAP password

Response (200 OK):

{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": 1743523200,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"name": "User"
}
}

Status codes:

CodeDescription
200Login successful
400Validation error
401Invalid credentials or user not found

Example:

curl -k -X POST https://localhost:8081/api/v1/auth/ldap/active-directory/login \
-H "Content-Type: application/json" \
-d '{"username": "jdoe", "password": "mypassword"}'

Tokens

POST /api/v1/auth/tokens

Create a new API token.

Authentication: Required (Bearer token)

Request body:

FieldTypeRequiredDescription
namestringYesToken name (e.g., ci-pipeline)
scopesstringNoComma-separated scopes: read, write, delete (default: read)

Response (201 Created):

{
"token": "arc_a1b2c3d4e5f6g7h8i9j0...",
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "ci-pipeline",
"prefix": "arc_a1b2c3d4",
"scopes": "read,write",
"created_at": "2026-04-01T12:00:00Z"
}

Status codes:

CodeDescription
201Token created
400Validation error (missing name)
401Not authenticated

Example:

curl -k -X POST https://localhost:8081/api/v1/auth/tokens \
-H "Authorization: Bearer arc_existing_token" \
-H "Content-Type: application/json" \
-d '{"name": "ci-pipeline", "scopes": "read,write"}'

Notes:

  • The full token value is only returned once at creation time. Store it securely.
  • Tokens are stored as SHA-256 hashes on the server.

GET /api/v1/auth/tokens

List all API tokens for the authenticated user.

Authentication: Required (Bearer token)

Response (200 OK):

{
"tokens": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "ci-pipeline",
"prefix": "arc_a1b2c3d4",
"scopes": "read,write",
"created_at": "2026-04-01T12:00:00Z"
}
]
}

Example:

curl -k https://localhost:8081/api/v1/auth/tokens \
-H "Authorization: Bearer arc_your_token"

DELETE /api/v1/auth/tokens/{id}

Revoke an API token. The token immediately stops working.

Authentication: Required (Bearer token)

Path parameters:

ParameterDescription
idToken UUID

Response (200 OK):

{
"message": "token revoked"
}

Status codes:

CodeDescription
200Token revoked
400Missing token ID
401Not authenticated
404Token not found

Example:

curl -k -X DELETE https://localhost:8081/api/v1/auth/tokens/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer arc_your_token"

Realms

POST /api/v1/realms

Create a new realm.

Authentication: Required (Bearer token)

Request body:

FieldTypeRequiredDescription
slugstringYesRealm slug (2-50 chars, lowercase alphanumeric with hyphens)
namestringYesDisplay name
descriptionstringNoRealm description

Response (201 Created):

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "my-app",
"name": "My Application",
"description": "Production secrets for my app",
"user_id": "660e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-04-01T12:00:00Z"
}

Status codes:

CodeDescription
201Realm created
400Validation error (invalid slug, missing fields)
401Not authenticated
409Slug already exists

Example:

curl -k -X POST https://localhost:8081/api/v1/realms \
-H "Authorization: Bearer arc_your_token" \
-H "Content-Type: application/json" \
-d '{"slug": "my-app", "name": "My Application"}'

GET /api/v1/realms

List all realms for the authenticated user.

Authentication: Required (Bearer token)

Response (200 OK):

{
"realms": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "my-app",
"name": "My Application",
"created_at": "2026-04-01T12:00:00Z"
}
]
}

Example:

curl -k https://localhost:8081/api/v1/realms \
-H "Authorization: Bearer arc_your_token"

GET /api/v1/realms/{slug}

Get details for a single realm.

Authentication: Required (Bearer token)

Path parameters:

ParameterDescription
slugRealm slug

Response (200 OK):

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "my-app",
"name": "My Application",
"description": "Production secrets for my app",
"user_id": "660e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-04-01T12:00:00Z"
}

Status codes:

CodeDescription
200Realm found
401Not authenticated
404Realm not found

Example:

curl -k https://localhost:8081/api/v1/realms/my-app \
-H "Authorization: Bearer arc_your_token"

DELETE /api/v1/realms/{slug}

Soft-delete a realm and all its contents. Requires realms:manage capability.

Authentication: Required (Bearer token with admin role)

Path parameters:

ParameterDescription
slugRealm slug

Response (200 OK):

{
"message": "realm deleted"
}

Status codes:

CodeDescription
200Realm deleted
401Not authenticated
403Insufficient permissions
404Realm not found

Example:

curl -k -X DELETE https://localhost:8081/api/v1/realms/my-app \
-H "Authorization: Bearer arc_your_token"

Secrets

POST /api/v1/realms/{slug}/secrets

Store a secret. Creates or updates the key in the specified environment.

Authentication: Required (Bearer token)

Path parameters:

ParameterDescription
slugRealm slug

Request body:

FieldTypeRequiredDescription
keystringYesSecret key name
valuestringYesSecret value (plaintext or pre-encrypted)
environmentstringNoEnvironment: dev, staging, prod, test (default: dev)
commentstringNoComment or description
expires_atstringNoISO 8601 expiration timestamp
pre_encryptedboolNoIf true, value is stored as-is (must start with arcan:v1:)

Response (201 Created):

{
"key": "DATABASE_URL",
"environment": "prod",
"version": 1,
"created_at": "2026-04-01T12:00:00Z",
"updated_at": "2026-04-01T12:00:00Z"
}

Status codes:

CodeDescription
201Secret created/updated
400Validation error (missing key/value, invalid environment)
401Not authenticated
404Realm not found

Example:

curl -k -X POST https://localhost:8081/api/v1/realms/my-app/secrets \
-H "Authorization: Bearer arc_your_token" \
-H "Content-Type: application/json" \
-d '{"key": "DATABASE_URL", "value": "postgres://user:pass@db:5432/app", "environment": "prod"}'

GET /api/v1/realms/{slug}/secrets

List all secrets in a realm.

Authentication: Required (Bearer token)

Path parameters:

ParameterDescription
slugRealm slug

Query parameters:

ParameterDefaultDescription
envFilter by environment (dev, staging, prod, test)

Response (200 OK):

{
"secrets": [
{
"key": "DATABASE_URL",
"value": "postgres://user:pass@db:5432/app",
"environment": "prod",
"version": 1,
"updated_at": "2026-04-01T12:00:00Z"
}
]
}

Example:

# List all secrets in a realm
curl -k https://localhost:8081/api/v1/realms/my-app/secrets \
-H "Authorization: Bearer arc_your_token"

# Filter by environment
curl -k "https://localhost:8081/api/v1/realms/my-app/secrets?env=prod" \
-H "Authorization: Bearer arc_your_token"

GET /api/v1/realms/{slug}/secrets/{key}

Retrieve a single secret value.

Authentication: Required (Bearer token)

Path parameters:

ParameterDescription
slugRealm slug
keySecret key name

Query parameters:

ParameterDefaultDescription
envdevEnvironment to read from

Response (200 OK):

{
"key": "DATABASE_URL",
"value": "postgres://user:pass@db:5432/app",
"environment": "prod",
"version": 1,
"updated_at": "2026-04-01T12:00:00Z"
}

Status codes:

CodeDescription
200Secret found
401Not authenticated
404Realm or secret not found

Example:

curl -k "https://localhost:8081/api/v1/realms/my-app/secrets/DATABASE_URL?env=prod" \
-H "Authorization: Bearer arc_your_token"

DELETE /api/v1/realms/{slug}/secrets/{key}

Delete a secret by key.

Authentication: Required (Bearer token)

Path parameters:

ParameterDescription
slugRealm slug
keySecret key name

Query parameters:

ParameterDefaultDescription
envdevEnvironment to delete from

Response (200 OK):

{
"message": "secret deleted"
}

Status codes:

CodeDescription
200Secret deleted
401Not authenticated
404Realm or secret not found

Example:

curl -k -X DELETE "https://localhost:8081/api/v1/realms/my-app/secrets/OLD_KEY?env=dev" \
-H "Authorization: Bearer arc_your_token"

POST /api/v1/encrypt

Encrypt a plaintext value using the server's encryption key.

Authentication: Required (Bearer token)

Request body:

FieldTypeRequiredDescription
valuestringYesPlaintext value to encrypt

Response (200 OK):

{
"ciphertext": "arcan:v1:aGVsbG8gd29ybGQ..."
}

Status codes:

CodeDescription
200Encryption successful
400Missing value
401Not authenticated

Example:

curl -k -X POST https://localhost:8081/api/v1/encrypt \
-H "Authorization: Bearer arc_your_token" \
-H "Content-Type: application/json" \
-d '{"value": "my-secret-value"}'

Policy

GET /api/v1/policy/roles

List all available roles and their capabilities.

Authentication: Required (Bearer token)

Response (200 OK):

{
"roles": [
{
"name": "admin",
"capabilities": [
"secrets:read",
"secrets:write",
"secrets:delete",
"realms:manage",
"policy:manage",
"audit:read"
]
},
{
"name": "member",
"capabilities": [
"secrets:read",
"secrets:write",
"audit:read"
]
},
{
"name": "viewer",
"capabilities": [
"secrets:read",
"audit:read"
]
}
]
}

Example:

curl -k https://localhost:8081/api/v1/policy/roles \
-H "Authorization: Bearer arc_your_token"

POST /api/v1/realms/{slug}/policy/bindings

Assign a role to a user within a realm. Only admins can manage policy.

Authentication: Required (Bearer token with admin role)

Path parameters:

ParameterDescription
slugRealm slug

Request body:

FieldTypeRequiredDescription
user_idstringYesUser ID to bind
rolestringYesRole: admin, member, viewer

Response (201 Created):

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"realm_id": "660e8400-e29b-41d4-a716-446655440000",
"user_id": "770e8400-e29b-41d4-a716-446655440000",
"role": "member",
"granted_by": "880e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-04-01T12:00:00Z"
}

Status codes:

CodeDescription
201Binding created
400Validation error (missing fields, invalid role)
401Not authenticated
403Only admins can manage policy
404Realm not found
409User already has a role in this realm

Example:

curl -k -X POST https://localhost:8081/api/v1/realms/my-app/policy/bindings \
-H "Authorization: Bearer arc_your_token" \
-H "Content-Type: application/json" \
-d '{"user_id": "770e8400-e29b-41d4-a716-446655440000", "role": "member"}'

GET /api/v1/realms/{slug}/policy/bindings

List all role bindings in a realm.

Authentication: Required (Bearer token)

Path parameters:

ParameterDescription
slugRealm slug

Response (200 OK):

{
"bindings": [
{
"user_id": "770e8400-e29b-41d4-a716-446655440000",
"role": "member",
"granted_by": "880e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-04-01T12:00:00Z"
}
]
}

Example:

curl -k https://localhost:8081/api/v1/realms/my-app/policy/bindings \
-H "Authorization: Bearer arc_your_token"

DELETE /api/v1/realms/{slug}/policy/bindings/{userID}

Remove a user's role binding from a realm. Only admins can manage policy.

Authentication: Required (Bearer token with admin role)

Path parameters:

ParameterDescription
slugRealm slug
userIDUser ID to unbind

Response (200 OK):

{
"message": "binding removed"
}

Status codes:

CodeDescription
200Binding removed
401Not authenticated
403Only admins can manage policy
404Realm or binding not found

Example:

curl -k -X DELETE https://localhost:8081/api/v1/realms/my-app/policy/bindings/770e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer arc_your_token"

Engines

GET /api/v1/engines

List all registered engine types (built-in and plugin).

Authentication: Required (Bearer token)

Response (200 OK):

{
"engines": [
{
"type": "totp",
"description": "TOTP/HOTP one-time password engine",
"capabilities": ["secret_generation", "secret_validation"]
}
]
}

Example:

curl -k https://localhost:8081/api/v1/engines \
-H "Authorization: Bearer arc_your_token"

POST /api/v1/realms/{slug}/engines/{type}/generate

Generate a secret using a specific engine.

Authentication: Required (Bearer token)

Path parameters:

ParameterDescription
slugRealm slug
typeEngine type (e.g., totp)

Request body: Engine-specific JSON parameters (varies by engine).

TOTP example request:

{
"issuer": "MyApp",
"account": "[email protected]"
}

Response (200 OK): Engine-specific JSON output.

TOTP example response:

{
"secret": "JBSWY3DPEHPK3PXP",
"url": "otpauth://totp/MyApp:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=MyApp",
"qr_code": "data:image/png;base64,..."
}

Status codes:

CodeDescription
200Generation successful
400Engine does not support generation
401Not authenticated
404Engine type not found

Example:

curl -k -X POST https://localhost:8081/api/v1/realms/my-app/engines/totp/generate \
-H "Authorization: Bearer arc_your_token" \
-H "Content-Type: application/json" \
-d '{"issuer": "MyApp", "account": "[email protected]"}'

POST /api/v1/realms/{slug}/engines/{type}/validate

Validate a secret using a specific engine.

Authentication: Required (Bearer token)

Path parameters:

ParameterDescription
slugRealm slug
typeEngine type (e.g., totp)

Request body: Engine-specific JSON parameters.

TOTP example request:

{
"secret": "JBSWY3DPEHPK3PXP",
"code": "123456"
}

Response (200 OK):

{
"valid": true
}

Status codes:

CodeDescription
200Validation completed (check valid field)
400Engine does not support validation
401Not authenticated
404Engine type not found

Example:

curl -k -X POST https://localhost:8081/api/v1/realms/my-app/engines/totp/validate \
-H "Authorization: Bearer arc_your_token" \
-H "Content-Type: application/json" \
-d '{"secret": "JBSWY3DPEHPK3PXP", "code": "123456"}'

Audit

GET /api/v1/audit/events

Query the audit log for security and operational events.

Authentication: Required (Bearer token)

Query parameters:

ParameterDefaultDescription
realmFilter by realm slug
typeFilter by event type (e.g., secret.set, auth.login, auth.register)
actorFilter by actor (user ID or email)
limit50Maximum events to return

Response (200 OK):

{
"events": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-01T12:00:00Z",
"type": "secret.set",
"actor": "[email protected]",
"realm": "my-app",
"data": "{\"key\":\"DATABASE_URL\",\"environment\":\"prod\"}"
}
]
}

Common event types:

TypeDescription
auth.registerUser registration
auth.loginSuccessful login
auth.login.failedFailed login attempt
auth.token.createdAPI token created
secret.setSecret created or updated
secret.deleteSecret deleted

Example:

# List recent events
curl -k "https://localhost:8081/api/v1/audit/events" \
-H "Authorization: Bearer arc_your_token"

# Filter by realm and limit
curl -k "https://localhost:8081/api/v1/audit/events?realm=my-app&limit=20" \
-H "Authorization: Bearer arc_your_token"

# Filter by event type
curl -k "https://localhost:8081/api/v1/audit/events?type=secret.set" \
-H "Authorization: Bearer arc_your_token"

# Filter by actor
curl -k "https://localhost:8081/api/v1/audit/[email protected]" \
-H "Authorization: Bearer arc_your_token"