---
title: "Review proposed live changes"
method: POST
path: "/sequences/{sequence_id}/changes/preview/"
tags: ["sequences"]
---

# Review proposed live changes

`POST /sequences/{sequence_id}/changes/preview/`

## Request

- Base URL: `https://api.getoneprofile.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getoneprofile.ai/sequences/{sequence_id}/changes/preview/`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

## Path parameters

- `sequence_id` string, uuid, required

## Request body

- SequenceChangesSchema: A person's waiting changes to a live sequence, as Review changes holds them.
  - `agent` OutboundAgentUpdateSchema: A change to an agent, sent whole against the version it was read at. The configuration travels as one object rather than as fifteen optional fields: the AI writing tab reads the agent, edits it and sends it back, and ``expected_version`` makes two people editing at once a 409 rather than a silent overwrite.
    - `config` OutboundAgentConfig: The versioned configuration one outbound agent carries, stored as JSON on its row. Everything a person sets on the Agent tab that is not already a column of the saved-agent row (``model_key`` and the research switch live there). ``message_prompts`` is keyed by an opaque slot key the caller mints (the step id); the agent never learns what a slot means.
      - `campaign` CampaignBrief: What one sequence's campaign is: the tested campaign it runs and why. Written by tailoring and by nothing else; the Agent tab reads it and never sends it back (the service keeps the stored one on every other write). Tailoring now rewrites the steps' instructions themselves and writes only ``reason`` and ``goal`` here. ``audience``, ``why_now``, ``angle`` and ``step_notes`` are what it wrote before: kept, and still read by the writer, for the agents that carry them. They were written by a model from the audience table, so they are background, never facts: only ``intent``, the person's own words, may be stated as fact.
        - `angle` string
        - `audience` string
        - `campaign_key` string, required
        - `chosen_by_person` boolean
        - `goal` string
        - `intent` string
        - `minted` string[]
        - `reason` string
        - `step_notes` object
        - `table_name` string
        - `tailoring_off` boolean
        - `why_now` string, nullable
      - `cta_label` string, nullable
      - `cta_url` string, nullable
      - `goal` string
      - `language` string
      - `message_prompts` object
      - `person_research` boolean
      - `personality_preset` 'direct' | 'consultative' | 'bold' | 'custom': The voice an agent writes in. Each preset is a real prompt; CUSTOM is the author's.
      - `personality_prompt` string
      - `reply_drafting` boolean
      - `reply_prompt` string
      - `translate` boolean
      - `write_from_excluded_column_ids` string[]
    - `expected_version` integer, required: The version the editor was opened at.
    - `model_key` string, nullable: The model it writes with: swift, core or deep.
    - `research` boolean, nullable: Whether the first message may research the person on the web.
  - `audience` AudiencePutSchema
    - `column_mapping` ColumnMappingSchema: Which audience columns carry a meaning, all detected from the table. One field per channel (email, LinkedIn) plus the company domain the coverage line reports on. Leave the whole object out when binding an audience and the columns are detected server-side. Send it only to correct a detection: a first name, a company or a last-contacted date is no longer part of this, because every column on the table is already addressable as a `{{Column name}}` variable.
      - `company_domain_column_id` string, uuid, nullable: The account identity, reported on by the audience coverage counts. Unmapped, the coverage count is zero.
      - `email_column_id` string, uuid, nullable: Where the send address is read from, and the deduplication key: two rows sharing an address are one contact.
      - `email_deliverability_column_id` string, uuid, nullable: The column holding each address's deliverability verdict, usually the Verify email column over the email column; detected when the table has one. Read only while an email column is mapped, and dropped without one. An address is emailed only when its verdict is one of `email_deliverability_results`; any other verdict skips the email step (LinkedIn steps still run), and an address not verified yet waits for its verdict. Unmapped, every address is emailed unverified.
      - `email_deliverability_results` EmailDeliverability[]: The verdicts the sequence emails: valid, catch-all, risky, unknown, invalid or disposable. Valid alone by default; an empty list reads as the default. An answer the column could not give counts as unknown.
      - `linkedin_url_column_id` string, uuid, nullable: Required by LinkedIn steps; a row without one skips them.
    - `existing_contacts` 'keep' | 'restart'
    - `expected_updated_at` string, date-time, nullable
    - `pause_if_running` boolean
    - `table_id` string, uuid, required
    - `view_id` string, uuid, nullable: A saved view narrowing the audience to its rows; null is the whole table.
  - `copy_from_sequence_id` string, uuid, nullable
  - `expected_agent_version` integer, nullable
  - `expected_updated_at` string, date-time, required
  - `graph` SequenceGraphPutSchema
    - `edges` SequenceEdgeSchema[]
      - `branch` 'default' | 'yes' | 'no' | 'connected' | 'already_connected' | 'not_accepted': Which way an edge leaves its step. DEFAULT for message-step flow; YES/NO for a condition's two branches. A connection request leaves by the answer it got: CONNECTED (accepted, and already connected unless that has a branch of its own), ALREADY_CONNECTED (present only when the request splits it out) and NOT_ACCEPTED.
      - `from_step_id` string, uuid, nullable: None = the start node.
      - `to_step_id` string, uuid, nullable: None = an end node.
    - `expected_updated_at` string, date-time, nullable: Autosave concurrency token; a mismatch means another tab saved first (409).
    - `steps` SequenceStepSchemaInput[]
      - `agent_prompt_key` string, nullable: Read-only: the agent's slot for this step's prompt (the step's id). Set by the server on every agent-written step; ignored on write.
      - `agent_subject` boolean: On an agent-written email that starts a thread: the agent writes the subject line too, and `subject_segments` is ignored. Off, the typed subject is sent. Ignored on a reply and on written steps.
      - `authoring` 'written' | 'agent': Who writes an email step's words. WRITTEN is the default and what every step was before: the author's own subject and body, with variables, snippets and spintax resolved per contact. AGENT holds an instruction instead of a body; the whole email is written for each contact at send time from that instruction, the row and the workspace's written business context, and priced per contact.
      - `body_segments` TemplateSegmentSchema[]
        - `builtin` 'sender_first_name' | 'sender_full_name' | 'sender_title' | 'sender_booking_link' | 'sending_domain' | 'sequence_name' | 'greeting' | 'today' | 'yesterday' | 'tomorrow' | 'day_plus_2' | 'day_plus_3' | 'day_plus_4': Non-column variables the composer offers. Weekday variables render the *name* of the day ("Thursday") so a cadence can reference real days without weekly rewrites.
        - `column_id` string, uuid, nullable
        - `kind` 'text' | 'column' | 'builtin' | 'spintax', required: One piece of a subject or body template.
        - `options` string[]: Spintax alternatives; one is chosen per contact.
        - `required` boolean
        - `text` string
      - `condition_kind` 'has_email' | 'has_linkedin_url' | 'linkedin_connected' | 'bounced' | 'replied' | 'linkedin_invite_accepted' | 'linkedin_open_profile': The branching conditions. ``BOUNCED`` and ``REPLIED`` are only pickable after a message step exists earlier on the same path: enforced at save, explained in the picker.
      - `condition_window_days` integer, nullable: How many days the step waits for its answer. On a check that waits (Replied?, Email bounced?), its window. On a connection request, how long it waits for the request to be accepted before the contact goes down its `not_accepted` branch: null is saved as 14, and a change holds every contact already waiting to the new window.
      - `id` string, uuid, required
      - `kind` 'message' | 'condition' | 'linkedin_profile_view' | 'linkedin_connection_request' | 'linkedin_message' | 'linkedin_inmail' | 'linkedin_withdraw_invitation' | 'linkedin_follow' | 'linkedin_like_post', required: What a graph node does. The LINKEDIN_* kinds do not send anything themselves. They enqueue browser work and park the contact until it runs, because the action happens in the customer's own browser and that browser may be closed. Every kind here is something the product can carry out end to end. There is deliberately no phone-call or text-message kind: the engine cannot dial or text, and a step it can only hand to a person to do elsewhere is a to-do list wearing a sequence's clothes, not a channel this product supports.
      - `position` integer
      - `prompt` string: What the agent is asked to write at this step. Stored on the agent, not the step, and saved with the graph: this is the one way to change it.
      - `reply_to_step_id` string, uuid, nullable: The earlier email step this email replies to: it goes out under that step's send to the contact (Re: its subject, In-Reply-To its Message-ID), from the mailbox that sent it. Must be an email step that comes before this one on the graph. Null starts a new thread. Only on email steps.
      - `requires_approval` boolean: Hold this step for approval even when its channel would run on its own. One-way: true adds an approval, false never removes the one the channel's resolved mode already wants.
      - `subject_segments` TemplateSegmentSchema[], nullable: The subject of an email that starts a thread. Ignored on a reply (`reply_to_step_id`), whose subject is the thread's, and on an email whose subject the agent writes (`agent_subject`). An email that starts a thread with neither fails preflight until a subject is added. A blank subject on an email after another one, with no `reply_to_step_id`, is saved as a reply to the nearest earlier email, which is what it used to mean.
        - `builtin` 'sender_first_name' | 'sender_full_name' | 'sender_title' | 'sender_booking_link' | 'sending_domain' | 'sequence_name' | 'greeting' | 'today' | 'yesterday' | 'tomorrow' | 'day_plus_2' | 'day_plus_3' | 'day_plus_4': Non-column variables the composer offers. Weekday variables render the *name* of the day ("Thursday") so a cadence can reference real days without weekly rewrites.
        - `column_id` string, uuid, nullable
        - `kind` 'text' | 'column' | 'builtin' | 'spintax', required: One piece of a subject or body template.
        - `options` string[]: Spintax alternatives; one is chosen per contact.
        - `required` boolean
        - `text` string
      - `title` string, nullable: What the step is called on its card, in the customer's words: set by the library campaign that laid it out, or by whoever saves the graph. Null names it by its place in the cadence (Opener, New angle, Break-up). Sending the stored title back, or leaving it out, keeps it while the step keeps its kind and who writes it; a different title renames the step, and an empty one clears the name.
      - `wait_amount` integer
      - `wait_unit` 'days' | 'hours'
  - `mapping` ColumnMappingSchema: Which audience columns carry a meaning, all detected from the table. One field per channel (email, LinkedIn) plus the company domain the coverage line reports on. Leave the whole object out when binding an audience and the columns are detected server-side. Send it only to correct a detection: a first name, a company or a last-contacted date is no longer part of this, because every column on the table is already addressable as a `{{Column name}}` variable.
    - `company_domain_column_id` string, uuid, nullable: The account identity, reported on by the audience coverage counts. Unmapped, the coverage count is zero.
    - `email_column_id` string, uuid, nullable: Where the send address is read from, and the deduplication key: two rows sharing an address are one contact.
    - `email_deliverability_column_id` string, uuid, nullable: The column holding each address's deliverability verdict, usually the Verify email column over the email column; detected when the table has one. Read only while an email column is mapped, and dropped without one. An address is emailed only when its verdict is one of `email_deliverability_results`; any other verdict skips the email step (LinkedIn steps still run), and an address not verified yet waits for its verdict. Unmapped, every address is emailed unverified.
    - `email_deliverability_results` EmailDeliverability[]: The verdicts the sequence emails: valid, catch-all, risky, unknown, invalid or disposable. Valid alone by default; an empty list reads as the default. An answer the column could not give counts as unknown.
    - `linkedin_url_column_id` string, uuid, nullable: Required by LinkedIn steps; a row without one skips them.
  - `prompt_proposal` OutboundPromptProposalAcceptSchema: Accept a proposal, writing the prompts as the agent's next version.
    - `base_version` integer, required: The version the proposal was written against.
    - `feedback_cutoff` string, date-time, nullable: The proposal's read time; feedback edited later stays pending.
    - `feedback_ids` string[]: The feedback the proposal read, stamped as consumed.
    - `items` object, required: The accepted prompt per slot key; the reply prompt travels under 'reply'.
  - `restore_version` integer, nullable
  - `review_token` string, nullable
  - `sender_profile_ids` string[], nullable
  - `settings` SequenceUpdateSchema
    - `bcc_addresses` string[], nullable
    - `cc_addresses` string[], nullable
    - `continuous_enrolment` boolean, nullable
    - `email_approval_mode` string, nullable
    - `expected_updated_at` string, date-time, nullable: The updated_at the client last saw; a stale value is refused with 409 instead of clobbering a concurrent editor.
    - `linkedin_approval_mode` string, nullable
    - `linkedin_check_max_seconds` integer, nullable: Retired. Acceptance checks now use the service-managed interval.
    - `linkedin_check_min_seconds` integer, nullable: Retired. Acceptance checks now use the service-managed interval.
    - `multithreading` object, nullable
    - `name` string, nullable
    - `opportunity_values` object, nullable
    - `opt_out_footer_text` string, nullable
    - `plain_text_only` boolean, nullable
    - `sending_priority` string, nullable
    - `sending_window` SendingWindowSchema: When a sequence may send, as the send engine reads it. Without ``days`` it sends at any hour of any day, which is the settings page's Send any time: it keeps its timezone and its minimum gap between emails. Every rule here is checked on save, running or not, and a window that breaks one is refused with a 422 naming the part at fault.
      - `days` SendingDaysSchema: The days a sequence may send on. A day left out, or null, is closed.
        - `fri` SendingHoursSchema: One open day's sending hours, read on the window's timezone.
          - `end` string, required: When it closes, as HH:MM, after start on the same day. Hours cannot run past midnight, so 23:59 is the latest close.
          - `start` string, required: When sending opens, as HH:MM on a 24-hour clock, such as 09:00.
        - `mon` SendingHoursSchema: One open day's sending hours, read on the window's timezone.
          - `end` string, required: When it closes, as HH:MM, after start on the same day. Hours cannot run past midnight, so 23:59 is the latest close.
          - `start` string, required: When sending opens, as HH:MM on a 24-hour clock, such as 09:00.
        - `sat` SendingHoursSchema: One open day's sending hours, read on the window's timezone.
          - `end` string, required: When it closes, as HH:MM, after start on the same day. Hours cannot run past midnight, so 23:59 is the latest close.
          - `start` string, required: When sending opens, as HH:MM on a 24-hour clock, such as 09:00.
        - `sun` SendingHoursSchema: One open day's sending hours, read on the window's timezone.
          - `end` string, required: When it closes, as HH:MM, after start on the same day. Hours cannot run past midnight, so 23:59 is the latest close.
          - `start` string, required: When sending opens, as HH:MM on a 24-hour clock, such as 09:00.
        - `thu` SendingHoursSchema: One open day's sending hours, read on the window's timezone.
          - `end` string, required: When it closes, as HH:MM, after start on the same day. Hours cannot run past midnight, so 23:59 is the latest close.
          - `start` string, required: When sending opens, as HH:MM on a 24-hour clock, such as 09:00.
        - `tue` SendingHoursSchema: One open day's sending hours, read on the window's timezone.
          - `end` string, required: When it closes, as HH:MM, after start on the same day. Hours cannot run past midnight, so 23:59 is the latest close.
          - `start` string, required: When sending opens, as HH:MM on a 24-hour clock, such as 09:00.
        - `wed` SendingHoursSchema: One open day's sending hours, read on the window's timezone.
          - `end` string, required: When it closes, as HH:MM, after start on the same day. Hours cannot run past midnight, so 23:59 is the latest close.
          - `start` string, required: When sending opens, as HH:MM on a 24-hour clock, such as 09:00.
      - `min_interval_minutes` integer, nullable: The shortest gap between two emails from one mailbox, a whole number from 0 to 240 minutes. Null or 0 spaces them automatically from each mailbox's daily capacity.
      - `sender_timezone` boolean, nullable: Whether the hours are read in each sender's own timezone, so a New York sender and a London one each send 9 to 5 their time. False reads them in `timezone` for everyone. Absent or null means each sender's; an update that leaves it out keeps what is stored.
      - `timezone` string, nullable: The IANA timezone the hours are read in, such as Europe/Berlin: the one timezone when `sender_timezone` is false, else the fallback for a sender with none. UTC when absent.
    - `spending_limit_tenths` integer, nullable
  - `training_revision` integer, nullable
  - `training_rewrite` boolean
  - `training_session_id` string, uuid, nullable
  - `training_slot_keys` string[], nullable

## Response `200`

Successful Response

- SequenceChangesPreviewSchema
  - `active_contacts` integer, required
  - `approvals_released` integer: Cards waiting for approval in Tasks that the change lets go out without it, at most: those on a step whose approval it switches off, held only by the sequence's own approval for their channel, which it loosens, or held by a sender's own setting when it takes a sender off (that sender may have been the strictest). A card the remaining senders' setting or the agent's words on review still holds stays, and so does one a person edited and has not approved.
  - `audience_restart` AudienceRestartReviewSchema: An audience change that starts contacts over: who starts again, and who heard from it.
    - `at_least` boolean: True when the table is longer than the review reads: the counts are floors.
    - `contacted_before` integer, required: How many of the selected rows were contacted before.
    - `contacts` integer, required: Contacts already in the sequence who start again from the first step.
  - `changes` SequenceChangeReadSchema[], required
    - `after` unknown, required
    - `before` unknown, required
    - `field` string, nullable: The stored field the row is about, so a screen that words that field itself (a sending window, a call to action link) can draw the value in its own words.
    - `group` SequenceChangeGroupSchema: The part of the sequence a review row is about, which the dialog groups its rows under. A step's rows sit under the step's name as the edit leaves it, with the name it had before when the edit renamed it (``was``), and ``state`` when the edit adds or removes the whole step (Natsuki, 2026-10-08: the review reads as one list grouped by step and by tab, never one flat table).
      - `key` string, required: Stable within one review: the rows sharing it form one group.
      - `kind` 'step' | 'connections' | 'writing' | 'writing_action' | 'audience' | 'senders' | 'settings', required
      - `state` 'added' | 'removed', nullable
      - `step_id` string, uuid, nullable
      - `step_kind` 'message' | 'condition' | 'linkedin_profile_view' | 'linkedin_connection_request' | 'linkedin_message' | 'linkedin_inmail' | 'linkedin_withdraw_invitation' | 'linkedin_follow' | 'linkedin_like_post': What a graph node does. The LINKEDIN_* kinds do not send anything themselves. They enqueue browser work and park the contact until it runs, because the action happens in the customer's own browser and that browser may be closed. Every kind here is something the product can carry out end to end. There is deliberately no phone-call or text-message kind: the engine cannot dial or text, and a step it can only hand to a person to do elsewhere is a to-do list wearing a sequence's clothes, not a channel this product supports.
      - `title` string, required: The heading, as the screen that owns the part names it.
      - `was` string, nullable: A step's name before the change, when the change renamed it.
    - `label` string, required
    - `name` string, nullable: The row's own name inside its group, without the group's.
    - `undo` SequenceChangeUndoSchema: Which part of the staged change set an Undo on the row takes back. ``fields`` are the stored fields of the section (or of the step) the row is about; a connection names the way out by its step (None for the start) and its branch. The dialog puts each back as it stands on the sequence now.
      - `branch` 'default' | 'yes' | 'no' | 'connected' | 'already_connected' | 'not_accepted': Which way an edge leaves its step. DEFAULT for message-step flow; YES/NO for a condition's two branches. A connection request leaves by the answer it got: CONNECTED (accepted, and already connected unless that has a branch of its own), ALREADY_CONNECTED (present only when the request splits it out) and NOT_ACCEPTED.
      - `fields` string[]
      - `kind` 'settings' | 'agent' | 'agent_config' | 'step' | 'step_added' | 'step_removed' | 'connection' | 'senders' | 'mapping' | 'audience' | 'writing_action', required
      - `step_id` string, uuid, nullable
  - `finished_contacts_at_most` boolean: True when the two finished-contact counts are of who stands at those ends, the most that go on, rather than each contact judged: more stand there than the review judges one by one. They are judged when the change applies.
  - `finished_contacts_awaiting_answer` object: For each connection request whose branches all ended and one of which the change gives a step, by step id: how many contacts who finished once it was sent go back to waiting for its answer, judged as above.
  - `finished_contacts_continuing` object: For each step a contact who had finished goes on to, by step id: how many finished at an end the change connects to that step, and go on to it once the change applies. Those who since replied, opted out, bounced, went on the do-not-contact list, answered or joined another sequence, were reached by one, left the audience, or lost their mailbox stay finished. Empty when the change connects no end anyone finished at.
  - `mailbox_stopped_contacts` integer, required: Live contacts the change stops at their next email because it removes the sender whose mailbox emails them: a contact is only ever emailed from one mailbox. LinkedIn steps before that email still run.
  - `processing_contacts` integer, required
  - `removed_step_contacts` integer, required
  - `removed_step_going_on` integer: Live contacts on the steps the change removes who go on to what now follows them, as a new contact would, or finish there when nothing does; the rest of those on removed steps stop (removed_step_contacts).
  - `removed_steps` RemovedStepReviewSchema[]: Each removed step anybody stands on, by name, with what happens to them: the review says it per step. `removed_step_contacts` and `removed_step_going_on` are the same counts summed.
    - `finishing` integer, required: Contacts whose way in now leads to an end: they finish.
    - `going_on` integer, required: Contacts who go on to the step their way in now leads to.
    - `lines` RemovedStepLineSchema[]: The review's sentences for this step, in order: who stops, who goes on and where, who finishes. The AI Assistant's card says the same sentences.
      - `line` string, required
      - `stops` boolean, required: True when it says contacts stop, which reads as a warning.
    - `name` string, required: The step's name, as its card named it.
    - `next_step` string, nullable: Where those going on go, when it is one step for all of them.
    - `step_id` string, uuid, required
    - `stopped` integer, required: Contacts with no way in to follow, who stop.
  - `review_token` string, required
  - `rewritten_messages` integer: Messages the agent already wrote and nobody has sent, edited or approved that the change has it write again: their step's prompt, subject or thread changed, or how the agent writes did. A person's own edit and words a person approved are kept.
  - `rewritten_messages_tenths` integer: The most writing those messages again costs, in tenths of a credit: each at the dearest turn the agent's settings allow.
  - `shortened_window_contacts` object: For each connection request whose answer window the change shortens, by step id: how many contacts waiting for its answer have already waited longer than the new window, so their wait ends as soon as the change applies. Empty when no window shortens.
  - `waiting_contacts` integer, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Sequence, sender, audience, or writing setup not found.
- `409`: Configuration changed or review is stale.
- `422`: Proposed configuration is invalid.
- `423`: This action requires pausing the sequence.
- `500`: Internal server error.
- `502`: Bad gateway error.
- `503`: Service unavailable.

---

[API](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile.md) · [All operations](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/llms.txt) · [OpenAPI document](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/revisions/4a6bd71e3f02?raw)
