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

# Save every rule under a tag key at once

> Declarative batch behind one Save. The body carries the rules the key should end up with, in the order it should evaluate them: a member with an id updates that rule, a member without one creates a rule, and any existing rule the body omits is deleted. Array order is rank order, and rank is precedence, so the submitted order is the attribution model. expected_rules_revision is the key's rules_revision as the client loaded it. It counts writes to the rules under the key and nothing else, so editing the key's description or regenerating its rules elsewhere no longer invalidates an open editor. The revision is re-read inside the key's lock and any mismatch returns 409 without writing anything. It is required: omitting it is a 400 rather than an unconditional overwrite, so a client that forgets the field cannot silently discard another editor's work. rules is required too: omitting it is a 400, and only an explicit empty array clears the key. Every member is compiled before the transaction opens, so a batch that would partially fail returns 400 with per-member errors and writes nothing. Editing a rule an agent wrote promotes it to owned, while reordering or pausing it does not. Every member is written as an expression, a clause or a node, exactly one of the three, on the same terms as the single-rule endpoints. Responds with the key and its rules in their new order: key carries the rules_revision this save produced, read back inside the same transaction, so it is exactly what the next save must send as expected_rules_revision, and the client replaces its state from the response without refetching.



## OpenAPI

````yaml /api-reference/costgraph/openapi.json put /api/v1/tenant/virtual-tag-keys/{key}/rules
openapi: 3.0.0
info:
  description: Read and manage your CostGraph organization, spend, alerts, and settings.
  title: CostGraph API
  contact: {}
  version: '1.0'
servers:
  - url: https://api.costgraph.ai
security: []
tags:
  - name: ai
    x-group: AI
  - name: ai-serving
    x-group: AI serving
  - name: anomalies
    x-group: Anomalies
  - name: auth
    x-group: Auth
  - name: billing
    x-group: Billing
  - name: billing-export
    x-group: Billing export
  - name: budgets
    x-group: Budgets
  - name: ci
    x-group: CI
  - name: compute-recommendations
    x-group: Compute recommendations
  - name: config
    x-group: Config
  - name: cost
    x-group: Cost
  - name: gpus
    x-group: GPUs
  - name: graphai
    x-group: Graph AI
  - name: infracost
    x-group: Infracost
  - name: integrations
    x-group: Integrations
  - name: invitations
    x-group: Invitations
  - name: kubernetes-clusters
    x-group: Kubernetes clusters
  - name: marketplace
    x-group: Marketplace
  - name: network-requests
    x-group: Network requests
  - name: notifications
    x-group: Notifications
  - name: oauth
    x-group: OAuth
  - name: oauth-clients
    x-group: OAuth clients
  - name: opencost
    x-group: OpenCost
  - name: organization
    x-group: Audit log
  - name: organizations
    x-group: Organizations
  - name: placement-alternatives
    x-group: Placement alternatives
  - name: reports
    x-group: Reports
  - name: service-map
    x-group: Service map
  - name: settings
    x-group: Settings
  - name: sso
    x-group: Single sign-on
  - name: tenants
    x-group: Tenants
  - name: user
    x-group: Users
  - name: virtual-machines
    x-group: Virtual machines
  - name: virtual-tags
    x-group: Virtual tags
  - name: workflows
    x-group: Workflows
paths:
  /api/v1/tenant/virtual-tag-keys/{key}/rules:
    put:
      tags:
        - virtual-tags
      summary: Save every rule under a tag key at once
      description: >-
        Declarative batch behind one Save. The body carries the rules the key
        should end up with, in the order it should evaluate them: a member with
        an id updates that rule, a member without one creates a rule, and any
        existing rule the body omits is deleted. Array order is rank order, and
        rank is precedence, so the submitted order is the attribution model.
        expected_rules_revision is the key's rules_revision as the client loaded
        it. It counts writes to the rules under the key and nothing else, so
        editing the key's description or regenerating its rules elsewhere no
        longer invalidates an open editor. The revision is re-read inside the
        key's lock and any mismatch returns 409 without writing anything. It is
        required: omitting it is a 400 rather than an unconditional overwrite,
        so a client that forgets the field cannot silently discard another
        editor's work. rules is required too: omitting it is a 400, and only an
        explicit empty array clears the key. Every member is compiled before the
        transaction opens, so a batch that would partially fail returns 400 with
        per-member errors and writes nothing. Editing a rule an agent wrote
        promotes it to owned, while reordering or pausing it does not. Every
        member is written as an expression, a clause or a node, exactly one of
        the three, on the same terms as the single-rule endpoints. Responds with
        the key and its rules in their new order: key carries the rules_revision
        this save produced, read back inside the same transaction, so it is
        exactly what the next save must send as expected_rules_revision, and the
        client replaces its state from the response without refetching.
      parameters:
        - description: Tenant ID
          name: X-CostGraph-Tenant-ID
          in: header
          required: true
          schema:
            type: string
        - description: Tag key
          name: key
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/virtualtag.RuleBatchRequest'
        description: The rules the key should end up with, in rank order
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/responses.SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/virtualtag.SavedKeyRules'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/responses.ValidationErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/responses.ErrorResponse'
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/responses.ErrorResponse'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/responses.ErrorResponse'
      security:
        - BearerAuth: []
components:
  schemas:
    virtualtag.RuleBatchRequest:
      type: object
      properties:
        expected_rules_revision:
          type: integer
        rules:
          type: array
          items:
            $ref: '#/components/schemas/virtualtag.RuleBatchMember'
    responses.SuccessResponse:
      type: object
      required:
        - message
        - status
      properties:
        data: {}
        message:
          type: string
          example: some message
        status:
          type: string
          example: success
    virtualtag.SavedKeyRules:
      type: object
      properties:
        key:
          $ref: '#/components/schemas/virtualtag.Key'
        rules:
          type: array
          items:
            $ref: '#/components/schemas/virtualtag.Rule'
    responses.ValidationErrorResponse:
      type: object
      required:
        - message
        - status
      properties:
        errors:
          type: object
          additionalProperties:
            type: string
        message:
          type: string
          example: Validation failed
        status:
          type: string
          example: error
    responses.ErrorResponse:
      type: object
      required:
        - message
        - status
      properties:
        message:
          type: string
          example: some message
        status:
          type: string
          example: error
    virtualtag.RuleBatchMember:
      type: object
      properties:
        active:
          type: boolean
        clause:
          $ref: '#/components/schemas/virtualtag.Clause'
        description:
          type: string
        expression:
          type: string
        id:
          type: string
        inherited_from:
          type: string
        key:
          type: string
        node:
          $ref: '#/components/schemas/virtualtag.Node'
        origin:
          type: string
        rule_type:
          type: string
        scope:
          type: string
        source_text:
          type: string
        valid_from:
          type: string
        valid_to:
          type: string
        value:
          type: string
    virtualtag.Key:
      type: object
      properties:
        compiled_at:
          type: string
        created_at:
          type: string
        created_by:
          type: string
        created_by_user:
          $ref: '#/components/schemas/tenants.TenantUser'
        description:
          type: string
        exported:
          type: boolean
        exported_to:
          type: string
        id:
          type: string
        key:
          type: string
        last_proposed_at:
          type: string
        proposal_fingerprint:
          type: string
        rule_count:
          type: integer
        rules_revision:
          type: integer
        tenant_id:
          type: string
        updated_at:
          type: string
        updated_by:
          type: string
        updated_by_kind:
          type: string
        updated_by_user:
          $ref: '#/components/schemas/tenants.TenantUser'
    virtualtag.Rule:
      type: object
      properties:
        active:
          type: boolean
        created_at:
          type: string
        description:
          type: string
        expression:
          type: string
        expression_hash:
          type: string
        field:
          type: string
        id:
          type: string
        inherited_from:
          type: string
        key:
          type: string
        match_value:
          type: string
        operator:
          type: string
        origin:
          type: string
        rank:
          type: number
        rule_type:
          type: string
        scope:
          type: string
        source_text:
          type: string
        tenant_id:
          type: string
        updated_at:
          type: string
        valid_from:
          type: string
        valid_to:
          type: string
        value:
          type: string
    virtualtag.Clause:
      type: object
      properties:
        field:
          type: string
        match_value:
          type: string
        operator:
          type: string
    virtualtag.Node:
      type: object
      properties:
        children:
          type: array
          items:
            $ref: '#/components/schemas/virtualtag.Node'
        clause:
          $ref: '#/components/schemas/virtualtag.Clause'
        kind:
          type: string
    tenants.TenantUser:
      type: object
      required:
        - active
        - email
        - id
        - name
        - role
      properties:
        active:
          type: boolean
        email:
          type: string
        id:
          type: string
        name:
          type: string
        role:
          type: string
  securitySchemes:
    BearerAuth:
      description: Enter "Bearer {token}"
      type: apiKey
      name: Authorization
      in: header

````

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