---
title: "Stop journey"
method: POST
path: "/api/v1/journeys/{journeyId}/stop"
tags: ["Journeys"]
---

# Stop journey

`POST /api/v1/journeys/{journeyId}/stop`

## Request

- Base URL: `https://www.useboom.ai`
- URL: `https://www.useboom.ai/api/v1/journeys/{journeyId}/stop`
- Auth: HTTP bearer

Retire a live journey: no one else is enrolled, and anyone already enrolled who has not been messaged yet is dropped before their message goes out. Conversations already in progress continue normally. Requires confirm: true. This cannot be undone — re-running the campaign means publishing again.

## Path parameters

- `journeyId` string, required: A published journey id, or the initiative id (resolves to its live journey).

## Request body

- object
  - `confirm` boolean: Must be true to stop. Stopping halts customer outreach.

## Response `201`

Success

- object
  - `id` string, required: Journey (workflow version) id.
  - `initiativeId` string, required: The initiative this journey belongs to.
  - `status` 'DRAFT' | 'PUBLISHED' | 'STOPPED', required: DRAFT (editable), PUBLISHED (live, frozen), or STOPPED (retired).
  - `version` integer, required: Version number within the initiative.
  - `definition` object, required: The full journey graph.
    - `id` string: Journey id; carried for round-trip ergonomics. Optional on create.
    - `name` string, required: Journey name.
    - `metadata` object: Optional journey-level metadata.
      - `initiativeType` string
      - `version` integer
      - `cancelOnEvents` object: Cancel an in-flight run when any of these events arrive.
        - `eventNames` string[], required
        - `matchRunKey` boolean: Cancel only the run whose key equals this event's `parallelRunsBy` property, instead of every run the person has. Leave it unset on a journey with several runs at once: that already matches on the key, and setting it to false there is refused at publish, because one completed payment would stop the person's other payments too.
      - `environmentId` string: Pinned environment whose non-secret {{env.*}} values HTTP nodes resolve against. Frozen at publish. The server preserves the stored value when a whole-graph update omits it.
    - `actions` union[], required: The nodes. Exactly one ENTRY, and at least one end point (EXIT or ROUTE_TO_INITIATIVE), are required to publish.
      - union
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'ENTRY', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Trigger node config. Exactly one ENTRY node per journey.
            - `description` string
            - `triggerType` 'manual' | 'segment' | 'cdp_event' | 'inbound': How people enter: manual (added by an operator or API call), segment (on entering a segment), cdp_event (when an event is received), or inbound (when a message arrives on one of the org's channels). Defaults to manual.
            - `segmentId` string: Segment id — required when triggerType is segment.
            - `segmentName` string: Cached segment display name (optional).
            - `eventName` string: CDP event name — required when triggerType is cdp_event.
            - `inboundAction` 'WHATSAPP_MESSAGE' | 'SMS_MESSAGE' | 'INSTAGRAM_MESSAGE' | 'MESSENGER_MESSAGE' | 'INSTAGRAM_COMMENT' | 'FACEBOOK_COMMENT': Which arrival fires the trigger — required when triggerType is inbound. INSTAGRAM_COMMENT / FACEBOOK_COMMENT listen to public comments on the account's posts instead of DMs; they require Instagram or Messenger channels whose connection was granted comment permission, and enroll the commenter without opening a conversation.
            - `inboundChannelIds` string[]: Channel ids to listen on — at least one required when triggerType is inbound. Must be NATIVE WhatsApp, SMS, Instagram or Messenger channels that are not deleted — WhatsApp and SMS additionally need an ONLINE sender, Instagram and Messenger have no sender row and need none (a Messenger Page still on the V1 bridge is withheld as NOT_NATIVE); journeys_set_trigger names the eligible ids if you pass an ineligible one. For a comment action, only Instagram and Messenger channels are valid. Several published journeys may share one channel as long as their keywords differ: publishing a rule whose keyword (or blank catch-all) is already taken on that channel is refused with INBOUND_CHANNEL_TAKEN. Each arriving message enrols exactly one of them, the most specific matching rule, ties going to the older initiative.
            - `inboundKeyword` string: The word the inbound message must match for this journey to enrol — e.g. 'PRECIO'. Blank or absent means the trigger is the catch-all and fires on ANY message arriving on the channel, which is almost never what you want beside other journeys on the same number. Several journeys may share one channel as long as their keywords differ — two rules with the same keyword on one channel, or two blank ones, are refused at publish with INBOUND_CHANNEL_TAKEN. The keyword is matched accent-, case- and punctuation-insensitively, and stored normalized.
            - `inboundMatchMode` 'EXACT' | 'CONTAINS' | 'ALL_WORDS': How inboundKeyword is matched: EXACT (the message is only-the-word), ALL_WORDS (the message contains all-the-words, in any order), or CONTAINS (the message contains-the-word, adjacent and in order). All three are word-based, so no mode ever matches inside a longer word ('precioso' does not match 'precio'). Defaults to EXACT. Ignored when inboundKeyword is blank.
            - `maxEnrollments` integer: Frequency cap: max enrollments per person within enrollmentWindow. Both-or-neither with enrollmentWindow.
            - `enrollmentWindow` string: Rolling window for the frequency cap.
            - `parallelRunsBy` string: Let one person have several runs of this journey at the same time, one per distinct value of this event property (e.g. "paymentLinkId"). Use it when a person can have several of the same thing in flight, two payment links or two orders, and each needs its own sequence. Three things to know. It works on cdp_event triggers only, because the run is identified by a property of the triggering event; an event that does not carry the property is refused rather than enrolled. The journey becomes SEND ONLY: no wait-for-reply, conversation or AI nodes, because an inbound message belongs to the person and not to one run, so a single reply would wake every run at once. And every stop event listed in cancelOnEvents matches on this same property, so a completed one stops only its own run and leaves the person's other runs alive. Publish is refused if any of that is violated.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'SEND_MESSAGE', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Sends WhatsApp: an approved template (default), or free-text prose with mode 'free_text'.
            - `mode` 'template' | 'free_text': What this send carries: 'template' (an approved WhatsApp template, the default when omitted) or 'free_text' (prose you write, needing `body` instead of `templateId`). free_text emits SENT and WINDOW_CLOSED, both of which must be wired — Meta accepts free-form content only within 24 hours of the customer's last message, checked before sending. Prefer this over the deprecated SEND_WHATSAPP_TEXT node, which is the same send under an older name.
            - `templateId` string: Approved WhatsApp template id. Resolve ids from the catalog.
            - `templateName` string
            - `body` string: The message text, for mode 'free_text' only. Placeholders are named, like {{customer.name}}, and EVERY one must have a matching entry in `bindings` or publish is refused: an unbound placeholder is sent literally, braces and all. Bind an upstream HTTP response with engagement.nodeOutputs.<nodeId>.body.<field>.
            - `bindings` object: Map of {{placeholder}} name → variable reference, for `body`. Template mode uses `templateBindings` instead.
            - `channel` 'whatsapp': Always whatsapp. Other channels have their own node kind — SEND_SMS for a text, SEND_INSTAGRAM for Instagram, SEND_MESSENGER for Facebook Messenger.
            - `templateBindings` object: Map of template placeholder → variable reference, or fixed text prefixed `literal:` (e.g. `{ "2": "literal:BIENVENIDA20" }`) for a value that is the same for every recipient. EVERY placeholder the template declares (body, header, footer, button URL) needs an entry or publish is refused, since an unbound one is sent literally. The only exception: a template whose sole placeholder is {{1}}, with no map at all, falls back to the customer name (not on a Transactional).
            - `channelId` string: Sending WhatsApp channel (WABA) id. Required before publishing.
            - `sendImmediately` boolean: Deprecated business-hours gate; prefer a preceding DELAY.
            - `delivery` 'immediate' | 'draft' | 'draft_continue': How the send goes out. Omit for 'immediate'. 'draft' holds the rendered send in the Drafts queue until an org member approves or rejects it, and the run WAITS at this node: approve sends it and continues on SENT; reject emits REJECTED (optional edge — unwired, the run ends). 'draft_continue' queues the same draft but the run moves on at once over the SENT edge, before anything is sent (the step records QUEUED, not SENT): approving later only sends the message, rejecting only discards it, neither moves the customer, and there is no REJECTED handle. Use it to review a sample of a journey's messages without holding its runs, e.g. while migrating from another tool. A wait-for-reply after a queued WhatsApp send starts counting before the message goes out, so give it a timeout longer than the review takes. Both draft modes are refused (`drafts_disabled`) while drafts are turned off for the organization; a published drafted send reached while they're off is skipped (SKIPPED), never sent unreviewed.
            - `draftTtlDays` integer: Drafted sends only (either draft mode): a draft nobody reviewed within this many days (1-30) expires and is never sent. A waiting run continues on REJECTED when its draft expires. Omit to keep drafts until someone decides them.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'SEND_SMS', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Sends a plain-text SMS.
            - `body` string: The message text. Free text — SMS has no approved-template concept. Placeholders are named, like {{customer.name}}, and EVERY one must have a matching entry in `bindings` or publish is refused: an unbound placeholder is sent literally, braces and all. The opt-out footer and the brand are added by the system; do not write them into the body.
            - `channelId` string: Sending SMS channel id. Required before publishing.
            - `bindings` object: Map of {{placeholder}} name → variable reference. The builder writes the path as its own name ({"customer.name": "customer.name"}); an alias is allowed as long as the body uses the alias.
            - `optOutText` string: Opt-out line for this node, overriding the organization's. Must contain the STOP keyword. Omit to use the organization's.
            - `allowEmoji` boolean: Keep emoji instead of stripping them. Defaults to false, because emoji force UCS-2 encoding, which cuts a message part from 160 characters to 70 and roughly doubles the cost.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'SEND_WHATSAPP_TEXT', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Sends free-text WhatsApp — author-written prose instead of an approved template. Emits SENT or WINDOW_CLOSED, and BOTH must be wired. Meta accepts free-form content only within 24 hours of the customer's last message; the node checks that BEFORE sending (Twilio would accept an out-of-window message and fail it asynchronously with 63016), so WINDOW_CLOSED means nothing was sent and is where you put an approved template. A node with no upstream WAIT_FOR_REPLY / MANAGE_CONVERSATION will usually take WINDOW_CLOSED — publish warns but does not block, because a customer who wrote recently on this number in ANY journey still has an open window.
            - `body` string: The message text. Free text — no Meta template approval needed, which is the point of this node. Placeholders are named, like {{customer.name}}, and EVERY one must have a matching entry in `bindings` or publish is refused: an unbound placeholder is sent literally, braces and all. Bind an upstream HTTP response with engagement.nodeOutputs.<nodeId>.body.<field>.
            - `channelId` string: Sending WhatsApp channel (WABA) id. Required before publishing; it is also the per-number send throttle key.
            - `bindings` object: Map of {{placeholder}} name → variable reference. The builder writes the path as its own name ({"customer.name": "customer.name"}); an alias is allowed as long as the body uses the alias.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'SEND_INSTAGRAM', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Sends free-text Instagram from a chosen native account — no template concept, so every message is author-written prose. Emits SENT or WINDOW_CLOSED, and BOTH must be wired. Meta accepts free-form content only within 24 hours of the customer's last message; the node checks that BEFORE sending, so WINDOW_CLOSED means nothing was sent. Instagram has no template fallback, so WINDOW_CLOSED usually leads to EXIT — say so explicitly rather than leaving it dangling. Exception: in a run an INSTAGRAM_COMMENT trigger started, a shut window sends one private reply to that comment instead (same account, comment under 7 days old and not yet replied to); Meta decides whether it lands.
            - `body` string: The message text. Free text — Instagram has no approved-template concept. Placeholders are named, like {{customer.name}}, and EVERY one must have a matching entry in `bindings` or publish is refused: an unbound placeholder is sent literally, braces and all.
            - `channelId` string: Sending native Instagram channel id. Required before publishing, with no org-primary fallback.
            - `bindings` object: Map of {{placeholder}} name → variable reference. The builder writes the path as its own name ({"customer.name": "customer.name"}); an alias is allowed as long as the body uses the alias.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'SEND_EMAIL', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Sends a PUBLISHED email template to the customer.
            - `templateId` string: A PUBLISHED email template id from journeys_email_templates. Required before publishing; a draft or archived template is refused at publish.
            - `templateName` string: The template's name, cached so the canvas can label the node without a lookup. Copy it from journeys_email_templates.
            - `bindings` object: Map of the template's variable key (its `variables[].key` in journeys_email_templates) → variable reference such as customer.name, or engagement.chatLink for the link that opens this run's web chat. Bind only what you want to reroute: a variable left out resolves its own `path`, as the template was authored.
            - `fallbacks` object: Map of the template's variable key → text to send when that variable resolves blank (no value for this person), e.g. { "customer.name": "cliente" }, keyed by `variables[].key` like `bindings`. Overrides the template's own fallback for this step only. A variable with no fallback anywhere renders empty.
            - `fromSenderIdOverride` string: Sender id to send From for this node only, overriding the template's pick and the org default. Must be a sender with usableAsFrom: true in journeys_email_templates. Omit to inherit.
            - `replyToSenderIdOverride` string: Sender id to use as Reply-To for this node only. Omit to inherit the template, then the org default.
            - `delivery` 'immediate' | 'draft' | 'draft_continue': How the send goes out. Omit for 'immediate'. 'draft' holds the rendered send in the Drafts queue until an org member approves or rejects it, and the run WAITS at this node: approve sends it and continues on SENT; reject emits REJECTED (optional edge — unwired, the run ends). 'draft_continue' queues the same draft but the run moves on at once over the SENT edge, before anything is sent (the step records QUEUED, not SENT): approving later only sends the message, rejecting only discards it, neither moves the customer, and there is no REJECTED handle. Use it to review a sample of a journey's messages without holding its runs, e.g. while migrating from another tool. A wait-for-reply after a queued WhatsApp send starts counting before the message goes out, so give it a timeout longer than the review takes. Both draft modes are refused (`drafts_disabled`) while drafts are turned off for the organization; a published drafted send reached while they're off is skipped (SKIPPED), never sent unreviewed.
            - `draftTtlDays` integer: Drafted sends only (either draft mode): a draft nobody reviewed within this many days (1-30) expires and is never sent. A waiting run continues on REJECTED when its draft expires. Omit to keep drafts until someone decides them.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'SEND_MESSENGER', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Sends free-text Facebook Messenger from a chosen Page — every message is author-written prose. Emits SENT or WINDOW_CLOSED, and BOTH must be wired. Meta accepts it only within 24 hours of the customer's last message; the node checks that BEFORE sending, so WINDOW_CLOSED means nothing was sent. There is no template fallback, so WINDOW_CLOSED usually leads to EXIT — say so explicitly rather than leaving it dangling. Exception: in a run an FACEBOOK_COMMENT trigger started, a shut window sends one private reply to that comment instead (same Page, comment under 7 days old and not yet replied to); Meta decides whether it lands.
            - `body` string: The message text, at most 2000 characters. Free text — no approved-template concept. Placeholders are named, like {{customer.name}}, and EVERY one must have a matching entry in `bindings` or publish is refused: an unbound placeholder is sent literally, braces and all.
            - `channelId` string: Sending native Messenger channel (Facebook Page) id, from journeys_message_channels (sendNodeKind SEND_MESSENGER). Required before publishing, with no org-primary fallback.
            - `bindings` object: Map of {{placeholder}} name → variable reference. The builder writes the path as its own name ({"customer.name": "customer.name"}); an alias is allowed as long as the body uses the alias.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'CONVERSATION_BLOCK', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Legacy combined wait + AI conversation block.
            - `goal` string
            - `maxTimeout` string: How long to wait for a reply before timing out.
            - `mode` 'AGENT' | 'ESCALATE'
            - `outputs` object[]
              - …
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'WAIT_FOR_REPLY', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Waits for the person to reply.
            - `maxTimeout` string: How long to wait for the first reply before timing out.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'MANAGE_CONVERSATION', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Runs the AI-led conversation.
            - `mode` 'AGENT' | 'ESCALATE': AGENT (AI-led) or ESCALATE (human handoff). Defaults to AGENT.
            - `goal` string: Instructions for this conversation step only, on top of the initiative objective (e.g. 'Confirm the delivery address before anything else'). AGENT mode only; ignored in ESCALATE.
            - `inactivityTimeout` string: Optional custom idle window (e.g. '2h', '30m') that overrides the global inactive-conversation close. Resets on each inbound message; after this much silence the node closes the conversation (routes CLOSED), unless the AI has escalated it to a human, in which case it stays open. Hard cap 24h; under 30m is allowed but flagged 'not recommended'. Requires the journey-inactivity-timeout flag.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'DISPATCH_EVENT', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Records a CDP event for the person.
            - `eventName` string, required: CDP event name to record. Must match the CDP name pattern.
            - `properties` object: Literal string values, or { var: path } bindings resolved to typed values at dispatch time.
            - `description` string
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'DELAY', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Pauses the run.
            - `mode` 'duration' | 'until_date' | 'until_weekday': How the wait is measured. Defaults to duration.
            - `duration` string: For mode duration.
            - `targetAt` string: ISO 8601 instant, for mode until_date.
            - `weekdays` integer[]: ISO weekdays 1=Mon..7=Sun, for mode until_weekday.
            - `windowStartMinutes` integer
            - `windowEndMinutes` integer
            - `timezone` string: IANA timezone. Load-bearing for until_weekday.
            - `description` string
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'DECISION', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Two-way branch. Emits YES / NO.
            - `logic` 'AND' | 'OR', required
            - `conditions` union[], required
              - …
            - `description` string
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'CASE', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: N-way switch on a person attribute.
            - `selectionPath` string, required: Value to switch on; must start with one of "attributes.", "engagement.extracted.", "engagement.nodeOutputs.", "matched." (e.g. attributes.plan_tier). "matched.<type>.<attr>" (e.g. matched.order.order_status) switches on the CURRENT value of an attribute of the row that put the person in the entry segment — the first matched row of that type, read live; segment-triggered journeys only, and the type must be in the segment's matched types. A formatting helper ("| currency:MXN", "| count") is REFUSED here, unlike on a DECISION condition: CASE reads this path raw, so a helper would resolve blank and route case:default on every run.
            - `branches` object[], required: Up to 10 branches. A case:default handle is always required.
              - …
            - `description` string
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'RANDOM_SPLIT', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Randomly routes each run down one of N weighted arms.
            - `arms` object[], required: At least 2 and at most 10 arms whose weights total exactly 100. There is no default handle — every roll lands on a declared arm.
              - …
            - `description` string
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'HTTP_REQUEST', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Calls an external HTTP endpoint. Emits SUCCESS or FAILED.
            - `method` 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', required: HTTP method.
            - `url` string, required: Target URL. Supports {{variable}} placeholders. Resolvable roots here: customer.<field> (a single flat field of the customer record), person.<attribute> (CDP), env.<key> (journey environment), engagement.context.*, engagement.workflowState.*, engagement.extracted.*, engagement.nodeOutputs.<nodeId>.*, engagement.documents (json body, whole quoted token, flag-gated), and engagement.conversation.id / engagement.conversation.inboxUrl — the conversation this run is talking to and its Shared Inbox deep link. Anything else substitutes as an empty string and is reported at publish as UNRESOLVABLE_HTTP_TOKEN. Note engagement.chatLink is SEND_EMAIL-only and does NOT resolve here.
            - `headers` object[]: Request headers as key/value pairs. Values support the same {{variable}} placeholders as the url. Resolvable roots here: customer.<field> (a single flat field of the customer record), person.<attribute> (CDP), env.<key> (journey environment), engagement.context.*, engagement.workflowState.*, engagement.extracted.*, engagement.nodeOutputs.<nodeId>.*, engagement.documents (json body, whole quoted token, flag-gated), and engagement.conversation.id / engagement.conversation.inboxUrl — the conversation this run is talking to and its Shared Inbox deep link. Anything else substitutes as an empty string and is reported at publish as UNRESOLVABLE_HTTP_TOKEN. Note engagement.chatLink is SEND_EMAIL-only and does NOT resolve here.
              - …
            - `body` object: Request body. mode none sends no body. Supports the same {{variable}} placeholders as the url; in json mode a whole quoted token ("{{engagement.documents}}") substitutes as a JSON array. Resolvable roots here: customer.<field> (a single flat field of the customer record), person.<attribute> (CDP), env.<key> (journey environment), engagement.context.*, engagement.workflowState.*, engagement.extracted.*, engagement.nodeOutputs.<nodeId>.*, engagement.documents (json body, whole quoted token, flag-gated), and engagement.conversation.id / engagement.conversation.inboxUrl — the conversation this run is talking to and its Shared Inbox deep link. Anything else substitutes as an empty string and is reported at publish as UNRESOLVABLE_HTTP_TOKEN. Note engagement.chatLink is SEND_EMAIL-only and does NOT resolve here.
              - …
            - `credentialId` string: Deprecated/legacy: HttpCredential row id used to authenticate the request. Prefer credentialKey.
            - `credentialKey` string: Logical credential key; the pinned environment selects the secret.
            - `timeoutMs` integer: Request timeout in milliseconds. Max 30000 (30s).
            - `maxAttempts` integer: Max attempts including the first. Max 5.
            - `documentUrlTtlSeconds` integer: TTL (seconds, ≤86400) for presigned engagement.documents URLs this node emits. Default 86400.
            - `documentMimeTypes` string[]: Allowlist of MIME types this node may egress in engagement.documents, e.g. ['application/pdf'] for an endpoint that only accepts PDFs. Omit (or pass an empty list) to send every document type — the default. A document whose type is not listed is withheld and counted in the step's documents.filteredCount, never silently dropped. Match is exact, trimmed and case-insensitive; a document with no recorded type is withheld whenever a list is set. Each entry must be a MIME type an inbound document can actually have, because a well-formed typo would match nothing and withhold everything: image/jpeg, image/png, image/webp, image/gif, application/pdf, video/mp4, text/vcard, text/html, application/msword, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/octet-stream.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'ROUTE_TO_INITIATIVE', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Terminal. Ends this engagement and enrolls the customer into another initiative, moving the live conversation onto a new session. Emits no signals — it has no outgoing edges. Needs a signed-in user (MCP or the in-app assistant): it moves live conversations to another initiative's agent, so an API key cannot add it.
            - `initiativeId` string, required: Target initiative id to enroll this customer into.
            - `context` object: Values handed to the target initiative's run. Each key lands in the target run as engagement.workflowState.<key>, readable by its DECISION/CASE nodes and by its agent. A value is a literal string, or { var: 'engagement.extracted.<slug>' } (or any other variable path) resolved at route time.
            - `description` string
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'REQUEST_CALL_PERMISSION', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Asks the customer for permission to call them on WhatsApp.
            - `channelId` string: WhatsApp channel id from journeys_message_channels with `callingEnabled: true`. Required before publishing.
            - `timeout` string: How long to wait for the customer's answer before TIMEOUT. Default 48h, at most 7d.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'PLACE_CALL', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Calls the customer on WhatsApp; the initiative's voice agent talks.
            - `channelId` string: WhatsApp channel id from journeys_message_channels with `callingEnabled: true`. Required before publishing.
            - `maxWait` string: How long to wait for the call's outcome before TIMEOUT. Default 2h, at most 24h.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'SEND_REWARD', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Sends the customer a gift card through the organization’s connected Tremendous account.
            - `amount` number: Reward value in `currency`, at most two decimals. Must fall in the curated range (USD 1–2000, MXN 1–20000). Required before publishing.
            - `currency` 'USD' | 'MXN': Reward currency. Required before publishing.
            - `criterion` string: Who qualifies, in plain words (at most 500 characters), e.g. "Finished the interview and gave a reason for leaving". Judged by an AI against the conversation when the run reaches this node. Empty = everyone who reaches the node qualifies.
            - `delivery` 'conversation' | 'email': 'conversation': a redemption link on WhatsApp, sent with the organization's approved reward template (REWARD_DELIVERY) from the journey's WhatsApp number. 'email': the reward provider emails the customer (needs an email on the person). Required before publishing.
            - `approval` 'review': How each reward is approved. Only 'review' exists: a person approves each one in Drafts.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'SHOPIFY_UPSERT_CUSTOMER', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Creates or updates the person as a Customer in the organization's connected Shopify store.
            - `sourceId` string: The Shopify store to write to: a Shopify source id from cdp_sources_list (provider SHOPIFY, status ACTIVE). Required before publishing.
            - `tags` string[]: Tags ADDED to the customer (never replacing the store's own), at most 10. Each may mix text and {{variables}} from engagement.* (e.g. {{engagement.workflowState.form_name}} for an event property, {{engagement.extracted.<key>}} for conversation-extracted data), person.* or customer.*; filled when the step runs, then trimmed to 40 characters, and a tag that fills blank is dropped. The 40-character limit applies to the fixed text. Absent = ["boom-lead"]; [] = none.
            - `note` string: Customer note, written only when the customer is created; an existing customer's note is never touched. May mix text and {{variables}} (same roots as tags); at most 500 characters of fixed text, and the filled note is cut to 500.
            - `syncMarketingConsent` boolean: Default true: when the triggering event records a marketing opt-in (Lead Capture `consent`, an embedded form's `consent_marketing`), the customer is subscribed in Shopify for email (if they have one) and SMS (if they have a phone). Never unsubscribes, and never re-subscribes a customer who unsubscribed in Shopify.
            - `updateExisting` 'fill_blanks' | 'never': What happens when the store already has this customer (matched by email, then phone). 'fill_blanks' (default): only blank fields are filled, tags added and an opt-in applied — names, email and phone the store has are never overwritten. 'never': the customer is left untouched and the node takes SKIPPED.
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'LOAD_OBJECT', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Loads one custom object record so later steps can use its attributes.
            - `objectType` string: The custom object type name (cdp_custom_object_types_list `name`). Required before publishing.
            - `saveAs` string: The variable name later steps read the record under: saveAs 'brand' → {{brand.logo_url}}, {{brand.primary_color}}; saveAs 'order' → {{order.total | currency:MXN}}. One lowercase word, unique per journey, not customer/person/engagement/event/env. Required before publishing. Publishing checks every <saveAs>.<field> a later step (or its email template) reads is loaded by this node.
            - `lookup` union: How to find the record: 'key' (an id the run carries) or 'relationship' (follow links from the run's person).
              - …
            - `attributes` string[]: The record's attributes to make available to later steps (at least 1, at most 30), read as {{<saveAs>.<attr>}}. Strings longer than 2000 characters are cut.
            - `orderBy` object: When several records match, the one with the lowest ('asc') or highest ('desc') value of this attribute wins; records missing it come last. Without orderBy, several matches load nothing.
              - …
        - object
          - `id` string, required: Node id, unique within the journey.
          - `kind` 'EXIT', required
          - `name` string: The label shown on the canvas. Required before publishing.
          - `position` object: Explicit canvas position. Omit it and the server auto-lays-out the graph top-down; only set it to preserve a hand-arranged layout.
            - `x` number, required
            - `y` number, required
          - `inputs` object: Terminal node.
            - `outcome` string
    - `edges` object[], required: The connections between node handles.
      - `from` string, required: Source node id.
      - `to` string, required: Target node id.
      - `sourceHandle` string, required: The emitted signal that activates this edge (e.g. SENT, REPLIED, YES, case:<id>). A handle may wire to at most one node.
      - `metadata` object
        - `signalCategory` 'lifecycle' | 'system'
        - `signalDescription` string

## Other responses

- `400`: Validation failed or the request cannot proceed.
- `401`: Missing, malformed, or revoked API key.
- `404`: The resource does not exist in this organization.
- `409`: Conflicts with the current state (duplicates, wrong lifecycle state).
- `422`: The request is well-formed but semantically invalid.
- `429`: Rate limit exceeded — retry after `Retry-After`.
- `500`: Internal server error.
- `503`: Transient error — retry with a narrower request.

## Changes

- **2026-11-03** `7c7a773b12ce`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/useboom/apis/boom-api/changes/api/v1/journeys/:journeyId/stop/post.md)

---

[API](https://skmtc.dev/useboom/apis/boom-api.md) - [All operations](https://skmtc.dev/useboom/apis/boom-api/llms.txt) - [OpenAPI document](https://skmtc.dev/useboom/apis/boom-api/revisions/d596d92ed092?raw)
