Skip to content

API Reference

Complete documentation for all Policy Service endpoints.

Base URL

Environment URL
Development http://localhost:8009
Docker Compose http://policy-service:8009
Production Configure via environment

Authentication

All authorization endpoints require a JWT in the Authorization header:

Authorization: Bearer <jwt-token>

The JWT must be issued by the configured Keycloak realm.

Common Headers

Header Required Description
Authorization Yes* Bearer token (*except health endpoints)
Content-Type Yes application/json
X-Request-Id No Correlation ID for tracing
X-Source-Service No Calling service name (for audit)

Common Response Fields

All authorization responses include:

Field Type Description
allowed boolean Whether the action is permitted
reason string Human-readable explanation
request_id string Request identifier for tracing

Authorization Endpoints

POST /authorize

Generic authorization check for any resource type.

Request Body:

{
  "resource": {
    "type": "dataset | pipeline | dt | topic | userdata",
    "id": "resource-identifier",
    "attributes": {
      "access_level": "open | internal | restricted"
    }
  },
  "action": {
    "name": "read | write | admin | ...",
    "context": {}
  },
  "context": {}
}

Response:

{
  "allowed": true,
  "reason": "user has viewer access and client has dataset.query scope",
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}

Example:

curl -X POST http://localhost:8009/authorize \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "resource": {
      "type": "dataset",
      "id": "ds-energy-consumption",
      "attributes": {"access_level": "internal"}
    },
    "action": {"name": "read"}
  }'

Dataset Endpoints

POST /dataset/access

Check access to a specific dataset.

Request Body:

{
  "dataset_id": "string",
  "access_level": "open | internal | restricted",
  "action": "read | write | admin"
}

Response:

{
  "allowed": true,
  "reason": "user has viewer access and client has dataset.query scope",
  "request_id": "uuid"
}

Access Matrix:

Level Anonymous Viewers Editors Managers Admins
open read read read read read/write
internal read* read/write* read/write* read/write
restricted read/write*

*Requires appropriate client scope (dataset.query or dataset.admin)

POST /dataset/filters

Get row-level filters to apply to dataset queries.

Request Body:

{
  "dataset_id": "string",
  "access_level": "open | internal | restricted"
}

Response:

{
  "allowed": true,
  "filters": [
    {
      "field": "organization_id",
      "operator": "eq",
      "value": "org-123"
    },
    {
      "field": "classification",
      "operator": "in",
      "value": ["public", "internal"]
    }
  ],
  "reason": "filters applied based on user context",
  "request_id": "uuid"
}

Filter Operators:

Operator Description Example
eq Equals field = value
ne Not equals field != value
in In list field IN (values)
gt Greater than field > value
lt Less than field < value

Pipeline Endpoints

POST /pipeline/transition

Validate a pipeline state transition.

Request Body:

{
  "pipeline_id": "string",
  "from_state": "pending | started | running | completed | failed | cancelled",
  "to_state": "pending | started | running | completed | failed | cancelled"
}

Response:

{
  "allowed": true,
  "reason": "valid transition from started to running",
  "request_id": "uuid"
}

Valid Transitions:

From To Description
pending started Pipeline begins execution
pending cancelled Pipeline cancelled before start
started running Execution proceeds
started cancelled Pipeline cancelled after start
running completed Successful completion
running failed Execution error

Digital Twin Endpoints

POST /dt/access

Check access to digital twin operations.

Request Body:

{
  "dt_id": "string",
  "action": "read | write | simulate | admin"
}

Response:

{
  "allowed": true,
  "reason": "user can read dt data",
  "request_id": "uuid"
}

Action Requirements:

Action Users Services
read viewers+ with dt.read dt.read scope
write editors+ with dt.write dt.write scope
simulate managers+ with dt.simulate dt.simulate scope
admin admins with dt.admin dt.admin scope

POST /dt/event

Authorize a digital twin event emission.

Request Body:

{
  "dt_id": "string",
  "event_type": "string",
  "simulation_state": "string | null"
}

Response:

{
  "allowed": true,
  "reason": "service can emit dt events",
  "request_id": "uuid"
}

MQTT Endpoints

Compatible with mosquitto-go-auth HTTP backend.

POST /mqtt/user

Authenticate an MQTT client.

Request:

The JWT should be passed in the Authorization header:

Authorization: Bearer <jwt>

Response:

{
  "ok": true,
  "reason": "authenticated"
}

HTTP Status Codes:

Code Meaning
200 Authentication successful
403 Authentication failed

POST /mqtt/acl

Check topic access for an authenticated client.

Request Body (form-encoded or JSON):

{
  "username": "jwt-token-or-username",
  "topic": "celine/digital-twin/events/pump/123",
  "clientid": "client-id",
  "acc": 1
}

Access Mask (acc):

Value Permission
1 Read
2 Write (Publish)
4 Subscribe
3 Read + Write
5 Read + Subscribe
7 All

Response:

{
  "ok": true,
  "reason": "authorized"
}

POST /mqtt/superuser

Check if a client has superuser (admin) access.

Request Body:

{
  "username": "jwt-token-or-username"
}

Response:

{
  "ok": true,
  "reason": "superuser"
}

User Data Endpoints

POST /userdata/access

Check access to user-owned resources.

Request Body:

{
  "resource_type": "dashboard | profile | settings",
  "resource_id": "string",
  "owner_id": "user-id-who-owns-resource",
  "action": "read | write | delete | share"
}

Response:

{
  "allowed": true,
  "reason": "user accessing own data",
  "request_id": "uuid"
}

Access Rules:

Scenario Allowed
Owner accessing own data
Resource shared with user ✅ (read only)
Resource shared with user's group ✅ (read only)
Admin with userdata.admin scope
Other users

Health Endpoints

GET /health

Liveness check — is the service running?

Response:

{
  "status": "healthy",
  "version": "0.1.0",
  "policies_loaded": true,
  "details": {
    "policy_count": 6
  }
}

GET /ready

Readiness check — is the service ready to accept requests?

Response:

{
  "status": "healthy",
  "version": "0.1.0",
  "policies_loaded": true,
  "details": {
    "policy_count": 6,
    "cache": {
      "hits": 1234,
      "misses": 56
    }
  }
}

POST /reload

Hot-reload policies from disk.

Response:

{
  "status": "success",
  "policy_count": 6
}

Error Responses

400 Bad Request

Invalid request format or unknown resource type.

{
  "detail": "Unknown resource type: invalid"
}

401 Unauthorized

Missing or invalid JWT.

{
  "detail": "Invalid authorization header format"
}

403 Forbidden

Valid JWT but access denied (for MQTT endpoints).

{
  "ok": false,
  "reason": "insufficient privileges"
}

500 Internal Server Error

Policy evaluation failure.

{
  "detail": "Policy evaluation failed: ..."
}

Rate Limiting

Production deployments should implement rate limiting. Recommended limits:

Endpoint Limit
/authorize 1000 req/sec per client
/health 100 req/sec
/reload 1 req/min

OpenAPI Specification

The service exposes an OpenAPI spec at:

GET /openapi.json
GET /docs        # Swagger UI
GET /redoc       # ReDoc UI