🔧 Control Simulator API Reference
📌 Overview
The Control Simulator API provides programmatic access to control testing, trace generation, and test history management.
Base URL: /api/v1/pis
Authentication: All endpoints require Bearer token authentication.
🔧 Endpoints
GET /policies/loadable
Get controls available for testing filtered by environment.
Query Parameters:
environment(required):sandboxorproduction
Response:
[
{
"id": 1,
"name": "Admin Access Control",
"description": "Controls admin access to sensitive resources",
"status": "enabled",
"environment": "sandbox",
"resource_name": "API Gateway",
"bouncer_name": "Bouncer-US-East-01",
"target_action": ["GET", "POST", "DELETE"]
}
]
Example:
GET /api/v1/pis/policies/loadable?environment=sandbox
Authorization: Bearer {token}
GET /policies/{policy_id}/attributes
Extract input attributes from a control's Rego code using OPA AST parsing.
Path Parameters:
policy_id(required): Control ID
Response:
[
{
"key": "user.role",
"label": "User Role",
"type": "string",
"default_value": "",
"description": "Extracted from input.user.role"
},
{
"key": "resource.sensitivity",
"label": "Resource Sensitivity",
"type": "string",
"default_value": "",
"description": "Extracted from input.resource.sensitivity"
}
]
Attribute Types:
string: Text valuesnumber: Numeric valuesboolean: true/falsearray: List of valuesobject: Nested structure
POST /policies/{policy_id}/simulate
Run a control simulation with provided input data.
Path Parameters:
policy_id(required): Control ID
Query Parameters:
environment(required):sandboxorproduction
Request Body:
{
"input_json": {
"user": {
"role": "admin",
"id": "user-123"
},
"resource": {
"type": "api",
"sensitivity": "high"
},
"action": "read"
},
"test_type": "decision"
}
Response:
{
"simulation_id": 42,
"decision": "ALLOW",
"reason": "Access ALLOWED by control 'Admin Access Control'. All control conditions were satisfied.",
"trace": [
{
"step_number": 1,
"step_type": "policy_matched",
"description": "Control 'Admin Access Control' selected for evaluation",
"result": true,
"details": {
"policy_id": 1,
"policy_name": "Admin Access Control"
}
}
],
"input_json": { /* ... */ },
"output_json": { /* ... */ },
"execution_time_ms": 45.3,
"timestamp": "2025-01-05T10:30:00Z"
}
Decision Values:
ALLOW: Access grantedDENY: Access deniedMASK: Access granted with data maskingNO_MATCH: No control matched, default action applied
Trace Step Types:
policy_matched: Control was selectedcondition_eval: Condition evaluationfinal_decision: Final decision computed
GET /policies/{policy_id}/test-history
Get test history for a specific control.
Path Parameters:
policy_id(required): Control ID
Query Parameters:
limit(optional, default: 50, max: 200): Results per pageoffset(optional, default: 0): Pagination offsetstart_date(optional): ISO 8601 datetimeend_date(optional): ISO 8601 datetime
Response:
{
"entries": [
{
"id": 42,
"policy_id": 1,
"policy_name": "Admin Access Control",
"user": "john.doe",
"decision": "ALLOW",
"execution_time_ms": 45.3,
"timestamp": "2025-01-05T10:30:00Z",
"input_summary": "role=admin, action=read",
"can_export": true,
"environment": "sandbox"
}
],
"total": 150,
"limit": 50,
"offset": 0,
"has_more": true
}
GET /test-history
Get test history for the current user across all controls.
Query Parameters:
limit(optional, default: 50, max: 200): Results per pageoffset(optional, default: 0): Pagination offsetstart_date(optional): ISO 8601 datetimeend_date(optional): ISO 8601 datetime
Response: Same format as per-control history
POST /export/{simulation_id}
Export a simulation result in various formats.
Path Parameters:
simulation_id(required): Simulation log ID
Query Parameters:
format(required):json,csv, orpdf
Response: File download with appropriate Content-Type
Content-Types:
application/jsonfor JSONtext/csvfor CSVapplication/pdffor PDF
Example:
POST /api/v1/pis/export/42?format=json
Authorization: Bearer {token}
GET /scenarios
List saved test scenarios for the current user.
Query Parameters:
policy_id(optional): Filter by control ID
Response:
[
{
"id": 10,
"name": "Admin Happy Path",
"description": "Test admin access during business hours",
"policy_id": 1,
"policy_name": "Admin Access Control",
"attributes": {
"user.role": "admin",
"context.time": "14:00:00"
},
"created_at": "2025-01-05T09:00:00Z",
"last_used": "2025-01-05T10:30:00Z"
}
]
POST /scenarios
Save a new test scenario.
Request Body:
{
"policy_id": 1,
"name": "Admin Happy Path",
"description": "Test admin access during business hours",
"attributes": {
"user.role": "admin",
"context.time": "14:00:00"
}
}
Response: Same as GET /scenarios (single scenario)
PUT /scenarios/{scenario_id}
Update an existing test scenario.
Path Parameters:
scenario_id(required): Scenario ID
Request Body: Same as POST /scenarios
Response: Updated scenario
DELETE /scenarios/{scenario_id}
Delete a test scenario.
Path Parameters:
scenario_id(required): Scenario ID
Response:
{
"message": "Test scenario deleted successfully"
}
📌 Data Models
PolicyAttribute
interface PolicyAttribute {
key: string; // Attribute path without "input." prefix
label: string; // Human-readable label
type: string; // "string" | "number" | "boolean" | "array"
default_value: any; // Type-appropriate default
description?: string; // Description
}
TraceStep
interface TraceStep {
step_number: number; // Sequential step number
step_type: string; // "policy_matched" | "condition_eval" | "final_decision"
description: string; // Human-readable description
result: boolean; // Pass/fail
details?: Record<string, any>; // Additional details
}
SimulationResult
interface SimulationResult {
simulation_id: number | null;
decision: string; // "ALLOW" | "DENY" | "MASK" | "NO_MATCH"
reason: string; // Human-readable explanation
trace: TraceStep[]; // Execution trace
input_json: Record<string, any>;
output_json: Record<string, any>;
execution_time_ms: number;
timestamp: string; // ISO 8601
}
📌 Error Responses
All endpoints use standard HTTP status codes:
200: Success400: Bad request (invalid parameters)401: Unauthorized (missing/invalid token)404: Not found (control/scenario doesn't exist)500: Server error
Error Format:
{
"detail": "Error message describing what went wrong"
}
📌 Rate Limits
No explicit rate limits for authenticated users. Simulation execution is limited to 30 seconds per request.
📌 Examples
Complete Test Flow
// 1. Get loadable controls
const policies = await fetch('/api/v1/pis/policies/loadable?environment=sandbox', {
headers: { 'Authorization': `Bearer ${token}` }
});
// 2. Get attributes for selected control
const attributes = await fetch('/api/v1/pis/policies/1/attributes', {
headers: { 'Authorization': `Bearer ${token}` }
});
// 3. Run simulation
const result = await fetch('/api/v1/pis/policies/1/simulate?environment=sandbox', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
input_json: {
user: { role: 'admin' },
resource: { type: 'api' },
action: 'read'
},
test_type: 'decision'
})
});
// 4. Save as scenario
await fetch('/api/v1/pis/scenarios', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
policy_id: 1,
name: 'Admin Access Test',
description: 'Test admin access to API',
attributes: {
'user.role': 'admin',
'resource.type': 'api',
'action': 'read'
}
})
});
📌 Implementation Notes
Attribute Extraction
The attribute extraction uses:
opa parse --format jsonto get AST- Recursive traversal to find
input.*references - Type inference from usage patterns in code
- Label generation from attribute paths
OPA Simulation
Simulations execute:
- Create isolated OPA bundle with control code
- Run
opa eval --explain=fullfor tracing - Parse trace output into structured steps
- Log to audit trail and simulation logs table
Audit Logging
Every simulation creates:
- Audit log entry with event type
POLICY_SIMULATION_RUN - Detailed simulation log in
policy_simulation_logstable - Linked via
simulation_idfor compliance tracking