---
title: "Send one step as a test email with the waiting changes"
method: POST
path: "/sequences/{sequence_id}/changes/test-send/"
tags: ["sequences"]
---

# Send one step as a test email with the waiting changes

`POST /sequences/{sequence_id}/changes/test-send/`

## 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/test-send/`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

A real test email of the step as it will send once the change set is applied, applying nothing: real mailbox, real signature, variables resolved; free of credits and analytics. A step the agent writes goes out in the words its Preview wrote for this contact and these changes, kept for 30 minutes; only when there are none is it written, spending one of the day's free previews per step written.

## Path parameters

- `sequence_id` string, uuid, required

## Request body

- SequenceChangesTestSendSchema: A test email of one step as a person's waiting changes would send it.
  - `changes` SequenceChangesSchema, required: 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
  - `mailbox_id` string, uuid, required
  - `record_id` string, uuid, required: The contact the variables render against.
  - `step_id` string, uuid, required
  - `to_address` string, required

## Response `200`

Successful Response

- TestSendResponseSchema
  - `provider_message_id` string, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: The sequence, the step, the contact, the mailbox or a writing setup it reads is gone.
- `409`: The audience is not configured yet, or the real send would skip this contact (a missing column), or hold the agent's words for a person (a defect such as stray formatting).
- `422`: The changes set an Effort the catalog does not offer (`model_key`), or the agent wrote nothing usable with them (`step_id`, error type `draft_not_written`).
- `429`: Today's free agent-written previews are spent.
- `500`: Internal server error.
- `502`: The mailbox could not send.
- `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/dc5ac3c84444?raw)
