# Update Workflow

PUT /api/v2/projects/{projectId}/workflows/{workflowId}

Updates a workflow. Only the fields present in changes are written, so omitted keys stay as they are.

## Request

- projectId: string, path, required — ID of the project that owns the workflow.
- workflowId: string, path, required — ID of the workflow to update.
- changes: object, required — Fields to change. Anything omitted is left as-is.
  - contactsFilterGroups: object[] — Audience filter groups. A contact must match at least one group to enter the workflow.
    - filters: object[], required — Attribute filters combined with groupType.
    - groupType: AND | OR | NOT, required — How filters in this group are combined.
    - oneDayEventFilters: object[] — Event filters evaluated over the last 24 hours only.
    - requireSubscribed: boolean — When true, the group is ANDed with `$subscribed == true`. Set by email sending, not the filter UI.
  - frequency: ONCE | EVERY — ONCE runs a contact through at most once. EVERY re-enters them each time the trigger fires, subject to maxRuns and minDelay.
  - maxRuns: integer — Maximum times a contact can go through this workflow. Null means no cap.
  - minDelayAmount: integer — Minimum delay between runs for the same contact. Pair with minDelayUnit.
  - minDelayUnit: MINUTES | HOURS | DAYS — Unit for minDelayAmount: MINUTES, HOURS, or DAYS.
  - name: string — Display name of the workflow.
  - projectId: string
  - status: INACTIVE | PAUSED | ACTIVE — INACTIVE, PAUSED, or ACTIVE.
  - triggerEvent: string — Name of the analytics event that starts a run, e.g. signup.

## Response

- 200: Successful response
  - contactsFilterGroups: object[], required
    - filters: object[], required — Attribute filters combined with groupType.
    - groupType: AND | OR | NOT, required — How filters in this group are combined.
    - oneDayEventFilters: object[] — Event filters evaluated over the last 24 hours only.
    - requireSubscribed: boolean — When true, the group is ANDed with `$subscribed == true`. Set by email sending, not the filter UI.
  - createdAt: string, required
  - frequency: ONCE | EVERY, required, default ONCE
  - id: string, required
  - name: string, required
  - projectId: string, required
  - status: INACTIVE | PAUSED | ACTIVE, required, default INACTIVE
  - triggerEvent: string, required
  - updatedAt: string, required
  - maxRuns: integer
  - minDelayAmount: integer, default 5
  - minDelayUnit: MINUTES | HOURS | DAYS, default MINUTES
- 400: Invalid input data
- 401: Authorization not provided
- 403: Insufficient access
- 404: Not found
- 500: Internal server error
