API Reference
All endpoints require the X-Scopebound-API-Key header unless noted otherwise.
Base URL: https://your-partner.api.scopebound.ai
Enforcement API
POST /v1/provision
Provision a scoped JWT for an agent session. The JWT encodes the role's allowed tools, rate limits, TTL, and any parameter or time constraints — verified on every enforce call without a database lookup.
role_id accepts either the role UUID or the role name.
Request
Response
{
"jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_id": "47bb16ec-e06a-4090-9a4a-e0873e680545",
"expires_at": "2026-04-29T04:19:40Z"
}
POST /v1/enforce
Enforce policy on a tool call before it executes. Returns allow or deny in under 5ms.
Request
{
"jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"tool_name": "read_invoices",
"call_args": {
"status": "pending",
"amount": 25000,
"env": "staging"
},
"call_id": "optional-idempotency-key"
}
Note — Environment enforcement: If the role has
allowed_envsconfigured, passenvincall_args. Calls without anenvfield are allowed regardless ofallowed_envs(backward compatible).
Response — allow
Response — deny
{
"decision": "deny",
"call_id": "abc123",
"deny_code": "SCOPE_VIOLATION",
"severity": "medium",
"reason": "tool \"delete_invoice\" is not in allowed_tools",
"retry_guidance": "none",
"latency_ms": 1.1
}
Deny codes
| Code | Severity | Meaning |
|---|---|---|
SCOPE_VIOLATION |
medium | Tool not in the role's allowed_tools list |
PARAMETER_VIOLATION |
high | A call_args field violates a parameter constraint on the tool |
ENV_VIOLATION |
high | call_args.env is not in the role's allowed_envs list |
TIME_VIOLATION |
medium | Current UTC time is outside the role's allowed_hours or allowed_days window |
DATA_LIMIT_EXCEEDED |
high | call_args.limit exceeds the role's max_rows data scope limit |
DELEGATION_DEPTH_EXCEEDED |
critical | Agent has no remaining delegation depth to spawn a sub-agent |
SESSION_EXPIRED |
low | The JWT has expired |
RATE_LIMIT_EXCEEDED |
medium | Session has exceeded its per-minute or per-hour rate limit |
BEHAVIORAL_DRIFT |
high | Session's tool call pattern has deviated significantly from baseline |
POST /v1/mcp/enforce
Dedicated endpoint for MCP tool calls. Identical to /v1/enforce but records MCP-specific audit event types.
Management API
POST /mgmt/v1/roles
Create a new agent role.
Request
{
"name": "invoice-processor",
"description": "Processes invoices — read only",
"allowed_tools": ["read_invoices", "send_email"],
"default_ttl_seconds": 3600,
"max_delegation_depth": 1,
"rate_limit_per_minute": 30,
"rate_limit_per_hour": 500,
"parent_role_id": "base-agent",
"parameter_constraints": {
"send_email": [
{"field": "to", "operator": "regex", "value": ".*@company\\.com$"}
],
"read_invoices": [
{"field": "amount", "operator": "lt", "value": 50000}
]
},
"allowed_hours_start": 8,
"allowed_hours_end": 20,
"allowed_days": [0, 1, 2, 3, 4],
"data_scope": {
"allowed_envs": ["staging", "production"],
"max_rows": 1000
},
"webhook_url": "https://your-server.com/webhooks/scopebound",
"webhook_secret": "your-hmac-secret"
}
Field reference
| Field | Type | Description |
|---|---|---|
name |
string | Unique role name. Accepted by /v1/provision as an alternative to UUID. |
allowed_tools |
string[] | Explicit whitelist of tool names the agent may call. |
default_ttl_seconds |
int | Session lifetime in seconds. Short-lived sessions (900s) recommended for write agents. |
max_delegation_depth |
int | Maximum sub-agent delegation depth. 0 = no sub-agents. |
rate_limit_per_minute |
int | Token bucket limit per session per minute. 0 = no limit. |
rate_limit_per_hour |
int | Token bucket limit per session per hour. 0 = no limit. |
parent_role_id |
string | UUID or name of a parent role. Child inherits all parent allowed_tools additively. Max inheritance depth: 2. |
parameter_constraints |
object | Per-tool field-level constraints. See Parameter Constraints. |
allowed_hours_start |
int | UTC hour (0–23) when access begins. 0 = no restriction. |
allowed_hours_end |
int | UTC hour (0–23) when access ends (exclusive). 0 = no restriction. |
allowed_days |
int[] | Days of week when access is allowed. 0=Mon, 1=Tue, ..., 6=Sun. Empty = all days. |
data_scope.allowed_envs |
string[] | Environments this role may operate in. Empty = unrestricted. |
data_scope.max_rows |
int | Maximum row count a single call may request. 0 = unrestricted. |
webhook_url |
string | URL to receive HMAC-signed deny event notifications. |
webhook_secret |
string | HMAC-SHA256 signing secret for webhook payloads. Never returned in full — only webhook_secret_hint is returned. |
Response
{
"id": "3bcedc38-a87a-4c2a-bc50-7693cb90af2d",
"name": "invoice-processor",
"allowed_tools": ["read_invoices", "send_email"],
"default_ttl": 3600,
"rate_limit_per_minute": 30,
"rate_limit_per_hour": 500,
"parent_role_id": "base-agent-uuid",
"parameter_constraints": {
"send_email": [{"field": "to", "operator": "regex", "value": ".*@company\\.com$"}]
},
"allowed_hours_start": 8,
"allowed_hours_end": 20,
"allowed_days": [0, 1, 2, 3, 4],
"webhook_url": "https://your-server.com/webhooks/scopebound",
"webhook_secret_hint": "your-hma***",
"created_at": "2026-04-29T12:00:00Z",
"updated_at": "2026-04-29T12:00:00Z"
}
Note — Webhook secret: The full
webhook_secretis never returned. Usewebhook_secret_hint(first 8 chars +***) to verify which secret is configured.
GET /mgmt/v1/roles
Get a role by name or list all roles.
GET /mgmt/v1/roles— list all rolesGET /mgmt/v1/roles?name=invoice-processor— get role by nameGET /mgmt/v1/roles/{uuid}— get role by UUID
PUT /mgmt/v1/roles/{id}
Update a role. Same request body as POST. Role name cannot be changed after creation.
Parameter Constraints
Parameter constraints let you enforce field-level rules on tool call arguments. Configured per tool in the role's parameter_constraints map.
Constraint object
Supported operators
| Operator | Type | Description | Example |
|---|---|---|---|
eq |
any | Exact match | {"field": "status", "operator": "eq", "value": "pending"} |
lt |
number | Less than | {"field": "amount", "operator": "lt", "value": 50000} |
gt |
number | Greater than | {"field": "priority", "operator": "gt", "value": 0} |
contains |
string | Substring match | {"field": "note", "operator": "contains", "value": "approved"} |
regex |
string | Regular expression | {"field": "to", "operator": "regex", "value": ".*@company\\.com$"} |
in |
any | Value in list | {"field": "region", "operator": "in", "value": ["us-east", "us-west"]} |
Note — Optional fields: If a constrained field is missing from
call_args, the constraint is skipped (allow). This preserves backward compatibility for agents that don't always pass every argument.
Deny code: PARAMETER_VIOLATION (severity: high)
Role Inheritance
A child role can inherit tools from a parent role by setting parent_role_id. Inheritance is additive — the child gains all parent tools plus its own. The parent is never affected by the child.
base-agent → allowed_tools: [read_invoices, read_vendors]
└── extended-agent → allowed_tools: [send_email]
effective: [read_invoices, read_vendors, send_email]
└── senior-agent → allowed_tools: [approve_invoice]
effective: [read_invoices, read_vendors, send_email, approve_invoice]
- Maximum inheritance depth: 2 (grandparent → parent → child)
- Child can only add tools, never remove parent tools
- Circular inheritance is rejected at role creation time
- Provisioning a child role returns a JWT with the full merged tool list
Time-Based Access Controls
Restrict when an agent session may call tools using UTC hour windows and day-of-week lists.
This allows access Monday–Friday, 08:00–19:59 UTC.
| Field | Values | Notes |
|---|---|---|
allowed_hours_start |
0–23 UTC | Inclusive. Set to 0 with allowed_hours_end 0 for no restriction. |
allowed_hours_end |
0–23 UTC | Exclusive. Access denied at this hour and beyond. |
allowed_days |
0=Mon … 6=Sun | Empty array = all days allowed. |
Deny code: TIME_VIOLATION (severity: medium)
Bundle Versioning API
GET /mgmt/v1/bundles
List all stored policy bundle versions, newest first. Maximum 10 versions retained.
Response
{
"count": 2,
"versions": [
{
"id": "feed6359-c16c-4d1c-910d-6440c8b95b60",
"version": "1.0.0",
"checksum": "e47bc950ffd3a8c362ded5e5f7b6d988706acd94991340b1f0c7da4c9bcddd55",
"bundle_path": "rules/bundle-history/bundle-20260429-172000.tar.gz",
"is_active": false,
"uploaded_at": "2026-04-29T17:20:00Z",
"activated_at": "2026-04-29T17:20:00Z",
"description": "add rate limit rule"
},
{
"id": "8ddcf3ca-1281-4668-9333-f067b989337f",
"version": "1.0.0",
"checksum": "a1b2c3d4...",
"bundle_path": "rules/bundle-history/bundle-20260429-171340.tar.gz",
"is_active": true,
"uploaded_at": "2026-04-29T17:13:40Z",
"activated_at": "2026-04-29T17:21:00Z",
"description": "baseline v1"
}
]
}
POST /mgmt/v1/bundles/upload
Upload a new policy bundle. The bundle is a signed tar.gz containing .rego files and a manifest.json. On success the bundle is immediately extracted and the evaluator hot-reloaded — no restart required.
Request
Raw tar.gz bundle as the request body. Optional description query parameter.
tar -czf bundle.tar.gz -C rules .
curl -X POST "https://your-partner.api.scopebound.ai/mgmt/v1/bundles/upload?description=add+new+rule" \
-H "X-Scopebound-API-Key: your-key" \
-H "Content-Type: application/octet-stream" \
--data-binary @bundle.tar.gz
Response
{
"id": "feed6359-c16c-4d1c-910d-6440c8b95b60",
"version": "1.0.0",
"checksum": "e47bc950...",
"is_active": true,
"uploaded_at": "2026-04-29T17:20:00Z",
"description": "add new rule"
}
POST /mgmt/v1/bundles/rollback/{id}
Roll back to a previously uploaded bundle version. The stored bundle is re-extracted and the evaluator hot-reloaded — no restart required.
curl -X POST "https://your-partner.api.scopebound.ai/mgmt/v1/bundles/rollback/8ddcf3ca-1281-4668-9333-f067b989337f" \
-H "X-Scopebound-API-Key: your-key"
Response
{
"message": "rollback successful",
"version_id": "8ddcf3ca-1281-4668-9333-f067b989337f",
"version": "1.0.0",
"checksum": "a1b2c3d4..."
}
Analytics API
GET /mgmt/v1/analytics
Get usage analytics for your enforcement plane.
Query parameters
| Parameter | Default | Max | Description |
|---|---|---|---|
window_hours |
24 | 168 | Time window in hours |
Response
{
"window_hours": 24,
"total_calls": 1250,
"allow_count": 1180,
"deny_count": 70,
"allow_rate": 0.94,
"deny_rate": 0.06,
"top_denied_tools": [
{"tool_name": "delete_invoice", "deny_count": 45},
{"tool_name": "export_data", "deny_count": 25}
],
"calls_by_hour": [
{"hour": "2026-04-29T00:00:00Z", "allow_count": 48, "deny_count": 2}
]
}
Health Check
GET /healthz
No authentication required.
Response
{
"status": "ok",
"sprint": "7",
"uptime_seconds": 3600,
"bundle_version": "1.0.0",
"bundle_last_synced_at": "2026-04-29T12:00:00Z",
"db_status": "ok",
"last_chain_verified_at": "2026-04-29T12:00:00Z"
}
Webhook Payloads
When a deny event fires, Scopebound sends an HMAC-SHA256 signed POST to your webhook_url.
Headers
Payload
{
"event": "deny",
"deny_code": "SCOPE_VIOLATION",
"severity": "medium",
"tool_name": "delete_invoice",
"agent_id": "agent:3bcedc38-a87a-4c2a-bc50-7693cb90af2d",
"role": "invoice-processor",
"session_id": "47bb16ec-e06a-4090-9a4a-e0873e680545",
"reason": "tool \"delete_invoice\" is not in allowed_tools",
"timestamp": "2026-04-29T12:00:00Z"
}
Verify the signature — Python
import hmac, hashlib
def verify(secret: str, payload: bytes, signature: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
Verify the signature — Go