Skip to content

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

{
  "role_id": "invoice-processor",
  "parent_session_id": "optional-parent-session-id"
}

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_envs configured, pass env in call_args. Calls without an env field are allowed regardless of allowed_envs (backward compatible).

Response — allow

{
  "decision": "allow",
  "call_id": "abc123",
  "latency_ms": 1.2
}

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_secret is never returned. Use webhook_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 roles
  • GET /mgmt/v1/roles?name=invoice-processor — get role by name
  • GET /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

{"field": "amount", "operator": "lt", "value": 50000}

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.

{
  "allowed_hours_start": 8,
  "allowed_hours_end": 20,
  "allowed_days": [0, 1, 2, 3, 4]
}

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

X-Scopebound-Signature: sha256=<hmac-hex>
Content-Type: application/json

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

func verify(secret string, payload []byte, signature string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signature))
}