Chapter 38 · Volume V — Reference

Admin Endpoints

38.1 Overview

ADL provides administrative endpoints for system introspection, domain management, user administration, and audit logging. These endpoints are used by admin interfaces, monitoring tools, and operational scripts.

All admin endpoints require authentication unless explicitly marked as public. User management endpoints require the admin role.


38.2 System Infrastructure

Health Check

GET /health

Auth: None (public)

Response (200): Direct response, no envelope

{
  "status": "healthy"
}

Version Information

GET /version

Auth: None (public)

Response (200): Direct response, no envelope

{
  "version": "0.3.1",
  "build": "2026-05-17T14:30:00Z",
  "commit": "a1b2c3d"
}

Server Status

GET /api/v1/status

Auth: None (public)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "uptime": 86400,
    "memory": {
      "rss": 52428800,
      "heapTotal": 41943040,
      "heapUsed": 31457280
    },
    "domains": 3,
    "routes": 127
  }
}

Plugin List

GET /api/v1/plugins

Auth: None (public)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "plugins": [
      { "name": "rest", "type": "transport", "status": "running" },
      { "name": "sqlite", "type": "store", "status": "running" },
      { "name": "authd", "type": "handler", "status": "running" },
      { "name": "adl", "type": "handler", "status": "running" },
      { "name": "seeder", "type": "handler", "status": "running" }
    ]
  }
}

Route List

GET /api/v1/routes

Auth: None (public)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "count": 127,
    "routes": [
      { "method": "GET", "path": "/api/v1/blog/posts", "action": "adl.blog.post.list" },
      { "method": "POST", "path": "/api/v1/auth/login", "action": "auth.login" },
      { "method": "GET", "path": "/health", "action": "kernel.health" }
    ]
  }
}

38.3 Domain Management

List Domains

GET /api/v1/domains

Auth: None (public)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "domains": [
      {
        "name": "blog",
        "version": "1.0.0",
        "status": "active",
        "resources": ["Post", "Tag", "Comment"],
        "createdAt": "2026-01-01T00:00:00Z",
        "updatedAt": "2026-01-15T10:00:00Z"
      },
      {
        "name": "orders",
        "version": "2.1.0",
        "status": "active",
        "resources": ["Order", "OrderItem", "Product", "Customer"],
        "createdAt": "2026-02-01T00:00:00Z",
        "updatedAt": "2026-03-10T14:00:00Z"
      }
    ]
  }
}

Get Domain

GET /api/v1/domains/{name}

Auth: None (public)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "name": "blog",
    "version": "1.0.0",
    "status": "active",
    "checksum": "a1b2c3d4e5f6...",
    "resources": ["Post", "Tag", "Comment"],
    "createdAt": "2026-01-01T00:00:00Z",
    "updatedAt": "2026-01-15T10:00:00Z",
    "migrations": [
      {
        "id": 1,
        "fromVersion": null,
        "toVersion": "1.0.0",
        "status": "success",
        "stepsApplied": 5,
        "stepsSkipped": 0,
        "appliedAt": "2026-01-01T00:00:00Z"
      }
    ]
  }
}

Get Domain Schema

GET /api/v1/domains/{domain}/schema

Auth: None (public)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "domain": {
      "name": "blog",
      "version": "1.0.0"
    },
    "resources": {
      "Post": {
        "label": "Post",
        "plural": "Posts",
        "fields": {
          "id": {
            "type": "uuid",
            "primaryKey": true,
            "required": true,
            "label": "ID"
          },
          "title": {
            "type": "string",
            "required": true,
            "maxLength": 200,
            "label": "Title"
          },
          "status": {
            "type": "enum",
            "ref": "PostStatus",
            "label": "Status"
          }
        },
        "stateMachine": {
          "field": "status",
          "initial": "draft",
          "transitions": {
            "publish": {
              "from": ["draft", "review"],
              "to": "published",
              "label": "Publish",
              "permissions": ["editor", "admin"]
            }
          }
        },
        "permissions": {
          "list": ["*"],
          "get": ["*"],
          "create": ["authenticated"],
          "update": ["owner", "editor", "admin"],
          "delete": ["admin"]
        },
        "features": {
          "softDelete": true,
          "search": true,
          "audit": true
        },
        "relationships": {
          "comments": {
            "type": "hasMany",
            "resource": "Comment",
            "foreignKey": "postId"
          },
          "tags": {
            "type": "belongsToMany",
            "resource": "Tag",
            "junction": "post_tags"
          }
        }
      }
    },
    "enums": {
      "PostStatus": {
        "values": {
          "draft": { "label": "Draft", "color": "#94a3b8" },
          "review": { "label": "In Review", "color": "#f59e0b" },
          "published": { "label": "Published", "color": "#10b981" }
        }
      }
    }
  }
}

This endpoint is the primary discovery mechanism for admin UIs.

Submit Domain

POST /api/v1/domains
Content-Type: application/json

{
  "content": "<domain YAML or JSON string>",
  "format": "yaml",
  "allow_destructive": false
}

Auth: Required (admin role)

Body fields:

  • content (required) — the ADL document as a YAML or JSON string
  • format (optional) — "yaml" or "json"; auto-detected if omitted
  • allow_destructive (optional, default false) — execute destructive migrations (drops, type narrowing) instead of refusing/deferring them (§30.6)

Response (201): Domain created or updated with migration applied

{
  "status": "ok",
  "code": 201,
  "data": {
    "domain": "blog",
    "version": "1.1.0",
    "status": "active",
    "resources": [
      { "name": "Post", "table": "blog_posts", "fields": 6 }
    ],
    "ddl_count": 3
  }
}

Response (422): Migration refused (destructive change without allow_destructive)

{
  "status": "error",
  "code": 422,
  "error_code": "DESTRUCTIVE_MIGRATION",
  "error_message": "Migration contains destructive type changes that would risk data loss. Rename fields or use a Phase 3 explicit destructive migration.",
  "data": [
    "Type change for Post.views is narrowing (potentially destructive). Set allow_destructive: true to proceed."
  ]
}

38.4 User Management

All user management endpoints require the admin role.

List Users

GET /api/v1/admin/users
    ?limit=50&offset=0&search=alice&status=active

Auth: Required (admin role)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "items": [
      {
        "id": "a1b2c3d4-...",
        "username": "alice",
        "email": "alice@example.com",
        "status": "active",
        "verified": true,
        "createdAt": "2026-01-01T00:00:00Z"
      }
    ],
    "count": 1
  }
}

Get User

GET /api/v1/admin/users/{username}

Auth: Required (admin role)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "id": "a1b2c3d4-...",
    "username": "alice",
    "email": "alice@example.com",
    "status": "active",
    "verified": true,
    "roles": ["editor", "author"],
    "createdAt": "2026-01-01T00:00:00Z",
    "lastLoginAt": "2026-05-17T10:00:00Z"
  }
}

Set User Roles

POST /api/v1/admin/users/{username}/roles

Auth: Required (admin role)

Request:

{
  "roles": ["editor", "reviewer"]
}

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "username": "alice",
    "roles": ["editor", "reviewer"]
  }
}

Verify User

POST /api/v1/admin/users/{username}/verify

Auth: Required (admin role)

Manually marks a user as email-verified.

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "username": "alice",
    "verified": true
  }
}

Suspend User

POST /api/v1/admin/users/{username}/suspend

Auth: Required (admin role)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "username": "alice",
    "status": "suspended"
  }
}

Suspended users receive HTTP 403 USER_SUSPENDED on all authenticated requests.

Unsuspend User

POST /api/v1/admin/users/{username}/unsuspend

Auth: Required (admin role)

Reverses suspension.


38.5 Role Management

List Roles

GET /api/v1/admin/roles

Auth: Required (admin role)

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "items": [
      { "name": "admin", "description": "Full system access" },
      { "name": "editor", "description": "Content editor" },
      { "name": "author", "description": "Content author" }
    ],
    "count": 3
  }
}

Create Role

POST /api/v1/admin/roles

Auth: Required (admin role)

Request:

{
  "name": "reviewer",
  "description": "Content reviewer"
}

Response (201):

{
  "status": "ok",
  "code": 201,
  "data": {
    "name": "reviewer",
    "description": "Content reviewer"
  }
}

38.6 Audit Log

Query Audit Log

GET /api/v1/admin/audit
    ?username=alice
    &action=adl.blog.post.create
    &status_code=200
    &after=2026-01-01T00:00:00Z
    &before=2026-01-31T23:59:59Z
    &limit=100
    &offset=0

Auth: Required (admin role)

Query Parameters:

ParameterTypeDescription
usernamestringFilter by username
actionstringFilter by action name
status_codeintegerFilter by HTTP status
afterdatetimeRecords after timestamp
beforedatetimeRecords before timestamp
limitintegerMax records (1-500, default 100)
offsetintegerRecords to skip

Response (200):

{
  "status": "ok",
  "code": 200,
  "data": {
    "items": [
      {
        "id": "uuid",
        "username": "alice",
        "action": "adl.blog.post.create",
        "transport": "rest",
        "remoteAddr": "192.168.1.100",
        "statusCode": 201,
        "durationMs": 45,
        "timestamp": "2026-05-17T10:30:00Z"
      }
    ],
    "count": 1
  }
}

38.7 Reserved Domain Names

The following names cannot be used as domain names (case-insensitive):

auth, users, admin, domains, status, plugins, routes, version,
openapi, echo

These collide with infrastructure routes under /api/v1/.


38.8 Integration Points

FeatureIntegration
Authentication (§29)User management endpoints
Migration (§30)Domain submission triggers migration pipeline
Audit (§8)Audit log query endpoint
Permissions (§11)Admin role requirement on management endpoints
Schema (§5)Schema endpoint for runtime introspection
OpenAPI (§37)Per-domain OpenAPI spec

End of Chapter 38.

Next: Chapter 39 — Error Contract