# Journeys

Source: https://kirora.io/docs/api/reference/journeys

A journey is an automation that contacts enter on a trigger, such as an event or joining a list, and move through step by step over time. Where a campaign is one send to everyone at once, a journey sends to each contact when they reach each step. A live journey can be edited: the change reaches new entries, and a send step's content reaches everyone who has not reached the step yet. One an agent key wrote needs a person to confirm it before it goes live. See [Objects and rules](https://kirora.io/docs/api/models#journey-definitions). A contact's journeys: `GET /v1/contacts/{id}/journeys`.

## Endpoints

- [List journeys](https://kirora.io/docs/api/reference/listJourneys): `GET /v1/journeys`
- [Create a journey](https://kirora.io/docs/api/reference/createJourney): `POST /v1/journeys`
- [Retrieve a journey](https://kirora.io/docs/api/reference/getJourney): `GET /v1/journeys/{id}`
- [Update a journey](https://kirora.io/docs/api/reference/updateJourney): `PATCH /v1/journeys/{id}`
- [Delete a journey](https://kirora.io/docs/api/reference/deleteJourney): `DELETE /v1/journeys/{id}`
- [Simulate a journey](https://kirora.io/docs/api/reference/simulateJourney): `POST /v1/journeys/{id}/simulate`
- [Activate a journey](https://kirora.io/docs/api/reference/activateJourney): `POST /v1/journeys/{id}/activate`
- [Pause a journey](https://kirora.io/docs/api/reference/pauseJourney): `POST /v1/journeys/{id}/pause`
- [End a journey](https://kirora.io/docs/api/reference/endJourney): `POST /v1/journeys/{id}/end`
- [Duplicate a journey](https://kirora.io/docs/api/reference/duplicateJourney): `POST /v1/journeys/{id}/duplicate`
- [Retrieve a journey's metrics](https://kirora.io/docs/api/reference/getJourneyMetrics): `GET /v1/journeys/{id}/metrics`
- [Retrieve a journey's metrics step by step](https://kirora.io/docs/api/reference/getJourneyStepMetrics): `GET /v1/journeys/{id}/step-metrics`
- [Check a journey before activating](https://kirora.io/docs/api/reference/getJourneyActivationCheck): `GET /v1/journeys/{id}/activation-check`

## The Journey object

```json
{
  "id": "0192f3a4-3d5e-7a7f-8c43-6b8c9d0e1f2a",
  "name": "Welcome series",
  "trigger": {
    "type": "event",
    "name": "Signed Up",
    "reentry": {
      "mode": "once"
    }
  },
  "exitConditions": [],
  "steps": [
    {
      "id": "wait-an-hour",
      "name": "Wait an hour",
      "type": "wait",
      "duration": {
        "value": 1,
        "unit": "hours"
      }
    },
    {
      "id": "welcome-email",
      "name": "Welcome email",
      "type": "send",
      "channel": "email",
      "content": {
        "subject": "Welcome to Acme Shop",
        "previewText": "Your first order ships free",
        "from": "Acme Shop <hello@mail.acme-shop.com>",
        "templateSnapshot": {
          "schemaVersion": 1,
          "blocks": [
            {
              "id": "title",
              "type": "heading",
              "text": "Welcome to Acme Shop",
              "size": "28px"
            },
            {
              "id": "intro",
              "type": "text",
              "text": "Hi {{ contact.firstName }}, your first order ships free."
            },
            {
              "id": "shop",
              "type": "button",
              "text": "Start shopping",
              "href": "https://acme-shop.com"
            }
          ],
          "theme": {}
        }
      },
      "nextStep": null
    }
  ],
  "messageLimits": {
    "apply": true,
    "retryFor": {
      "value": 24,
      "unit": "hours"
    }
  },
  "utm": null,
  "maxEntries": null,
  "enrolledCount": 128,
  "activeCount": 12,
  "status": "active",
  "pendingAgentReview": false,
  "tags": [
    "Onboarding"
  ],
  "revision": 2,
  "createdAt": "2026-10-01T09:30:00.000Z",
  "updatedAt": "2026-10-02T14:05:00.000Z"
}
```

- `id` (string, required, uuid)
- `name` (string, required)
- `trigger` ([object](https://kirora.io/docs/api/models#journey-definitions), required) — What starts an enrollment.
  - One of: **Event trigger**
    - `type` ("event", required)
    - `name` (string, required, 1–128 characters) — The event that starts an enrollment.
    - `where` (object[], optional, at least 1 item, at most 5 items) — Conditions on the event's properties; all must hold.
      - `property` (string, required, 1–64 characters) — The event property key.
      - `operator` (string, required, one of `equals`, `notEquals`, `gt`, `gte`, `lt`, `lte`) — `notEquals` also matches an event without the property. `equals` and `notEquals` compare the property as text, ignoring case and accents.
      - `value` (string, required, up to 255 characters) — What to compare with, as text (`"pro"`, `"true"`); a number (`"50"`) for `gt`, `gte`, `lt` and `lte`.
    - `reentry` (object, required, default `{"mode":"once"}`) — Whether a contact may enter again: `once` (the default), or `repeatable` once `waitAfterEntry` has passed since their last entry started.
      - One of: **Once**
        - `mode` ("once", required)
      - One of: **Repeatable**
        - `mode` ("repeatable", required)
        - `waitAfterEntry` (DurationOrZero, required) — How long after a contact's last entry started before they may enter again, at most 3,650 days; zero lets entries overlap.
  - One of: **List trigger**
    - `type` ("list", required)
    - `listId` (string, optional, uuid) — The list whose new members enter; unset, joining any list starts it.
    - `reentry` (object, required, default `{"mode":"once"}`) — Whether a contact may enter again: `once` (the default), or `repeatable` once `waitAfterEntry` has passed since their last entry started.
      - One of: **Once**
        - `mode` ("once", required)
      - One of: **Repeatable**
        - `mode` ("repeatable", required)
        - `waitAfterEntry` (DurationOrZero, required) — How long after a contact's last entry started before they may enter again, at most 3,650 days; zero lets entries overlap.
  - One of: **Segment trigger**
    - `type` ("segment", required)
    - `segmentId` (string, optional, uuid) — The segment whose matches enter; a draft may leave it unset.
    - `on` (string, required, one of `enter`, `exit`, default `"enter"`) — `enter`: when a contact begins matching it; `exit`: when they stop. Found by the background sweep within 15 minutes.
    - `reentry` (object, required, default `{"mode":"once"}`) — Whether a contact may enter again: `once` (the default), or `repeatable` once `waitAfterEntry` has passed since their last entry started.
      - One of: **Once**
        - `mode` ("once", required)
      - One of: **Repeatable**
        - `mode` ("repeatable", required)
        - `waitAfterEntry` (DurationOrZero, required) — How long after a contact's last entry started before they may enter again, at most 3,650 days; zero lets entries overlap.
  - One of: **Form trigger**
    - `type` ("form", required)
    - `formId` (string, optional, uuid) — The form whose submissions enter; unset, any form starts it.
    - `where` (object[], optional, at least 1 item, at most 5 items) — Conditions on the submission, such as `outcome` `equals` `created` for new contacts only; all must hold.
      - `property` (string, required, 1–64 characters) — The event property key.
      - `operator` (string, required, one of `equals`, `notEquals`, `gt`, `gte`, `lt`, `lte`) — `notEquals` also matches an event without the property. `equals` and `notEquals` compare the property as text, ignoring case and accents.
      - `value` (string, required, up to 255 characters) — What to compare with, as text (`"pro"`, `"true"`); a number (`"50"`) for `gt`, `gte`, `lt` and `lte`.
    - `reentry` (object, required, default `{"mode":"once"}`) — Whether a contact may enter again: `once` (the default), or `repeatable` once `waitAfterEntry` has passed since their last entry started.
      - One of: **Once**
        - `mode` ("once", required)
      - One of: **Repeatable**
        - `mode` ("repeatable", required)
        - `waitAfterEntry` (DurationOrZero, required) — How long after a contact's last entry started before they may enter again, at most 3,650 days; zero lets entries overlap.
  - One of: **Attribute trigger**
    - `type` ("attribute", required)
    - `key` (string, required, pattern `^[\p{L}\p{N}\p{M}_.-]{1,64}$`) — The contact attribute whose change starts an enrollment.
    - `where` (object[], optional, at least 1 item, at most 5 items) — Conditions on the change: `value` is the new value, `previous_value` the old one. For a list attribute, `value` `equals` an item when it was added.
      - `property` (string, required, 1–64 characters) — The event property key.
      - `operator` (string, required, one of `equals`, `notEquals`, `gt`, `gte`, `lt`, `lte`) — `notEquals` also matches an event without the property. `equals` and `notEquals` compare the property as text, ignoring case and accents.
      - `value` (string, required, up to 255 characters) — What to compare with, as text (`"pro"`, `"true"`); a number (`"50"`) for `gt`, `gte`, `lt` and `lte`.
    - `reentry` (object, required, default `{"mode":"once"}`) — Whether a contact may enter again: `once` (the default), or `repeatable` once `waitAfterEntry` has passed since their last entry started.
      - One of: **Once**
        - `mode` ("once", required)
      - One of: **Repeatable**
        - `mode` ("repeatable", required)
        - `waitAfterEntry` (DurationOrZero, required) — How long after a contact's last entry started before they may enter again, at most 3,650 days; zero lets entries overlap.
  - One of: **Date trigger**
    - `field` (string, required, pattern `^[A-Za-z][A-Za-z0-9_]{0,63}$`) — The contact date field the journey runs from.
    - `recurrence` (string, required, one of `once`, `annual`) — `once` runs on the date a single time; `annual` repeats every year.
    - `offset` (SignedDuration, required) — How far from the date to start, in whole days: negative before it, positive after, at most 365 days either way. Defaults to the day itself.
      - `value` (integer, required)
      - `unit` (string, required, one of `seconds`, `minutes`, `hours`, `days`)
    - `time` (string, required, pattern `^(?:[01]\d|2[0-3]):[0-5]\d$`) — Time of day to start, as `HH:MM` in the contact's timezone, else the workspace's.
    - `leapDay` (string, required, one of `feb28`, `mar1`, `skip`, default `"feb28"`) — For an annual 29 February date in a common year: run on 28 Feb, on 1 Mar, or skip.
    - `type` ("date", required)
- `exitConditions` (object[], required) — Events that end a contact's enrollment early.
  - One of: **Event exit**
    - `type` ("event", required)
    - `name` (string, required, 1–128 characters) — The event that ends a contact's enrollment when they do it.
    - `where` (object[], optional, at least 1 item, at most 5 items) — Conditions on the event's properties; all must hold.
      - `property` (string, required, 1–64 characters) — The event property key.
      - `operator` (string, required, one of `equals`, `notEquals`, `gt`, `gte`, `lt`, `lte`) — `notEquals` also matches an event without the property. `equals` and `notEquals` compare the property as text, ignoring case and accents.
      - `value` (string, required, up to 255 characters) — What to compare with, as text (`"pro"`, `"true"`); a number (`"50"`) for `gt`, `gte`, `lt` and `lte`.
  - One of: **Conversion goal exit**
    - `type` ("conversion_goal", required)
- `steps` ([object[]](https://kirora.io/docs/api/models#journey-definitions), required) — The steps, in the order of the default path. People partway through keep the steps they entered with; an edit applies to new entries.
  - Any of: **Wait step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("wait", required)
    - `duration` (Duration, optional) — How long to wait; at most 365 days. Or set `until` or `businessDays`.
      - `value` (integer, required, min > 0)
      - `unit` (string, required, one of `seconds`, `minutes`, `hours`, `days`)
    - `until` (object, optional) — Wait until a date the contact or the triggering event holds, instead.
      - `from` (string, required, one of `contact`, `event`) — `contact`: an attribute or date; `event`: the trigger event.
      - `key` (string, required, 1–128 characters) — The attribute or date key (`trial_ends_at`), or the event property; a dotted path reads a nested one.
      - `offset` (SignedDuration, optional) — How far before (negative) or after the date; 3 days before is `{ "value": -3, "unit": "days" }`. At most 365 days either way.
        - `value` (integer, required)
        - `unit` (string, required, one of `seconds`, `minutes`, `hours`, `days`)
    - `businessDays` (integer, optional, min 1, max 260) — Wait this many working days instead, counted in the contact's local time and ending at the same time of day: the days of `window.days` when set, else Monday to Friday.
    - `window` (object, optional) — Hours the wait may end in, in the contact's local time.
      - `startHour` (integer, required, min 0, max 23) — Hour of day (0-23) the window opens, inclusive.
      - `endHour` (integer, required, min 1, max 24) — Hour of day (1-24) the window closes; it must be after `startHour`.
      - `days` (integer[], optional, at least 1 item, at most 7 items) — Days of the week the window is open, 0 = Sunday to 6 = Saturday; every day when left out.
    - `nextStep` (string | null, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to run next. Omitted, the next step in `steps` runs (after the last one the journey ends); `null` ends the journey here. The last step of a branch should set `null`, or the enrollment continues into the steps that follow it.
  - Any of: **Wait-until step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("wait_until", required)
    - `event` (object, required) — The event to wait for, and the conditions it must meet.
      - `name` (string, required, 1–128 characters) — The event to wait for.
      - `where` (object[], optional, at least 1 item, at most 5 items) — Conditions on the event's properties; all must hold.
        - `property` (string, required, 1–64 characters) — The event property key.
        - `operator` (string, required, one of `equals`, `notEquals`, `gt`, `gte`, `lt`, `lte`) — `notEquals` also matches an event without the property. `equals` and `notEquals` compare the property as text, ignoring case and accents.
        - `value` (string, required, up to 255 characters) — What to compare with, as text (`"pro"`, `"true"`); a number (`"50"`) for `gt`, `gte`, `lt` and `lte`.
    - `timeout` (Duration, required) — How long to wait for the event: 1 minute to 90 days.
      - `value` (integer, required, min > 0)
      - `unit` (string, required, one of `seconds`, `minutes`, `hours`, `days`)
    - `onMatchStep` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to take when the event happens.
    - `onTimeoutStep` (string | null, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to take on timeout; `null` ends the journey.
  - Any of: **Digest step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("digest", required)
    - `gatherFor` (Duration, required) — How long to gather events: 1 minute to 7 days.
      - `value` (integer, required, min > 0)
      - `unit` (string, required, one of `seconds`, `minutes`, `hours`, `days`)
    - `nextStep` (string | null, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to run next. Omitted, the next step in `steps` runs (after the last one the journey ends); `null` ends the journey here. The last step of a branch should set `null`, or the enrollment continues into the steps that follow it.
  - Any of: **Email send step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("send", required)
    - `channel` ("email", required)
    - `content` (object, required) — The email, as saved.
      - `subject` (string, optional, 1–998 characters) — The subject line. Merge tags are allowed.
      - `previewText` (string, optional, 1–255 characters) — Shown by inboxes after the subject. Merge tags are allowed; keep it near 100 characters.
      - `html` (string, optional, 1–512,000 characters) — The HTML body. Rendered from the design when one is set.
      - `text` (string, optional, 1–512,000 characters) — The plain-text alternative.
      - `from` (string, optional, 1–320 characters) — The sender address, on a verified sending domain.
      - `replyTo` (string, optional, email) — Where replies go; the part before the @ must be ASCII.
      - `templateSnapshot` ([object](https://kirora.io/docs/api/models#template-documents), optional) — The email: a design, or HTML. Copied from a template, or written here; later template edits do not change it.
        - One of: **DesignSnapshot**
        - One of: **HtmlSnapshot**
    - `nextStep` (string | null, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to run next. Omitted, the next step in `steps` runs (after the last one the journey ends); `null` ends the journey here. The last step of a branch should set `null`, or the enrollment continues into the steps that follow it.
    - `onUnreachableStep` (string, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to go to when the contact cannot receive this channel; unset skips the send.
    - `onFailureStep` (string, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to go to when the message cannot be queued because the plan's sending allowance is used up or its marketing contacts are past the plan's limit (recorded `skipped`, reason `plan_limit_reached`); later delivery failures do not branch. Unset continues to `nextStep`.
    - `topicId` (string, optional, uuid) — Sends only to contacts the topic reaches; unset sends to everyone with consent.
    - `transactional` (boolean, optional) — A service message the contact needs whatever their marketing choices (an order receipt, a booking reminder): sent without email consent or a topic, not held by quiet hours or message limits, with no unsubscribe link, as a transactional send is. Only an address that bounced, or marked transactional email as spam, is skipped. Not for promotions.
  - Any of: **Push send step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("send", required)
    - `channel` ("push", required)
    - `content` (object, required) — The notification. Any part may be missing on a draft.
      - `pushProjectId` (string, optional, uuid) — The push app to send through.
      - `push` (object, optional) — The notification content.
        - `title` (string, optional, 1–100 characters) — The notification title (up to 100 characters).
        - `body` (string, optional, 1–500 characters) — The notification text (up to 500 characters).
        - `actionUrl` (string, optional, uri, up to 2,048 characters) — The HTTPS page opened when the notification is tapped.
        - `imageUrl` (string, optional, uri, up to 2,048 characters) — A large picture: web and Android show it; iOS needs the app's service extension.
        - `iconUrl` (string, optional, uri, up to 2,048 characters) — Web only; iOS and Android show the app's own notification icon.
        - `data` (map<string, string>, optional, at most 10 keys, keys: pattern `^[A-Za-z0-9_.-]{1,64}$`, values: up to 1,024 characters) — Up to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
        - `buttons` (object[], optional, at least 1 item, at most 2 items) — Up to 2 action buttons.
        - `collapseKey` (string, optional, pattern `^[A-Za-z0-9_-]{1,64}$`) — A newer notification with the same key replaces the older one on the device.
        - `sound` (string, optional, 1–64 characters) — The name of a sound the app ships; the device's default if unset.
        - `badge` (integer, optional, min 0, max 99999) — The number shown on the iOS app icon.
        - `ttl` (DurationOrZero, optional) — How long a provider keeps trying an offline device, up to 28 days; zero delivers now or never. Defaults to one day.
        - `urgency` (string, optional, one of `high`, `normal`) — `high` asks the push service to deliver promptly, even to a sleeping device; `normal` lets it wait for a good moment to save battery. Device settings and the app still decide whether and how it is shown. Unset, a transactional push is `high` and any other `normal`.
        - `overrides` (object, optional) — Fields that differ on one platform, each replacing the shared one there (a whole `data` map, not merged key by key). On iOS and Android `actionUrl` may be an app link such as `myapp://orders`; the shared one and the web one must be HTTPS.
    - `nextStep` (string | null, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to run next. Omitted, the next step in `steps` runs (after the last one the journey ends); `null` ends the journey here. The last step of a branch should set `null`, or the enrollment continues into the steps that follow it.
    - `onUnreachableStep` (string, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to go to when the contact cannot receive this channel; unset skips the send.
    - `onFailureStep` (string, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to go to when the message cannot be queued because the plan's sending allowance is used up or its marketing contacts are past the plan's limit (recorded `skipped`, reason `plan_limit_reached`); later delivery failures do not branch. Unset continues to `nextStep`.
    - `topicId` (string, optional, uuid) — Sends only to contacts the topic reaches; unset sends to everyone with consent.
  - Any of: **Branch step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("branch", required)
    - `where` (object[], required, at least 1 item, at most 5 items) — Conditions on the trigger event's properties; all must hold.
      - `property` (string, required, 1–64 characters) — The event property key.
      - `operator` (string, required, one of `equals`, `notEquals`, `gt`, `gte`, `lt`, `lte`) — `notEquals` also matches an event without the property. `equals` and `notEquals` compare the property as text, ignoring case and accents.
      - `value` (string, required, up to 255 characters) — What to compare with, as text (`"pro"`, `"true"`); a number (`"50"`) for `gt`, `gte`, `lt` and `lte`.
    - `onMatchStep` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to take when the conditions hold.
    - `otherwiseStep` (string | null, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to take when they do not; `null` ends the journey.
  - Any of: **Filter step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("filter", required)
    - `mode` (string, required, one of `once`, `continuous`) — `once` checks the rules here only; `continuous` re-checks them before every later step.
    - `rules` ([object](https://kirora.io/docs/api/models#segment-rules), required) — The contact must match these rules.
      - Any of: **Match all**
        - `all` (object[], required, at least 1 item, at most 20 items) — Every rule or nested group must match.
      - Any of: **Match any**
        - `any` (object[], required, at least 1 item, at most 20 items) — At least one rule or nested group must match.
    - `onMatchStep` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to take when the contact matches.
    - `otherwiseStep` (string | null, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to take when they do not; `null` ends the journey.
  - Any of: **Split step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("split", required)
    - `paths` (object[], required, at least 1 item, at most 5 items) — Paths checked in order; the first whose rules match is taken.
      - `rules` ([object](https://kirora.io/docs/api/models#segment-rules), required) — The contact takes this path when they match.
        - Any of: **Match all**
        - Any of: **Match any**
      - `nextStep` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step this path leads to.
    - `otherwiseStep` (string | null, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to take when no path matches; `null` ends the journey.
  - Any of: **Random split step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("random_split", required)
    - `paths` (object[], required, at least 2 items, at most 5 items) — Two to five paths whose shares add up to 100. A contact takes the same path each time it enters, until the shares change.
      - `percent` (integer, required, min 1, max 99) — Share of the contacts taking this path, in percent.
      - `nextStep` (string | null, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step this path leads to; `null` is a holdout.
  - Any of: **Action step**
    - `id` (string, required, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The step's id, unique within the journey: transitions name it, and its metrics follow it across edits. Reusing a deleted step's id merges their metrics.
    - `name` (string, optional, 1–255 characters) — A label for the step, shown in metrics.
    - `type` ("action", required)
    - `action` (object, required) — What this step does to the contact.
      - One of: **Set attribute**
        - `kind` ("set_attribute", required)
        - `key` (string, required, pattern `^[\p{L}\p{N}\p{M}_.-]{1,64}$`) — The contact attribute's key.
        - `value` (string | number | boolean | null, required) — The value to set; `null` clears it.
      - One of: **Add to or remove from list attribute**
        - `kind` (string, required, one of `add_to_attribute`, `remove_from_attribute`)
        - `key` (string, required, pattern `^[\p{L}\p{N}\p{M}_.-]{1,64}$`) — The contact attribute's key.
        - `value` (string | number | boolean, required) — The one item to add to, or remove from, the list; other items are kept.
      - One of: **Add to list**
        - `kind` ("add_to_list", required)
        - `listId` (string, required, uuid) — The list to add the contact to.
      - One of: **Remove from list**
        - `kind` ("remove_from_list", required)
        - `listId` (string, required, uuid) — The list to remove the contact from.
      - One of: **Send webhook**
        - `kind` ("webhook", required)
        - `webhookId` (string, required, uuid) — The webhook endpoint to notify.
        - `data` (map<string, any>, required, default `{}`) — Extra data sent in the `journey.action_reached` payload.
    - `nextStep` (string | null, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — The id of the step to run next. Omitted, the next step in `steps` runs (after the last one the journey ends); `null` ends the journey here. The last step of a branch should set `null`, or the enrollment continues into the steps that follow it.
- `messageLimits` (MessageLimitUse, required)
  - `apply` (boolean, required) — Whether its messages count toward the workspace's message limits and are held by them.
  - `retryFor` (Duration, optional) — How long a message over a limit keeps being retried before it is skipped with reason `message_limit`: whole hours, at most 48. Unset, it is skipped at once. Absent when `apply` is false.
    - `value` (integer, required, min > 0)
    - `unit` (string, required, one of `seconds`, `minutes`, `hours`, `days`)
- `utm` (object | null, required) — Link tags for the journey's email; `null` follows the workspace.
  - `enabled` (boolean, required)
  - `source` (string, optional, pattern `^[\p{L}\p{N}\p{M} ._\-+/]+$`, 1–100 characters)
  - `medium` (string, optional, pattern `^[\p{L}\p{N}\p{M} ._\-+/]+$`, 1–100 characters)
  - `campaign` (string, optional, pattern `^[\p{L}\p{N}\p{M} ._\-+/]+$`, 1–100 characters)
  - `content` (string, optional, pattern `^[\p{L}\p{N}\p{M} ._\-+/]+$`, 1–100 characters)
  - `term` (string, optional, pattern `^[\p{L}\p{N}\p{M} ._\-+/]+$`, 1–100 characters)
- `maxEntries` (integer | null, required, min > 0) — The most enrollments the journey starts over its life; `null` has no cap. It is full once `enrolledCount` reaches it: it stays `active` but nobody new enters.
- `enrolledCount` (integer, required, min 0) — Enrollments ever started.
- `activeCount` (integer, required, min 0) — Enrollments in progress now.
- `status` (string, required, one of `draft`, `active`, `paused`, `ended`) — `ended`: nobody enters and enrollments in progress were exited; it cannot restart.
- `pendingAgentReview` (boolean, required) — An agent key authored or edited it and it is a draft or paused, so it waits in the agent review queue: activating needs a person's approval.
- `tags` (string[], required) — Tag names, sorted without case.
- `revision` (integer, required, min > 0, max 2147483647) — Raised by every edit. Send it back as `expectedRevision` on the update so a change made since you read it is refused instead of overwritten.
- `createdAt` (string, required, date-time)
- `updatedAt` (string, required, date-time)
