---
title: "Replace a legacy or expanded approval workflow definition"
method: PUT
path: "/approval-workflows/condition-models/v2/{approval_workflow_id}"
tags: ["Approval Workflow Models"]
---

# Replace a legacy or expanded approval workflow definition

`PUT /approval-workflows/condition-models/v2/{approval_workflow_id}`

## Request

- Base URL: `https://api.rillet.com`
- URL: `https://api.rillet.com/approval-workflows/condition-models/v2/{approval_workflow_id}`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/rillet/apis/rillet-accounting-api/docs/approval-workflows/condition-models/v2/:approval_workflow_id/put.ts> (a function with typed inputs; its response is checked against the schema)

## Path parameters

- `approval_workflow_id` string, uuid, required

## Request body

- ApprovalWorkflowModelV2Request: Condition-model version 2. Supply exactly one of condition_group (legacy) and rule (expanded). Expanded authoring requires its feature flag. Blank submission_sources retains manual-entry coverage. Updates keep type and active unchanged.
  - `name` string, required
  - `type` 'JOURNAL_ENTRY' | 'CONTRACT' | 'INVOICE' | 'BILL' | 'CHARGE' | 'CHARGE_REVERSAL' | 'REIMBURSEMENT', required: The kind of record a workflow applies to. Contract and Invoice are retained for backward-compatible reads but cannot be updated through this endpoint.
  - `active` boolean, required: Required, and must equal the workflow's current value — activation is a dedicated operation in Rillet, so a definition update never changes it. Read the workflow first if you are unsure.
  - `condition_group` ApprovalWorkflowConditionGroupRequest: A set of conditions combined by one logical operator, together with any nested groups. A group must carry at least one condition or one nested group; a workflow may carry at most five conditions across the whole tree, and nesting at most three levels deep. `groups` must be empty for now: nested combinations are not evaluated yet and are rejected, so a workflow currently has exactly one group.
    - `logic` 'AND' | 'OR', required: How the workflow's conditions combine: `AND` requires every condition to match, `OR` any of them.
    - `conditions` ApprovalWorkflowConditionRequest[], required
      - union: One condition to write. `condition_type` selects the variant.
        - ApprovalWorkflowCustomerConditionRequest: Matches on the record's customer. `ANY_OF` matches when the customer is one of the listed customers, `NONE_OF` when it is not.
          - `id` string, uuid: Reuse a saved condition ID or omit it for a new condition.
          - `condition_type` string, required
          - `operator` 'ANY_OF' | 'NONE_OF', required: How a condition compares against a set of values: `ANY_OF` matches when at least one of them applies, `NONE_OF` when none does.
          - `customer_ids` string[], required: The customers this condition looks for.
        - ApprovalWorkflowAmountConditionRequest: Matches on the entry's total debit amount.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `operator` 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS', required
          - `threshold` union, required: The amount a condition compares against, either in a specific currency or as a plain number.
            - ApprovalWorkflowMonetaryThreshold: A threshold in one currency. Amounts are compared without conversion, so an entry in another currency does not match.
              - …
            - ApprovalWorkflowNumericThreshold: A threshold without a currency: the entry's amount is compared as a number, whatever its currency.
              - …
        - ApprovalWorkflowSubsidiaryConditionRequest: Matches on the entry's subsidiary.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
          - `subsidiary_id` string, uuid, required
        - ApprovalWorkflowCreatedByUserConditionRequest: Matches on the user who created the entry or, if it has since been edited, who last edited it — an edit starts a fresh approval, attributed to the editor. Entries Rillet generates itself have no user, so `EQUALS` never matches them and `NOT_EQUALS` always does.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
          - `user_id` string, uuid, required
        - ApprovalWorkflowAuraAssistedConditionRequest: Matches entries whose triggering create or edit was made with Aura's assistance; a later edit made without Aura is evaluated as non-Aura. `EQUALS` matches Aura-assisted changes, `NOT_EQUALS` the rest.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
        - ApprovalWorkflowUiSubmittedConditionRequest: Matches entries whose triggering create or edit was submitted through the Rillet web application without Aura assistance. A later non-UI edit is evaluated as non-UI.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
        - ApprovalWorkflowApiSubmittedConditionRequest: Matches entries whose triggering create or edit was submitted directly through the public REST API. MCP tool actions are tracked separately. A later non-API edit is evaluated as non-API.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
        - ApprovalWorkflowMcpSubmittedConditionRequest: Matches entries whose triggering create or edit was submitted through a Rillet MCP tool. Direct public REST API calls are tracked separately. A later non-MCP edit is evaluated as non-MCP.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
        - ApprovalWorkflowFieldConditionRequest: Matches on a custom field value, looking at both the entry and its line items. `EQUALS` matches when any of them carries the value, `NOT_EQUALS` when none does.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
          - `field_id` string, uuid, required
          - `field_value_id` string, uuid, required
        - ApprovalWorkflowAccountConditionRequest: Matches on the GL accounts an entry's line items post to. `ANY_OF` matches when a line item posts to any of the listed accounts, `NONE_OF` when none of them is used.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `operator` 'ANY_OF' | 'NONE_OF', required: How a condition compares against a set of values: `ANY_OF` matches when at least one of them applies, `NONE_OF` when none does.
          - `account_ids` string[], required: The accounts this condition looks for.
        - ApprovalWorkflowSourceTypeConditionRequest: Matches on how the entry came to exist. A journal-entry workflow with no condition of this type applies to manually created entries only. A workflow can carry at most one.
          - `id` string, uuid: Send the id of an existing condition to keep it, or omit it to have a new one generated.
          - `condition_type` string, required
          - `source_type` 'MANUAL' | 'SYSTEM' | 'ALL', required: Whether a journal entry was created by a person (`MANUAL`) or by Rillet from another record such as a bill, invoice or amortisation schedule (`SYSTEM`). `ALL` matches either.
        - ApprovalWorkflowVendorConditionRequest: Matches on the bill vendor or Charge merchant. Supported only for Bill and Charge workflows.
          - `id` string, uuid: Send the existing condition ID to retain it; omit it to have the server generate a new one.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
          - `vendor_id` string, uuid, required
        - ApprovalWorkflowProviderConditionRequest: Matches any or none of the selected expense providers.
          - `id` string, uuid
          - `condition_type` string, required
          - `operator` 'ANY_OF' | 'NONE_OF', required: How a condition compares against a set of values: `ANY_OF` matches when at least one of them applies, `NONE_OF` when none does.
          - `providers` ApprovalWorkflowProvider[], required
        - ApprovalWorkflowOperationConditionRequest: Matches a CREATE or UPDATE proposal.
          - `id` string, uuid
          - `condition_type` string, required
          - `operation` 'CREATE' | 'UPDATE', required
    - `groups` ApprovalWorkflowConditionGroupRequest[]: Nested groups. Rejected while nesting is unsupported.
  - `approval_layers` ApprovalWorkflowLayerRequest[], required
    - union: One layer of approvers to write. `approver_type` selects the variant.
      - ApprovalWorkflowUserLayerRequest: A layer approved by the named users.
        - `id` string, uuid: Send the id of an existing layer to keep it, or omit it to have a new one generated.
        - `order` integer, required: Position of the layer in the sequence. Layers must be numbered consecutively from 1, with no gaps or repeats.
        - `approver_type` string, required
        - `require_all` boolean, required: Whether every listed user must approve. Only `false` is accepted today — requiring every approver is not something the Rillet app can configure yet.
        - `approver_user_ids` string[], required: Users allowed to approve at this layer. Each must be an active member of the organization.
      - ApprovalWorkflowRoleLayerRequest: A layer approved by anyone holding one of the given roles, resolved when the approval happens.
        - `id` string, uuid: Send the id of an existing layer to keep it, or omit it to have a new one generated.
        - `order` integer, required: Position of the layer in the sequence. Layers must be numbered consecutively from 1, with no gaps or repeats.
        - `approver_type` string, required
        - `require_all` boolean, required: Whether each listed role must be represented among the approvals. Only `false` is accepted today — requiring every approver is not something the Rillet app can configure yet.
        - `role_ids` string[], required: Roles allowed to approve at this layer.
  - `methods` ApprovalWorkflowMethod[], required: Only `["IN_APP"]` is accepted today: `EMAIL` and `SLACK` deliver nothing, so they are rejected rather than stored.
  - `submission_sources` ApprovalWorkflowConfigurableSubmissionSource[]
  - `condition_model_version` 2, required
  - `rule` ApprovalRuleV2: Versioned conditions. Maximum 50 predicates, 100 nodes, four nesting levels, and one collection scope per path. Collection predicates match within the same line. Decimal values are strings; money requires an explicit currency and basis.
    - `version` 2, required
    - `expression` union, required
      - ApprovalExpressionPredicateV2
        - `id` string, uuid, required
        - `kind` string, required
        - `fieldId` string, required
        - `operator` 'EQUALS' | 'NOT_EQUALS' | 'IN' | 'NOT_IN' | 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'CONTAINS' | 'NOT_CONTAINS' | 'STARTS_WITH' | 'ENDS_WITH' | 'IS_PRESENT' | 'IS_MISSING', required
        - `operand` union
          - ApprovalOperandScalarV2
            - `kind` string, required
            - `value` union, required
              - …
          - ApprovalOperandValuesV2
            - `kind` string, required
            - `values` ApprovalValueV2[], required
              - …
        - `textCase` 'SENSITIVE' | 'INSENSITIVE', required
      - ApprovalExpressionGroupV2
        - `id` string, uuid, required
        - `kind` string, required
        - `logic` 'AND' | 'OR', required
        - `children` ApprovalExpressionV2[], required
      - ApprovalExpressionCollectionV2
        - `id` string, uuid, required
        - `kind` string, required
        - `collectionId` string, required
        - `quantifier` 'ANY' | 'ALL' | 'NONE', required
        - `expression` ApprovalExpressionV2, required (recursive)
  - `deadline_days` integer, nullable

## Response `200`

Success

- ApprovalWorkflowModelV2: An approval workflow with explicit condition-model version negotiation. Legacy definitions retain condition_group; expanded definitions return rule. Neither representation is flattened or omitted.
  - `id` string, uuid, required
  - `name` string, required
  - `type` 'JOURNAL_ENTRY' | 'CONTRACT' | 'INVOICE' | 'BILL' | 'CHARGE' | 'CHARGE_REVERSAL' | 'REIMBURSEMENT', required: The kind of record a workflow applies to. Contract and Invoice are retained for backward-compatible reads but cannot be updated through this endpoint.
  - `active` boolean, required: Only active workflows are evaluated. Read-only here: a workflow is activated and deactivated in Rillet, so this value can change without a definition update.
  - `priority` integer, required: Evaluation order among the active workflows of the same type, ascending and zero-based — the first workflow whose conditions match is the one applied, and later ones are skipped. Assigned by the server: a workflow is put last when created, and again when reactivated. Not necessarily unique; ties are resolved by `id`.
  - `condition_group` ApprovalWorkflowConditionGroup: A set of conditions combined by one logical operator, together with any nested groups, which combine into it the same way. A group carries at least one condition or one nested group. Today a workflow always has a single group with no nested groups; `groups` exists so that combinations such as `A AND (B OR C)` can be expressed without reshaping this contract.
    - `logic` 'AND' | 'OR', required: How the workflow's conditions combine: `AND` requires every condition to match, `OR` any of them.
    - `conditions` ApprovalWorkflowCondition[], required: Conditions in this group, exactly as stored. A workflow carries at most five conditions across the whole tree, not five per group. A journal-entry workflow with no `SOURCE_TYPE` condition applies to manually created entries only, whatever its other conditions say: that restriction is implied rather than stored. Add a `SOURCE_TYPE` condition to have a workflow apply to entries Rillet generates from bills, invoices or amortisation schedules.
      - union: One condition of a workflow. `condition_type` selects the variant.
        - ApprovalWorkflowAmountCondition: Matches on the entry's total debit amount.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS', required
          - `threshold` union, required: The amount a condition compares against, either in a specific currency or as a plain number.
            - ApprovalWorkflowMonetaryThreshold: A threshold in one currency. Amounts are compared without conversion, so an entry in another currency does not match.
              - …
            - ApprovalWorkflowNumericThreshold: A threshold without a currency: the entry's amount is compared as a number, whatever its currency.
              - …
        - ApprovalWorkflowSubsidiaryCondition: Matches on the entry's subsidiary.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
          - `subsidiary_id` string, uuid, required
        - ApprovalWorkflowCreatedByUserCondition: Matches on the user who created the entry or, if it has since been edited, who last edited it — an edit starts a fresh approval, attributed to the editor. Entries Rillet generates itself have no user, so `EQUALS` never matches them and `NOT_EQUALS` always does.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
          - `user_id` string, uuid, required
        - ApprovalWorkflowAuraAssistedCondition: Matches entries whose triggering create or edit was made with Aura's assistance; a later edit made without Aura is evaluated as non-Aura. `EQUALS` matches Aura-assisted changes, `NOT_EQUALS` the rest.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
        - ApprovalWorkflowUiSubmittedCondition: Matches entries whose triggering create or edit was submitted through the Rillet web application without Aura assistance. A later non-UI edit is evaluated as non-UI.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
        - ApprovalWorkflowApiSubmittedCondition: Matches entries whose triggering create or edit was submitted directly through the public REST API. MCP tool actions are tracked separately. A later non-API edit is evaluated as non-API.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
        - ApprovalWorkflowMcpSubmittedCondition: Matches entries whose triggering create or edit was submitted through a Rillet MCP tool. Direct public REST API calls are tracked separately. A later non-MCP edit is evaluated as non-MCP.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
        - ApprovalWorkflowFieldCondition: Matches on a custom field value, looking at both the entry and its line items. `EQUALS` matches when any of them carries the value, `NOT_EQUALS` when none does.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
          - `field_id` string, uuid, required
          - `field_value_id` string, uuid, required
        - ApprovalWorkflowAccountCondition: Matches on the GL accounts an entry's line items post to. `ANY_OF` matches when a line item posts to any of the listed accounts, `NONE_OF` when none of them is used.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'ANY_OF' | 'NONE_OF', required: How a condition compares against a set of values: `ANY_OF` matches when at least one of them applies, `NONE_OF` when none does.
          - `account_ids` string[], required: The accounts this condition looks for.
        - ApprovalWorkflowSourceTypeCondition: Matches on how the entry came to exist. A journal-entry workflow with no condition of this type applies to manually created entries only. A workflow can carry at most one.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `source_type` 'MANUAL' | 'SYSTEM' | 'ALL', required: Whether a journal entry was created by a person (`MANUAL`) or by Rillet from another record such as a bill, invoice or amortisation schedule (`SYSTEM`). `ALL` matches either.
        - ApprovalWorkflowVendorCondition: Matches on the bill vendor or Charge merchant.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'EQUALS' | 'NOT_EQUALS', required
          - `vendor_id` string, uuid, required
        - ApprovalWorkflowCustomerCondition: Matches on the record's customer. `ANY_OF` matches when the customer is one of the listed customers, `NONE_OF` when it is not.
          - `id` string, uuid, required: Identifies the condition. Server-generated, and returned on every read.
          - `condition_type` string, required
          - `operator` 'ANY_OF' | 'NONE_OF', required: How a condition compares against a set of values: `ANY_OF` matches when at least one of them applies, `NONE_OF` when none does.
          - `customer_ids` string[], required: The customers this condition looks for.
        - ApprovalWorkflowProviderCondition: Matches the provider that supplied the expense.
          - `id` string, uuid, required
          - `condition_type` string, required
          - `operator` 'ANY_OF' | 'NONE_OF', required: How a condition compares against a set of values: `ANY_OF` matches when at least one of them applies, `NONE_OF` when none does.
          - `providers` ApprovalWorkflowProvider[], required
        - ApprovalWorkflowOperationCondition: Matches the proposed expense operation.
          - `id` string, uuid, required
          - `condition_type` string, required
          - `operation` 'CREATE' | 'UPDATE', required
    - `groups` ApprovalWorkflowConditionGroup[]: Nested groups, each combined into this one by `logic`. Nesting is at most three levels deep. Always empty today.
  - `approval_layers` ApprovalWorkflowLayer[], required: Layers of approvers in ascending `order`, each one actionable only once the previous is satisfied. The entry is approved when every layer is satisfied. At most five.
    - union: One layer of approvers. `approver_type` selects the variant.
      - ApprovalWorkflowUserLayer: A layer approved by the named users.
        - `id` string, uuid, required: Identifies the layer. Server-generated, and returned on every read.
        - `order` integer, required: Position of the layer in the sequence, starting at 1. Layers are numbered consecutively with no gaps or repeats.
        - `approver_type` string, required
        - `require_all` boolean, required: When true, every listed user must approve; when false, one approval satisfies the layer. Workflows configured in the Rillet app always use false.
        - `approver_user_ids` string[], required: Users allowed to approve at this layer. Each must be an active member of the organization.
      - ApprovalWorkflowRoleLayer: A layer approved by anyone holding one of the given roles. Membership is resolved when the approval happens, not when the workflow is saved, so changing who holds a role changes who can approve entries already awaiting approval.
        - `id` string, uuid, required: Identifies the layer. Server-generated, and returned on every read.
        - `order` integer, required: Position of the layer in the sequence, starting at 1. Layers are numbered consecutively with no gaps or repeats.
        - `approver_type` string, required
        - `require_all` boolean, required: When true, each listed role must be represented among the approvals; when false, one approval satisfies the layer. Workflows configured in the Rillet app always use false.
        - `role_ids` string[], required: Roles allowed to approve at this layer.
  - `methods` ApprovalWorkflowMethod[], required: How approvers are asked to act. `IN_APP` is the only method Rillet acts on today; `EMAIL` and `SLACK` can appear on workflows configured before they were withdrawn, and no notification is delivered for them.
  - `submission_sources` ApprovalWorkflowSubmissionSource[]
  - `updated_at` string, date-time, required: ISO 8601 timestamp in UTC timezone (must end with 'Z'). Covers activation and reordering as well as definition changes.
  - `condition_model_version` 2, required
  - `rule` ApprovalRuleV2: Versioned conditions. Maximum 50 predicates, 100 nodes, four nesting levels, and one collection scope per path. Collection predicates match within the same line. Decimal values are strings; money requires an explicit currency and basis.
    - `version` 2, required
    - `expression` union, required
      - ApprovalExpressionPredicateV2
        - `id` string, uuid, required
        - `kind` string, required
        - `fieldId` string, required
        - `operator` 'EQUALS' | 'NOT_EQUALS' | 'IN' | 'NOT_IN' | 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'CONTAINS' | 'NOT_CONTAINS' | 'STARTS_WITH' | 'ENDS_WITH' | 'IS_PRESENT' | 'IS_MISSING', required
        - `operand` union
          - ApprovalOperandScalarV2
            - `kind` string, required
            - `value` union, required
              - …
          - ApprovalOperandValuesV2
            - `kind` string, required
            - `values` ApprovalValueV2[], required
              - …
        - `textCase` 'SENSITIVE' | 'INSENSITIVE', required
      - ApprovalExpressionGroupV2
        - `id` string, uuid, required
        - `kind` string, required
        - `logic` 'AND' | 'OR', required
        - `children` ApprovalExpressionV2[], required
      - ApprovalExpressionCollectionV2
        - `id` string, uuid, required
        - `kind` string, required
        - `collectionId` string, required
        - `quantifier` 'ANY' | 'ALL' | 'NONE', required
        - `expression` ApprovalExpressionV2, required (recursive)
  - `deadline_days` integer, nullable

## Other responses

- `default`: Error

## Changes

- **2026-11-03** `c393d0102005`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/rillet/apis/rillet-accounting-api/changes/approval-workflows/condition-models/v2/:approval_workflow_id/put.md)

---

[API](https://skmtc.dev/rillet/apis/rillet-accounting-api.md) - [All operations](https://skmtc.dev/rillet/apis/rillet-accounting-api/llms.txt) - [OpenAPI document](https://skmtc.dev/rillet/apis/rillet-accounting-api/revisions/c393d0102005?raw)
