---
title: "Updates an approval workflow"
method: PUT
path: "/approval-workflows/{approval_workflow_id}"
tags: ["Approval Workflows"]
---

# Updates an approval workflow

`PUT /approval-workflows/{approval_workflow_id}`

## Request

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

Replaces an approval workflow's definition. The whole definition is required — a partial body is
rejected, not merged, and fields this contract does not define are ignored. Send `type` and
`active` back as read; changing either here is rejected, and combinations the Rillet app cannot
configure are rejected too (see the individual fields).

Entries already awaiting approval are unaffected, because an entry copies the layers it needs when
approval starts; only entries registered after this call use the new definition. Deadlines and layer
escalation rules are not part of this contract and are cleared, exactly as saving a workflow in
Rillet clears them.

## Path parameters

- `approval_workflow_id` string, uuid, required

## Request body

- ApprovalWorkflowRequest: An approval workflow's definition, as sent when creating or replacing one. A workflow's `type` never changes, and `active` changes only through activation in Rillet, so an update must send both back 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, required: 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[]

## Response `200`

OK

- ApprovalWorkflow: An approval workflow: the conditions deciding which entries it applies to, and the ordered layers of approvers that must sign off.
  - `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, required: 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.

## 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/: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)
