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 witharc_ - 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:
| Code | Description |
|---|---|
200 | All components healthy |
503 | One 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:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User email address |
password | string | Yes | Password (minimum 12 characters) |
name | string | No | Display 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:
| Code | Description |
|---|---|
201 | User created |
400 | Validation error (missing fields, password too short) |
403 | Registration disabled or admin-only |
409 | Email 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:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User email address |
password | string | Yes | User password |
Response (200 OK):
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": 1743523200,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"name": "Admin"
}
}
Status codes:
| Code | Description |
|---|---|
200 | Login successful |
400 | Validation error (missing fields) |
401 | Invalid 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:
| Parameter | Description |
|---|---|
provider | Provider 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:
| Parameter | Description |
|---|---|
provider | Provider name |
Query parameters:
| Parameter | Description |
|---|---|
code | Authorization code from the IdP |
state | CSRF 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:
| Parameter | Description |
|---|---|
provider | Provider 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:
| Parameter | Description |
|---|---|
provider | Provider 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:
| Parameter | Description |
|---|---|
provider | Provider 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:
| Parameter | Description |
|---|---|
provider | Provider name (e.g., active-directory) |
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
username | string | Yes | LDAP username or DN |
password | string | Yes | LDAP password |
Response (200 OK):
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": 1743523200,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"name": "User"
}
}
Status codes:
| Code | Description |
|---|---|
200 | Login successful |
400 | Validation error |
401 | Invalid 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:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Token name (e.g., ci-pipeline) |
scopes | string | No | Comma-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:
| Code | Description |
|---|---|
201 | Token created |
400 | Validation error (missing name) |
401 | Not 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:
| Parameter | Description |
|---|---|
id | Token UUID |
Response (200 OK):
{
"message": "token revoked"
}
Status codes:
| Code | Description |
|---|---|
200 | Token revoked |
400 | Missing token ID |
401 | Not authenticated |
404 | Token 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:
| Field | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Realm slug (2-50 chars, lowercase alphanumeric with hyphens) |
name | string | Yes | Display name |
description | string | No | Realm 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:
| Code | Description |
|---|---|
201 | Realm created |
400 | Validation error (invalid slug, missing fields) |
401 | Not authenticated |
409 | Slug 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:
| Parameter | Description |
|---|---|
slug | Realm 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:
| Code | Description |
|---|---|
200 | Realm found |
401 | Not authenticated |
404 | Realm 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:
| Parameter | Description |
|---|---|
slug | Realm slug |
Response (200 OK):
{
"message": "realm deleted"
}
Status codes:
| Code | Description |
|---|---|
200 | Realm deleted |
401 | Not authenticated |
403 | Insufficient permissions |
404 | Realm 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:
| Parameter | Description |
|---|---|
slug | Realm slug |
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Secret key name |
value | string | Yes | Secret value (plaintext or pre-encrypted) |
environment | string | No | Environment: dev, staging, prod, test (default: dev) |
comment | string | No | Comment or description |
expires_at | string | No | ISO 8601 expiration timestamp |
pre_encrypted | bool | No | If 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:
| Code | Description |
|---|---|
201 | Secret created/updated |
400 | Validation error (missing key/value, invalid environment) |
401 | Not authenticated |
404 | Realm 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:
| Parameter | Description |
|---|---|
slug | Realm slug |
Query parameters:
| Parameter | Default | Description |
|---|---|---|
env | Filter 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:
| Parameter | Description |
|---|---|
slug | Realm slug |
key | Secret key name |
Query parameters:
| Parameter | Default | Description |
|---|---|---|
env | dev | Environment 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:
| Code | Description |
|---|---|
200 | Secret found |
401 | Not authenticated |
404 | Realm 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:
| Parameter | Description |
|---|---|
slug | Realm slug |
key | Secret key name |
Query parameters:
| Parameter | Default | Description |
|---|---|---|
env | dev | Environment to delete from |
Response (200 OK):
{
"message": "secret deleted"
}
Status codes:
| Code | Description |
|---|---|
200 | Secret deleted |
401 | Not authenticated |
404 | Realm 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:
| Field | Type | Required | Description |
|---|---|---|---|
value | string | Yes | Plaintext value to encrypt |
Response (200 OK):
{
"ciphertext": "arcan:v1:aGVsbG8gd29ybGQ..."
}
Status codes:
| Code | Description |
|---|---|
200 | Encryption successful |
400 | Missing value |
401 | Not 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:
| Parameter | Description |
|---|---|
slug | Realm slug |
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
user_id | string | Yes | User ID to bind |
role | string | Yes | Role: 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:
| Code | Description |
|---|---|
201 | Binding created |
400 | Validation error (missing fields, invalid role) |
401 | Not authenticated |
403 | Only admins can manage policy |
404 | Realm not found |
409 | User 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:
| Parameter | Description |
|---|---|
slug | Realm 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:
| Parameter | Description |
|---|---|
slug | Realm slug |
userID | User ID to unbind |
Response (200 OK):
{
"message": "binding removed"
}
Status codes:
| Code | Description |
|---|---|
200 | Binding removed |
401 | Not authenticated |
403 | Only admins can manage policy |
404 | Realm 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:
| Parameter | Description |
|---|---|
slug | Realm slug |
type | Engine 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:
| Code | Description |
|---|---|
200 | Generation successful |
400 | Engine does not support generation |
401 | Not authenticated |
404 | Engine 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:
| Parameter | Description |
|---|---|
slug | Realm slug |
type | Engine type (e.g., totp) |
Request body: Engine-specific JSON parameters.
TOTP example request:
{
"secret": "JBSWY3DPEHPK3PXP",
"code": "123456"
}
Response (200 OK):
{
"valid": true
}
Status codes:
| Code | Description |
|---|---|
200 | Validation completed (check valid field) |
400 | Engine does not support validation |
401 | Not authenticated |
404 | Engine 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:
| Parameter | Default | Description |
|---|---|---|
realm | Filter by realm slug | |
type | Filter by event type (e.g., secret.set, auth.login, auth.register) | |
actor | Filter by actor (user ID or email) | |
limit | 50 | Maximum 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:
| Type | Description |
|---|---|
auth.register | User registration |
auth.login | Successful login |
auth.login.failed | Failed login attempt |
auth.token.created | API token created |
secret.set | Secret created or updated |
secret.delete | Secret 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"