Security & operations API
This page documents security and operations API endpoints. It covers the public content categories endpoint (read-only, no admin role required), the configuration changelog audit trail, and the shutdown/drain lifecycle API.
Base URL: https://platform.arbitex.ai
Content categories — public endpoint
Section titled “Content categories — public endpoint”The public content categories endpoint returns the active category taxonomy without requiring admin privileges. This is useful for client applications that need to display category labels or build category-aware UIs.
Authentication: Authorization: Bearer <user-jwt> — any authenticated user.
List active categories
Section titled “List active categories”GET /api/content-categoriesReturns all enabled content categories visible to the authenticated user’s organization. Unlike the admin endpoint (/api/v1/admin/content-categories), this endpoint:
- Only returns enabled categories (no
enabledfilter needed) - Omits admin-only fields (
keyword_count,is_custom) - Does not support create/update/delete operations
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
domain |
string | — | Filter by domain (e.g., medical, financial, legal) |
limit |
integer | 100 |
Results per page (max 500) |
offset |
integer | 0 |
Pagination offset |
Request
curl "https://platform.arbitex.ai/api/content-categories?domain=financial" \ -H "Authorization: Bearer $USER_TOKEN"Response 200 OK
{ "categories": [ { "id": "financial/investment-research", "domain": "financial", "subcategory": "investment-research", "label": "Investment Research", "description": "Equity analysis, market research, investment recommendations" }, { "id": "financial/ma-analysis", "domain": "financial", "subcategory": "ma-analysis", "label": "M&A Analysis", "description": "Deal structuring, due diligence, valuation" }, { "id": "financial/trading", "domain": "financial", "subcategory": "trading", "label": "Trading Signals", "description": "Market timing, trading strategies, price predictions" }, { "id": "financial/tax", "domain": "financial", "subcategory": "tax", "label": "Tax Advice", "description": "Tax planning, filing strategy, cross-border tax" } ], "total": 4, "limit": 100, "offset": 0}Response fields
| Field | Type | Description |
|---|---|---|
categories |
array | List of category objects |
categories[].id |
string | Category identifier in domain/subcategory format |
categories[].domain |
string | Top-level classification domain |
categories[].subcategory |
string | Subcategory slug |
categories[].label |
string | Human-readable display name |
categories[].description |
string | Description of the category scope |
total |
integer | Total matching categories |
limit |
integer | Page size used |
offset |
integer | Pagination offset used |
Error responses
| Status | Description |
|---|---|
401 |
Missing or invalid authentication token |
403 |
Content categories feature is not enabled for this organization |
Admin CRUD endpoints
Section titled “Admin CRUD endpoints”For full category management (create, update, delete, statistics), see the Platform Admin API — Content Categories.
Configuration changelog API
Section titled “Configuration changelog API”The configuration changelog provides a tamper-evident audit trail of all administrative configuration changes. Every change to policy rules, DLP pipeline settings, content categories, feature flags, and system configuration is recorded with the actor, timestamp, and a structured diff of the change.
Authentication: Authorization: Bearer <platform-admin-jwt>
Changelog entry object
Section titled “Changelog entry object”| Field | Type | Description |
|---|---|---|
id |
string | Changelog entry UUID |
timestamp |
datetime | ISO 8601 timestamp of the change |
actor |
object | User who made the change |
actor.user_id |
string | User UUID |
actor.email |
string | User email address |
actor.role |
string | Role at time of change (platform_admin, org_admin) |
actor.ip_address |
string | Source IP address |
resource_type |
string | Type of resource changed (see table below) |
resource_id |
string | Identifier of the changed resource |
action |
string | created, updated, or deleted |
field_changes |
array | List of field-level changes (for updated actions) |
field_changes[].field |
string | Field name that changed |
field_changes[].old_value |
any | Previous value |
field_changes[].new_value |
any | New value |
snapshot_before |
object | Full resource state before the change (for updated and deleted) |
snapshot_after |
object | Full resource state after the change (for created and updated) |
hmac |
string | HMAC-SHA256 of the entry for tamper detection |
Tracked resource types
Section titled “Tracked resource types”| Resource Type | Description |
|---|---|
policy_rule |
Policy engine rules |
policy_pack |
Policy packs |
policy_chain |
Policy chains |
dlp_rule |
DLP pipeline rules |
dlp_pattern |
Custom DLP patterns |
content_category |
Content category definitions |
content_category_keyword |
Category keyword changes |
feature_flag |
Organization feature flags |
system_config |
System configuration keys |
model_route |
Model routing configuration |
webhook |
Webhook definitions |
siem_connector |
SIEM connector configuration |
kill_switch |
Kill switch activation/deactivation |
List configuration changes
Section titled “List configuration changes”GET /api/v1/admin/config/changelogReturns configuration change history in reverse chronological order (newest first).
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
resource_type |
string | — | Filter by resource type (e.g., policy_rule, kill_switch) |
resource_id |
string | — | Filter by specific resource identifier |
actor_id |
string | — | Filter by the user who made the change |
action |
string | — | Filter by action: created, updated, deleted |
from |
datetime | 30 days ago | Start of time range (ISO 8601) |
to |
datetime | now | End of time range (ISO 8601) |
limit |
integer | 50 |
Results per page (max 200) |
cursor |
string | — | Cursor for pagination (from next_cursor in response) |
Request
curl "https://platform.arbitex.ai/api/v1/admin/config/changelog?resource_type=policy_rule&limit=10" \ -H "Authorization: Bearer $ADMIN_TOKEN"Response 200 OK
{ "entries": [ { "id": "cl_01HZ_A1B2C3", "timestamp": "2026-03-14T14:22:05Z", "actor": { "user_id": "usr_01HZ_ADMIN1", "role": "platform_admin", "ip_address": "10.0.1.50" }, "resource_type": "policy_rule", "resource_id": "rule_01HZ_MNPI_BLOCK", "action": "updated", "field_changes": [ { "field": "action", "old_value": "flag", "new_value": "block" }, { "field": "alert.severity", "old_value": "medium", "new_value": "critical" } ], "snapshot_before": { "name": "Investment research MNPI detection", "action": "flag", "alert": { "severity": "medium", "channels": ["compliance-team"] }, "enabled": true }, "snapshot_after": { "name": "Investment research MNPI detection", "action": "block", "alert": { "severity": "critical", "channels": ["compliance-team", "legal-team"] }, "enabled": true }, "hmac": "sha256:a4f8e2c1d..." }, { "id": "cl_01HZ_D4E5F6", "timestamp": "2026-03-14T09:15:30Z", "actor": { "user_id": "usr_01HZ_ADMIN2", "role": "org_admin", "ip_address": "10.0.2.80" }, "resource_type": "policy_rule", "resource_id": "rule_01HZ_MEDICAL_RESTRICT", "action": "created", "field_changes": [], "snapshot_before": null, "snapshot_after": { "name": "Medical content — non-clinical block", "action": "block", "conditions": [ { "field": "content.category_domain", "operator": "equals", "value": "medical" }, { "field": "user.groups", "operator": "not_contains", "value": "clinical-staff" } ], "enabled": true }, "hmac": "sha256:b7c3d4e5f..." } ], "total": 847, "next_cursor": "cl_01HZ_G7H8I9", "has_more": true}Response fields
| Field | Type | Description |
|---|---|---|
entries |
array | List of changelog entry objects |
total |
integer | Total entries matching the query |
next_cursor |
string or null | Cursor for the next page |
has_more |
boolean | Whether more results exist |
Error responses
| Status | Description |
|---|---|
400 |
Invalid query parameters (bad date format, unknown resource_type) |
401 |
Missing or invalid authentication token |
403 |
Caller does not have platform admin role |
Example: Audit policy changes by a specific admin
Section titled “Example: Audit policy changes by a specific admin”# All changes by a specific admin in the last 7 dayscurl "https://platform.arbitex.ai/api/v1/admin/config/changelog?\actor_id=usr_01HZ_ADMIN1&\from=2026-03-07T00:00:00Z&\limit=50" \ -H "Authorization: Bearer $ADMIN_TOKEN"Example: Track kill switch events
Section titled “Example: Track kill switch events”# All kill switch activations and deactivationscurl "https://platform.arbitex.ai/api/v1/admin/config/changelog?\resource_type=kill_switch&\from=2026-01-01T00:00:00Z" \ -H "Authorization: Bearer $ADMIN_TOKEN"Example: Detect policy weakening
Section titled “Example: Detect policy weakening”# Policy rule changes where action was downgraded# Filter client-side for field_changes where field=action and new_value is less restrictivecurl "https://platform.arbitex.ai/api/v1/admin/config/changelog?\resource_type=policy_rule&\action=updated&\limit=200" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ | jq '.entries[] | select(.field_changes[] | .field == "action" and .old_value == "block" and (.new_value == "flag" or .new_value == "allow"))'For using the changelog in security investigations, see the Security Operations — Policy Tampering Runbook.
Shutdown and drain API
Section titled “Shutdown and drain API”The shutdown/drain API provides graceful shutdown capabilities for the Arbitex platform. A drain operation stops accepting new requests while allowing in-flight requests to complete, enabling zero-downtime maintenance and emergency shutdowns.
Authentication: Authorization: Bearer <platform-admin-jwt>
Drain states
Section titled “Drain states”| State | Description |
|---|---|
running |
Normal operation — accepting and processing requests |
draining |
Not accepting new requests; in-flight requests completing |
drained |
All in-flight requests completed; ready for shutdown |
shutdown |
Process is terminating |
running ──→ draining ──→ drained ──→ shutdown ↑ │ └────────────┘ (cancel drain)Initiate drain
Section titled “Initiate drain”POST /api/v1/admin/shutdown/drainBegins a graceful drain sequence. The platform immediately stops accepting new AI requests (returns 503 Service Unavailable to new callers) and waits for in-flight requests to complete.
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
reason |
string | Yes | — | Reason for the drain (recorded in audit log) |
timeout_seconds |
integer | No | 300 |
Maximum time to wait for in-flight requests before force-terminating (30–3600) |
scope |
string | No | platform |
Drain scope: platform (all services) or api (API gateway only) |
notify_webhook |
boolean | No | true |
Send webhook notification when drain completes |
Request
curl -X POST "https://platform.arbitex.ai/api/v1/admin/shutdown/drain" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "reason": "Scheduled maintenance window — 2026-03-14 22:00 UTC", "timeout_seconds": 600, "scope": "platform", "notify_webhook": true }'Response 202 Accepted
{ "drain_id": "drain_01HZ_M1N2O3", "state": "draining", "started_at": "2026-03-14T22:00:05Z", "timeout_seconds": 600, "timeout_at": "2026-03-14T22:10:05Z", "scope": "platform", "reason": "Scheduled maintenance window — 2026-03-14 22:00 UTC", "initiated_by": { "user_id": "usr_01HZ_ADMIN1", }, "in_flight": { "total": 23, "chat_completions": 18, "admin_operations": 5 }}Response fields
| Field | Type | Description |
|---|---|---|
drain_id |
string | Unique drain operation identifier |
state |
string | Current drain state |
started_at |
datetime | When the drain began |
timeout_seconds |
integer | Configured timeout |
timeout_at |
datetime | When force-termination will occur if drain is not complete |
scope |
string | Drain scope |
reason |
string | Operator-provided reason |
initiated_by |
object | Admin who initiated the drain |
in_flight.total |
integer | Total in-flight requests at drain start |
in_flight.chat_completions |
integer | In-flight chat completion requests |
in_flight.admin_operations |
integer | In-flight admin API operations |
Error responses
| Status | Description |
|---|---|
400 |
Invalid request body (missing reason, timeout out of range) |
403 |
Caller does not have platform admin role |
409 |
A drain operation is already in progress |
Check drain status
Section titled “Check drain status”GET /api/v1/admin/shutdown/statusReturns the current shutdown/drain state of the platform. This endpoint remains available even during a drain (it is exempt from the request rejection).
Request
curl "https://platform.arbitex.ai/api/v1/admin/shutdown/status" \ -H "Authorization: Bearer $ADMIN_TOKEN"Response 200 OK — when running normally
{ "state": "running", "drain_id": null, "in_flight": { "total": 142, "chat_completions": 128, "admin_operations": 14 }, "uptime_seconds": 864000}Response 200 OK — during active drain
{ "state": "draining", "drain_id": "drain_01HZ_M1N2O3", "started_at": "2026-03-14T22:00:05Z", "timeout_at": "2026-03-14T22:10:05Z", "reason": "Scheduled maintenance window — 2026-03-14 22:00 UTC", "initiated_by": { "user_id": "usr_01HZ_ADMIN1", }, "in_flight": { "total": 3, "chat_completions": 2, "admin_operations": 1 }, "elapsed_seconds": 45, "uptime_seconds": 864045}Response 200 OK — when drained
{ "state": "drained", "drain_id": "drain_01HZ_M1N2O3", "started_at": "2026-03-14T22:00:05Z", "drained_at": "2026-03-14T22:00:52Z", "reason": "Scheduled maintenance window — 2026-03-14 22:00 UTC", "in_flight": { "total": 0, "chat_completions": 0, "admin_operations": 0 }, "elapsed_seconds": 47, "uptime_seconds": 864052}Response fields
| Field | Type | Description |
|---|---|---|
state |
string | Current state: running, draining, drained, shutdown |
drain_id |
string or null | Active drain operation ID (null when running) |
started_at |
datetime | When the active drain began (present when draining/drained) |
drained_at |
datetime | When all in-flight requests completed (present when drained) |
timeout_at |
datetime | Force-termination deadline (present when draining) |
reason |
string | Drain reason (present when draining/drained) |
initiated_by |
object | Admin who started the drain (present when draining/drained) |
in_flight |
object | Current in-flight request counts |
elapsed_seconds |
integer | Seconds since drain started (present when draining/drained) |
uptime_seconds |
integer | Platform uptime in seconds |
Cancel a drain
Section titled “Cancel a drain”DELETE /api/v1/admin/shutdown/drainCancels an in-progress drain and returns the platform to normal operation. Only valid when state is draining (not yet drained).
Request
curl -X DELETE "https://platform.arbitex.ai/api/v1/admin/shutdown/drain" \ -H "Authorization: Bearer $ADMIN_TOKEN"Response 200 OK
{ "state": "running", "cancelled_drain_id": "drain_01HZ_M1N2O3", "cancelled_at": "2026-03-14T22:01:30Z", "message": "Drain cancelled. Platform is accepting new requests."}Error responses
| Status | Description |
|---|---|
403 |
Caller does not have platform admin role |
409 |
No drain in progress, or drain has already completed (state is drained) |
Drain lifecycle example
Section titled “Drain lifecycle example”A typical maintenance window follows this sequence:
# 1. Check current statecurl "https://platform.arbitex.ai/api/v1/admin/shutdown/status" \ -H "Authorization: Bearer $ADMIN_TOKEN"# → state: "running", in_flight: 142
# 2. Initiate draincurl -X POST "https://platform.arbitex.ai/api/v1/admin/shutdown/drain" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"reason": "Kernel update - maintenance window MW-2026-0314", "timeout_seconds": 300}'# → state: "draining", in_flight: 142
# 3. Poll status until drainedcurl "https://platform.arbitex.ai/api/v1/admin/shutdown/status" \ -H "Authorization: Bearer $ADMIN_TOKEN"# → state: "draining", in_flight: 3 ... wait ...# → state: "drained", in_flight: 0
# 4. Proceed with maintenance (restart, upgrade, etc.)# The platform process can now be safely stopped.
# 5. After restart, verifycurl "https://platform.arbitex.ai/api/v1/admin/shutdown/status" \ -H "Authorization: Bearer $ADMIN_TOKEN"# → state: "running", uptime_seconds: 12Audit trail
Section titled “Audit trail”All drain operations are recorded in the configuration changelog:
| Event | resource_type |
action |
|---|---|---|
| Drain initiated | shutdown_drain |
created |
| Drain completed | shutdown_drain |
updated (state → drained) |
| Drain cancelled | shutdown_drain |
deleted |
| Force-termination (timeout) | shutdown_drain |
updated (state → shutdown, force: true) |
Use the Configuration Changelog API to query drain history.
Endpoint summary
Section titled “Endpoint summary”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/content-categories |
User JWT | List active content categories |
GET |
/api/v1/admin/config/changelog |
Admin JWT | List configuration change history |
POST |
/api/v1/admin/shutdown/drain |
Admin JWT | Initiate graceful drain |
GET |
/api/v1/admin/shutdown/status |
Admin JWT | Check shutdown/drain state |
DELETE |
/api/v1/admin/shutdown/drain |
Admin JWT | Cancel an in-progress drain |
Related
Section titled “Related”- Platform Admin API — Content Categories — admin CRUD endpoints for category management
- Content Categories Guide — category taxonomy, admin UI, and use cases
- Security Operations — incident response workflows and runbooks
- Admin Day-2 Operations — operational procedures including kill switch
- Monitoring and Alerting — Prometheus metrics and Grafana dashboards