Purple Journeys API OpenAPI

Purple Journeys is the suite's employee journey system: the category that tools like ServiceNow Journey Designer, Serval Journeys, Workday Journeys and Enboarder sit in. A journey TEMPLATE describes a lifecycle event (new hire onboarding, offboarding, parental leave, an internal transfer) as PHASES that unlock on dates relative to an anchor (start date, last working day, leave start, return, effective date) or on earlier phases, each holding STEPS assigned to a role: the subject, their manager, a buddy, HR, IT, facilities, payroll, security, or the system. Starting a template for one person creates a JOURNEY and one TASK per step, dated from the anchor and locked until its phase opens and its prerequisites are done. People complete their tasks; automated steps run themselves when they unlock; status checks wait in processing until told they passed; the journey keeps progress_percent, counts, health (warning when anything is overdue, failing when a step errored) and up_next. Everything that happens is on the ACTIVITY feed. Where to start: list-templates shows what exists; list-steps is the step library (every step of every template: what other journeys ask people to do, by system, role and kind); create-templates takes a whole template in one call (phases, steps, roles, due offsets, dependencies, each step's processing system, forms and links) and returns it normalized with warnings instead of rejecting it; preview-templates shows the per-role timeline for a given anchor date; add-steps and remove-step edit it; publish-templates makes it startable (apply_to_active fans new steps into running journeys); create-journeys starts it for a person; list-my-tasks is what the current user owes, list-my-journeys their own journeys, get-my-journey one journey as they see it (next up, their tasks in due order, what each waits for), list-team-journeys their reports'. While a journey runs, add-task adds an ad hoc task the template did not foresee (who, when, why); flag-tasks marks any task as bumpy with a note; submit-tasks sends a form step's values and completes it; complete, start, skip, reopen and reassign move the rest. Every skip, reassignment, reopen and late completion is recorded on the task, and get-insights-templates (Suggested changes in the UI) reads them back across a template's journeys: ad hoc tasks grouped by title with the add-steps body that would promote each into the template, skipped steps, late steps with median days late, reassignments and flags. Webhooks: subscribe with POST /api/purple-suite/instances/{id}/webhooks and an events filter. Besides the kit's journeys.templates|journeys|tasks.created|updated|deleted, this app emits journeys.journey.started|completed|cancelled, journeys.task.unlocked|completed|overdue|added|flagged and journeys.task.form_submitted (task, journey, step_key, values) on every form submission, plus journeys.template.published. Role fields are emails: subject_email, manager_email, buddy_email, assignee_email. Dates are YYYY-MM-DD; timestamps are ISO 8601. The manager-experience app also has a journeys collection: that one only paints a vendor API for a read-only plugin; this app is where journeys are authored and run. --- **Acting as a user (`X-PS-Impersonate-User`).** Every endpoint accepts an optional `X-PS-Impersonate-User` request header whose value is the email of the instance user to act as. The effective user resolves in this order: the header if present, else the instance's configured default MCP user, else none. User-scoped paths additionally accept the literal `me` in place of an email, and Graph-style apps also serve `/me/…` twins of their `/users/{email}/…` paths that resolve the same way. This header is honored by all operations but is intentionally omitted from each operation's parameter list so it is not surfaced as a per-tool argument by MCP clients; see the `ImpersonateUser` entry under `components.parameters`.

This reference is readable without JavaScript. Download the OpenAPI document.

Runtime

/api/purple-suite/journeys

Purple Journeys is the suite's employee journey system: the category that tools like ServiceNow Journey Designer, Serval Journeys, Workday Journeys and Enboarder sit in. A journey TEMPLATE describes a lifecycle event (new hire onboarding, offboarding, parental leave, an internal transfer) as PHASES that unlock on dates relative to an anchor (start date, last working day, leave start, return, effective date) or on earlier phases, each holding STEPS assigned to a role: the subject, their manager, a buddy, HR, IT, facilities, payroll, security, or the system. Starting a template for one person creates a JOURNEY and one TASK per step, dated from the anchor and locked until its phase opens and its prerequisites are done. People complete their tasks; automated steps run themselves when they unlock; status checks wait in processing until told they passed; the journey keeps progress_percent, counts, health (warning when anything is overdue, failing when a step errored) and up_next. Everything that happens is on the ACTIVITY feed. Where to start: list-templates shows what exists; list-steps is the step library (every step of every template: what other journeys ask people to do, by system, role and kind); create-templates takes a whole template in one call (phases, steps, roles, due offsets, dependencies, each step's processing system, forms and links) and returns it normalized with warnings instead of rejecting it; preview-templates shows the per-role timeline for a given anchor date; add-steps and remove-step edit it; publish-templates makes it startable (apply_to_active fans new steps into running journeys); create-journeys starts it for a person; list-my-tasks is what the current user owes, list-my-journeys their own journeys, get-my-journey one journey as they see it (next up, their tasks in due order, what each waits for), list-team-journeys their reports'. While a journey runs, add-task adds an ad hoc task the template did not foresee (who, when, why); flag-tasks marks any task as bumpy with a note; submit-tasks sends a form step's values and completes it; complete, start, skip, reopen and reassign move the rest. Every skip, reassignment, reopen and late completion is recorded on the task, and get-insights-templates (Suggested changes in the UI) reads them back across a template's journeys: ad hoc tasks grouped by title with the add-steps body that would promote each into the template, skipped steps, late steps with median days late, reassignments and flags. Webhooks: subscribe with POST /api/purple-suite/instances/{id}/webhooks and an events filter. Besides the kit's journeys.templates|journeys|tasks.created|updated|deleted, this app emits journeys.journey.started|completed|cancelled, journeys.task.unlocked|completed|overdue|added|flagged and journeys.task.form_submitted (task, journey, step_key, values) on every form submission, plus journeys.template.published. Role fields are emails: subject_email, manager_email, buddy_email, assignee_email. Dates are YYYY-MM-DD; timestamps are ISO 8601. The manager-experience app also has a journeys collection: that one only paints a vendor API for a read-only plugin; this app is where journeys are authored and run. --- **Acting as a user (`X-PS-Impersonate-User`).** Every endpoint accepts an optional `X-PS-Impersonate-User` request header whose value is the email of the instance user to act as. The effective user resolves in this order: the header if present, else the instance's configured default MCP user, else none. User-scoped paths additionally accept the literal `me` in place of an email, and Graph-style apps also serve `/me/…` twins of their `/users/{email}/…` paths that resolve the same way. This header is honored by all operations but is intentionally omitted from each operation's parameter list so it is not surfaced as a per-tool argument by MCP clients; see the `ImpersonateUser` entry under `components.parameters`.

Servers

[
  {
    "url": "https://marketplace.moveworks.com/api/purple-suite/journeys"
  }
]

Authentication schemes

{
  "patAuth": {
    "type": "http",
    "scheme": "bearer"
  }
}

Each operation lists its security requirements. An empty security array permits an unauthenticated request. Use the scheme and headers declared for the selected contract.

Operations

GET /templates

List journey templates

Templates are reusable definitions of a lifecycle event (onboarding, offboarding, leave, transfer) as phases and steps. Start here to see what can be started. _filter "status eq 'published'" for the ones journeys can start from; _filter "journey_type eq 'onboarding'". Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "List journey templates",
  "description": "Templates are reusable definitions of a lifecycle event (onboarding, offboarding, leave, transfer) as phases and steps. Start here to see what can be started. _filter \"status eq 'published'\" for the ones journeys can start from; _filter \"journey_type eq 'onboarding'\". Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "query",
      "name": "limit",
      "schema": {
        "type": "integer",
        "default": 50
      },
      "description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
    },
    {
      "in": "query",
      "name": "cursor",
      "schema": {
        "type": "string"
      },
      "description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/JourneyTemplate"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query (e.g. malformed $filter)"
    }
  }
}

POST /templates

Create a journey template from a whole spec

Takes the entire template in one call: name, journey_type, anchor, trigger, phases (each with an unlock rule) and steps (key, phase, title, kind, assignee_role, due offset, depends_on, required). The server normalizes it (derives keys from names, defaults due dates to the phase opening, orders steps by phase) and VALIDATES it, returning the stored template plus a warnings array rather than rejecting it: unknown roles or kinds, depends_on to a step that does not exist, cycles (the edge that points back to an earlier step is dropped), a phase gated on its own completion through after_phase (it opens at once instead), a step due before its phase opens, a step without a phase. Only a missing name, a step without a title or duplicate keys are errors (400). The template is a draft; preview-templates to check the timeline, add-steps to adjust, publish-templates to make it startable.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Create a journey template from a whole spec",
  "description": "Takes the entire template in one call: name, journey_type, anchor, trigger, phases (each with an unlock rule) and steps (key, phase, title, kind, assignee_role, due offset, depends_on, required). The server normalizes it (derives keys from names, defaults due dates to the phase opening, orders steps by phase) and VALIDATES it, returning the stored template plus a warnings array rather than rejecting it: unknown roles or kinds, depends_on to a step that does not exist, cycles (the edge that points back to an earlier step is dropped), a phase gated on its own completion through after_phase (it opens at once instead), a step due before its phase opens, a step without a phase. Only a missing name, a step without a title or duplicate keys are errors (400). The template is a draft; preview-templates to check the timeline, add-steps to adjust, publish-templates to make it startable.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    }
  ],
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TemplateSpec"
        }
      }
    }
  },
  "responses": {
    "201": {
      "description": "The normalized template, with warnings",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/JourneyTemplate"
          }
        }
      }
    },
    "400": {
      "description": "Missing name, a step without a title, duplicate phase or step keys, or a non-array phases/steps"
    }
  }
}

GET /templates/{id}

Get a journey template

The full template: phases (with unlock rules), steps (role, kind, due offset, dependencies), roles, counts, version, status and any validation warnings.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Get a journey template",
  "description": "The full template: phases (with unlock rules), steps (role, kind, due offset, dependencies), roles, counts, version, status and any validation warnings.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Found",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/JourneyTemplate"
          }
        }
      }
    },
    "404": {
      "description": "Not found"
    }
  }
}

PATCH /templates/{id}

Update a journey template

Change name, description, journey_type, anchor, trigger, or replace phases/steps wholesale (the result is normalized and validated the same way as create). To add or change a few steps use add-steps instead. status may be set to retired; publishing goes through publish-templates, and a published or retired template cannot go back to draft (clone it instead). A retired template cannot change at all (409); clone it and edit the copy. A published template that changes structurally is marked has_unpublished_changes until it is published again. Journeys already running keep the copy of the phases and steps they took when they started.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Update a journey template",
  "description": "Change name, description, journey_type, anchor, trigger, or replace phases/steps wholesale (the result is normalized and validated the same way as create). To add or change a few steps use add-steps instead. status may be set to retired; publishing goes through publish-templates, and a published or retired template cannot go back to draft (clone it instead). A retired template cannot change at all (409); clone it and edit the copy. A published template that changes structurally is marked has_unpublished_changes until it is published again. Journeys already running keep the copy of the phases and steps they took when they started.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "description": "Fields to update (partial)",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/JourneyTemplate"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "Updated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/JourneyTemplate"
          }
        }
      }
    },
    "404": {
      "description": "Not found"
    }
  }
}

DELETE /templates/{id}

Delete a journey template

Removes the template. Journeys already started from it keep running on their own copy of the phases and steps (template_snapshot), so nothing about them changes; new journeys can no longer be started from it. To stop starts while keeping the template, set status to retired instead.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Delete a journey template",
  "description": "Removes the template. Journeys already started from it keep running on their own copy of the phases and steps (template_snapshot), so nothing about them changes; new journeys can no longer be started from it. To stop starts while keeping the template, set status to retired instead.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Deleted",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "success": {
                "type": "boolean"
              }
            }
          }
        }
      }
    },
    "404": {
      "description": "Not found"
    }
  }
}

POST /templates/{id}/steps

Add or change steps on a template

Upserts steps by key: a step whose key exists is patched with the fields given (e.g. { key, due } to move a due date, { key, required: false } to make it optional); any other key is added (title required; it goes in the phase named, or the first phase). Phases can ride along in phases, matched by key the same way, and steps nested under a phase (phases[].steps) are added to that phase. The whole template is re-normalized and re-validated; the response is the template with warnings plus the keys created and updated. A depends_on in this request that would close a cycle is the edge dropped (with a warning); steps the request did not name are never rewritten. On a published template this marks has_unpublished_changes; publish-templates with apply_to_active to push new steps into running journeys.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__add_steps__templates",
  "summary": "Add or change steps on a template",
  "description": "Upserts steps by key: a step whose key exists is patched with the fields given (e.g. { key, due } to move a due date, { key, required: false } to make it optional); any other key is added (title required; it goes in the phase named, or the first phase). Phases can ride along in phases, matched by key the same way, and steps nested under a phase (phases[].steps) are added to that phase. The whole template is re-normalized and re-validated; the response is the template with warnings plus the keys created and updated. A depends_on in this request that would close a cycle is the edge dropped (with a warning); steps the request did not name are never rewritten. On a published template this marks has_unpublished_changes; publish-templates with apply_to_active to push new steps into running journeys.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JT-0001"
      },
      "description": "Template id"
    }
  ],
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/StepsUpsertRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The template after the change",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/StepsUpsertResult"
          }
        }
      }
    },
    "400": {
      "description": "steps missing or not an array, or a new step without a title"
    },
    "404": {
      "description": "Template not found"
    },
    "409": {
      "description": "The template is retired"
    }
  }
}

DELETE /templates/{id}/steps/{stepKey}

Remove a step from a template

Deletes one step by key. Other steps that depended on it lose that dependency (reported in warnings). Journeys already running keep their tasks.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__remove_step__templates",
  "summary": "Remove a step from a template",
  "description": "Deletes one step by key. Other steps that depended on it lose that dependency (reported in warnings). Journeys already running keep their tasks.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JT-0001"
      },
      "description": "Template id"
    },
    {
      "in": "path",
      "name": "stepKey",
      "required": true,
      "schema": {
        "type": "string",
        "example": "sign_paperwork"
      },
      "description": "The step's key within the template"
    }
  ],
  "responses": {
    "200": {
      "description": "The template after the change",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/JourneyTemplate"
          }
        }
      }
    },
    "404": {
      "description": "Template or step not found; the message lists the step keys that exist"
    },
    "409": {
      "description": "The template is retired"
    }
  }
}

POST /templates/{id}/preview

Preview a template as a per-role timeline

Returns one row per step, grouped by role (subject first, then manager, buddy, hr, it, facilities, payroll, security, system) and ordered by due offset: role, phase, title, kind, required, due label (start -7d) and, when anchor_date is given, the actual due_date and the day the phase opens. as_role narrows it to one role, which is how a manager or a new hire sees their own list. Use it before publishing and before starting a journey to confirm the dates read right. Also returns the template's warnings.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Preview a template as a per-role timeline",
  "description": "Returns one row per step, grouped by role (subject first, then manager, buddy, hr, it, facilities, payroll, security, system) and ordered by due offset: role, phase, title, kind, required, due label (start -7d) and, when anchor_date is given, the actual due_date and the day the phase opens. as_role narrows it to one role, which is how a manager or a new hire sees their own list. Use it before publishing and before starting a journey to confirm the dates read right. Also returns the template's warnings.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JT-0001"
      },
      "description": "Template id"
    }
  ],
  "requestBody": {
    "required": false,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/PreviewRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The timeline",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TemplatePreview"
          }
        }
      }
    },
    "400": {
      "description": "Bad anchor_date or an as_role the template does not use"
    },
    "404": {
      "description": "Template not found"
    }
  }
}

POST /templates/{id}/publish

Publish a template (optionally applying it to active journeys)

Makes the template startable. The first publish keeps version 1; publishing a published template that changed bumps the version and clears has_unpublished_changes. Without apply_to_active, journeys already running are untouched: they keep the copy of the phases and steps they started with. With apply_to_active true, every active journey of this template takes the new phases and steps as its template_snapshot, gets a task for each step it does not have yet and did not exclude at start (ready if that phase is already open, otherwise locked), re-gates and re-dates its open, unstarted tasks from the new phases (a phase whose unlock rule changed, a due offset that moved; work in flight keeps its gate), assigns new role tasks to the people the journey started with, and moves to the new template_version; completed and cancelled journeys are left alone. A template with no steps cannot be published.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Publish a template (optionally applying it to active journeys)",
  "description": "Makes the template startable. The first publish keeps version 1; publishing a published template that changed bumps the version and clears has_unpublished_changes. Without apply_to_active, journeys already running are untouched: they keep the copy of the phases and steps they started with. With apply_to_active true, every active journey of this template takes the new phases and steps as its template_snapshot, gets a task for each step it does not have yet and did not exclude at start (ready if that phase is already open, otherwise locked), re-gates and re-dates its open, unstarted tasks from the new phases (a phase whose unlock rule changed, a due offset that moved; work in flight keeps its gate), assigns new role tasks to the people the journey started with, and moves to the new template_version; completed and cancelled journeys are left alone. A template with no steps cannot be published.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JT-0001"
      },
      "description": "Template id"
    }
  ],
  "requestBody": {
    "required": false,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/PublishRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The published template, with how many journeys it was applied to",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/PublishResult"
          }
        }
      }
    },
    "404": {
      "description": "Template not found"
    },
    "409": {
      "description": "The template is retired or has no steps"
    }
  }
}

GET /steps

List the step library (what other journeys ask people to do)

Every step of every template in the instance as one read model, grouped by title: kind, assignee_role, system, due offset from the anchor, the phase it sits in, which templates use it (template_names, used_in_count) and whether it was promoted from an ad hoc task (origin). This answers "what do other journeys ask people to do": research it before designing a template, then reuse the rows as steps in create-templates or add-steps. system=itsm (or _filter "system eq 'itsm'") for what journeys ask the ITSM system to do; assignee_role=manager for what managers are asked; _search "laptop" across titles, descriptions and template names; _orderby "used_in_count desc" for the most common steps. Built from the templates on each call; nothing is stored. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "List the step library (what other journeys ask people to do)",
  "description": "Every step of every template in the instance as one read model, grouped by title: kind, assignee_role, system, due offset from the anchor, the phase it sits in, which templates use it (template_names, used_in_count) and whether it was promoted from an ad hoc task (origin). This answers \"what do other journeys ask people to do\": research it before designing a template, then reuse the rows as steps in create-templates or add-steps. system=itsm (or _filter \"system eq 'itsm'\") for what journeys ask the ITSM system to do; assignee_role=manager for what managers are asked; _search \"laptop\" across titles, descriptions and template names; _orderby \"used_in_count desc\" for the most common steps. Built from the templates on each call; nothing is stored. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "query",
      "name": "kind",
      "schema": {
        "type": "string",
        "enum": [
          "todo",
          "form",
          "esign",
          "document",
          "content",
          "learning",
          "survey",
          "approval",
          "automated",
          "status_check",
          "meeting",
          "link"
        ]
      },
      "description": "Only this kind of step: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link"
    },
    {
      "in": "query",
      "name": "assignee_role",
      "schema": {
        "type": "string",
        "enum": [
          "subject",
          "manager",
          "buddy",
          "hr",
          "it",
          "facilities",
          "payroll",
          "security",
          "system"
        ]
      },
      "description": "Only steps for this role: subject, manager, buddy, hr, it, facilities, payroll, security, system"
    },
    {
      "in": "query",
      "name": "system",
      "schema": {
        "type": "string",
        "enum": [
          "identity",
          "access",
          "itsm",
          "hris",
          "lms",
          "chat",
          "calendar",
          "pages",
          "mail",
          "expense",
          "people"
        ]
      },
      "description": "Only steps processed in this system: identity, access, itsm, hris, lms, chat, calendar, pages, mail, expense, people"
    },
    {
      "in": "query",
      "name": "origin",
      "schema": {
        "type": "string",
        "enum": [
          "template",
          "promoted"
        ]
      },
      "description": "template or promoted (the step came from an ad hoc group)"
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/StepLibraryRow"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query, or no current user for a /me route"
    }
  }
}

GET /templates/{id}/insights

Suggested changes for a template: what its journeys have been teaching

Reads across every journey of the template (any version): ad hoc tasks grouped by normalized title (count, which journeys, who added them and why, the suggested phase, role and due offset, and promote: the add-steps body that would add the group to the template; promoted_step_key when a step already covers it), the steps people skipped (with notes), the steps that ran late (count, median and worst days late, by role), the steps that were reassigned, and every flag with its note and who raised it. This is what goes wrong in this journey and what to add. To promote a group call add-steps-templates with the group's promote body (the new step records promoted_from), then publish-templates with apply_to_active to reach the journeys under way. Computed on every call; nothing is stored. Skips and lateness leave cancelled journeys out.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Suggested changes for a template: what its journeys have been teaching",
  "description": "Reads across every journey of the template (any version): ad hoc tasks grouped by normalized title (count, which journeys, who added them and why, the suggested phase, role and due offset, and promote: the add-steps body that would add the group to the template; promoted_step_key when a step already covers it), the steps people skipped (with notes), the steps that ran late (count, median and worst days late, by role), the steps that were reassigned, and every flag with its note and who raised it. This is what goes wrong in this journey and what to add. To promote a group call add-steps-templates with the group's promote body (the new step records promoted_from), then publish-templates with apply_to_active to reach the journeys under way. Computed on every call; nothing is stored. Skips and lateness leave cancelled journeys out.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JT-0001"
      },
      "description": "Template id"
    }
  ],
  "responses": {
    "200": {
      "description": "The insights",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TemplateInsights"
          }
        }
      }
    },
    "404": {
      "description": "Template not found"
    }
  }
}

POST /templates/{id}/clone

Clone a template into a new draft

Copies phases and steps into a new draft template (version 1) with the given name or Copy of the original. Use it to make a variant (contractor onboarding from employee onboarding) or to revise a retired template.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Clone a template into a new draft",
  "description": "Copies phases and steps into a new draft template (version 1) with the given name or Copy of the original. Use it to make a variant (contractor onboarding from employee onboarding) or to revise a retired template.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JT-0001"
      },
      "description": "Template id"
    }
  ],
  "requestBody": {
    "required": false,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CloneRequest"
        }
      }
    }
  },
  "responses": {
    "201": {
      "description": "The new draft",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/JourneyTemplate"
          }
        }
      }
    },
    "404": {
      "description": "Template not found"
    }
  }
}

GET /journeys

List journeys

Every journey in the instance (one per person per lifecycle event) with status, health, progress_percent, counts and up_next. _filter "status eq 'active' and health eq 'warning'" for the ones that need attention; _filter "anchor_date ge 2026-11-01" for upcoming start dates; _orderby "anchor_date asc". For the current user's own journeys use list-my-journeys; for their reports' journeys list-team-journeys. Not to be confused with manager-experience's journeys, which only paint a vendor API. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "List journeys",
  "description": "Every journey in the instance (one per person per lifecycle event) with status, health, progress_percent, counts and up_next. _filter \"status eq 'active' and health eq 'warning'\" for the ones that need attention; _filter \"anchor_date ge 2026-11-01\" for upcoming start dates; _orderby \"anchor_date asc\". For the current user's own journeys use list-my-journeys; for their reports' journeys list-team-journeys. Not to be confused with manager-experience's journeys, which only paint a vendor API. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "query",
      "name": "limit",
      "schema": {
        "type": "integer",
        "default": 50
      },
      "description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
    },
    {
      "in": "query",
      "name": "cursor",
      "schema": {
        "type": "string"
      },
      "description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    },
    {
      "in": "query",
      "name": "status",
      "schema": {
        "type": "string",
        "enum": [
          "draft",
          "active",
          "completed",
          "cancelled"
        ]
      },
      "description": "Only this status: draft, active, completed, cancelled"
    },
    {
      "in": "query",
      "name": "health",
      "schema": {
        "type": "string",
        "enum": [
          "passing",
          "warning",
          "failing"
        ]
      },
      "description": "Only this health: passing, warning, failing"
    },
    {
      "in": "query",
      "name": "subject_email",
      "schema": {
        "type": "string",
        "format": "email"
      },
      "description": "Journeys for this person"
    },
    {
      "in": "query",
      "name": "manager_email",
      "schema": {
        "type": "string",
        "format": "email"
      },
      "description": "Journeys where this person is the manager"
    },
    {
      "in": "query",
      "name": "template_id",
      "schema": {
        "type": "string"
      },
      "description": "Journeys started from this template"
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Journey"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query (e.g. malformed $filter)"
    }
  }
}

POST /journeys

Start a journey for a person from a published template

Creates the journey and one task per step, dated from anchor_date (each step's due offset) and locked until its phase opens and its prerequisites are done; automated steps whose phase is already open run at once. Roles resolve to people: subject_email is given; manager_email defaults to the subject's manager on the identity roster; buddy_email must be given when the template has buddy steps; hr, it, facilities, payroll and security resolve from the roster by department unless role_assignments overrides them, and the result is recorded on the journey (role_assignments) so later tasks for those roles go to the same people. Fails with 409 if the template is a draft (publish it first) or the person already has an active journey of this template. Returns the journey with its tasks.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Start a journey for a person from a published template",
  "description": "Creates the journey and one task per step, dated from anchor_date (each step's due offset) and locked until its phase opens and its prerequisites are done; automated steps whose phase is already open run at once. Roles resolve to people: subject_email is given; manager_email defaults to the subject's manager on the identity roster; buddy_email must be given when the template has buddy steps; hr, it, facilities, payroll and security resolve from the roster by department unless role_assignments overrides them, and the result is recorded on the journey (role_assignments) so later tasks for those roles go to the same people. Fails with 409 if the template is a draft (publish it first) or the person already has an active journey of this template. Returns the journey with its tasks.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    }
  ],
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/StartJourneyRequest"
        }
      }
    }
  },
  "responses": {
    "201": {
      "description": "The journey and its tasks",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/StartJourneyResult"
          }
        }
      }
    },
    "400": {
      "description": "Missing template_id, subject_email or anchor_date; a manager or buddy the template needs that could not be resolved; a bad email"
    },
    "404": {
      "description": "Template not found"
    },
    "409": {
      "description": "The template is not published, or the person already has an active journey of it"
    }
  }
}

GET /journeys/{id}

Get a journey

One journey with its rollup (progress, counts, health, up_next, all as of today) and the template_snapshot it runs on (phases and steps as they were when it started or was last applied). Reading it opens any task whose phase has opened on the calendar since the journey was last written (automations run, the feed records them). Tasks are listed separately with list-journey-tasks.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Get a journey",
  "description": "One journey with its rollup (progress, counts, health, up_next, all as of today) and the template_snapshot it runs on (phases and steps as they were when it started or was last applied). Reading it opens any task whose phase has opened on the calendar since the journey was last written (automations run, the feed records them). Tasks are listed separately with list-journey-tasks.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Found",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Journey"
          }
        }
      }
    },
    "404": {
      "description": "Not found"
    }
  }
}

PATCH /journeys/{id}

Update a journey

Change manager_email, buddy_email, anchor_date or subject_name. A new manager or buddy takes over that role's open tasks. A moved anchor date re-dates the open template tasks from the journey's own rules: phases and dues that hang off the anchor move with it, while an immediate phase (opened on the start day), an ad hoc task (dated on its own) and a due fixed when a task unlocked keep their dates; ready tasks nobody has started whose phase now opens in the future are re-locked. Status changes go through cancel-journeys; progress moves through the task actions.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Update a journey",
  "description": "Change manager_email, buddy_email, anchor_date or subject_name. A new manager or buddy takes over that role's open tasks. A moved anchor date re-dates the open template tasks from the journey's own rules: phases and dues that hang off the anchor move with it, while an immediate phase (opened on the start day), an ad hoc task (dated on its own) and a due fixed when a task unlocked keep their dates; ready tasks nobody has started whose phase now opens in the future are re-locked. Status changes go through cancel-journeys; progress moves through the task actions.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "description": "Fields to update (partial)",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Journey"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "Updated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Journey"
          }
        }
      }
    },
    "404": {
      "description": "Not found"
    }
  }
}

DELETE /journeys/{id}

Delete a journey

Removes the journey with its tasks and activity. To end a journey while keeping its history, use cancel-journeys instead.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Delete a journey",
  "description": "Removes the journey with its tasks and activity. To end a journey while keeping its history, use cancel-journeys instead.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JNY-0001"
      },
      "description": "Journey id"
    }
  ],
  "responses": {
    "200": {
      "description": "Deleted",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "success": {
                "type": "boolean"
              },
              "deleted_tasks": {
                "type": "integer"
              },
              "deleted_activity": {
                "type": "integer"
              }
            }
          }
        }
      }
    },
    "404": {
      "description": "Journey not found"
    }
  }
}

POST /journeys/{id}/cancel

Cancel a journey

Ends an active journey: every open task is skipped with the reason as its note, status becomes cancelled, and the activity feed records it. Done tasks keep their history. Use it when a start date falls through or a leave is withdrawn.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Cancel a journey",
  "description": "Ends an active journey: every open task is skipped with the reason as its note, status becomes cancelled, and the activity feed records it. Done tasks keep their history. Use it when a start date falls through or a leave is withdrawn.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JNY-0001"
      },
      "description": "Journey id"
    }
  ],
  "requestBody": {
    "required": false,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CancelJourneyRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The cancelled journey",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Journey"
          }
        }
      }
    },
    "404": {
      "description": "Journey not found"
    },
    "409": {
      "description": "The journey is already completed or cancelled"
    }
  }
}

GET /journeys/{id}/tasks

List the tasks of one journey

Every task of the journey with status, assignee, due date, lock reason and dependencies: the detail view, as of today (a phase that opened on the calendar since the last write is opened first, and its automations run). _filter "assignee_role eq 'manager'" for one column; _filter "status in ('ready','in_progress')" for what can be worked now; origin=ad_hoc for the tasks added by hand; _orderby "due_date asc". Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__list__journey_tasks",
  "summary": "List the tasks of one journey",
  "description": "Every task of the journey with status, assignee, due date, lock reason and dependencies: the detail view, as of today (a phase that opened on the calendar since the last write is opened first, and its automations run). _filter \"assignee_role eq 'manager'\" for one column; _filter \"status in ('ready','in_progress')\" for what can be worked now; origin=ad_hoc for the tasks added by hand; _orderby \"due_date asc\". Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JNY-0001"
      },
      "description": "Journey id"
    },
    {
      "in": "query",
      "name": "status",
      "schema": {
        "type": "string",
        "enum": [
          "locked",
          "ready",
          "in_progress",
          "processing",
          "done",
          "skipped",
          "error"
        ]
      },
      "description": "Only this status: locked, ready, in_progress, processing, done, skipped, error"
    },
    {
      "in": "query",
      "name": "assignee_role",
      "schema": {
        "type": "string",
        "enum": [
          "subject",
          "manager",
          "buddy",
          "hr",
          "it",
          "facilities",
          "payroll",
          "security",
          "system"
        ]
      },
      "description": "Only this role: subject, manager, buddy, hr, it, facilities, payroll, security, system"
    },
    {
      "in": "query",
      "name": "assignee_email",
      "schema": {
        "type": "string",
        "format": "email"
      },
      "description": "Only this person's tasks"
    },
    {
      "in": "query",
      "name": "origin",
      "schema": {
        "type": "string",
        "enum": [
          "template",
          "ad_hoc"
        ]
      },
      "description": "template steps or ad hoc tasks: template or ad_hoc"
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/JourneyTask"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query, or no current user for a /me route"
    },
    "404": {
      "description": "Journey not found"
    }
  }
}

POST /journeys/{id}/tasks

Add an ad hoc task to a running journey

Adds a task the template did not foresee to this one journey: title, who does it (assignee_email, or assignee_role to use whoever holds that role on the journey), due_date, and reason (why it was needed). The task joins the phase that is open now, is ready at once, and records origin ad_hoc, added_by (the current user) and the reason; the journey's ad_hoc_count goes up. kind defaults to todo (form and link tasks are accepted with form or link); system defaults from the kind and title. Ad hoc tasks are what get-insights-templates groups by title so the owner can promote the recurring ones into the template with add-steps-templates. Not for the template itself: use add-steps-templates for that.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__add_task__journeys",
  "summary": "Add an ad hoc task to a running journey",
  "description": "Adds a task the template did not foresee to this one journey: title, who does it (assignee_email, or assignee_role to use whoever holds that role on the journey), due_date, and reason (why it was needed). The task joins the phase that is open now, is ready at once, and records origin ad_hoc, added_by (the current user) and the reason; the journey's ad_hoc_count goes up. kind defaults to todo (form and link tasks are accepted with form or link); system defaults from the kind and title. Ad hoc tasks are what get-insights-templates groups by title so the owner can promote the recurring ones into the template with add-steps-templates. Not for the template itself: use add-steps-templates for that.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JNY-0001"
      },
      "description": "Journey id"
    }
  ],
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/AdHocTaskRequest"
        }
      }
    }
  },
  "responses": {
    "201": {
      "description": "The new task (ready) and the journey's new rollup",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TaskActionResult"
          }
        }
      }
    },
    "400": {
      "description": "Missing title, due_date or assignee; a role nobody holds on this journey; a bad kind or system"
    },
    "404": {
      "description": "Journey not found"
    },
    "409": {
      "description": "The journey is completed or cancelled"
    }
  }
}

GET /journeys/{id}/activity

List the activity feed of one journey

Who did what and when on this journey, oldest first by default: started, tasks completed or skipped, automations that ran, a template version applied, cancellation. _orderby "at desc" for newest first. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__list__journey_activity",
  "summary": "List the activity feed of one journey",
  "description": "Who did what and when on this journey, oldest first by default: started, tasks completed or skipped, automations that ran, a template version applied, cancellation. _orderby \"at desc\" for newest first. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JNY-0001"
      },
      "description": "Journey id"
    },
    {
      "in": "query",
      "name": "kind",
      "schema": {
        "type": "string",
        "enum": [
          "journey_started",
          "journey_updated",
          "journey_completed",
          "journey_cancelled",
          "task_unlocked",
          "task_started",
          "task_completed",
          "task_submitted",
          "task_skipped",
          "task_reopened",
          "task_reassigned",
          "task_added",
          "task_flagged",
          "task_unflagged",
          "automation_completed",
          "status_check_started",
          "template_applied"
        ]
      },
      "description": "Only this kind of entry: journey_started, journey_updated, journey_completed, journey_cancelled, task_unlocked, task_started, task_completed, task_submitted, task_skipped, task_reopened, task_reassigned, task_added, task_flagged, task_unflagged, automation_completed, status_check_started, template_applied"
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/JourneyActivity"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query, or no current user for a /me route"
    },
    "404": {
      "description": "Journey not found"
    }
  }
}

GET /tasks

List journey tasks across journeys

Every task in every journey. Useful for a queue view (assignee_email=... or _filter "assignee_role eq 'it' and status eq 'ready'"), for finding what is late (_filter "is_overdue eq true"), what was flagged (_filter "flagged eq true"), what was added by hand (origin=ad_hoc) and what was submitted (_filter "submission ne null"). Rows are projected to today: a task locked only on a date whose day has come reads ready. For the current user's own tasks use list-my-tasks; for one journey list-journey-tasks. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "List journey tasks across journeys",
  "description": "Every task in every journey. Useful for a queue view (assignee_email=... or _filter \"assignee_role eq 'it' and status eq 'ready'\"), for finding what is late (_filter \"is_overdue eq true\"), what was flagged (_filter \"flagged eq true\"), what was added by hand (origin=ad_hoc) and what was submitted (_filter \"submission ne null\"). Rows are projected to today: a task locked only on a date whose day has come reads ready. For the current user's own tasks use list-my-tasks; for one journey list-journey-tasks. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "query",
      "name": "limit",
      "schema": {
        "type": "integer",
        "default": 50
      },
      "description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
    },
    {
      "in": "query",
      "name": "cursor",
      "schema": {
        "type": "string"
      },
      "description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    },
    {
      "in": "query",
      "name": "status",
      "schema": {
        "type": "string",
        "enum": [
          "locked",
          "ready",
          "in_progress",
          "processing",
          "done",
          "skipped",
          "error"
        ]
      },
      "description": "Only this status: locked, ready, in_progress, processing, done, skipped, error"
    },
    {
      "in": "query",
      "name": "journey_id",
      "schema": {
        "type": "string"
      },
      "description": "Only tasks of this journey"
    },
    {
      "in": "query",
      "name": "assignee_email",
      "schema": {
        "type": "string",
        "format": "email"
      },
      "description": "Only tasks assigned to this person"
    },
    {
      "in": "query",
      "name": "assignee_role",
      "schema": {
        "type": "string",
        "enum": [
          "subject",
          "manager",
          "buddy",
          "hr",
          "it",
          "facilities",
          "payroll",
          "security",
          "system"
        ]
      },
      "description": "Only tasks for this role: subject, manager, buddy, hr, it, facilities, payroll, security, system"
    },
    {
      "in": "query",
      "name": "kind",
      "schema": {
        "type": "string",
        "enum": [
          "todo",
          "form",
          "esign",
          "document",
          "content",
          "learning",
          "survey",
          "approval",
          "automated",
          "status_check",
          "meeting",
          "link"
        ]
      },
      "description": "Only this kind of step: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link"
    },
    {
      "in": "query",
      "name": "system",
      "schema": {
        "type": "string",
        "enum": [
          "identity",
          "access",
          "itsm",
          "hris",
          "lms",
          "chat",
          "calendar",
          "pages",
          "mail",
          "expense",
          "people"
        ]
      },
      "description": "Only tasks processed in this system: identity, access, itsm, hris, lms, chat, calendar, pages, mail, expense, people"
    },
    {
      "in": "query",
      "name": "origin",
      "schema": {
        "type": "string",
        "enum": [
          "template",
          "ad_hoc"
        ]
      },
      "description": "template steps or ad hoc tasks: template or ad_hoc"
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/JourneyTask"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query (e.g. malformed $filter)"
    }
  }
}

GET /tasks/{id}

Get a journey task

One task with its status, due date, dependencies, lock reason and completion details.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Get a journey task",
  "description": "One task with its status, due date, dependencies, lock reason and completion details.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Found",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/JourneyTask"
          }
        }
      }
    },
    "404": {
      "description": "Not found"
    }
  }
}

PATCH /tasks/{id}

Update a journey task

Change title, description, due_date, note, required, payload, system or reason. Status moves with complete-tasks, start-tasks, skip-tasks and reopen-tasks; the person moves with reassign-tasks (which records reassigned_from and logs the change, so insights see it); the journey's counts and health are recomputed when a due date moves.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Update a journey task",
  "description": "Change title, description, due_date, note, required, payload, system or reason. Status moves with complete-tasks, start-tasks, skip-tasks and reopen-tasks; the person moves with reassign-tasks (which records reassigned_from and logs the change, so insights see it); the journey's counts and health are recomputed when a due date moves.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "requestBody": {
    "description": "Fields to update (partial)",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/JourneyTask"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "Updated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/JourneyTask"
          }
        }
      }
    },
    "404": {
      "description": "Not found"
    }
  }
}

POST /tasks/{id}/complete

Complete a task

Marks a ready, in-progress or processing task done (completed_by is the current user) and unlocks what depended on it: dependents whose prerequisites are now all done and whose phase is open become ready, automated steps among them run to done, status checks move to processing. Returns the task, the tasks it unlocked and the journey's new progress. A locked task cannot be completed; the 409 says what it is waiting for. Use skip-tasks for a step that should not happen.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__complete__tasks",
  "summary": "Complete a task",
  "description": "Marks a ready, in-progress or processing task done (completed_by is the current user) and unlocks what depended on it: dependents whose prerequisites are now all done and whose phase is open become ready, automated steps among them run to done, status checks move to processing. Returns the task, the tasks it unlocked and the journey's new progress. A locked task cannot be completed; the 409 says what it is waiting for. Use skip-tasks for a step that should not happen.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JTK-0001-03"
      },
      "description": "Task id"
    }
  ],
  "requestBody": {
    "required": false,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TaskNoteRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The task after the action, the tasks it unlocked, and the journey's new rollup",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TaskActionResult"
          }
        }
      }
    },
    "400": {
      "description": "Invalid input"
    },
    "404": {
      "description": "Task not found"
    },
    "409": {
      "description": "The task is not in a state that allows this action; the message says what it is waiting for or what to call instead"
    }
  }
}

POST /tasks/{id}/start

Start a task (mark it in progress)

Moves a ready task to in_progress so others can see it is being worked. Locked and finished tasks cannot be started.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__start__tasks",
  "summary": "Start a task (mark it in progress)",
  "description": "Moves a ready task to in_progress so others can see it is being worked. Locked and finished tasks cannot be started.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JTK-0001-03"
      },
      "description": "Task id"
    }
  ],
  "requestBody": {
    "required": false,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TaskNoteRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The task after the action, the tasks it unlocked, and the journey's new rollup",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TaskActionResult"
          }
        }
      }
    },
    "400": {
      "description": "Invalid input"
    },
    "404": {
      "description": "Task not found"
    },
    "409": {
      "description": "The task is not in a state that allows this action; the message says what it is waiting for or what to call instead"
    }
  }
}

POST /tasks/{id}/skip

Skip a task

Marks a task skipped: it counts toward progress and satisfies anything that depended on it, so dependents unlock as if it were done. Skipping a required step returns a warning. Works on locked tasks too.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__skip__tasks",
  "summary": "Skip a task",
  "description": "Marks a task skipped: it counts toward progress and satisfies anything that depended on it, so dependents unlock as if it were done. Skipping a required step returns a warning. Works on locked tasks too.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JTK-0001-03"
      },
      "description": "Task id"
    }
  ],
  "requestBody": {
    "required": false,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TaskNoteRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The task after the action, the tasks it unlocked, and the journey's new rollup",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TaskActionResult"
          }
        }
      }
    },
    "400": {
      "description": "Invalid input"
    },
    "404": {
      "description": "Task not found"
    },
    "409": {
      "description": "The task is not in a state that allows this action; the message says what it is waiting for or what to call instead"
    }
  }
}

POST /tasks/{id}/reopen

Reopen a done or skipped task

Puts a finished task back to ready (or locked, if its phase or prerequisites say so), clears its completion, and re-locks any task that was waiting on it and has not been started: tasks that depend on it, and tasks in a phase that opens only when this task's phase is complete. A completed journey goes back to active.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__reopen__tasks",
  "summary": "Reopen a done or skipped task",
  "description": "Puts a finished task back to ready (or locked, if its phase or prerequisites say so), clears its completion, and re-locks any task that was waiting on it and has not been started: tasks that depend on it, and tasks in a phase that opens only when this task's phase is complete. A completed journey goes back to active.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JTK-0001-03"
      },
      "description": "Task id"
    }
  ],
  "requestBody": {
    "required": false,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TaskNoteRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The task after the action, the tasks it unlocked, and the journey's new rollup",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TaskActionResult"
          }
        }
      }
    },
    "400": {
      "description": "Invalid input"
    },
    "404": {
      "description": "Task not found"
    },
    "409": {
      "description": "The task is not in a state that allows this action; the message says what it is waiting for or what to call instead"
    }
  }
}

POST /tasks/{id}/reassign

Reassign a task to another person

Changes assignee_email on an open task, records the previous person as reassigned_from and logs it on the activity feed. To change who holds a whole role on the journey (a new manager), update the journey's manager_email instead.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__reassign__tasks",
  "summary": "Reassign a task to another person",
  "description": "Changes assignee_email on an open task, records the previous person as reassigned_from and logs it on the activity feed. To change who holds a whole role on the journey (a new manager), update the journey's manager_email instead.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JTK-0001-03"
      },
      "description": "Task id"
    }
  ],
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/ReassignTaskRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The task after the action, the tasks it unlocked, and the journey's new rollup",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TaskActionResult"
          }
        }
      }
    },
    "400": {
      "description": "Invalid input"
    },
    "404": {
      "description": "Task not found"
    },
    "409": {
      "description": "The task is not in a state that allows this action; the message says what it is waiting for or what to call instead"
    }
  }
}

POST /tasks/{id}/flag

Flag a task as bumpy

Marks a task (in any state, done included) as something that did not go smoothly, with a note saying what: flagged, flag_note, flagged_by and flagged_at are set, the journey's flag_count goes up, and the flag shows in get-insights-templates for the template's owner. Any participant can flag; flagging again replaces the note. unflag-tasks clears it.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Flag a task as bumpy",
  "description": "Marks a task (in any state, done included) as something that did not go smoothly, with a note saying what: flagged, flag_note, flagged_by and flagged_at are set, the journey's flag_count goes up, and the flag shows in get-insights-templates for the template's owner. Any participant can flag; flagging again replaces the note. unflag-tasks clears it.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JTK-0001-03"
      },
      "description": "Task id"
    }
  ],
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/FlagTaskRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The task after the action, the tasks it unlocked, and the journey's new rollup",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TaskActionResult"
          }
        }
      }
    },
    "400": {
      "description": "Invalid input"
    },
    "404": {
      "description": "Task not found"
    },
    "409": {
      "description": "The task is not in a state that allows this action; the message says what it is waiting for or what to call instead"
    }
  }
}

POST /tasks/{id}/unflag

Clear a task's flag

Clears flagged, flag_note, flagged_by and flagged_at on a flagged task. 409 when the task is not flagged.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Clear a task's flag",
  "description": "Clears flagged, flag_note, flagged_by and flagged_at on a flagged task. 409 when the task is not flagged.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JTK-0001-03"
      },
      "description": "Task id"
    }
  ],
  "responses": {
    "200": {
      "description": "The task after the action, the tasks it unlocked, and the journey's new rollup",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TaskActionResult"
          }
        }
      }
    },
    "400": {
      "description": "Invalid input"
    },
    "404": {
      "description": "Task not found"
    },
    "409": {
      "description": "The task is not in a state that allows this action; the message says what it is waiting for or what to call instead"
    }
  }
}

POST /tasks/{id}/submit

Submit a form task's values (completes it)

For a task that carries a form (get-tasks shows form.fields): validates values against the fields (required present, select among options, dates YYYY-MM-DD, numbers, emails, checkboxes; values for unknown fields are dropped with a warning), stores submission { values, submitted_at, submitted_by } on the task, marks it done and unlocks its dependents exactly like complete-tasks. Emits journeys.task.form_submitted (task, journey, step_key, values) as well as journeys.task.completed. 400 lists what is missing or wrong, with the form's fields; 409 when the task has no form (use complete-tasks), is locked, or is already done.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Submit a form task's values (completes it)",
  "description": "For a task that carries a form (get-tasks shows form.fields): validates values against the fields (required present, select among options, dates YYYY-MM-DD, numbers, emails, checkboxes; values for unknown fields are dropped with a warning), stores submission { values, submitted_at, submitted_by } on the task, marks it done and unlocks its dependents exactly like complete-tasks. Emits journeys.task.form_submitted (task, journey, step_key, values) as well as journeys.task.completed. 400 lists what is missing or wrong, with the form's fields; 409 when the task has no form (use complete-tasks), is locked, or is already done.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JTK-0001-03"
      },
      "description": "Task id"
    }
  ],
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/SubmitTaskRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "The task after the action, the tasks it unlocked, and the journey's new rollup",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TaskActionResult"
          }
        }
      }
    },
    "400": {
      "description": "Invalid input"
    },
    "404": {
      "description": "Task not found"
    },
    "409": {
      "description": "The task is not in a state that allows this action; the message says what it is waiting for or what to call instead"
    }
  }
}

GET /activity

List journey activity across journeys

The audit trail: who did what, when, on which journey and task. journey_id=JNY-0001 for one journey (or list-journey-activity); kind=task_completed for one kind; _filter "kind eq 'task_completed' and at ge 2026-11-01T00:00:00Z"; _orderby "at desc". Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "List journey activity across journeys",
  "description": "The audit trail: who did what, when, on which journey and task. journey_id=JNY-0001 for one journey (or list-journey-activity); kind=task_completed for one kind; _filter \"kind eq 'task_completed' and at ge 2026-11-01T00:00:00Z\"; _orderby \"at desc\". Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "query",
      "name": "limit",
      "schema": {
        "type": "integer",
        "default": 50
      },
      "description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
    },
    {
      "in": "query",
      "name": "cursor",
      "schema": {
        "type": "string"
      },
      "description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    },
    {
      "in": "query",
      "name": "journey_id",
      "schema": {
        "type": "string"
      },
      "description": "Only entries of this journey"
    },
    {
      "in": "query",
      "name": "kind",
      "schema": {
        "type": "string",
        "enum": [
          "journey_started",
          "journey_updated",
          "journey_completed",
          "journey_cancelled",
          "task_unlocked",
          "task_started",
          "task_completed",
          "task_submitted",
          "task_skipped",
          "task_reopened",
          "task_reassigned",
          "task_added",
          "task_flagged",
          "task_unflagged",
          "automation_completed",
          "status_check_started",
          "template_applied"
        ]
      },
      "description": "Only this kind of entry: journey_started, journey_updated, journey_completed, journey_cancelled, task_unlocked, task_started, task_completed, task_submitted, task_skipped, task_reopened, task_reassigned, task_added, task_flagged, task_unflagged, automation_completed, status_check_started, template_applied"
    },
    {
      "in": "query",
      "name": "actor",
      "schema": {
        "type": "string"
      },
      "description": "Only entries by this person (email) or system"
    },
    {
      "in": "query",
      "name": "task_id",
      "schema": {
        "type": "string"
      },
      "description": "Only entries about this task"
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/JourneyActivity"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query (e.g. malformed $filter)"
    }
  }
}

GET /activity/{id}

Get an activity entry

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "summary": "Get an activity entry",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string"
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Found",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/JourneyActivity"
          }
        }
      }
    },
    "404": {
      "description": "Not found"
    }
  }
}

GET /me/tasks

List my tasks

Tasks assigned to the current user (the X-PS-Impersonate-User header, or the instance's default MCP user) across every journey: what they owe. Common calls: _filter "status in ('ready','in_progress')" for what can be done now; _filter "is_overdue eq true" for what is late; _filter "due_date le 2026-11-17" for everything due by a date; _orderby "due_date asc". Locked tasks show locked_reason and depends_on. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__list__my_tasks",
  "summary": "List my tasks",
  "description": "Tasks assigned to the current user (the X-PS-Impersonate-User header, or the instance's default MCP user) across every journey: what they owe. Common calls: _filter \"status in ('ready','in_progress')\" for what can be done now; _filter \"is_overdue eq true\" for what is late; _filter \"due_date le 2026-11-17\" for everything due by a date; _orderby \"due_date asc\". Locked tasks show locked_reason and depends_on. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "query",
      "name": "status",
      "schema": {
        "type": "string",
        "enum": [
          "locked",
          "ready",
          "in_progress",
          "processing",
          "done",
          "skipped",
          "error"
        ]
      },
      "description": "Only this status: locked, ready, in_progress, processing, done, skipped, error"
    },
    {
      "in": "query",
      "name": "journey_id",
      "schema": {
        "type": "string"
      },
      "description": "Only tasks of this journey"
    },
    {
      "in": "query",
      "name": "kind",
      "schema": {
        "type": "string",
        "enum": [
          "todo",
          "form",
          "esign",
          "document",
          "content",
          "learning",
          "survey",
          "approval",
          "automated",
          "status_check",
          "meeting",
          "link"
        ]
      },
      "description": "Only this kind of step: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link"
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/JourneyTask"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query, or no current user for a /me route"
    }
  }
}

GET /me/journeys

List my journeys

Journeys where the current user is the subject (their own onboarding, leave, transfer), with progress, health and up_next. get-my-journey shows one of them as a checklist. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__list__my_journeys",
  "summary": "List my journeys",
  "description": "Journeys where the current user is the subject (their own onboarding, leave, transfer), with progress, health and up_next. get-my-journey shows one of them as a checklist. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "query",
      "name": "status",
      "schema": {
        "type": "string",
        "enum": [
          "draft",
          "active",
          "completed",
          "cancelled"
        ]
      },
      "description": "Only this status: draft, active, completed, cancelled"
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Journey"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query, or no current user for a /me route"
    }
  }
}

GET /me/journeys/{id}

Get one journey as the current user sees it

The current user's view of a journey (the X-PS-Impersonate-User header, or the instance's default MCP user): next_up (the soonest task they can act on), their tasks soonest due first, each with its status, due date, waiting_for (the unmet prerequisites: titles and who holds them) and unlocks_on for date-locked tasks, plus people (subject, manager, buddy with names) and counts. As of today: a phase that opened on the calendar since the journey was last written is opened first (its automations run). Works for whoever takes part: the subject sees their own checklist, the manager, buddy, HR or IT see what they owe on this person's journey. 404 when the current user has no part in it.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__get__my_journey",
  "summary": "Get one journey as the current user sees it",
  "description": "The current user's view of a journey (the X-PS-Impersonate-User header, or the instance's default MCP user): next_up (the soonest task they can act on), their tasks soonest due first, each with its status, due date, waiting_for (the unmet prerequisites: titles and who holds them) and unlocks_on for date-locked tasks, plus people (subject, manager, buddy with names) and counts. As of today: a phase that opened on the calendar since the journey was last written is opened first (its automations run). Works for whoever takes part: the subject sees their own checklist, the manager, buddy, HR or IT see what they owe on this person's journey. 404 when the current user has no part in it.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "path",
      "name": "id",
      "required": true,
      "schema": {
        "type": "string",
        "example": "JNY-0001"
      },
      "description": "Journey id"
    }
  ],
  "responses": {
    "200": {
      "description": "The journey as a checklist",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/MyJourney"
          }
        }
      }
    },
    "400": {
      "description": "No current user"
    },
    "404": {
      "description": "Journey not found, or the current user has no part in it"
    }
  }
}

GET /me/team/journeys

List my team's journeys

Journeys where the current user is the manager: their reports' onboarding, leaves and transfers, with health and what is up next, so a manager can see who is late. _filter "health eq 'warning'" for the ones with overdue tasks. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__list__team_journeys",
  "summary": "List my team's journeys",
  "description": "Journeys where the current user is the manager: their reports' onboarding, leaves and transfers, with health and what is up next, so a manager can see who is late. _filter \"health eq 'warning'\" for the ones with overdue tasks. Supports server-side querying with _filter/_search/_orderby/_select/_top/_skip/_count (OData $-params, renamed with a leading underscore for MCP).",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    },
    {
      "in": "query",
      "name": "status",
      "schema": {
        "type": "string",
        "enum": [
          "draft",
          "active",
          "completed",
          "cancelled"
        ]
      },
      "description": "Only this status: draft, active, completed, cancelled"
    },
    {
      "in": "query",
      "name": "health",
      "schema": {
        "type": "string",
        "enum": [
          "passing",
          "warning",
          "failing"
        ]
      },
      "description": "Only this health: passing, warning, failing"
    },
    {
      "in": "query",
      "name": "$filter",
      "schema": {
        "type": "string"
      },
      "description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
    },
    {
      "in": "query",
      "name": "$select",
      "schema": {
        "type": "string"
      },
      "description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
    },
    {
      "in": "query",
      "name": "$orderby",
      "schema": {
        "type": "string"
      },
      "description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
    },
    {
      "in": "query",
      "name": "$search",
      "schema": {
        "type": "string"
      },
      "description": "Free-text, case-insensitive substring match across the resource's searchable fields."
    },
    {
      "in": "query",
      "name": "$top",
      "schema": {
        "type": "integer"
      },
      "description": "Maximum number of items to return (page size)."
    },
    {
      "in": "query",
      "name": "$skip",
      "schema": {
        "type": "integer"
      },
      "description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
    },
    {
      "in": "query",
      "name": "$count",
      "schema": {
        "type": "boolean"
      },
      "description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
    }
  ],
  "responses": {
    "200": {
      "description": "Paginated list",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Journey"
                }
              },
              "nextCursor": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
              },
              "total": {
                "type": "integer",
                "description": "Total documents in the collection."
              },
              "@odata.count": {
                "type": "integer",
                "description": "Present only when $count=true: total matches after $filter/$search."
              },
              "@odata.truncated": {
                "type": "boolean",
                "description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "Invalid OData query, or no current user for a /me route"
    }
  }
}

POST /admin/seed-samples

Load the sample templates and journeys (admin)

Writes this app's sample world into the instance: the four published templates (new hire onboarding, offboarding, parental leave, internal transfer) and eight journeys in mixed states with their tasks and activity, the same data a new instance gets from POST /api/purple-suite/instances/{id}/seed, built from the instance's own roster. For an instance created before this app existed, or emptied since. Does nothing while any of this app's collections (templates, journeys, tasks, activity, start claims) holds a record (seeded false, with the current counts) unless force is true, which clears this app's collections first and writes the sample world again. Other apps' data is never touched. Templates are written last; a write that stops partway answers 500 and says to retry with force true. Instance owners only; not exposed as an MCP tool.

Authentication

[
  {
    "patAuth": []
  }
]

Request, responses and operation details

{
  "operationId": "journeys__seed__samples",
  "x-mcp": false,
  "summary": "Load the sample templates and journeys (admin)",
  "description": "Writes this app's sample world into the instance: the four published templates (new hire onboarding, offboarding, parental leave, internal transfer) and eight journeys in mixed states with their tasks and activity, the same data a new instance gets from POST /api/purple-suite/instances/{id}/seed, built from the instance's own roster. For an instance created before this app existed, or emptied since. Does nothing while any of this app's collections (templates, journeys, tasks, activity, start claims) holds a record (seeded false, with the current counts) unless force is true, which clears this app's collections first and writes the sample world again. Other apps' data is never touched. Templates are written last; a write that stops partway answers 500 and says to retry with force true. Instance owners only; not exposed as an MCP tool.",
  "parameters": [
    {
      "$ref": "#/components/parameters/InstanceId"
    }
  ],
  "requestBody": {
    "required": false,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/SeedSamplesRequest"
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "Whether the sample world was written, and the counts now in the instance",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/SeedSamplesResult"
          }
        }
      }
    },
    "403": {
      "description": "A viewer token: only the instance owner can load sample data"
    },
    "500": {
      "description": "A write stopped partway, so the instance holds part of the sample world; the message says to retry with force true"
    }
  }
}

Schemas and reusable components

securitySchemes

patAuth

{
  "type": "http",
  "scheme": "bearer"
}

parameters

InstanceId

{
  "in": "header",
  "name": "X-Instance-ID",
  "required": true,
  "schema": {
    "type": "string",
    "example": "inst_abc123"
  },
  "description": "The Purple Suite instance ID that scopes all data"
}

ImpersonateUser

{
  "in": "header",
  "name": "X-PS-Impersonate-User",
  "required": false,
  "schema": {
    "type": "string",
    "format": "email"
  },
  "description": "Optional. Act as a specific user in the target instance — the value is that user's email (must exist in the instance). Resolution order for the effective user: (1) this header, if set; (2) the instance's configured default MCP user; (3) otherwise no user is bound. User-scoped paths (e.g. /users/{email}/…) also accept the literal \"me\", and Graph-style apps serve /me/… twins of those paths (Graph: /me ≡ /users/{signed-in-user}) — both resolve to whatever this header (or the default MCP user) points at. An explicit path email always wins over the header. The unified MCP server does not send this header — it relies on the per-instance default — so it is primarily for direct REST/HTTP callers."
}

schemas

JourneyTemplate

{
  "type": "object",
  "description": "A reusable definition of a lifecycle event: phases, steps, roles and when things unlock and fall due.",
  "properties": {
    "id": {
      "type": "string",
      "readOnly": true,
      "example": "JT-0001"
    },
    "name": {
      "type": "string",
      "example": "New hire onboarding"
    },
    "journey_type": {
      "type": "string",
      "enum": [
        "onboarding",
        "offboarding",
        "job_change",
        "leave",
        "return_to_work",
        "relocation",
        "custom"
      ],
      "example": "onboarding",
      "description": "What kind of lifecycle event: onboarding (anchored on the start_date), offboarding (last_working_day), job_change (effective_date), leave (leave_start_date), return_to_work (return_date), relocation (effective_date) or custom (effective_date). Defaults to custom; any other value is stored as custom with a warning."
    },
    "description": {
      "type": "string",
      "example": "Everything between the signed offer and the 90-day review."
    },
    "anchor": {
      "type": "string",
      "enum": [
        "start_date",
        "last_working_day",
        "leave_start_date",
        "return_date",
        "effective_date"
      ],
      "example": "start_date",
      "description": "The date every offset hangs off. Defaults by journey_type (onboarding: start_date, offboarding: last_working_day, leave: leave_start_date, return_to_work: return_date, otherwise effective_date)."
    },
    "trigger": {
      "type": "object",
      "description": "How journeys of this template start. manual: someone starts them. hris_event: an HR event (hire, termination, job_change) with lead_days before the anchor. catalog_form: the subject submits a form. agent_chat: an assistant starts them.",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "manual",
            "hris_event",
            "catalog_form",
            "agent_chat"
          ],
          "example": "hris_event",
          "description": "One of manual, hris_event, catalog_form, agent_chat."
        },
        "event": {
          "type": "string",
          "nullable": true,
          "example": "hire"
        },
        "lead_days": {
          "type": "integer",
          "nullable": true,
          "example": 14
        }
      }
    },
    "phases": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "A phase groups steps and decides when they become available. Steps may be nested here (steps: [...]) or listed flat on the template with a phase key.",
        "required": [
          "name"
        ],
        "properties": {
          "key": {
            "type": "string",
            "example": "pre_start",
            "description": "Stable key; derived from the name when omitted."
          },
          "name": {
            "type": "string",
            "example": "Before start"
          },
          "order": {
            "type": "integer",
            "example": 1,
            "description": "Display and evaluation order; defaults to position."
          },
          "unlock": {
            "type": "object",
            "description": "When the phase opens. immediate: when the journey starts. offset: offset_days from the journey's anchor date (negative is before it). after_phase: when every task in after_phase is done or skipped. condition: when the phase before it is complete.",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "immediate",
                  "offset",
                  "after_phase",
                  "condition"
                ],
                "example": "offset",
                "description": "One of immediate, offset, after_phase, condition."
              },
              "offset_days": {
                "type": "integer",
                "example": -14,
                "description": "Days from the anchor date (offset unlocks)."
              },
              "after_phase": {
                "type": "string",
                "example": "pre_start",
                "description": "Phase key whose completion opens this phase (after_phase unlocks)."
              }
            }
          },
          "visible_to": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "subject",
                "manager",
                "buddy",
                "hr",
                "it",
                "facilities",
                "payroll",
                "security",
                "system"
              ]
            },
            "description": "Roles that see this phase (subject, manager, buddy, hr, it, facilities, payroll, security, system); empty means everyone."
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "One step of a template; every journey started from the template gets one task per step.",
              "required": [
                "title"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "example": "sign_paperwork",
                  "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
                },
                "phase": {
                  "type": "string",
                  "example": "pre_start",
                  "description": "Key of the phase the step belongs to (defaults to the first phase)."
                },
                "title": {
                  "type": "string",
                  "example": "Sign offer letter, NDA and policy acknowledgements"
                },
                "description": {
                  "type": "string",
                  "example": "Three documents in one signing session."
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "todo",
                    "form",
                    "esign",
                    "document",
                    "content",
                    "learning",
                    "survey",
                    "approval",
                    "automated",
                    "status_check",
                    "meeting",
                    "link"
                  ],
                  "example": "esign",
                  "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
                },
                "assignee_role": {
                  "type": "string",
                  "enum": [
                    "subject",
                    "manager",
                    "buddy",
                    "hr",
                    "it",
                    "facilities",
                    "payroll",
                    "security",
                    "system"
                  ],
                  "example": "manager",
                  "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
                },
                "due": {
                  "type": "object",
                  "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
                  "properties": {
                    "anchor": {
                      "type": "string",
                      "enum": [
                        "journey_anchor",
                        "phase_unlock",
                        "assignment"
                      ],
                      "example": "journey_anchor",
                      "description": "One of journey_anchor, phase_unlock, assignment."
                    },
                    "offset_days": {
                      "type": "integer",
                      "example": -7
                    }
                  }
                },
                "depends_on": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "example": [
                    "confirm_details"
                  ],
                  "description": "Step keys that must be done or skipped before this one unlocks."
                },
                "required": {
                  "type": "boolean",
                  "example": true,
                  "description": "Optional steps can be skipped without a warning."
                },
                "payload": {
                  "type": "object",
                  "nullable": true,
                  "additionalProperties": true,
                  "description": "Kind-specific detail (a form name, document ids, a course list)."
                },
                "system": {
                  "type": "string",
                  "enum": [
                    "identity",
                    "access",
                    "itsm",
                    "hris",
                    "lms",
                    "chat",
                    "calendar",
                    "pages",
                    "mail",
                    "expense",
                    "people"
                  ],
                  "example": "itsm",
                  "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
                },
                "form": {
                  "type": "object",
                  "nullable": true,
                  "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
                  "properties": {
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "description": "One field of a form step. Give a key or a label (the other is derived).",
                        "properties": {
                          "key": {
                            "type": "string",
                            "example": "legal_name",
                            "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                          },
                          "label": {
                            "type": "string",
                            "example": "Legal name"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "text",
                              "textarea",
                              "select",
                              "date",
                              "number",
                              "email",
                              "checkbox"
                            ],
                            "example": "text",
                            "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                          },
                          "required": {
                            "type": "boolean",
                            "example": true
                          },
                          "options": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "example": [
                              "14-inch",
                              "16-inch"
                            ],
                            "description": "The choices of a select field."
                          },
                          "placeholder": {
                            "type": "string",
                            "nullable": true,
                            "example": "As it appears on your ID"
                          }
                        }
                      }
                    },
                    "submit_label": {
                      "type": "string",
                      "nullable": true,
                      "example": "Save details",
                      "description": "Label for the submit control; the UI defaults it."
                    }
                  }
                },
                "link": {
                  "type": "object",
                  "nullable": true,
                  "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
                  "properties": {
                    "url": {
                      "type": "string",
                      "example": "https://intranet.example.com/first-day"
                    },
                    "label": {
                      "type": "string",
                      "example": "Open the first-day agenda",
                      "description": "Defaults to the step title."
                    }
                  }
                },
                "promoted_from": {
                  "type": "string",
                  "nullable": true,
                  "example": "set_up_vpn_access",
                  "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
                }
              }
            },
            "description": "Steps of this phase, as an alternative to listing them flat with a phase key. Stored flat either way."
          }
        }
      }
    },
    "steps": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "One step of a template; every journey started from the template gets one task per step.",
        "required": [
          "title"
        ],
        "properties": {
          "key": {
            "type": "string",
            "example": "sign_paperwork",
            "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
          },
          "phase": {
            "type": "string",
            "example": "pre_start",
            "description": "Key of the phase the step belongs to (defaults to the first phase)."
          },
          "title": {
            "type": "string",
            "example": "Sign offer letter, NDA and policy acknowledgements"
          },
          "description": {
            "type": "string",
            "example": "Three documents in one signing session."
          },
          "kind": {
            "type": "string",
            "enum": [
              "todo",
              "form",
              "esign",
              "document",
              "content",
              "learning",
              "survey",
              "approval",
              "automated",
              "status_check",
              "meeting",
              "link"
            ],
            "example": "esign",
            "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
          },
          "assignee_role": {
            "type": "string",
            "enum": [
              "subject",
              "manager",
              "buddy",
              "hr",
              "it",
              "facilities",
              "payroll",
              "security",
              "system"
            ],
            "example": "manager",
            "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
          },
          "due": {
            "type": "object",
            "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
            "properties": {
              "anchor": {
                "type": "string",
                "enum": [
                  "journey_anchor",
                  "phase_unlock",
                  "assignment"
                ],
                "example": "journey_anchor",
                "description": "One of journey_anchor, phase_unlock, assignment."
              },
              "offset_days": {
                "type": "integer",
                "example": -7
              }
            }
          },
          "depends_on": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "confirm_details"
            ],
            "description": "Step keys that must be done or skipped before this one unlocks."
          },
          "required": {
            "type": "boolean",
            "example": true,
            "description": "Optional steps can be skipped without a warning."
          },
          "payload": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "Kind-specific detail (a form name, document ids, a course list)."
          },
          "system": {
            "type": "string",
            "enum": [
              "identity",
              "access",
              "itsm",
              "hris",
              "lms",
              "chat",
              "calendar",
              "pages",
              "mail",
              "expense",
              "people"
            ],
            "example": "itsm",
            "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
          },
          "form": {
            "type": "object",
            "nullable": true,
            "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
            "properties": {
              "fields": {
                "type": "array",
                "items": {
                  "type": "object",
                  "description": "One field of a form step. Give a key or a label (the other is derived).",
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "legal_name",
                      "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                    },
                    "label": {
                      "type": "string",
                      "example": "Legal name"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "text",
                        "textarea",
                        "select",
                        "date",
                        "number",
                        "email",
                        "checkbox"
                      ],
                      "example": "text",
                      "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                    },
                    "required": {
                      "type": "boolean",
                      "example": true
                    },
                    "options": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "14-inch",
                        "16-inch"
                      ],
                      "description": "The choices of a select field."
                    },
                    "placeholder": {
                      "type": "string",
                      "nullable": true,
                      "example": "As it appears on your ID"
                    }
                  }
                }
              },
              "submit_label": {
                "type": "string",
                "nullable": true,
                "example": "Save details",
                "description": "Label for the submit control; the UI defaults it."
              }
            }
          },
          "link": {
            "type": "object",
            "nullable": true,
            "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
            "properties": {
              "url": {
                "type": "string",
                "example": "https://intranet.example.com/first-day"
              },
              "label": {
                "type": "string",
                "example": "Open the first-day agenda",
                "description": "Defaults to the step title."
              }
            }
          },
          "promoted_from": {
            "type": "string",
            "nullable": true,
            "example": "set_up_vpn_access",
            "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
          }
        }
      },
      "description": "Flat list of steps, each naming its phase. Steps nested under phases are accepted too."
    },
    "status": {
      "type": "string",
      "enum": [
        "draft",
        "published",
        "retired"
      ],
      "example": "published",
      "description": "draft, published or retired. Only published templates can start journeys. Set with publish-templates; retire with update."
    },
    "version": {
      "type": "integer",
      "readOnly": true,
      "example": 2,
      "description": "Bumped by publish-templates when a published template changed."
    },
    "roles": {
      "type": "array",
      "readOnly": true,
      "items": {
        "type": "string"
      },
      "example": [
        "subject",
        "manager",
        "buddy",
        "hr",
        "it",
        "system"
      ],
      "description": "Every assignee_role the steps use, in display order."
    },
    "counts": {
      "type": "object",
      "readOnly": true,
      "properties": {
        "phases": {
          "type": "integer",
          "example": 5
        },
        "steps": {
          "type": "integer",
          "example": 29
        },
        "by_role": {
          "type": "object",
          "additionalProperties": {
            "type": "integer"
          },
          "example": {
            "subject": 14,
            "manager": 8
          }
        }
      }
    },
    "warnings": {
      "type": "array",
      "readOnly": true,
      "items": {
        "type": "string"
      },
      "description": "What normalization repaired or doubts about the design (a dropped dependency, a step due before its phase opens). Empty when clean."
    },
    "has_unpublished_changes": {
      "type": "boolean",
      "readOnly": true,
      "example": false,
      "description": "True when a published template was edited since its last publish. New journeys keep starting from the published version (published_snapshot) until it is published again."
    },
    "published_snapshot": {
      "type": "object",
      "nullable": true,
      "readOnly": true,
      "description": "The phases and steps as last published, kept while has_unpublished_changes is true: what start-journeys uses until the edit is published. Null otherwise.",
      "properties": {
        "phases": {
          "type": "array",
          "items": {
            "type": "object",
            "description": "A phase groups steps and decides when they become available. Steps may be nested here (steps: [...]) or listed flat on the template with a phase key.",
            "required": [
              "name"
            ],
            "properties": {
              "key": {
                "type": "string",
                "example": "pre_start",
                "description": "Stable key; derived from the name when omitted."
              },
              "name": {
                "type": "string",
                "example": "Before start"
              },
              "order": {
                "type": "integer",
                "example": 1,
                "description": "Display and evaluation order; defaults to position."
              },
              "unlock": {
                "type": "object",
                "description": "When the phase opens. immediate: when the journey starts. offset: offset_days from the journey's anchor date (negative is before it). after_phase: when every task in after_phase is done or skipped. condition: when the phase before it is complete.",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "immediate",
                      "offset",
                      "after_phase",
                      "condition"
                    ],
                    "example": "offset",
                    "description": "One of immediate, offset, after_phase, condition."
                  },
                  "offset_days": {
                    "type": "integer",
                    "example": -14,
                    "description": "Days from the anchor date (offset unlocks)."
                  },
                  "after_phase": {
                    "type": "string",
                    "example": "pre_start",
                    "description": "Phase key whose completion opens this phase (after_phase unlocks)."
                  }
                }
              },
              "visible_to": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "subject",
                    "manager",
                    "buddy",
                    "hr",
                    "it",
                    "facilities",
                    "payroll",
                    "security",
                    "system"
                  ]
                },
                "description": "Roles that see this phase (subject, manager, buddy, hr, it, facilities, payroll, security, system); empty means everyone."
              },
              "steps": {
                "type": "array",
                "items": {
                  "type": "object",
                  "description": "One step of a template; every journey started from the template gets one task per step.",
                  "required": [
                    "title"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "sign_paperwork",
                      "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
                    },
                    "phase": {
                      "type": "string",
                      "example": "pre_start",
                      "description": "Key of the phase the step belongs to (defaults to the first phase)."
                    },
                    "title": {
                      "type": "string",
                      "example": "Sign offer letter, NDA and policy acknowledgements"
                    },
                    "description": {
                      "type": "string",
                      "example": "Three documents in one signing session."
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "todo",
                        "form",
                        "esign",
                        "document",
                        "content",
                        "learning",
                        "survey",
                        "approval",
                        "automated",
                        "status_check",
                        "meeting",
                        "link"
                      ],
                      "example": "esign",
                      "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
                    },
                    "assignee_role": {
                      "type": "string",
                      "enum": [
                        "subject",
                        "manager",
                        "buddy",
                        "hr",
                        "it",
                        "facilities",
                        "payroll",
                        "security",
                        "system"
                      ],
                      "example": "manager",
                      "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
                    },
                    "due": {
                      "type": "object",
                      "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
                      "properties": {
                        "anchor": {
                          "type": "string",
                          "enum": [
                            "journey_anchor",
                            "phase_unlock",
                            "assignment"
                          ],
                          "example": "journey_anchor",
                          "description": "One of journey_anchor, phase_unlock, assignment."
                        },
                        "offset_days": {
                          "type": "integer",
                          "example": -7
                        }
                      }
                    },
                    "depends_on": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "confirm_details"
                      ],
                      "description": "Step keys that must be done or skipped before this one unlocks."
                    },
                    "required": {
                      "type": "boolean",
                      "example": true,
                      "description": "Optional steps can be skipped without a warning."
                    },
                    "payload": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Kind-specific detail (a form name, document ids, a course list)."
                    },
                    "system": {
                      "type": "string",
                      "enum": [
                        "identity",
                        "access",
                        "itsm",
                        "hris",
                        "lms",
                        "chat",
                        "calendar",
                        "pages",
                        "mail",
                        "expense",
                        "people"
                      ],
                      "example": "itsm",
                      "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
                    },
                    "form": {
                      "type": "object",
                      "nullable": true,
                      "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
                      "properties": {
                        "fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "description": "One field of a form step. Give a key or a label (the other is derived).",
                            "properties": {
                              "key": {
                                "type": "string",
                                "example": "legal_name",
                                "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                              },
                              "label": {
                                "type": "string",
                                "example": "Legal name"
                              },
                              "type": {
                                "type": "string",
                                "enum": [
                                  "text",
                                  "textarea",
                                  "select",
                                  "date",
                                  "number",
                                  "email",
                                  "checkbox"
                                ],
                                "example": "text",
                                "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                              },
                              "required": {
                                "type": "boolean",
                                "example": true
                              },
                              "options": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                },
                                "example": [
                                  "14-inch",
                                  "16-inch"
                                ],
                                "description": "The choices of a select field."
                              },
                              "placeholder": {
                                "type": "string",
                                "nullable": true,
                                "example": "As it appears on your ID"
                              }
                            }
                          }
                        },
                        "submit_label": {
                          "type": "string",
                          "nullable": true,
                          "example": "Save details",
                          "description": "Label for the submit control; the UI defaults it."
                        }
                      }
                    },
                    "link": {
                      "type": "object",
                      "nullable": true,
                      "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
                      "properties": {
                        "url": {
                          "type": "string",
                          "example": "https://intranet.example.com/first-day"
                        },
                        "label": {
                          "type": "string",
                          "example": "Open the first-day agenda",
                          "description": "Defaults to the step title."
                        }
                      }
                    },
                    "promoted_from": {
                      "type": "string",
                      "nullable": true,
                      "example": "set_up_vpn_access",
                      "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
                    }
                  }
                },
                "description": "Steps of this phase, as an alternative to listing them flat with a phase key. Stored flat either way."
              }
            }
          }
        },
        "steps": {
          "type": "array",
          "items": {
            "type": "object",
            "description": "One step of a template; every journey started from the template gets one task per step.",
            "required": [
              "title"
            ],
            "properties": {
              "key": {
                "type": "string",
                "example": "sign_paperwork",
                "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
              },
              "phase": {
                "type": "string",
                "example": "pre_start",
                "description": "Key of the phase the step belongs to (defaults to the first phase)."
              },
              "title": {
                "type": "string",
                "example": "Sign offer letter, NDA and policy acknowledgements"
              },
              "description": {
                "type": "string",
                "example": "Three documents in one signing session."
              },
              "kind": {
                "type": "string",
                "enum": [
                  "todo",
                  "form",
                  "esign",
                  "document",
                  "content",
                  "learning",
                  "survey",
                  "approval",
                  "automated",
                  "status_check",
                  "meeting",
                  "link"
                ],
                "example": "esign",
                "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
              },
              "assignee_role": {
                "type": "string",
                "enum": [
                  "subject",
                  "manager",
                  "buddy",
                  "hr",
                  "it",
                  "facilities",
                  "payroll",
                  "security",
                  "system"
                ],
                "example": "manager",
                "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
              },
              "due": {
                "type": "object",
                "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
                "properties": {
                  "anchor": {
                    "type": "string",
                    "enum": [
                      "journey_anchor",
                      "phase_unlock",
                      "assignment"
                    ],
                    "example": "journey_anchor",
                    "description": "One of journey_anchor, phase_unlock, assignment."
                  },
                  "offset_days": {
                    "type": "integer",
                    "example": -7
                  }
                }
              },
              "depends_on": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "confirm_details"
                ],
                "description": "Step keys that must be done or skipped before this one unlocks."
              },
              "required": {
                "type": "boolean",
                "example": true,
                "description": "Optional steps can be skipped without a warning."
              },
              "payload": {
                "type": "object",
                "nullable": true,
                "additionalProperties": true,
                "description": "Kind-specific detail (a form name, document ids, a course list)."
              },
              "system": {
                "type": "string",
                "enum": [
                  "identity",
                  "access",
                  "itsm",
                  "hris",
                  "lms",
                  "chat",
                  "calendar",
                  "pages",
                  "mail",
                  "expense",
                  "people"
                ],
                "example": "itsm",
                "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
              },
              "form": {
                "type": "object",
                "nullable": true,
                "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
                "properties": {
                  "fields": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "description": "One field of a form step. Give a key or a label (the other is derived).",
                      "properties": {
                        "key": {
                          "type": "string",
                          "example": "legal_name",
                          "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                        },
                        "label": {
                          "type": "string",
                          "example": "Legal name"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "text",
                            "textarea",
                            "select",
                            "date",
                            "number",
                            "email",
                            "checkbox"
                          ],
                          "example": "text",
                          "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                        },
                        "required": {
                          "type": "boolean",
                          "example": true
                        },
                        "options": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "example": [
                            "14-inch",
                            "16-inch"
                          ],
                          "description": "The choices of a select field."
                        },
                        "placeholder": {
                          "type": "string",
                          "nullable": true,
                          "example": "As it appears on your ID"
                        }
                      }
                    }
                  },
                  "submit_label": {
                    "type": "string",
                    "nullable": true,
                    "example": "Save details",
                    "description": "Label for the submit control; the UI defaults it."
                  }
                }
              },
              "link": {
                "type": "object",
                "nullable": true,
                "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
                "properties": {
                  "url": {
                    "type": "string",
                    "example": "https://intranet.example.com/first-day"
                  },
                  "label": {
                    "type": "string",
                    "example": "Open the first-day agenda",
                    "description": "Defaults to the step title."
                  }
                }
              },
              "promoted_from": {
                "type": "string",
                "nullable": true,
                "example": "set_up_vpn_access",
                "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
              }
            }
          }
        }
      }
    },
    "published_at": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "readOnly": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    }
  }
}

TemplateSpec

{
  "type": "object",
  "description": "A whole template in one call. Phases and steps may be omitted and added later with add-steps; a template with no phases gets one called main.",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "type": "string",
      "example": "New hire onboarding"
    },
    "journey_type": {
      "type": "string",
      "enum": [
        "onboarding",
        "offboarding",
        "job_change",
        "leave",
        "return_to_work",
        "relocation",
        "custom"
      ],
      "example": "onboarding",
      "description": "What kind of lifecycle event: onboarding (anchored on the start_date), offboarding (last_working_day), job_change (effective_date), leave (leave_start_date), return_to_work (return_date), relocation (effective_date) or custom (effective_date). Defaults to custom; any other value is stored as custom with a warning."
    },
    "description": {
      "type": "string",
      "example": "Everything between the signed offer and the 90-day review."
    },
    "anchor": {
      "type": "string",
      "enum": [
        "start_date",
        "last_working_day",
        "leave_start_date",
        "return_date",
        "effective_date"
      ],
      "example": "start_date",
      "description": "The date every offset hangs off. Defaults by journey_type (onboarding: start_date, offboarding: last_working_day, leave: leave_start_date, return_to_work: return_date, otherwise effective_date)."
    },
    "trigger": {
      "type": "object",
      "description": "How journeys of this template start. manual: someone starts them. hris_event: an HR event (hire, termination, job_change) with lead_days before the anchor. catalog_form: the subject submits a form. agent_chat: an assistant starts them.",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "manual",
            "hris_event",
            "catalog_form",
            "agent_chat"
          ],
          "example": "hris_event",
          "description": "One of manual, hris_event, catalog_form, agent_chat."
        },
        "event": {
          "type": "string",
          "nullable": true,
          "example": "hire"
        },
        "lead_days": {
          "type": "integer",
          "nullable": true,
          "example": 14
        }
      }
    },
    "phases": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "A phase groups steps and decides when they become available. Steps may be nested here (steps: [...]) or listed flat on the template with a phase key.",
        "required": [
          "name"
        ],
        "properties": {
          "key": {
            "type": "string",
            "example": "pre_start",
            "description": "Stable key; derived from the name when omitted."
          },
          "name": {
            "type": "string",
            "example": "Before start"
          },
          "order": {
            "type": "integer",
            "example": 1,
            "description": "Display and evaluation order; defaults to position."
          },
          "unlock": {
            "type": "object",
            "description": "When the phase opens. immediate: when the journey starts. offset: offset_days from the journey's anchor date (negative is before it). after_phase: when every task in after_phase is done or skipped. condition: when the phase before it is complete.",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "immediate",
                  "offset",
                  "after_phase",
                  "condition"
                ],
                "example": "offset",
                "description": "One of immediate, offset, after_phase, condition."
              },
              "offset_days": {
                "type": "integer",
                "example": -14,
                "description": "Days from the anchor date (offset unlocks)."
              },
              "after_phase": {
                "type": "string",
                "example": "pre_start",
                "description": "Phase key whose completion opens this phase (after_phase unlocks)."
              }
            }
          },
          "visible_to": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "subject",
                "manager",
                "buddy",
                "hr",
                "it",
                "facilities",
                "payroll",
                "security",
                "system"
              ]
            },
            "description": "Roles that see this phase (subject, manager, buddy, hr, it, facilities, payroll, security, system); empty means everyone."
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "One step of a template; every journey started from the template gets one task per step.",
              "required": [
                "title"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "example": "sign_paperwork",
                  "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
                },
                "phase": {
                  "type": "string",
                  "example": "pre_start",
                  "description": "Key of the phase the step belongs to (defaults to the first phase)."
                },
                "title": {
                  "type": "string",
                  "example": "Sign offer letter, NDA and policy acknowledgements"
                },
                "description": {
                  "type": "string",
                  "example": "Three documents in one signing session."
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "todo",
                    "form",
                    "esign",
                    "document",
                    "content",
                    "learning",
                    "survey",
                    "approval",
                    "automated",
                    "status_check",
                    "meeting",
                    "link"
                  ],
                  "example": "esign",
                  "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
                },
                "assignee_role": {
                  "type": "string",
                  "enum": [
                    "subject",
                    "manager",
                    "buddy",
                    "hr",
                    "it",
                    "facilities",
                    "payroll",
                    "security",
                    "system"
                  ],
                  "example": "manager",
                  "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
                },
                "due": {
                  "type": "object",
                  "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
                  "properties": {
                    "anchor": {
                      "type": "string",
                      "enum": [
                        "journey_anchor",
                        "phase_unlock",
                        "assignment"
                      ],
                      "example": "journey_anchor",
                      "description": "One of journey_anchor, phase_unlock, assignment."
                    },
                    "offset_days": {
                      "type": "integer",
                      "example": -7
                    }
                  }
                },
                "depends_on": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "example": [
                    "confirm_details"
                  ],
                  "description": "Step keys that must be done or skipped before this one unlocks."
                },
                "required": {
                  "type": "boolean",
                  "example": true,
                  "description": "Optional steps can be skipped without a warning."
                },
                "payload": {
                  "type": "object",
                  "nullable": true,
                  "additionalProperties": true,
                  "description": "Kind-specific detail (a form name, document ids, a course list)."
                },
                "system": {
                  "type": "string",
                  "enum": [
                    "identity",
                    "access",
                    "itsm",
                    "hris",
                    "lms",
                    "chat",
                    "calendar",
                    "pages",
                    "mail",
                    "expense",
                    "people"
                  ],
                  "example": "itsm",
                  "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
                },
                "form": {
                  "type": "object",
                  "nullable": true,
                  "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
                  "properties": {
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "description": "One field of a form step. Give a key or a label (the other is derived).",
                        "properties": {
                          "key": {
                            "type": "string",
                            "example": "legal_name",
                            "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                          },
                          "label": {
                            "type": "string",
                            "example": "Legal name"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "text",
                              "textarea",
                              "select",
                              "date",
                              "number",
                              "email",
                              "checkbox"
                            ],
                            "example": "text",
                            "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                          },
                          "required": {
                            "type": "boolean",
                            "example": true
                          },
                          "options": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "example": [
                              "14-inch",
                              "16-inch"
                            ],
                            "description": "The choices of a select field."
                          },
                          "placeholder": {
                            "type": "string",
                            "nullable": true,
                            "example": "As it appears on your ID"
                          }
                        }
                      }
                    },
                    "submit_label": {
                      "type": "string",
                      "nullable": true,
                      "example": "Save details",
                      "description": "Label for the submit control; the UI defaults it."
                    }
                  }
                },
                "link": {
                  "type": "object",
                  "nullable": true,
                  "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
                  "properties": {
                    "url": {
                      "type": "string",
                      "example": "https://intranet.example.com/first-day"
                    },
                    "label": {
                      "type": "string",
                      "example": "Open the first-day agenda",
                      "description": "Defaults to the step title."
                    }
                  }
                },
                "promoted_from": {
                  "type": "string",
                  "nullable": true,
                  "example": "set_up_vpn_access",
                  "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
                }
              }
            },
            "description": "Steps of this phase, as an alternative to listing them flat with a phase key. Stored flat either way."
          }
        }
      }
    },
    "steps": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "One step of a template; every journey started from the template gets one task per step.",
        "required": [
          "title"
        ],
        "properties": {
          "key": {
            "type": "string",
            "example": "sign_paperwork",
            "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
          },
          "phase": {
            "type": "string",
            "example": "pre_start",
            "description": "Key of the phase the step belongs to (defaults to the first phase)."
          },
          "title": {
            "type": "string",
            "example": "Sign offer letter, NDA and policy acknowledgements"
          },
          "description": {
            "type": "string",
            "example": "Three documents in one signing session."
          },
          "kind": {
            "type": "string",
            "enum": [
              "todo",
              "form",
              "esign",
              "document",
              "content",
              "learning",
              "survey",
              "approval",
              "automated",
              "status_check",
              "meeting",
              "link"
            ],
            "example": "esign",
            "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
          },
          "assignee_role": {
            "type": "string",
            "enum": [
              "subject",
              "manager",
              "buddy",
              "hr",
              "it",
              "facilities",
              "payroll",
              "security",
              "system"
            ],
            "example": "manager",
            "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
          },
          "due": {
            "type": "object",
            "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
            "properties": {
              "anchor": {
                "type": "string",
                "enum": [
                  "journey_anchor",
                  "phase_unlock",
                  "assignment"
                ],
                "example": "journey_anchor",
                "description": "One of journey_anchor, phase_unlock, assignment."
              },
              "offset_days": {
                "type": "integer",
                "example": -7
              }
            }
          },
          "depends_on": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "confirm_details"
            ],
            "description": "Step keys that must be done or skipped before this one unlocks."
          },
          "required": {
            "type": "boolean",
            "example": true,
            "description": "Optional steps can be skipped without a warning."
          },
          "payload": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "Kind-specific detail (a form name, document ids, a course list)."
          },
          "system": {
            "type": "string",
            "enum": [
              "identity",
              "access",
              "itsm",
              "hris",
              "lms",
              "chat",
              "calendar",
              "pages",
              "mail",
              "expense",
              "people"
            ],
            "example": "itsm",
            "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
          },
          "form": {
            "type": "object",
            "nullable": true,
            "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
            "properties": {
              "fields": {
                "type": "array",
                "items": {
                  "type": "object",
                  "description": "One field of a form step. Give a key or a label (the other is derived).",
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "legal_name",
                      "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                    },
                    "label": {
                      "type": "string",
                      "example": "Legal name"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "text",
                        "textarea",
                        "select",
                        "date",
                        "number",
                        "email",
                        "checkbox"
                      ],
                      "example": "text",
                      "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                    },
                    "required": {
                      "type": "boolean",
                      "example": true
                    },
                    "options": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "14-inch",
                        "16-inch"
                      ],
                      "description": "The choices of a select field."
                    },
                    "placeholder": {
                      "type": "string",
                      "nullable": true,
                      "example": "As it appears on your ID"
                    }
                  }
                }
              },
              "submit_label": {
                "type": "string",
                "nullable": true,
                "example": "Save details",
                "description": "Label for the submit control; the UI defaults it."
              }
            }
          },
          "link": {
            "type": "object",
            "nullable": true,
            "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
            "properties": {
              "url": {
                "type": "string",
                "example": "https://intranet.example.com/first-day"
              },
              "label": {
                "type": "string",
                "example": "Open the first-day agenda",
                "description": "Defaults to the step title."
              }
            }
          },
          "promoted_from": {
            "type": "string",
            "nullable": true,
            "example": "set_up_vpn_access",
            "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
          }
        }
      },
      "description": "Flat list of steps, each naming its phase. Steps nested under phases are accepted too."
    }
  }
}

StepsUpsertRequest

{
  "type": "object",
  "description": "Steps to add or change, by key. At least one step must be given, either here or nested under phases[].steps.",
  "properties": {
    "steps": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "A step to add or change, named by key. For a key the template already has, only the fields given change (title is not required); a new key adds a step (title required) to the phase named, or the first phase.",
        "required": [
          "key"
        ],
        "properties": {
          "key": {
            "type": "string",
            "example": "sign_paperwork",
            "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
          },
          "phase": {
            "type": "string",
            "example": "pre_start",
            "description": "Key of the phase the step belongs to (defaults to the first phase)."
          },
          "title": {
            "type": "string",
            "example": "Sign offer letter, NDA and policy acknowledgements"
          },
          "description": {
            "type": "string",
            "example": "Three documents in one signing session."
          },
          "kind": {
            "type": "string",
            "enum": [
              "todo",
              "form",
              "esign",
              "document",
              "content",
              "learning",
              "survey",
              "approval",
              "automated",
              "status_check",
              "meeting",
              "link"
            ],
            "example": "esign",
            "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
          },
          "assignee_role": {
            "type": "string",
            "enum": [
              "subject",
              "manager",
              "buddy",
              "hr",
              "it",
              "facilities",
              "payroll",
              "security",
              "system"
            ],
            "example": "manager",
            "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
          },
          "due": {
            "type": "object",
            "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
            "properties": {
              "anchor": {
                "type": "string",
                "enum": [
                  "journey_anchor",
                  "phase_unlock",
                  "assignment"
                ],
                "example": "journey_anchor",
                "description": "One of journey_anchor, phase_unlock, assignment."
              },
              "offset_days": {
                "type": "integer",
                "example": -7
              }
            }
          },
          "depends_on": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "confirm_details"
            ],
            "description": "Step keys that must be done or skipped before this one unlocks."
          },
          "required": {
            "type": "boolean",
            "example": true,
            "description": "Optional steps can be skipped without a warning."
          },
          "payload": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "Kind-specific detail (a form name, document ids, a course list)."
          },
          "system": {
            "type": "string",
            "enum": [
              "identity",
              "access",
              "itsm",
              "hris",
              "lms",
              "chat",
              "calendar",
              "pages",
              "mail",
              "expense",
              "people"
            ],
            "example": "itsm",
            "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
          },
          "form": {
            "type": "object",
            "nullable": true,
            "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
            "properties": {
              "fields": {
                "type": "array",
                "items": {
                  "type": "object",
                  "description": "One field of a form step. Give a key or a label (the other is derived).",
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "legal_name",
                      "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                    },
                    "label": {
                      "type": "string",
                      "example": "Legal name"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "text",
                        "textarea",
                        "select",
                        "date",
                        "number",
                        "email",
                        "checkbox"
                      ],
                      "example": "text",
                      "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                    },
                    "required": {
                      "type": "boolean",
                      "example": true
                    },
                    "options": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "14-inch",
                        "16-inch"
                      ],
                      "description": "The choices of a select field."
                    },
                    "placeholder": {
                      "type": "string",
                      "nullable": true,
                      "example": "As it appears on your ID"
                    }
                  }
                }
              },
              "submit_label": {
                "type": "string",
                "nullable": true,
                "example": "Save details",
                "description": "Label for the submit control; the UI defaults it."
              }
            }
          },
          "link": {
            "type": "object",
            "nullable": true,
            "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
            "properties": {
              "url": {
                "type": "string",
                "example": "https://intranet.example.com/first-day"
              },
              "label": {
                "type": "string",
                "example": "Open the first-day agenda",
                "description": "Defaults to the step title."
              }
            }
          },
          "promoted_from": {
            "type": "string",
            "nullable": true,
            "example": "set_up_vpn_access",
            "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
          }
        }
      },
      "description": "Steps to add or change. A step whose key already exists is patched with the fields given (title is not required for a patch); any other key is added (title required)."
    },
    "phases": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "A phase to add or change, named by key. For a key the template already has, only the fields given change (name is not required); a new key adds a phase (name required). Steps nested under it are added to it.",
        "required": [
          "key"
        ],
        "properties": {
          "key": {
            "type": "string",
            "example": "pre_start",
            "description": "Stable key; derived from the name when omitted."
          },
          "name": {
            "type": "string",
            "example": "Before start"
          },
          "order": {
            "type": "integer",
            "example": 1,
            "description": "Display and evaluation order; defaults to position."
          },
          "unlock": {
            "type": "object",
            "description": "When the phase opens. immediate: when the journey starts. offset: offset_days from the journey's anchor date (negative is before it). after_phase: when every task in after_phase is done or skipped. condition: when the phase before it is complete.",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "immediate",
                  "offset",
                  "after_phase",
                  "condition"
                ],
                "example": "offset",
                "description": "One of immediate, offset, after_phase, condition."
              },
              "offset_days": {
                "type": "integer",
                "example": -14,
                "description": "Days from the anchor date (offset unlocks)."
              },
              "after_phase": {
                "type": "string",
                "example": "pre_start",
                "description": "Phase key whose completion opens this phase (after_phase unlocks)."
              }
            }
          },
          "visible_to": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "subject",
                "manager",
                "buddy",
                "hr",
                "it",
                "facilities",
                "payroll",
                "security",
                "system"
              ]
            },
            "description": "Roles that see this phase (subject, manager, buddy, hr, it, facilities, payroll, security, system); empty means everyone."
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "One step of a template; every journey started from the template gets one task per step.",
              "required": [
                "title"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "example": "sign_paperwork",
                  "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
                },
                "phase": {
                  "type": "string",
                  "example": "pre_start",
                  "description": "Key of the phase the step belongs to (defaults to the first phase)."
                },
                "title": {
                  "type": "string",
                  "example": "Sign offer letter, NDA and policy acknowledgements"
                },
                "description": {
                  "type": "string",
                  "example": "Three documents in one signing session."
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "todo",
                    "form",
                    "esign",
                    "document",
                    "content",
                    "learning",
                    "survey",
                    "approval",
                    "automated",
                    "status_check",
                    "meeting",
                    "link"
                  ],
                  "example": "esign",
                  "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
                },
                "assignee_role": {
                  "type": "string",
                  "enum": [
                    "subject",
                    "manager",
                    "buddy",
                    "hr",
                    "it",
                    "facilities",
                    "payroll",
                    "security",
                    "system"
                  ],
                  "example": "manager",
                  "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
                },
                "due": {
                  "type": "object",
                  "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
                  "properties": {
                    "anchor": {
                      "type": "string",
                      "enum": [
                        "journey_anchor",
                        "phase_unlock",
                        "assignment"
                      ],
                      "example": "journey_anchor",
                      "description": "One of journey_anchor, phase_unlock, assignment."
                    },
                    "offset_days": {
                      "type": "integer",
                      "example": -7
                    }
                  }
                },
                "depends_on": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "example": [
                    "confirm_details"
                  ],
                  "description": "Step keys that must be done or skipped before this one unlocks."
                },
                "required": {
                  "type": "boolean",
                  "example": true,
                  "description": "Optional steps can be skipped without a warning."
                },
                "payload": {
                  "type": "object",
                  "nullable": true,
                  "additionalProperties": true,
                  "description": "Kind-specific detail (a form name, document ids, a course list)."
                },
                "system": {
                  "type": "string",
                  "enum": [
                    "identity",
                    "access",
                    "itsm",
                    "hris",
                    "lms",
                    "chat",
                    "calendar",
                    "pages",
                    "mail",
                    "expense",
                    "people"
                  ],
                  "example": "itsm",
                  "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
                },
                "form": {
                  "type": "object",
                  "nullable": true,
                  "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
                  "properties": {
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "description": "One field of a form step. Give a key or a label (the other is derived).",
                        "properties": {
                          "key": {
                            "type": "string",
                            "example": "legal_name",
                            "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                          },
                          "label": {
                            "type": "string",
                            "example": "Legal name"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "text",
                              "textarea",
                              "select",
                              "date",
                              "number",
                              "email",
                              "checkbox"
                            ],
                            "example": "text",
                            "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                          },
                          "required": {
                            "type": "boolean",
                            "example": true
                          },
                          "options": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "example": [
                              "14-inch",
                              "16-inch"
                            ],
                            "description": "The choices of a select field."
                          },
                          "placeholder": {
                            "type": "string",
                            "nullable": true,
                            "example": "As it appears on your ID"
                          }
                        }
                      }
                    },
                    "submit_label": {
                      "type": "string",
                      "nullable": true,
                      "example": "Save details",
                      "description": "Label for the submit control; the UI defaults it."
                    }
                  }
                },
                "link": {
                  "type": "object",
                  "nullable": true,
                  "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
                  "properties": {
                    "url": {
                      "type": "string",
                      "example": "https://intranet.example.com/first-day"
                    },
                    "label": {
                      "type": "string",
                      "example": "Open the first-day agenda",
                      "description": "Defaults to the step title."
                    }
                  }
                },
                "promoted_from": {
                  "type": "string",
                  "nullable": true,
                  "example": "set_up_vpn_access",
                  "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
                }
              }
            },
            "description": "Steps of this phase, as an alternative to listing them flat with a phase key. Stored flat either way."
          }
        }
      },
      "description": "Phases to add or change at the same time, matched by key. Steps nested under a phase (steps: [...]) are added to that phase."
    }
  }
}

StepsUpsertResult

{
  "allOf": [
    {
      "$ref": "#/components/schemas/JourneyTemplate"
    },
    {
      "type": "object",
      "properties": {
        "created": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Keys of the steps added"
        },
        "updated": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Keys of the steps changed"
        }
      }
    }
  ]
}

PreviewRequest

{
  "type": "object",
  "properties": {
    "anchor_date": {
      "type": "string",
      "format": "date",
      "example": "2026-11-17",
      "description": "Date the journey would be anchored on; when given every row carries a due_date."
    },
    "as_role": {
      "type": "string",
      "example": "manager",
      "description": "Only the rows for this role (subject, manager, buddy, hr, it, facilities, payroll, security, system) or all."
    }
  }
}

TemplatePreview

{
  "type": "object",
  "properties": {
    "template_id": {
      "type": "string",
      "example": "JT-0001"
    },
    "name": {
      "type": "string"
    },
    "version": {
      "type": "integer"
    },
    "status": {
      "type": "string"
    },
    "anchor": {
      "type": "string",
      "enum": [
        "start_date",
        "last_working_day",
        "leave_start_date",
        "return_date",
        "effective_date"
      ]
    },
    "anchor_date": {
      "type": "string",
      "format": "date",
      "nullable": true
    },
    "as_role": {
      "type": "string",
      "nullable": true
    },
    "rows": {
      "type": "array",
      "description": "One row per step, grouped by role in display order, then by due offset: the timeline a chat can render as a table.",
      "items": {
        "type": "object",
        "properties": {
          "role": {
            "type": "string"
          },
          "phase": {
            "type": "string"
          },
          "phase_key": {
            "type": "string"
          },
          "step_key": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "todo",
              "form",
              "esign",
              "document",
              "content",
              "learning",
              "survey",
              "approval",
              "automated",
              "status_check",
              "meeting",
              "link"
            ]
          },
          "required": {
            "type": "boolean"
          },
          "due_offset_days": {
            "type": "integer",
            "nullable": true,
            "description": "Days from the anchor, when known up front"
          },
          "due_label": {
            "type": "string",
            "example": "start -7d"
          },
          "due_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "unlock_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "depends_on": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "system": {
            "type": "string",
            "enum": [
              "identity",
              "access",
              "itsm",
              "hris",
              "lms",
              "chat",
              "calendar",
              "pages",
              "mail",
              "expense",
              "people"
            ]
          },
          "form_field_count": {
            "type": "integer",
            "nullable": true,
            "description": "How many fields a form step collects; null for steps without a form."
          },
          "link_url": {
            "type": "string",
            "nullable": true
          }
        }
      }
    },
    "by_role": {
      "type": "object",
      "additionalProperties": {
        "type": "integer"
      }
    },
    "span_days": {
      "type": "integer",
      "description": "Days between the earliest and latest due offset"
    },
    "warnings": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  }
}

PublishRequest

{
  "type": "object",
  "properties": {
    "apply_to_active": {
      "type": "boolean",
      "example": true,
      "description": "Also add any step not yet present to every active journey of this template: ready when its phase is already open (or locked behind its dependencies), locked when the phase is still to come. Each added task records added_by_version."
    }
  }
}

PublishResult

{
  "allOf": [
    {
      "$ref": "#/components/schemas/JourneyTemplate"
    },
    {
      "type": "object",
      "properties": {
        "applied_to": {
          "type": "integer",
          "description": "Active journeys the new version was applied to"
        },
        "tasks_added": {
          "type": "integer",
          "description": "Tasks added across those journeys"
        }
      }
    }
  ]
}

CloneRequest

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "example": "Contractor onboarding",
      "description": "Name for the copy; defaults to \"Copy of <name>\"."
    }
  }
}

SeedSamplesRequest

{
  "type": "object",
  "properties": {
    "force": {
      "type": "boolean",
      "example": false,
      "description": "Clear this app's templates, journeys, tasks, activity and start claims first and write the sample world even when some are already there."
    }
  }
}

SeedSamplesResult

{
  "type": "object",
  "properties": {
    "seeded": {
      "type": "boolean",
      "readOnly": true,
      "description": "true when the sample world was written; false when any of this app's collections already held a record and force was not set (nothing changed)."
    },
    "templates": {
      "type": "integer",
      "readOnly": true,
      "example": 4
    },
    "journeys": {
      "type": "integer",
      "readOnly": true,
      "example": 8
    },
    "tasks": {
      "type": "integer",
      "readOnly": true
    },
    "activity": {
      "type": "integer",
      "readOnly": true
    },
    "start_claims": {
      "type": "integer",
      "readOnly": true,
      "description": "Start claims held; 0 after a seed, since they name journeys the seed replaced."
    }
  }
}

Journey

{
  "type": "object",
  "description": "One person's run of a template: dated tasks, progress and health. Started with create-journeys.",
  "properties": {
    "id": {
      "type": "string",
      "readOnly": true,
      "example": "JNY-0001"
    },
    "template_id": {
      "type": "string",
      "readOnly": true,
      "example": "JT-0001"
    },
    "template_name": {
      "type": "string",
      "readOnly": true,
      "example": "New hire onboarding"
    },
    "template_version": {
      "type": "integer",
      "readOnly": true,
      "example": 2,
      "description": "The template version the journey's tasks follow; publish with apply_to_active raises it."
    },
    "template_snapshot": {
      "type": "object",
      "nullable": true,
      "readOnly": true,
      "description": "The phases (with unlock rules) and step definitions this journey runs on: copied from the template when the journey started and replaced only by publish-templates with apply_to_active, so editing or deleting the template never changes a journey under way. Returned by get-journeys; list rows leave it out.",
      "properties": {
        "phases": {
          "type": "array",
          "items": {
            "type": "object",
            "description": "A phase groups steps and decides when they become available. Steps may be nested here (steps: [...]) or listed flat on the template with a phase key.",
            "required": [
              "name"
            ],
            "properties": {
              "key": {
                "type": "string",
                "example": "pre_start",
                "description": "Stable key; derived from the name when omitted."
              },
              "name": {
                "type": "string",
                "example": "Before start"
              },
              "order": {
                "type": "integer",
                "example": 1,
                "description": "Display and evaluation order; defaults to position."
              },
              "unlock": {
                "type": "object",
                "description": "When the phase opens. immediate: when the journey starts. offset: offset_days from the journey's anchor date (negative is before it). after_phase: when every task in after_phase is done or skipped. condition: when the phase before it is complete.",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "immediate",
                      "offset",
                      "after_phase",
                      "condition"
                    ],
                    "example": "offset",
                    "description": "One of immediate, offset, after_phase, condition."
                  },
                  "offset_days": {
                    "type": "integer",
                    "example": -14,
                    "description": "Days from the anchor date (offset unlocks)."
                  },
                  "after_phase": {
                    "type": "string",
                    "example": "pre_start",
                    "description": "Phase key whose completion opens this phase (after_phase unlocks)."
                  }
                }
              },
              "visible_to": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "subject",
                    "manager",
                    "buddy",
                    "hr",
                    "it",
                    "facilities",
                    "payroll",
                    "security",
                    "system"
                  ]
                },
                "description": "Roles that see this phase (subject, manager, buddy, hr, it, facilities, payroll, security, system); empty means everyone."
              },
              "steps": {
                "type": "array",
                "items": {
                  "type": "object",
                  "description": "One step of a template; every journey started from the template gets one task per step.",
                  "required": [
                    "title"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "sign_paperwork",
                      "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
                    },
                    "phase": {
                      "type": "string",
                      "example": "pre_start",
                      "description": "Key of the phase the step belongs to (defaults to the first phase)."
                    },
                    "title": {
                      "type": "string",
                      "example": "Sign offer letter, NDA and policy acknowledgements"
                    },
                    "description": {
                      "type": "string",
                      "example": "Three documents in one signing session."
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "todo",
                        "form",
                        "esign",
                        "document",
                        "content",
                        "learning",
                        "survey",
                        "approval",
                        "automated",
                        "status_check",
                        "meeting",
                        "link"
                      ],
                      "example": "esign",
                      "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
                    },
                    "assignee_role": {
                      "type": "string",
                      "enum": [
                        "subject",
                        "manager",
                        "buddy",
                        "hr",
                        "it",
                        "facilities",
                        "payroll",
                        "security",
                        "system"
                      ],
                      "example": "manager",
                      "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
                    },
                    "due": {
                      "type": "object",
                      "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
                      "properties": {
                        "anchor": {
                          "type": "string",
                          "enum": [
                            "journey_anchor",
                            "phase_unlock",
                            "assignment"
                          ],
                          "example": "journey_anchor",
                          "description": "One of journey_anchor, phase_unlock, assignment."
                        },
                        "offset_days": {
                          "type": "integer",
                          "example": -7
                        }
                      }
                    },
                    "depends_on": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "confirm_details"
                      ],
                      "description": "Step keys that must be done or skipped before this one unlocks."
                    },
                    "required": {
                      "type": "boolean",
                      "example": true,
                      "description": "Optional steps can be skipped without a warning."
                    },
                    "payload": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Kind-specific detail (a form name, document ids, a course list)."
                    },
                    "system": {
                      "type": "string",
                      "enum": [
                        "identity",
                        "access",
                        "itsm",
                        "hris",
                        "lms",
                        "chat",
                        "calendar",
                        "pages",
                        "mail",
                        "expense",
                        "people"
                      ],
                      "example": "itsm",
                      "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
                    },
                    "form": {
                      "type": "object",
                      "nullable": true,
                      "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
                      "properties": {
                        "fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "description": "One field of a form step. Give a key or a label (the other is derived).",
                            "properties": {
                              "key": {
                                "type": "string",
                                "example": "legal_name",
                                "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                              },
                              "label": {
                                "type": "string",
                                "example": "Legal name"
                              },
                              "type": {
                                "type": "string",
                                "enum": [
                                  "text",
                                  "textarea",
                                  "select",
                                  "date",
                                  "number",
                                  "email",
                                  "checkbox"
                                ],
                                "example": "text",
                                "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                              },
                              "required": {
                                "type": "boolean",
                                "example": true
                              },
                              "options": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                },
                                "example": [
                                  "14-inch",
                                  "16-inch"
                                ],
                                "description": "The choices of a select field."
                              },
                              "placeholder": {
                                "type": "string",
                                "nullable": true,
                                "example": "As it appears on your ID"
                              }
                            }
                          }
                        },
                        "submit_label": {
                          "type": "string",
                          "nullable": true,
                          "example": "Save details",
                          "description": "Label for the submit control; the UI defaults it."
                        }
                      }
                    },
                    "link": {
                      "type": "object",
                      "nullable": true,
                      "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
                      "properties": {
                        "url": {
                          "type": "string",
                          "example": "https://intranet.example.com/first-day"
                        },
                        "label": {
                          "type": "string",
                          "example": "Open the first-day agenda",
                          "description": "Defaults to the step title."
                        }
                      }
                    },
                    "promoted_from": {
                      "type": "string",
                      "nullable": true,
                      "example": "set_up_vpn_access",
                      "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
                    }
                  }
                },
                "description": "Steps of this phase, as an alternative to listing them flat with a phase key. Stored flat either way."
              }
            }
          }
        },
        "steps": {
          "type": "array",
          "items": {
            "type": "object",
            "description": "One step of a template; every journey started from the template gets one task per step.",
            "required": [
              "title"
            ],
            "properties": {
              "key": {
                "type": "string",
                "example": "sign_paperwork",
                "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
              },
              "phase": {
                "type": "string",
                "example": "pre_start",
                "description": "Key of the phase the step belongs to (defaults to the first phase)."
              },
              "title": {
                "type": "string",
                "example": "Sign offer letter, NDA and policy acknowledgements"
              },
              "description": {
                "type": "string",
                "example": "Three documents in one signing session."
              },
              "kind": {
                "type": "string",
                "enum": [
                  "todo",
                  "form",
                  "esign",
                  "document",
                  "content",
                  "learning",
                  "survey",
                  "approval",
                  "automated",
                  "status_check",
                  "meeting",
                  "link"
                ],
                "example": "esign",
                "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
              },
              "assignee_role": {
                "type": "string",
                "enum": [
                  "subject",
                  "manager",
                  "buddy",
                  "hr",
                  "it",
                  "facilities",
                  "payroll",
                  "security",
                  "system"
                ],
                "example": "manager",
                "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
              },
              "due": {
                "type": "object",
                "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
                "properties": {
                  "anchor": {
                    "type": "string",
                    "enum": [
                      "journey_anchor",
                      "phase_unlock",
                      "assignment"
                    ],
                    "example": "journey_anchor",
                    "description": "One of journey_anchor, phase_unlock, assignment."
                  },
                  "offset_days": {
                    "type": "integer",
                    "example": -7
                  }
                }
              },
              "depends_on": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "confirm_details"
                ],
                "description": "Step keys that must be done or skipped before this one unlocks."
              },
              "required": {
                "type": "boolean",
                "example": true,
                "description": "Optional steps can be skipped without a warning."
              },
              "payload": {
                "type": "object",
                "nullable": true,
                "additionalProperties": true,
                "description": "Kind-specific detail (a form name, document ids, a course list)."
              },
              "system": {
                "type": "string",
                "enum": [
                  "identity",
                  "access",
                  "itsm",
                  "hris",
                  "lms",
                  "chat",
                  "calendar",
                  "pages",
                  "mail",
                  "expense",
                  "people"
                ],
                "example": "itsm",
                "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
              },
              "form": {
                "type": "object",
                "nullable": true,
                "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
                "properties": {
                  "fields": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "description": "One field of a form step. Give a key or a label (the other is derived).",
                      "properties": {
                        "key": {
                          "type": "string",
                          "example": "legal_name",
                          "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                        },
                        "label": {
                          "type": "string",
                          "example": "Legal name"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "text",
                            "textarea",
                            "select",
                            "date",
                            "number",
                            "email",
                            "checkbox"
                          ],
                          "example": "text",
                          "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                        },
                        "required": {
                          "type": "boolean",
                          "example": true
                        },
                        "options": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "example": [
                            "14-inch",
                            "16-inch"
                          ],
                          "description": "The choices of a select field."
                        },
                        "placeholder": {
                          "type": "string",
                          "nullable": true,
                          "example": "As it appears on your ID"
                        }
                      }
                    }
                  },
                  "submit_label": {
                    "type": "string",
                    "nullable": true,
                    "example": "Save details",
                    "description": "Label for the submit control; the UI defaults it."
                  }
                }
              },
              "link": {
                "type": "object",
                "nullable": true,
                "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
                "properties": {
                  "url": {
                    "type": "string",
                    "example": "https://intranet.example.com/first-day"
                  },
                  "label": {
                    "type": "string",
                    "example": "Open the first-day agenda",
                    "description": "Defaults to the step title."
                  }
                }
              },
              "promoted_from": {
                "type": "string",
                "nullable": true,
                "example": "set_up_vpn_access",
                "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
              }
            }
          }
        }
      }
    },
    "excluded_steps": {
      "type": "array",
      "readOnly": true,
      "items": {
        "type": "string"
      },
      "example": [
        "buddy_lunch"
      ],
      "description": "Step keys left out when the journey started (exclude_steps on create-journeys); apply_to_active never adds them."
    },
    "journey_type": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "onboarding",
        "offboarding",
        "job_change",
        "leave",
        "return_to_work",
        "relocation",
        "custom"
      ]
    },
    "subject_email": {
      "type": "string",
      "format": "email",
      "example": "priya.sharma@acme.com",
      "description": "The person the journey is for.",
      "readOnly": true
    },
    "subject_name": {
      "type": "string",
      "example": "Priya Sharma"
    },
    "manager_email": {
      "type": "string",
      "format": "email",
      "example": "dana.whitfield@acme.com",
      "description": "The subject's manager; takes the manager-role tasks.",
      "nullable": true
    },
    "buddy_email": {
      "type": "string",
      "format": "email",
      "example": "marcus.lee@acme.com",
      "description": "The onboarding buddy; takes the buddy-role tasks.",
      "nullable": true
    },
    "role_assignments": {
      "type": "object",
      "readOnly": true,
      "additionalProperties": {
        "type": "string",
        "format": "email"
      },
      "example": {
        "it": "jordan.alvarez@acme.com",
        "hr": "elena.petrova@acme.com"
      },
      "description": "Who holds the hr, it, facilities, payroll and security roles on this journey, fixed when it started (the roster's picks, or role_assignments given to create-journeys). Tasks added later for a role (publish with apply_to_active, add-task by role) go to these people."
    },
    "anchor": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "start_date",
        "last_working_day",
        "leave_start_date",
        "return_date",
        "effective_date"
      ],
      "description": "Which date anchor_date is (from the template)."
    },
    "anchor_date": {
      "type": "string",
      "format": "date",
      "example": "2026-11-17",
      "description": "The date every due date hangs off (start date, last working day, leave start, return, effective date)."
    },
    "started_at": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    },
    "completed_at": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "readOnly": true
    },
    "cancelled_at": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "readOnly": true
    },
    "status": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "draft",
        "active",
        "completed",
        "cancelled"
      ],
      "description": "active while tasks remain, completed when every task is done or skipped, cancelled with cancel-journeys (draft is reserved and never set)."
    },
    "health": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "passing",
        "warning",
        "failing"
      ],
      "description": "failing when a task is in error, warning when anything is overdue, else passing."
    },
    "progress_percent": {
      "type": "integer",
      "readOnly": true,
      "example": 14,
      "description": "(done + skipped) / total"
    },
    "counts": {
      "type": "object",
      "readOnly": true,
      "properties": {
        "total": {
          "type": "integer"
        },
        "done": {
          "type": "integer"
        },
        "skipped": {
          "type": "integer"
        },
        "in_progress": {
          "type": "integer",
          "description": "in_progress plus processing"
        },
        "ready": {
          "type": "integer"
        },
        "locked": {
          "type": "integer"
        },
        "overdue": {
          "type": "integer"
        },
        "error": {
          "type": "integer"
        }
      }
    },
    "up_next": {
      "type": "string",
      "nullable": true,
      "readOnly": true,
      "example": "JTK-0001-03",
      "description": "The open task due soonest."
    },
    "up_next_title": {
      "type": "string",
      "nullable": true,
      "readOnly": true
    },
    "next_due_date": {
      "type": "string",
      "format": "date",
      "nullable": true,
      "readOnly": true,
      "description": "Earliest due date among open tasks."
    },
    "open_due_dates": {
      "type": "array",
      "readOnly": true,
      "items": {
        "type": "string",
        "format": "date"
      },
      "description": "Due dates of the open tasks, sorted. counts.overdue, is_overdue and health are computed from these as of today on every read."
    },
    "locked_unlock_dates": {
      "type": "array",
      "readOnly": true,
      "items": {
        "type": "string",
        "format": "date"
      },
      "description": "Unlock days of the tasks locked only on a date, sorted. Lists count the ones whose day has come as ready; reading one journey (get-journeys, list-journey-tasks, get-my-journey) opens them for real."
    },
    "is_overdue": {
      "type": "boolean",
      "readOnly": true,
      "description": "Any open task past its due date (computed as of today, on every read)."
    },
    "ad_hoc_count": {
      "type": "integer",
      "readOnly": true,
      "example": 1,
      "description": "Tasks added to this journey by hand (add-task)."
    },
    "flag_count": {
      "type": "integer",
      "readOnly": true,
      "example": 0,
      "description": "Tasks currently flagged as bumpy."
    },
    "late_count": {
      "type": "integer",
      "readOnly": true,
      "example": 2,
      "description": "Tasks finished after their due date."
    },
    "created_by": {
      "type": "string",
      "nullable": true,
      "readOnly": true,
      "description": "Who started it: an email, or system for an HRIS event or an assistant."
    },
    "source": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "manual",
        "hris_event",
        "catalog_form",
        "agent_chat"
      ]
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    }
  }
}

StartJourneyRequest

{
  "type": "object",
  "required": [
    "template_id",
    "subject_email"
  ],
  "properties": {
    "template_id": {
      "type": "string",
      "example": "JT-0001",
      "description": "A published template (list-templates)."
    },
    "subject_email": {
      "type": "string",
      "format": "email",
      "example": "priya.sharma@acme.com",
      "description": "The person the journey is for."
    },
    "anchor_date": {
      "type": "string",
      "format": "date",
      "example": "2026-11-17",
      "description": "The date the template's offsets hang off (start date for onboarding, last working day for offboarding, ...). Required unless the subject is on the identity roster with a start date and the template is anchored on start_date."
    },
    "manager_email": {
      "type": "string",
      "format": "email",
      "example": "dana.whitfield@acme.com",
      "description": "The subject's manager. Defaults to the manager on the identity roster; required when the template has manager steps and none is known."
    },
    "buddy_email": {
      "type": "string",
      "format": "email",
      "example": "marcus.lee@acme.com",
      "description": "A teammate who is not the manager. Required when the template has buddy steps."
    },
    "subject_name": {
      "type": "string",
      "example": "Priya Sharma",
      "description": "Defaults to the roster name or a name derived from the email."
    },
    "source": {
      "type": "string",
      "enum": [
        "manual",
        "hris_event",
        "catalog_form",
        "agent_chat"
      ],
      "example": "agent_chat",
      "description": "How this journey came to start: manual, hris_event, catalog_form, agent_chat. Defaults to manual."
    },
    "role_assignments": {
      "type": "object",
      "additionalProperties": {
        "type": "string",
        "format": "email"
      },
      "example": {
        "it": "jordan.alvarez@acme.com"
      },
      "description": "Override who takes the hr, it, facilities, payroll or security tasks (they otherwise resolve from the identity roster by department)."
    },
    "exclude_steps": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "example": [
        "buddy_lunch"
      ],
      "description": "Step keys to leave out of this journey."
    }
  }
}

StartJourneyResult

{
  "allOf": [
    {
      "$ref": "#/components/schemas/Journey"
    },
    {
      "type": "object",
      "properties": {
        "tasks": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/JourneyTask"
          },
          "description": "Every task created, already evaluated: ready, locked, or done when an automated step ran at once."
        }
      }
    }
  ]
}

CancelJourneyRequest

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string",
      "example": "Offer withdrawn",
      "description": "Recorded on the activity feed and on every open task, which is skipped."
    }
  }
}

JourneyTask

{
  "type": "object",
  "description": "One step of one journey, for one assignee, with a due date and a status.",
  "properties": {
    "id": {
      "type": "string",
      "readOnly": true,
      "example": "JTK-0001-03"
    },
    "journey_id": {
      "type": "string",
      "readOnly": true,
      "example": "JNY-0001"
    },
    "template_id": {
      "type": "string",
      "readOnly": true,
      "example": "JT-0001"
    },
    "subject_email": {
      "type": "string",
      "format": "email",
      "example": "priya.sharma@acme.com",
      "description": "The person the journey is for.",
      "readOnly": true
    },
    "phase_key": {
      "type": "string",
      "readOnly": true,
      "example": "pre_start"
    },
    "phase_name": {
      "type": "string",
      "readOnly": true,
      "example": "Before start"
    },
    "step_key": {
      "type": "string",
      "readOnly": true,
      "example": "sign_paperwork"
    },
    "title": {
      "type": "string",
      "example": "Sign offer letter, NDA and policy acknowledgements"
    },
    "description": {
      "type": "string"
    },
    "kind": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "todo",
        "form",
        "esign",
        "document",
        "content",
        "learning",
        "survey",
        "approval",
        "automated",
        "status_check",
        "meeting",
        "link"
      ]
    },
    "assignee_email": {
      "type": "string",
      "format": "email",
      "example": "dana.whitfield@acme.com",
      "description": "Who does it; null for system steps or an unresolved role. Moves only with reassign-tasks, which records reassigned_from and logs the change.",
      "nullable": true,
      "readOnly": true
    },
    "assignee_role": {
      "type": "string",
      "enum": [
        "subject",
        "manager",
        "buddy",
        "hr",
        "it",
        "facilities",
        "payroll",
        "security",
        "system"
      ],
      "example": "manager",
      "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation.",
      "readOnly": true
    },
    "due_date": {
      "type": "string",
      "format": "date",
      "example": "2026-11-17",
      "description": "When it is due. Null until a task whose due date hangs off its unlock day opens.",
      "nullable": true
    },
    "unlock_date": {
      "type": "string",
      "format": "date",
      "nullable": true,
      "readOnly": true,
      "description": "The day its phase opens, when the phase opens on a date."
    },
    "status": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "locked",
        "ready",
        "in_progress",
        "processing",
        "done",
        "skipped",
        "error"
      ],
      "description": "locked: phase not open or prerequisites not done. ready: can be worked. in_progress: started. processing: an automation or status check is running. done, skipped. error: an automation failed."
    },
    "locked_reason": {
      "type": "string",
      "nullable": true,
      "readOnly": true,
      "enum": [
        "date",
        "deps",
        "phase",
        null
      ],
      "description": "Why it is locked: its phase opens on a date, prerequisites are not done, or an earlier phase is not complete."
    },
    "depends_on": {
      "type": "array",
      "readOnly": true,
      "items": {
        "type": "string"
      },
      "description": "Step keys that must be done or skipped first."
    },
    "is_overdue": {
      "type": "boolean",
      "readOnly": true,
      "description": "due_date is past and the task is not done or skipped (computed as of today)."
    },
    "required": {
      "type": "boolean"
    },
    "payload": {
      "type": "object",
      "nullable": true,
      "additionalProperties": true
    },
    "unlocked_at": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "readOnly": true
    },
    "started_at": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "readOnly": true
    },
    "completed_at": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "readOnly": true
    },
    "completed_by": {
      "type": "string",
      "nullable": true,
      "readOnly": true,
      "description": "Email of who completed it, or system."
    },
    "note": {
      "type": "string",
      "nullable": true,
      "description": "Free text left when completing, skipping or updating."
    },
    "added_by_version": {
      "type": "integer",
      "readOnly": true,
      "example": 2,
      "description": "The template version that introduced this step to the journey."
    },
    "system": {
      "type": "string",
      "enum": [
        "identity",
        "access",
        "itsm",
        "hris",
        "lms",
        "chat",
        "calendar",
        "pages",
        "mail",
        "expense",
        "people"
      ],
      "example": "itsm",
      "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
    },
    "form": {
      "type": "object",
      "nullable": true,
      "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
      "properties": {
        "fields": {
          "type": "array",
          "items": {
            "type": "object",
            "description": "One field of a form step. Give a key or a label (the other is derived).",
            "properties": {
              "key": {
                "type": "string",
                "example": "legal_name",
                "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
              },
              "label": {
                "type": "string",
                "example": "Legal name"
              },
              "type": {
                "type": "string",
                "enum": [
                  "text",
                  "textarea",
                  "select",
                  "date",
                  "number",
                  "email",
                  "checkbox"
                ],
                "example": "text",
                "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
              },
              "required": {
                "type": "boolean",
                "example": true
              },
              "options": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "14-inch",
                  "16-inch"
                ],
                "description": "The choices of a select field."
              },
              "placeholder": {
                "type": "string",
                "nullable": true,
                "example": "As it appears on your ID"
              }
            }
          }
        },
        "submit_label": {
          "type": "string",
          "nullable": true,
          "example": "Save details",
          "description": "Label for the submit control; the UI defaults it."
        }
      },
      "readOnly": true
    },
    "link": {
      "type": "object",
      "nullable": true,
      "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
      "properties": {
        "url": {
          "type": "string",
          "example": "https://intranet.example.com/first-day"
        },
        "label": {
          "type": "string",
          "example": "Open the first-day agenda",
          "description": "Defaults to the step title."
        }
      },
      "readOnly": true
    },
    "origin": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "template",
        "ad_hoc"
      ],
      "example": "template",
      "description": "template: one of the template's steps. ad_hoc: added to this one journey with add-task."
    },
    "added_by": {
      "type": "string",
      "nullable": true,
      "readOnly": true,
      "description": "Who added an ad hoc task (an email, or api); null for template tasks."
    },
    "reason": {
      "type": "string",
      "nullable": true,
      "example": "Working from home three days a week; not in the standard kit",
      "description": "Why an ad hoc task was needed. get-insights-templates groups ad hoc tasks by title and carries the reasons."
    },
    "flagged": {
      "type": "boolean",
      "readOnly": true,
      "description": "Someone marked this task as bumpy (flag-tasks)."
    },
    "flag_note": {
      "type": "string",
      "nullable": true,
      "readOnly": true
    },
    "flagged_by": {
      "type": "string",
      "nullable": true,
      "readOnly": true
    },
    "flagged_at": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "readOnly": true
    },
    "completed_late": {
      "type": "boolean",
      "readOnly": true,
      "description": "Finished after its due date."
    },
    "days_late": {
      "type": "integer",
      "nullable": true,
      "readOnly": true,
      "description": "Days between the due date and completion (0 when on time); null until completed or without a due date."
    },
    "reassigned_from": {
      "type": "string",
      "nullable": true,
      "readOnly": true,
      "description": "The previous assignee after reassign-tasks."
    },
    "reopened_count": {
      "type": "integer",
      "readOnly": true,
      "description": "How many times reopen-tasks brought it back."
    },
    "submission": {
      "type": "object",
      "nullable": true,
      "readOnly": true,
      "description": "What submit-tasks stored for a form task. _filter \"submission ne null\" lists submitted tasks.",
      "properties": {
        "values": {
          "type": "object",
          "additionalProperties": true
        },
        "submitted_at": {
          "type": "string",
          "format": "date-time"
        },
        "submitted_by": {
          "type": "string"
        }
      }
    },
    "opened_at": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "readOnly": true,
      "description": "When a link task's page was opened (start or complete)."
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    }
  }
}

TaskNoteRequest

{
  "type": "object",
  "properties": {
    "note": {
      "type": "string",
      "example": "Signed in the portal this morning",
      "description": "Optional note recorded on the task and the activity feed."
    }
  }
}

ReassignTaskRequest

{
  "type": "object",
  "required": [
    "assignee_email"
  ],
  "properties": {
    "assignee_email": {
      "type": "string",
      "format": "email",
      "example": "jordan.alvarez@acme.com",
      "description": "The person who should do the task."
    },
    "note": {
      "type": "string",
      "description": "Optional note recorded on the activity feed."
    }
  }
}

TaskActionResult

{
  "type": "object",
  "properties": {
    "task": {
      "$ref": "#/components/schemas/JourneyTask"
    },
    "unlocked": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/JourneyTask"
      },
      "description": "Tasks this action opened, ran (automated) or set processing (status checks), in order."
    },
    "journey": {
      "$ref": "#/components/schemas/Journey"
    },
    "warnings": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "For example that a required step was skipped."
    }
  }
}

JourneyActivity

{
  "type": "object",
  "description": "One entry on a journey's feed: who did what, when.",
  "properties": {
    "id": {
      "type": "string",
      "readOnly": true,
      "example": "JACT-0001-004"
    },
    "journey_id": {
      "type": "string",
      "readOnly": true,
      "example": "JNY-0001"
    },
    "subject_email": {
      "type": "string",
      "format": "email",
      "example": "priya.sharma@acme.com",
      "description": "The person the journey is for.",
      "readOnly": true
    },
    "at": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    },
    "kind": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "journey_started",
        "journey_updated",
        "journey_completed",
        "journey_cancelled",
        "task_unlocked",
        "task_started",
        "task_completed",
        "task_submitted",
        "task_skipped",
        "task_reopened",
        "task_reassigned",
        "task_added",
        "task_flagged",
        "task_unflagged",
        "automation_completed",
        "status_check_started",
        "template_applied"
      ]
    },
    "actor": {
      "type": "string",
      "readOnly": true,
      "description": "Email of who acted, or system."
    },
    "task_id": {
      "type": "string",
      "nullable": true,
      "readOnly": true
    },
    "step_key": {
      "type": "string",
      "nullable": true,
      "readOnly": true
    },
    "title": {
      "type": "string",
      "readOnly": true,
      "example": "Completed Sign offer letter, NDA and policy acknowledgements"
    },
    "detail": {
      "type": "string",
      "nullable": true,
      "readOnly": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    }
  }
}

StepLibraryRow

{
  "type": "object",
  "description": "One step title as it appears across the instance's templates: what other journeys ask people to do. Built from the templates on every read; nothing is stored.",
  "properties": {
    "id": {
      "type": "string",
      "readOnly": true,
      "example": "order_and_ship_laptop_and_peripherals",
      "description": "The normalized title rows are grouped by."
    },
    "title": {
      "type": "string",
      "readOnly": true
    },
    "description": {
      "type": "string",
      "readOnly": true
    },
    "kind": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "todo",
        "form",
        "esign",
        "document",
        "content",
        "learning",
        "survey",
        "approval",
        "automated",
        "status_check",
        "meeting",
        "link"
      ]
    },
    "assignee_role": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "subject",
        "manager",
        "buddy",
        "hr",
        "it",
        "facilities",
        "payroll",
        "security",
        "system"
      ]
    },
    "system": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "identity",
        "access",
        "itsm",
        "hris",
        "lms",
        "chat",
        "calendar",
        "pages",
        "mail",
        "expense",
        "people"
      ]
    },
    "due_offset_days": {
      "type": "integer",
      "nullable": true,
      "readOnly": true,
      "description": "Days from the journey anchor, when known up front."
    },
    "due_label": {
      "type": "string",
      "readOnly": true,
      "example": "start -7d"
    },
    "phase_name": {
      "type": "string",
      "readOnly": true,
      "example": "Before start"
    },
    "template_ids": {
      "type": "array",
      "readOnly": true,
      "items": {
        "type": "string"
      }
    },
    "template_names": {
      "type": "array",
      "readOnly": true,
      "items": {
        "type": "string"
      },
      "example": [
        "New hire onboarding",
        "Internal transfer"
      ]
    },
    "used_in_count": {
      "type": "integer",
      "readOnly": true,
      "example": 2,
      "description": "How many template steps share this title."
    },
    "origin": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "template",
        "promoted"
      ],
      "description": "promoted when the step was added from an ad hoc group (promoted_from set)."
    },
    "promoted_from": {
      "type": "string",
      "nullable": true,
      "readOnly": true
    },
    "form_field_count": {
      "type": "integer",
      "nullable": true,
      "readOnly": true
    },
    "link_url": {
      "type": "string",
      "nullable": true,
      "readOnly": true
    },
    "depends_on_count": {
      "type": "integer",
      "readOnly": true
    }
  }
}

AdHocTaskRequest

{
  "type": "object",
  "description": "A task this one journey needs that the template did not foresee. Give who does it as assignee_email or as a role of the journey.",
  "required": [
    "title",
    "due_date"
  ],
  "properties": {
    "title": {
      "type": "string",
      "example": "Set up VPN access for remote work"
    },
    "description": {
      "type": "string"
    },
    "assignee_email": {
      "type": "string",
      "format": "email",
      "example": "jordan.alvarez@acme.com",
      "description": "Who does it. Either this or assignee_role."
    },
    "assignee_role": {
      "type": "string",
      "enum": [
        "subject",
        "manager",
        "buddy",
        "hr",
        "it",
        "facilities",
        "payroll",
        "security"
      ],
      "example": "it",
      "description": "A role of the journey (subject, manager, buddy, hr, it, facilities, payroll, security); resolves to the person holding it (the manager, the buddy, the IT contact)."
    },
    "due_date": {
      "type": "string",
      "format": "date",
      "example": "2026-11-19",
      "description": "When it is due."
    },
    "reason": {
      "type": "string",
      "example": "Working from home three days a week; not in the standard kit",
      "description": "Why it was needed. Recorded on the task and the activity feed; get-insights-templates groups ad hoc tasks by title and shows the reasons, so write the real one."
    },
    "kind": {
      "type": "string",
      "enum": [
        "todo",
        "form",
        "esign",
        "document",
        "content",
        "learning",
        "survey",
        "approval",
        "meeting",
        "link"
      ],
      "example": "todo",
      "description": "One of todo, form, esign, document, content, learning, survey, approval, meeting, link; defaults to todo. automated and status_check steps belong on the template."
    },
    "system": {
      "type": "string",
      "enum": [
        "identity",
        "access",
        "itsm",
        "hris",
        "lms",
        "chat",
        "calendar",
        "pages",
        "mail",
        "expense",
        "people"
      ],
      "example": "itsm",
      "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
    },
    "required": {
      "type": "boolean",
      "example": true
    },
    "form": {
      "type": "object",
      "nullable": true,
      "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
      "properties": {
        "fields": {
          "type": "array",
          "items": {
            "type": "object",
            "description": "One field of a form step. Give a key or a label (the other is derived).",
            "properties": {
              "key": {
                "type": "string",
                "example": "legal_name",
                "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
              },
              "label": {
                "type": "string",
                "example": "Legal name"
              },
              "type": {
                "type": "string",
                "enum": [
                  "text",
                  "textarea",
                  "select",
                  "date",
                  "number",
                  "email",
                  "checkbox"
                ],
                "example": "text",
                "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
              },
              "required": {
                "type": "boolean",
                "example": true
              },
              "options": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "14-inch",
                  "16-inch"
                ],
                "description": "The choices of a select field."
              },
              "placeholder": {
                "type": "string",
                "nullable": true,
                "example": "As it appears on your ID"
              }
            }
          }
        },
        "submit_label": {
          "type": "string",
          "nullable": true,
          "example": "Save details",
          "description": "Label for the submit control; the UI defaults it."
        }
      }
    },
    "link": {
      "type": "object",
      "nullable": true,
      "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
      "properties": {
        "url": {
          "type": "string",
          "example": "https://intranet.example.com/first-day"
        },
        "label": {
          "type": "string",
          "example": "Open the first-day agenda",
          "description": "Defaults to the step title."
        }
      }
    }
  }
}

FlagTaskRequest

{
  "type": "object",
  "required": [
    "note"
  ],
  "properties": {
    "note": {
      "type": "string",
      "example": "The security course link was broken for two days",
      "description": "What was bumpy, in a sentence."
    }
  }
}

SubmitTaskRequest

{
  "type": "object",
  "required": [
    "values"
  ],
  "properties": {
    "values": {
      "type": "object",
      "additionalProperties": true,
      "example": {
        "legal_name": "Priya Sharma",
        "emergency_contact_name": "Rohan Sharma"
      },
      "description": "The form's values keyed by field key (get-tasks shows the task's form.fields). Required fields must be present; select values must be one of the options; dates are YYYY-MM-DD."
    },
    "note": {
      "type": "string",
      "description": "Optional note recorded on the task and the activity feed."
    }
  }
}

TemplateInsights

{
  "type": "object",
  "description": "What a template's journeys have been teaching (the UI calls this Suggested changes). Computed from the journeys and tasks on every read; nothing is stored.",
  "properties": {
    "template_id": {
      "type": "string",
      "readOnly": true
    },
    "name": {
      "type": "string",
      "readOnly": true
    },
    "version": {
      "type": "integer",
      "readOnly": true
    },
    "as_of": {
      "type": "string",
      "format": "date",
      "readOnly": true
    },
    "journeys_considered": {
      "type": "integer",
      "readOnly": true,
      "description": "Journeys of this template, any version and status."
    },
    "journey_ids": {
      "type": "array",
      "readOnly": true,
      "items": {
        "type": "string"
      }
    },
    "summary": {
      "type": "object",
      "readOnly": true,
      "properties": {
        "ad_hoc_tasks": {
          "type": "integer"
        },
        "ad_hoc_groups": {
          "type": "integer"
        },
        "flags": {
          "type": "integer"
        },
        "skipped": {
          "type": "integer"
        },
        "late": {
          "type": "integer"
        },
        "reassigned": {
          "type": "integer"
        }
      }
    },
    "ad_hoc": {
      "type": "array",
      "readOnly": true,
      "description": "Ad hoc tasks grouped by normalized title, most common first. Each group carries promote: the add-steps body that would add it to the template.",
      "items": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "example": "set_up_vpn_access_for_remote_work"
          },
          "title": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "todo",
              "form",
              "esign",
              "document",
              "content",
              "learning",
              "survey",
              "approval",
              "automated",
              "status_check",
              "meeting",
              "link"
            ]
          },
          "system": {
            "type": "string",
            "enum": [
              "identity",
              "access",
              "itsm",
              "hris",
              "lms",
              "chat",
              "calendar",
              "pages",
              "mail",
              "expense",
              "people"
            ]
          },
          "assignee_role": {
            "type": "string"
          },
          "added_by_roles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "manager"
            ]
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "journeys": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "journey_id": {
                  "type": "string"
                },
                "subject_name": {
                  "type": "string"
                },
                "journey_status": {
                  "type": "string"
                },
                "task_id": {
                  "type": "string"
                },
                "task_status": {
                  "type": "string"
                },
                "added_by": {
                  "type": "string",
                  "nullable": true
                },
                "added_by_role": {
                  "type": "string",
                  "nullable": true
                },
                "reason": {
                  "type": "string",
                  "nullable": true
                },
                "due_date": {
                  "type": "string",
                  "format": "date",
                  "nullable": true
                },
                "assignee_email": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "suggested_phase": {
            "type": "object",
            "nullable": true,
            "properties": {
              "key": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "suggested_role": {
            "type": "string"
          },
          "suggested_due": {
            "type": "object",
            "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
            "properties": {
              "anchor": {
                "type": "string",
                "enum": [
                  "journey_anchor",
                  "phase_unlock",
                  "assignment"
                ],
                "example": "journey_anchor",
                "description": "One of journey_anchor, phase_unlock, assignment."
              },
              "offset_days": {
                "type": "integer",
                "example": -7
              }
            }
          },
          "promoted_step_key": {
            "type": "string",
            "nullable": true,
            "description": "The template step that already covers this group (promoted, or added by hand with the same title)."
          },
          "promote": {
            "type": "object",
            "description": "Pass this as the body of add-steps-templates to promote the group; the new step records promoted_from.",
            "properties": {
              "steps": {
                "type": "array",
                "items": {
                  "type": "object",
                  "description": "One step of a template; every journey started from the template gets one task per step.",
                  "required": [
                    "title"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "example": "sign_paperwork",
                      "description": "Stable key, unique in the template; derived from the title when omitted. Other steps reference it in depends_on."
                    },
                    "phase": {
                      "type": "string",
                      "example": "pre_start",
                      "description": "Key of the phase the step belongs to (defaults to the first phase)."
                    },
                    "title": {
                      "type": "string",
                      "example": "Sign offer letter, NDA and policy acknowledgements"
                    },
                    "description": {
                      "type": "string",
                      "example": "Three documents in one signing session."
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "todo",
                        "form",
                        "esign",
                        "document",
                        "content",
                        "learning",
                        "survey",
                        "approval",
                        "automated",
                        "status_check",
                        "meeting",
                        "link"
                      ],
                      "example": "esign",
                      "description": "What kind of work it is: todo, form, esign, document, content, learning, survey, approval, automated, status_check, meeting, link (any other value is stored as todo with a warning). automated steps run by themselves when they unlock; status_check steps wait in processing until complete is called; form steps carry form and are finished with submit-tasks; link steps carry link; everything else is done by a person with complete-tasks."
                    },
                    "assignee_role": {
                      "type": "string",
                      "enum": [
                        "subject",
                        "manager",
                        "buddy",
                        "hr",
                        "it",
                        "facilities",
                        "payroll",
                        "security",
                        "system"
                      ],
                      "example": "manager",
                      "description": "Who does the step. subject is the person the journey is for; manager and buddy are named when the journey starts; hr, it, facilities, payroll and security resolve to people on the identity roster; system is an automation."
                    },
                    "due": {
                      "type": "object",
                      "description": "When the task is due. journey_anchor: offset_days from the anchor date (start date, last working day, ...). phase_unlock: offset_days after the phase opens. assignment: offset_days after the task becomes ready. Defaults to the day the phase opens.",
                      "properties": {
                        "anchor": {
                          "type": "string",
                          "enum": [
                            "journey_anchor",
                            "phase_unlock",
                            "assignment"
                          ],
                          "example": "journey_anchor",
                          "description": "One of journey_anchor, phase_unlock, assignment."
                        },
                        "offset_days": {
                          "type": "integer",
                          "example": -7
                        }
                      }
                    },
                    "depends_on": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "confirm_details"
                      ],
                      "description": "Step keys that must be done or skipped before this one unlocks."
                    },
                    "required": {
                      "type": "boolean",
                      "example": true,
                      "description": "Optional steps can be skipped without a warning."
                    },
                    "payload": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Kind-specific detail (a form name, document ids, a course list)."
                    },
                    "system": {
                      "type": "string",
                      "enum": [
                        "identity",
                        "access",
                        "itsm",
                        "hris",
                        "lms",
                        "chat",
                        "calendar",
                        "pages",
                        "mail",
                        "expense",
                        "people"
                      ],
                      "example": "itsm",
                      "description": "The suite app that processes the step: identity (accounts, SSO), access (app access grants), itsm (laptop, badge, catalog items), hris (payroll, benefits, employment facts, signatures), lms (training), chat (channels, welcome posts), calendar (meetings), pages (guides, agendas, policies), mail (out of office, forwarding), expense, or people for a purely human to-do with no system behind it. When omitted it defaults from kind and title: automated steps go to identity, or access when the title mentions access, roles, apps, entitlements, groups or permissions; meeting to calendar; learning to lms; link and content to pages; form, esign and survey to hris; everything else to people."
                    },
                    "form": {
                      "type": "object",
                      "nullable": true,
                      "description": "The mini-form a form step collects. submit-tasks validates values against these fields, stores the submission on the task and completes it.",
                      "properties": {
                        "fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "description": "One field of a form step. Give a key or a label (the other is derived).",
                            "properties": {
                              "key": {
                                "type": "string",
                                "example": "legal_name",
                                "description": "Stable key the submission's values are keyed by; derived from the label when omitted."
                              },
                              "label": {
                                "type": "string",
                                "example": "Legal name"
                              },
                              "type": {
                                "type": "string",
                                "enum": [
                                  "text",
                                  "textarea",
                                  "select",
                                  "date",
                                  "number",
                                  "email",
                                  "checkbox"
                                ],
                                "example": "text",
                                "description": "text, textarea, select (needs options), date (YYYY-MM-DD), number, email or checkbox. Defaults to text."
                              },
                              "required": {
                                "type": "boolean",
                                "example": true
                              },
                              "options": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                },
                                "example": [
                                  "14-inch",
                                  "16-inch"
                                ],
                                "description": "The choices of a select field."
                              },
                              "placeholder": {
                                "type": "string",
                                "nullable": true,
                                "example": "As it appears on your ID"
                              }
                            }
                          }
                        },
                        "submit_label": {
                          "type": "string",
                          "nullable": true,
                          "example": "Save details",
                          "description": "Label for the submit control; the UI defaults it."
                        }
                      }
                    },
                    "link": {
                      "type": "object",
                      "nullable": true,
                      "description": "The page a link step opens (url required). Starting or completing the task records opened_at.",
                      "properties": {
                        "url": {
                          "type": "string",
                          "example": "https://intranet.example.com/first-day"
                        },
                        "label": {
                          "type": "string",
                          "example": "Open the first-day agenda",
                          "description": "Defaults to the step title."
                        }
                      }
                    },
                    "promoted_from": {
                      "type": "string",
                      "nullable": true,
                      "example": "set_up_vpn_access",
                      "description": "The ad hoc group (normalized title, from get-insights-templates) this step was promoted from. Set it when adding the step so the step library and insights show the step as promoted."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "skipped": {
      "type": "array",
      "readOnly": true,
      "description": "Template steps people skipped, with the notes; cancelled journeys are left out.",
      "items": {
        "type": "object",
        "properties": {
          "step_key": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "assignee_role": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "journeys": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "journey_id": {
                  "type": "string"
                },
                "subject_name": {
                  "type": "string"
                },
                "journey_status": {
                  "type": "string"
                },
                "task_id": {
                  "type": "string"
                },
                "task_status": {
                  "type": "string"
                },
                "note": {
                  "type": "string",
                  "nullable": true
                },
                "skipped_by": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      }
    },
    "late": {
      "type": "array",
      "readOnly": true,
      "description": "Steps finished after their due date or open past it, with the median and worst lateness in days.",
      "items": {
        "type": "object",
        "properties": {
          "step_key": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "assignee_role": {
            "type": "string"
          },
          "system": {
            "type": "string",
            "enum": [
              "identity",
              "access",
              "itsm",
              "hris",
              "lms",
              "chat",
              "calendar",
              "pages",
              "mail",
              "expense",
              "people"
            ]
          },
          "origin": {
            "type": "string",
            "enum": [
              "template",
              "ad_hoc"
            ]
          },
          "count": {
            "type": "integer"
          },
          "median_days_late": {
            "type": "number"
          },
          "max_days_late": {
            "type": "integer"
          },
          "journeys": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "journey_id": {
                  "type": "string"
                },
                "subject_name": {
                  "type": "string"
                },
                "journey_status": {
                  "type": "string"
                },
                "task_id": {
                  "type": "string"
                },
                "task_status": {
                  "type": "string"
                },
                "days_late": {
                  "type": "integer"
                },
                "due_date": {
                  "type": "string",
                  "format": "date",
                  "nullable": true
                },
                "assignee_email": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      }
    },
    "reassigned": {
      "type": "array",
      "readOnly": true,
      "description": "Steps that changed hands, from whom to whom.",
      "items": {
        "type": "object",
        "properties": {
          "step_key": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "assignee_role": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "journeys": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "journey_id": {
                  "type": "string"
                },
                "subject_name": {
                  "type": "string"
                },
                "journey_status": {
                  "type": "string"
                },
                "task_id": {
                  "type": "string"
                },
                "task_status": {
                  "type": "string"
                },
                "from": {
                  "type": "string",
                  "nullable": true
                },
                "to": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      }
    },
    "flags": {
      "type": "array",
      "readOnly": true,
      "description": "Every flagged task with its note, newest first.",
      "items": {
        "type": "object",
        "properties": {
          "task_id": {
            "type": "string"
          },
          "journey_id": {
            "type": "string"
          },
          "subject_name": {
            "type": "string"
          },
          "step_key": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "origin": {
            "type": "string",
            "enum": [
              "template",
              "ad_hoc"
            ]
          },
          "assignee_role": {
            "type": "string"
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "flagged_by": {
            "type": "string",
            "nullable": true
          },
          "flagged_by_role": {
            "type": "string",
            "nullable": true
          },
          "flagged_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "task_status": {
            "type": "string"
          }
        }
      }
    }
  }
}

MyJourney

{
  "type": "object",
  "description": "One journey as the current user sees it: their own tasks in due order, what each locked one waits for, the next thing to do, and the people on the journey.",
  "properties": {
    "journey_id": {
      "type": "string",
      "readOnly": true
    },
    "template_name": {
      "type": "string",
      "readOnly": true
    },
    "journey_type": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "onboarding",
        "offboarding",
        "job_change",
        "leave",
        "return_to_work",
        "relocation",
        "custom"
      ]
    },
    "status": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "draft",
        "active",
        "completed",
        "cancelled"
      ]
    },
    "anchor": {
      "type": "string",
      "readOnly": true,
      "enum": [
        "start_date",
        "last_working_day",
        "leave_start_date",
        "return_date",
        "effective_date"
      ]
    },
    "anchor_date": {
      "type": "string",
      "format": "date",
      "readOnly": true
    },
    "subject_email": {
      "type": "string",
      "format": "email",
      "readOnly": true
    },
    "subject_name": {
      "type": "string",
      "readOnly": true
    },
    "me": {
      "type": "string",
      "format": "email",
      "readOnly": true
    },
    "my_role": {
      "type": "string",
      "readOnly": true,
      "example": "subject",
      "description": "The current user's role on this journey (subject, manager, buddy, hr, it, ...; participant when they only hold tasks)."
    },
    "people": {
      "type": "object",
      "readOnly": true,
      "properties": {
        "subject": {
          "type": "object",
          "nullable": true,
          "properties": {
            "email": {
              "type": "string",
              "format": "email"
            },
            "name": {
              "type": "string"
            }
          }
        },
        "manager": {
          "type": "object",
          "nullable": true,
          "properties": {
            "email": {
              "type": "string",
              "format": "email"
            },
            "name": {
              "type": "string"
            }
          }
        },
        "buddy": {
          "type": "object",
          "nullable": true,
          "properties": {
            "email": {
              "type": "string",
              "format": "email"
            },
            "name": {
              "type": "string"
            }
          }
        }
      }
    },
    "next_up": {
      "readOnly": true,
      "nullable": true,
      "description": "The soonest open task the user can act on (or, when everything is locked, the soonest locked one); a task row as in tasks.",
      "allOf": [
        {
          "$ref": "#/components/schemas/JourneyTask"
        }
      ]
    },
    "tasks": {
      "type": "array",
      "readOnly": true,
      "description": "The user's tasks on this journey, soonest due first, each a JourneyTask plus waiting_for and unlocks_on.",
      "items": {
        "allOf": [
          {
            "$ref": "#/components/schemas/JourneyTask"
          },
          {
            "type": "object",
            "properties": {
              "waiting_for": {
                "type": "array",
                "description": "The unmet prerequisites of a locked task: their titles and who holds them.",
                "items": {
                  "type": "object",
                  "properties": {
                    "task_id": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "assignee_email": {
                      "type": "string",
                      "format": "email",
                      "nullable": true
                    },
                    "assignee_name": {
                      "type": "string",
                      "nullable": true
                    },
                    "assignee_role": {
                      "type": "string"
                    },
                    "due_date": {
                      "type": "string",
                      "format": "date",
                      "nullable": true
                    }
                  }
                }
              },
              "unlocks_on": {
                "type": "string",
                "format": "date",
                "nullable": true,
                "description": "For a task locked on a date, the day its phase opens."
              }
            }
          }
        ]
      }
    },
    "counts": {
      "type": "object",
      "readOnly": true,
      "properties": {
        "total": {
          "type": "integer"
        },
        "done": {
          "type": "integer"
        },
        "open": {
          "type": "integer"
        },
        "overdue": {
          "type": "integer"
        },
        "flagged": {
          "type": "integer"
        }
      }
    },
    "progress_percent": {
      "type": "integer",
      "readOnly": true,
      "description": "The whole journey's progress, not just the user's tasks."
    }
  }
}

Document metadata and extensions

{
  "openapi": "3.0.0",
  "info": {
    "title": "Purple Journeys API",
    "version": "1.0.0",
    "description": "Purple Journeys is the suite's employee journey system: the category that tools like ServiceNow Journey Designer, Serval Journeys, Workday Journeys and Enboarder sit in. A journey TEMPLATE describes a lifecycle event (new hire onboarding, offboarding, parental leave, an internal transfer) as PHASES that unlock on dates relative to an anchor (start date, last working day, leave start, return, effective date) or on earlier phases, each holding STEPS assigned to a role: the subject, their manager, a buddy, HR, IT, facilities, payroll, security, or the system. Starting a template for one person creates a JOURNEY and one TASK per step, dated from the anchor and locked until its phase opens and its prerequisites are done. People complete their tasks; automated steps run themselves when they unlock; status checks wait in processing until told they passed; the journey keeps progress_percent, counts, health (warning when anything is overdue, failing when a step errored) and up_next. Everything that happens is on the ACTIVITY feed. Where to start: list-templates shows what exists; list-steps is the step library (every step of every template: what other journeys ask people to do, by system, role and kind); create-templates takes a whole template in one call (phases, steps, roles, due offsets, dependencies, each step's processing system, forms and links) and returns it normalized with warnings instead of rejecting it; preview-templates shows the per-role timeline for a given anchor date; add-steps and remove-step edit it; publish-templates makes it startable (apply_to_active fans new steps into running journeys); create-journeys starts it for a person; list-my-tasks is what the current user owes, list-my-journeys their own journeys, get-my-journey one journey as they see it (next up, their tasks in due order, what each waits for), list-team-journeys their reports'. While a journey runs, add-task adds an ad hoc task the template did not foresee (who, when, why); flag-tasks marks any task as bumpy with a note; submit-tasks sends a form step's values and completes it; complete, start, skip, reopen and reassign move the rest. Every skip, reassignment, reopen and late completion is recorded on the task, and get-insights-templates (Suggested changes in the UI) reads them back across a template's journeys: ad hoc tasks grouped by title with the add-steps body that would promote each into the template, skipped steps, late steps with median days late, reassignments and flags. Webhooks: subscribe with POST /api/purple-suite/instances/{id}/webhooks and an events filter. Besides the kit's journeys.templates|journeys|tasks.created|updated|deleted, this app emits journeys.journey.started|completed|cancelled, journeys.task.unlocked|completed|overdue|added|flagged and journeys.task.form_submitted (task, journey, step_key, values) on every form submission, plus journeys.template.published. Role fields are emails: subject_email, manager_email, buddy_email, assignee_email. Dates are YYYY-MM-DD; timestamps are ISO 8601. The manager-experience app also has a journeys collection: that one only paints a vendor API for a read-only plugin; this app is where journeys are authored and run.\n\n---\n\n**Acting as a user (`X-PS-Impersonate-User`).** Every endpoint accepts an optional `X-PS-Impersonate-User` request header whose value is the email of the instance user to act as. The effective user resolves in this order: the header if present, else the instance's configured default MCP user, else none. User-scoped paths additionally accept the literal `me` in place of an email, and Graph-style apps also serve `/me/…` twins of their `/users/{email}/…` paths that resolve the same way. This header is honored by all operations but is intentionally omitted from each operation's parameter list so it is not surfaced as a per-tool argument by MCP clients; see the `ImpersonateUser` entry under `components.parameters`."
  },
  "security": [
    {
      "patAuth": []
    }
  ]
}