Barndoor policies define which MCP tools an agent can use and under what conditions. This guide walks through the full policy lifecycle with the v2 API, from drafting a policy to activating it, revising it, and reviewing its history.
Estimated Time : 15-20 minutes
Before You Begin
Before getting started with policy management, you’ll need:
A Barndoor account with admin privileges
A valid bearer token for the Barndoor API
At least one registered MCP server
At least one registered agent or application
Example shell setup:
export BASE_URL = "https://your-org.platform.barndoor.ai"
export TOKEN = "your-jwt"
export MCP_SERVER_ID = "server_123"
export APP_ID = "agent_456"
The v2 policy API replaces the older policy-style wrapper. In v2, you send a single policy object directly to /api/v2/policies, use application_ids to scope the policy, and define each rule with authorized, actions, roles_groups, and optional condition.
What You’ll Learn
In this guide, you will:
Validate policy scope before creation
Create a policy in DRAFT
Review and activate the policy
Update rules and metadata safely
List, search, and summarize policies
Audit policy changes through revisions
Clone and retire policies as part of normal lifecycle management
Step-by-Step Guide
Step 1: Validate your policy before creating it
Validate checks whether the policy name is unique and whether an active policy would overlap with the same MCP server and application scope.
Show Validation request example
curl -X POST " $BASE_URL /api/v2/policies/validate" \
-H "Authorization: Bearer $TOKEN " \
-H "Content-Type: application/json" \
-d '{
"name": "Slack outbound policy",
"mcp_server_id": "'" $MCP_SERVER_ID "'",
"application_ids": ["'" $APP_ID "'"]
}'
Typical response: {
"name_is_unique" : true ,
"no_overlap_if_active" : true ,
"overlapping_policy_names" : []
}
Use exclude_policy_id when validating an update to an existing policy so the current record is ignored during overlap checks.
Reference: POST /api/v2/policies/validate
Step 2: Create the policy as a draft
In v2, the request body is a single policy object. There is no resourcePolicy wrapper.
Show Draft policy payload example
curl -X POST " $BASE_URL /api/v2/policies" \
-H "Authorization: Bearer $TOKEN " \
-H "Content-Type: application/json" \
-d '{
"name": "Slack outbound policy",
"description": "Default outbound Slack controls for the support agent.",
"support_contact": "[email protected] ",
"tags": ["slack", "support"],
"status": "DRAFT",
"mcp_server_id": "'" $MCP_SERVER_ID "'",
"application_ids": ["'" $APP_ID "'"],
"rules": [
{
"name": "allow_all",
"authorized": true,
"actions": ["*"],
"roles_groups": ["*"]
},
{
"name": "block_general_channel",
"authorized": false,
"actions": ["tools/call:chat_postMessage"],
"roles_groups": ["*"],
"condition": {
"match": {
"all": {
"of": [
{
"expr": "request.resource.attr.channel == \"general\""
}
]
}
}
}
}
]
}'
Important v2 fields:
name: Unique policy name within the organization
mcp_server_id: MCP server this policy controls
application_ids: Agents or applications this policy applies to
status: DRAFT, ACTIVE, INACTIVE, or ARCHIVED
rules: Array of policy rules
Reference: POST /api/v2/policies
Step 3: Understand the v2 rule model
Each rule in v2 uses a simpler structure than the older effect-based format.
authorized: true means allow
authorized: false means deny
actions defines which MCP actions the rule applies to
roles_groups controls which role or group matchers apply
condition adds optional fine-grained logic
Single-expression condition: {
"condition" : {
"match" : {
"expr" : "request.resource.attr.channel == \" general \" "
}
}
}
Nested condition with all: {
"condition" : {
"match" : {
"all" : {
"of" : [
{ "expr" : "request.resource.attr.channel == \" general \" " },
{ "expr" : "request.principal.id == \" agent_456 \" " }
]
}
}
}
}
Step 4: Review the created policy
Capture the returned policy.id from the create response, then fetch the full policy document.
Show Fetch a policy by ID
curl -H "Authorization: Bearer $TOKEN " \
" $BASE_URL /api/v2/policies/POLICY_ID"
Confirm the following before rollout:
The status is still DRAFT
The correct application_ids are attached
The mcp_server_id is correct
The returned rules match your intended behavior
Reference: GET /api/v2/policies/{policy_id}
Step 5: Activate or update the policy
Once reviewed, move the policy into ACTIVE.
Show Activation and update examples
curl -X PATCH " $BASE_URL /api/v2/policies/POLICY_ID" \
-H "Authorization: Bearer $TOKEN " \
-H "Content-Type: application/json" \
-d '{
"status": "ACTIVE"
}'
You can also update metadata and rules through the same endpoint: curl -X PATCH " $BASE_URL /api/v2/policies/POLICY_ID" \
-H "Authorization: Bearer $TOKEN " \
-H "Content-Type: application/json" \
-d '{
"description": "Blocks posting to #general while allowing all other Slack tools.",
"tags": ["slack", "support", "production"],
"rules": [
{
"name": "allow_all",
"authorized": true,
"actions": ["*"],
"roles_groups": ["*"]
},
{
"name": "block_general_channel",
"authorized": false,
"actions": ["tools/call:chat_postMessage"],
"roles_groups": ["*"],
"condition": {
"match": {
"all": {
"of": [
{
"expr": "request.resource.attr.channel == \"general\""
}
]
}
}
}
}
]
}'
Reference: PATCH /api/v2/policies/{policy_id}
Step 6: List, search, and summarize policies
List policies:
Show List and search examples
curl -H "Authorization: Bearer $TOKEN " \
" $BASE_URL /api/v2/policies?page=1&limit=20"
Search and filter: curl -H "Authorization: Bearer $TOKEN " \
" $BASE_URL /api/v2/policies?search=slack&status=ACTIVE&mcp_server_id= $MCP_SERVER_ID &agent_id= $APP_ID "
Get status counts: curl -H "Authorization: Bearer $TOKEN " \
" $BASE_URL /api/v2/policies/summary"
Get filter definitions for a UI: curl -H "Authorization: Bearer $TOKEN " \
" $BASE_URL /api/v2/policies/filter-definitions"
References:
Step 7: Review revision history
Every meaningful change is available through the revisions endpoint.
Show Revision history example
curl -H "Authorization: Bearer $TOKEN " \
" $BASE_URL /api/v2/policies/POLICY_ID/revisions?changes_summary=true&page=1&limit=10"
Use revisions to:
Review what changed
See who changed it
Understand when the policy moved between lifecycle states
Reference: GET /api/v2/policies/{policy_id}/revisions
Step 8: Clone, deactivate, or archive a policy
Clone a policy to create a safe variation without rebuilding the rule set:
Show Clone and retirement examples
curl -X POST " $BASE_URL /api/v2/policies/POLICY_ID/clone" \
-H "Authorization: Bearer $TOKEN "
The cloned policy should be reviewed and edited before activation. To retire a policy gradually, move it to INACTIVE: curl -X PATCH " $BASE_URL /api/v2/policies/POLICY_ID" \
-H "Authorization: Bearer $TOKEN " \
-H "Content-Type: application/json" \
-d '{
"status": "INACTIVE"
}'
To remove it from normal circulation, move it to ARCHIVED: curl -X PATCH " $BASE_URL /api/v2/policies/POLICY_ID" \
-H "Authorization: Bearer $TOKEN " \
-H "Content-Type: application/json" \
-d '{
"status": "ARCHIVED"
}'
References:
Legacy Endpoints
The older engine-level toggles still exist:
Show Legacy endpoint references
For new policy lifecycle workflows, prefer the v2 endpoints and the status field. Use the legacy endpoints only when you explicitly need the older engine-level toggles.
Best Practices
Create policies as DRAFT first, then promote to ACTIVE
Validate before both create and update operations
Keep application_ids narrow to avoid overlap with other active policies
Use descriptive tags and a support_contact so ownership is clear
Review revisions instead of maintaining a separate manual change log
Clone stable policies before making large behavior changes