> ## Documentation Index
> Fetch the complete documentation index at: https://docs.barndoor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Update Policy

> Update a policy. Sending `rules` REPLACES the entire rule list.

There is no "add a rule" operation. To add one, read the policy first and send
every existing rule back alongside the new one — sending only the new rule
deletes the others, and the call still returns 200 with a policy that looks
perfectly healthy. Omit `rules` entirely to change only metadata.

Verify afterwards rather than assuming: `GET /v2/policies/{policy_id}/revisions`
lists what each change actually did, and `compare_policy_revisions` diffs two of
them. A revision summarising removals you did not intend is how you catch a
partial send — and it is also how you recover, since the diff records what was
taken out.

Other fields are a partial update: omitted ones are left alone. `mcp_server_id`
is immutable — rules reference server-specific tool actions, so retargeting is a
new policy, not an edit.



## OpenAPI

````yaml /api-reference/openapi.yaml patch /api/policy/v2/policies/{policy_id}
openapi: 3.1.0
info:
  title: Barndoor.ai Public API
  description: Unified public API specification for Barndoor.ai platform
  version: 2.18.0
servers:
  - url: https://{host}
    description: Barndoor public API
    variables:
      host:
        default: platform.barndoor.ai
        description: >-
          The host serving your Barndoor deployment. Defaults to production;
          override it for another environment or a dedicated deployment.
security:
  - ApiKey: []
  - OAuth2:
      - openid
      - profile
      - email
tags:
  - name: dlp
    x-group: Data Control Center
  - name: identity
    x-group: Identity
  - name: llm-gateway
    x-group: LLM Gateway
  - name: notification
    x-group: Notifications
  - name: policy
    x-group: Policies
  - name: registry
    x-group: MCP Gateway
  - name: system-management
    x-group: Log Exports
paths:
  /api/policy/v2/policies/{policy_id}:
    patch:
      tags:
        - policy
      summary: Update Policy
      description: >-
        Update a policy. Sending `rules` REPLACES the entire rule list.


        There is no "add a rule" operation. To add one, read the policy first
        and send

        every existing rule back alongside the new one — sending only the new
        rule

        deletes the others, and the call still returns 200 with a policy that
        looks

        perfectly healthy. Omit `rules` entirely to change only metadata.


        Verify afterwards rather than assuming: `GET
        /v2/policies/{policy_id}/revisions`

        lists what each change actually did, and `compare_policy_revisions`
        diffs two of

        them. A revision summarising removals you did not intend is how you
        catch a

        partial send — and it is also how you recover, since the diff records
        what was

        taken out.


        Other fields are a partial update: omitted ones are left alone.
        `mcp_server_id`

        is immutable — rules reference server-specific tool actions, so
        retargeting is a

        new policy, not an edit.
      operationId: update_policy
      parameters:
        - name: policy_id
          in: path
          required: true
          schema:
            type: string
            title: Policy Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePolicy'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PolicyDetailResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    UpdatePolicy:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Human-friendly policy name, unique within an organization
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: Longer description of the policy.
        support_contact:
          anyOf:
            - type: string
            - type: 'null'
          title: Support Contact
          description: Primary point of contact associated with the policy.
        tags:
          anyOf:
            - type: array
              items:
                type: string
            - type: 'null'
          title: Tags
          description: Tags for categorizing the policy.
        status:
          anyOf:
            - $ref: '#/components/schemas/PolicyStatus'
            - type: 'null'
          description: The current lifecycle status of the policy.
        application_ids:
          anyOf:
            - type: array
              items:
                type: string
            - type: 'null'
          title: Application Ids
          description: >-
            AI agents this policy applies to, as application UUIDs. An empty or
            omitted list means the policy applies to ALL agents — it must never
            contain an empty string. Retrieve real ids from `GET
            /api/registry/v1/agents`.
        rules:
          anyOf:
            - items:
                $ref: '#/components/schemas/PolicyRule-Input'
              type: array
            - type: 'null'
          title: Rules
          description: >-
            Full set of rules that govern the policy. This REPLACES the existing
            rule list on update — send the complete set, not a delta. Anything
            no rule grants is denied. Write ONE rule per distinct `roles_groups`
            set: put every action for a principal on a single rule and let the
            `:approve` suffix carry allow-vs-confirm, rather than splitting
            reads and writes across two rules naming the same principal.
      type: object
      title: UpdatePolicy
      description: Request body for partial updates to an existing policy.
    PolicyDetailResponse:
      properties:
        policy:
          $ref: '#/components/schemas/PolicyDetail'
      type: object
      required:
        - policy
      title: PolicyDetailResponse
      description: Envelope for detail responses.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    PolicyStatus:
      type: string
      enum:
        - DRAFT
        - ACTIVE
        - INACTIVE
        - ARCHIVED
      title: PolicyStatus
      description: >-
        Policy lifecycle status.


        - ``DRAFT``: authored but never enforced. Safe to build up
        incrementally.

        - ``ACTIVE``: enforcing on live tool calls.

        - ``INACTIVE``: previously active, now suspended. Rules are retained but
        not enforced.

        - ``ARCHIVED``: retired. Kept for audit history; not enforced and not
        offered for reuse.


        A newly created policy defaults to ``DRAFT``; set ``ACTIVE`` explicitly
        to begin enforcing.
    PolicyRule-Input:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: >-
            Optional display name for the rule. Letters, digits, spaces, hyphens
            and underscores only — a colon or other punctuation is rejected with
            a 422, which is easy to hit because rule names tend to read like
            `Security: view agents`. Names must also be unique within the policy
            once punctuation is normalized, so `My Rule` and `My-Rule` collide.
          default: ''
          examples:
            - Engineering read access
            - Support write with approval
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: >-
            Optional human-readable description for the rule; has no effect on
            enforcement
        authorized:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Authorized
          description: >-
            Effect of the rule: True grants the listed `actions`, False denies
            them. Access is denied by default, so a tool that no rule grants is
            already blocked — an explicit False rule is only needed to carve an
            exception out of a broader allow. The effective model is tri-state
            rather than binary: Always Allow is True on the plain action, Needs
            Approval is True on the `:approve` twin (see `actions`), and Blocked
            is simply the absence of a grant.
          default: false
        active:
          type: boolean
          title: Active
          description: >-
            Whether this rule is enforced. False keeps the rule on the policy
            but omits it from enforcement — use it to disable a rule without
            deleting it.
          default: true
        actions:
          items:
            type: string
          type: array
          title: Actions
          description: >-
            Tool actions this rule matches, in the wire format
            `tools/call:<tool_name>` — for example `tools/call:create_issue`.
            Use `*` to match every action on the server. Tool names are specific
            to the target MCP server: enumerate the server's real tools rather
            than guessing names. A trailing `:approve` selects the
            Needs-Approval variant of a tool
            (`tools/call:create_issue:approve`), which requires per-call human
            confirmation; the plain action is Always Allow. Do not grant both
            twins of the same tool in one rule.
          examples:
            - - tools/call:create_issue
            - - tools/call:delete_page:approve
            - - '*'
        roles_groups:
          items:
            type: string
          type: array
          title: Roles Groups
          description: >-
            Principals this rule applies to, as PREFIXED entries:
            `role:<role_name>` for an IdP/Keycloak role, `group:<group_name>`
            for a user group, or the literal `*` for all users (the default). A
            bare name with no `role:`/`group:` prefix matches NOTHING. Retrieve
            the organization's real role and group names from `GET
            /api/identity/public/v1/users/roles-groups`. A principal set should
            appear on at most ONE rule in a policy — collect all of its actions
            there instead of spreading them over several rules.
          examples:
            - - '*'
            - - group:Engineering
            - - role:admin
              - group:Platform
        condition:
          anyOf:
            - $ref: '#/components/schemas/PolicyRuleCondition-Input'
            - type: 'null'
          description: >-
            Optional CEL condition; the rule applies only when it evaluates
            true. Shape is `{"match": <matcher>}`. Omit entirely for an
            unconditional rule.
      type: object
      required:
        - actions
      title: PolicyRule
      description: >-
        A single rule: which principals may take which tool actions, optionally
        conditioned.
    PolicyDetail:
      properties:
        id:
          type: string
          format: uuid
          title: Id
        organization_id:
          type: string
          title: Organization Id
        name:
          type: string
          title: Name
        status:
          type: string
          title: Status
        support_contact:
          anyOf:
            - type: string
            - type: 'null'
          title: Support Contact
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
        mcp_server_id:
          type: string
          title: Mcp Server Id
        application_ids:
          items:
            type: string
          type: array
          title: Application Ids
        tags:
          items:
            type: string
          type: array
          title: Tags
        rules:
          items:
            $ref: '#/components/schemas/PolicyRule-Output'
          type: array
          title: Rules
        created_at:
          type: string
          format: date-time
          title: Created At
        updated_at:
          type: string
          format: date-time
          title: Updated At
        created_by_user_id:
          type: string
          title: Created By User Id
        updated_by_user_id:
          type: string
          title: Updated By User Id
      type: object
      required:
        - id
        - organization_id
        - name
        - status
        - mcp_server_id
        - created_at
        - updated_at
        - created_by_user_id
        - updated_by_user_id
      title: PolicyDetail
      description: Detailed representation of a policy including rules.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    PolicyRuleCondition-Input:
      properties:
        match:
          oneOf:
            - $ref: '#/components/schemas/Expression'
            - $ref: '#/components/schemas/OperatorAll-Input'
            - $ref: '#/components/schemas/OperatorAny-Input'
            - $ref: '#/components/schemas/OperatorNone-Input'
          title: Match
          description: >-
            Matcher tree. Either a single expression `{"expr": "..."}` or a
            boolean combinator `{"all"|"any"|"none": {"of": [<matcher>, ...]}}`,
            which nest arbitrarily. The variant is selected by WHICH KEY IS
            PRESENT — there is no separate discriminator field. Example: {"all":
            {"of": [{"expr": "P.attr.department == 'Engineering'"}, {"expr":
            "R.attr.env != 'prod'"}]}}
      type: object
      required:
        - match
      title: PolicyRuleCondition
    PolicyRule-Output:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: >-
            Optional display name for the rule. Letters, digits, spaces, hyphens
            and underscores only — a colon or other punctuation is rejected with
            a 422, which is easy to hit because rule names tend to read like
            `Security: view agents`. Names must also be unique within the policy
            once punctuation is normalized, so `My Rule` and `My-Rule` collide.
          default: ''
          examples:
            - Engineering read access
            - Support write with approval
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: >-
            Optional human-readable description for the rule; has no effect on
            enforcement
        authorized:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Authorized
          description: >-
            Effect of the rule: True grants the listed `actions`, False denies
            them. Access is denied by default, so a tool that no rule grants is
            already blocked — an explicit False rule is only needed to carve an
            exception out of a broader allow. The effective model is tri-state
            rather than binary: Always Allow is True on the plain action, Needs
            Approval is True on the `:approve` twin (see `actions`), and Blocked
            is simply the absence of a grant.
          default: false
        active:
          type: boolean
          title: Active
          description: >-
            Whether this rule is enforced. False keeps the rule on the policy
            but omits it from enforcement — use it to disable a rule without
            deleting it.
          default: true
        actions:
          items:
            type: string
          type: array
          title: Actions
          description: >-
            Tool actions this rule matches, in the wire format
            `tools/call:<tool_name>` — for example `tools/call:create_issue`.
            Use `*` to match every action on the server. Tool names are specific
            to the target MCP server: enumerate the server's real tools rather
            than guessing names. A trailing `:approve` selects the
            Needs-Approval variant of a tool
            (`tools/call:create_issue:approve`), which requires per-call human
            confirmation; the plain action is Always Allow. Do not grant both
            twins of the same tool in one rule.
          examples:
            - - tools/call:create_issue
            - - tools/call:delete_page:approve
            - - '*'
        roles_groups:
          items:
            type: string
          type: array
          title: Roles Groups
          description: >-
            Principals this rule applies to, as PREFIXED entries:
            `role:<role_name>` for an IdP/Keycloak role, `group:<group_name>`
            for a user group, or the literal `*` for all users (the default). A
            bare name with no `role:`/`group:` prefix matches NOTHING. Retrieve
            the organization's real role and group names from `GET
            /api/identity/public/v1/users/roles-groups`. A principal set should
            appear on at most ONE rule in a policy — collect all of its actions
            there instead of spreading them over several rules.
          examples:
            - - '*'
            - - group:Engineering
            - - role:admin
              - group:Platform
        condition:
          anyOf:
            - $ref: '#/components/schemas/PolicyRuleCondition-Output'
            - type: 'null'
          description: >-
            Optional CEL condition; the rule applies only when it evaluates
            true. Shape is `{"match": <matcher>}`. Omit entirely for an
            unconditional rule.
      type: object
      required:
        - actions
      title: PolicyRule
      description: >-
        A single rule: which principals may take which tool actions, optionally
        conditioned.
    Expression:
      properties:
        expr:
          type: string
          title: Expr
          description: >-
            A single CEL expression that must evaluate to a boolean. Top-level
            variables: C, G, P, R, V, constants, globals, request, runtime,
            variables. `P` is the calling principal and `R` the resource (the
            tool call being authorized). Commonly used attributes:
            `P.attr.groups` and `P.attr.roles` hold the UNPREFIXED role and
            group names from the user's profile — note the contrast with a
            rule's `roles_groups`, whose entries are `role:`/`group:`-prefixed;
            other IdP profile fields such as `P.attr.department` are available;
            and `R.attr.<argument>` exposes the tool call's own arguments.
            Expressions are compiled at write time and rejected if invalid or if
            they reference an unknown variable.
          examples:
            - P.attr.department == 'Engineering'
            - R.attr.repository.startsWith('barndoor-ai/')
      type: object
      required:
        - expr
      title: Expression
    OperatorAll-Input:
      properties:
        all:
          $ref: '#/components/schemas/ConditionOf-Input'
          description: Logical AND — every matcher in `of` must evaluate true.
      type: object
      required:
        - all
      title: OperatorAll
    OperatorAny-Input:
      properties:
        any:
          $ref: '#/components/schemas/ConditionOf-Input'
          description: Logical OR — at least one matcher in `of` must evaluate true.
      type: object
      required:
        - any
      title: OperatorAny
    OperatorNone-Input:
      properties:
        none:
          $ref: '#/components/schemas/ConditionOf-Input'
          description: >-
            Logical NOR — the rule applies only if NO matcher in `of` evaluates
            true.
      type: object
      required:
        - none
      title: OperatorNone
    PolicyRuleCondition-Output:
      properties:
        match:
          oneOf:
            - $ref: '#/components/schemas/Expression'
            - $ref: '#/components/schemas/OperatorAll-Output'
            - $ref: '#/components/schemas/OperatorAny-Output'
            - $ref: '#/components/schemas/OperatorNone-Output'
          title: Match
          description: >-
            Matcher tree. Either a single expression `{"expr": "..."}` or a
            boolean combinator `{"all"|"any"|"none": {"of": [<matcher>, ...]}}`,
            which nest arbitrarily. The variant is selected by WHICH KEY IS
            PRESENT — there is no separate discriminator field. Example: {"all":
            {"of": [{"expr": "P.attr.department == 'Engineering'"}, {"expr":
            "R.attr.env != 'prod'"}]}}
      type: object
      required:
        - match
      title: PolicyRuleCondition
    ConditionOf-Input:
      properties:
        of:
          items:
            oneOf:
              - $ref: '#/components/schemas/Expression'
              - $ref: '#/components/schemas/OperatorAll-Input'
              - $ref: '#/components/schemas/OperatorAny-Input'
              - $ref: '#/components/schemas/OperatorNone-Input'
          type: array
          title: Of
          description: >-
            Operand list for the enclosing `all`/`any`/`none` combinator. Each
            operand is itself a matcher — either `{"expr": "..."}` or a further
            nested combinator.
      type: object
      required:
        - of
      title: ConditionOf
    OperatorAll-Output:
      properties:
        all:
          $ref: '#/components/schemas/ConditionOf-Output'
          description: Logical AND — every matcher in `of` must evaluate true.
      type: object
      required:
        - all
      title: OperatorAll
    OperatorAny-Output:
      properties:
        any:
          $ref: '#/components/schemas/ConditionOf-Output'
          description: Logical OR — at least one matcher in `of` must evaluate true.
      type: object
      required:
        - any
      title: OperatorAny
    OperatorNone-Output:
      properties:
        none:
          $ref: '#/components/schemas/ConditionOf-Output'
          description: >-
            Logical NOR — the rule applies only if NO matcher in `of` evaluates
            true.
      type: object
      required:
        - none
      title: OperatorNone
    ConditionOf-Output:
      properties:
        of:
          items:
            oneOf:
              - $ref: '#/components/schemas/Expression'
              - $ref: '#/components/schemas/OperatorAll-Output'
              - $ref: '#/components/schemas/OperatorAny-Output'
              - $ref: '#/components/schemas/OperatorNone-Output'
          type: array
          title: Of
          description: >-
            Operand list for the enclosing `all`/`any`/`none` combinator. Each
            operand is itself a matcher — either `{"expr": "..."}` or a further
            nested combinator.
      type: object
      required:
        - of
      title: ConditionOf
  securitySchemes:
    ApiKey:
      type: http
      scheme: bearer
      description: >-
        A Barndoor API key, sent as `Authorization: Bearer <key>`. Keys are
        prefixed `bdai_` and are opaque — do not attempt to decode one.
        Interchangeable with an OAuth 2.0 access token.
    OAuth2:
      type: oauth2
      x-issuer: https://auth.barndoor.ai/realms/barndoor
      description: >-
        An OAuth 2.0 access token from Barndoor's identity provider, sent as
        `Authorization: Bearer <token>`. Public clients must use PKCE.
        Interchangeable with an API key.
      flows:
        authorizationCode:
          authorizationUrl: >-
            https://auth.barndoor.ai/realms/barndoor/protocol/openid-connect/auth
          tokenUrl: >-
            https://auth.barndoor.ai/realms/barndoor/protocol/openid-connect/token
          scopes:
            openid: Issue an OIDC ID token identifying the caller.
            profile: Read the caller's basic profile claims.
            email: Read the caller's email address.
            offline_access: Issue a refresh token. Optional — no operation requires it.
        clientCredentials:
          tokenUrl: >-
            https://auth.barndoor.ai/realms/barndoor/protocol/openid-connect/token
          scopes:
            openid: Issue an OIDC ID token identifying the caller.
            profile: Read the caller's basic profile claims.
            email: Read the caller's email address.
            offline_access: Issue a refresh token. Optional — no operation requires it.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.