# Workflows & schema

> List and read workflows, read a published version's tree and its JSON Schema, update workflow-level settings, and import or export the portable template.

Source: https://developers.processcart.com/guides/workflows

Operations: [Workflows endpoints](https://developers.processcart.com/reference/workflows).

A **workflow** is a template; runs are started from its **published version**. The builder owns the tree (sections, tasks, fields, rules); the API reads it, describes it as JSON Schema, moves it between workspaces as a portable template, and changes the workflow-level settings around it.

## Summary and detail

`GET /v1/workflows` lists the workflows the key may see (its allowlist applied; trashed workflows absent), by name, 50 per page up to 200. Each item is the library header:

```json
{
  "id": "3c1d9e2a-…", "name": "Order fulfillment", "description": "Every order from payment to shipment.",
  "status": "active", "icon": "package", "color": "#2563eb",
  "folder": { "id": "f0f1…", "name": "Operations" },
  "keyDateFieldKey": "ship_date", "kickoffFormEnabled": false,
  "publishedVersion": { "id": "a0b1…", "number": 4, "publishedAt": "2026-09-30T21:10:00.000Z" },
  "taskCount": 9,
  "createdAt": "2026-06-26T15:00:00.000Z", "updatedAt": "2026-09-30T21:10:00.000Z"
}
```

- `publishedVersion` is null until the workflow is published — such a workflow cannot start runs and has no schema.
- `updatedAt` moves on every change to the header, folder, status or published pointer; `updatedSince` turns the list into an [incremental sync](https://developers.processcart.com/guides/incremental-sync).
- `status` is `active` or `archived`; an archived workflow is hidden from the Run menu while its runs stay.

`GET /v1/workflows/{id}` adds the **published version's tree**: `sections[] → tasks[] → fields[]` plus `rules[]` — every task key and field key a run will carry. A task carries its `kind`, `isStop`, `defaultVisibility`, `dueRule`, `assignment` (by ids: users, groups, roles, the run starter, a Members field, a guest) and `guestConfig`; a field its `type`, `label`, `help`, `required`, `options` (choices, items, file `multiple`), `formatConfig`, `validation`, a static `defaultValue`, `formula`, `inKickoff`, `kickoffPosition` and `readOnly` (formula, file and Display Text fields, and any field carrying a formula, are read-only through the API). An unpublished workflow answers with `publishedVersion: null` and no sections; a workflow in Trash is a 404.

## JSON Schema

`GET /v1/workflows/{id}/schema` returns two JSON Schema (2020-12) documents generated from the published version:

```json
{
  "workflowId": "3c1d9e2a-…", "versionId": "a0b1…", "versionNumber": 4,
  "start":  { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Start a run", "type": "object", "properties": { "workflowId": …, "name": …, "fields": { … }, "priority": …, "labels": …, "dueAt": … }, "required": ["workflowId", "name", "fields"] },
  "fields": { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Set field values", "type": "object", "properties": { … }, "minProperties": 1, "maxProperties": 200 }
}
```

This example's workflow has a required kickoff field, so `start.required` lists `fields`; for a workflow without one it is `["workflowId", "name"]` and `fields` may be omitted — the per-workflow form of the reference, where `fields` is optional.

- `start` describes the `POST /v1/runs` body: every writable field under `fields`, with **`required` naming only the required kickoff fields no rule can hide** (a required field a rule may hide is described as "required at start unless a rule hides it"); a required checkbox is `const: true`.
- `fields` describes the `PATCH /v1/runs/{id}/fields` body: every writable field.
- Each property carries the field's `title`, its JSON type per the [typing rules](https://developers.processcart.com/guides/field-values), `enum` for dropdown choices / multi-choice entries / subtask keys, `minimum` / `maximum` bounds, `format` (`date`, `email`, `uri`), and the annotations `x-processcart-type`, `x-processcart-format`, `x-task` and `x-section`. Hidden fields are writable and marked so (never shown in the app, not secret).
- `versionId` is the **published** version; runs pin the version they started on, so an older run may carry fields this schema no longer lists.
- An unpublished workflow is a 409 `workflow_not_published`.

## Updating a workflow

`PATCH /v1/workflows/{id}` changes workflow-level settings — never the tree, which stays the builder's:

```json
{ "name": "Order fulfillment (US)", "description": "…", "folderId": "f0f1…", "keyDateFieldKey": "ship_date", "status": "active" }
```

Any subset. `folderId` must be a folder of your workspace (`null` for the main Workflows page; a key restricted to folders may only move within its folders) — any other id is a 422 (`errors[].code` `unknown_folder`); `keyDateFieldKey` must name a date-typed field of the published version — of the draft while the workflow has never been published (`null` clears it; a key the version lacks is `unknown_field`, a field that is not date-typed `not_a_date_field`) — and re-derives every live run's Key Date; `status: "archived"` hides the workflow from the Run menu. The answer is the summary. Audited.

## Template export and import

The **portable template** (specs §10.6) is the buildable workflow as JSON — sections, tasks, fields, formulas, rules, due rules and assignments by group or role name — with no ids, no people, no workspace settings, no secrets and no embedded images. Its envelope carries `formatVersion` and `engineVersion`, `exportedAt` and `source` (the workspace slug).

- `GET /v1/workflows/{id}/template` exports the published version (409 `workflow_not_published` otherwise). Keep it in version control, or feed it to another workspace — the [sandbox](https://developers.processcart.com/guides/sandbox) flow.
- `POST /v1/workflows` creates a workflow from one:

```json
{ "template": { "formatVersion": 1, "engineVersion": 1, "workflow": { … } },
  "name": "Order fulfillment", "description": "…", "folderId": "f0f1…", "publish": true }
```

The flow, in order: the envelope's versions are checked (a template from a newer build is a 422, `errors[].code` `unsupported_version`); the template is validated and sanitized; group assignments are matched to your workspace's groups by **name, case-insensitively** (an unknown group clears that assignment — counted in `clearedAssignments`); when `publish` is true (the default) the publish gate runs **before anything is written**, so a 422 creates nothing (a template that fails validation answers `invalid_template` entries in `errors[]`, one that fails the gate `publish_gate` entries, one that repeats a section, task or field key `duplicate_key`, and a `folderId` outside your workspace `unknown_folder`); then the workflow is created, published, and answered as `201 Created` with `Location`, the detail resource, an `import` report (`errors` — always empty on a 201 —, `warnings`, `counts`, `clearedAssignments`, `flaggedEmails` — static recipient addresses kept from the template, worth a review) and `publishWarnings` (the gate's non-blocking notes). `publish: false` leaves a draft to finish in the builder. A key restricted to workflows cannot create one; a key restricted to folders must pass a `folderId` among its folders.

Workflows cannot be moved to Trash or restored through the API in v1; run-start restrictions by group (folder restrictions in the app) are replaced, for keys, by the key's allowlist.
