---
title: "Resume workflow"
method: POST
path: "/api/workflows/{sid}/resume"
tags: ["workflows"]
---

# Resume workflow

`POST /api/workflows/{sid}/resume`

## Request

- Base URL: `https://beta.getsales.io/playtables/v1`
- URL: `https://beta.getsales.io/playtables/v1/api/workflows/{sid}/resume`
- Auth: HTTP bearer and API key in header `Team-SID`

draft|paused → active. Requires a published version - 422 invalid_transition otherwise (publish first). Opens the gates: source pollers enroll on the next cycle; held tasks dispatch again respecting sending windows and per-sender limits - overdue tasks are NOT burst-flushed, normal scheduling order applies. Idempotent on an already-active workflow. result: tasks_released_count.

Contract:
- MCP tool `resume_workflow`, registry package `mcp.playtables/workflows`, mount `playtables.workflows`.
- Operation `action`, response envelope `action`.
- Flags: dangerous: the MCP layer gates it behind a preview/commit token, and the effect cannot be undone through this API.

## Path parameters

- `sid` string, required: Workflow to activate; needs published_version_sid non-null (active is a no-op).

## Response `200`

`action` success envelope.

- ResumeWorkflowResponse
  - `success` true, required
  - `operation` 'action', required
  - `action` string, required: kebab-case verb; matches the route segment.
  - `item` object, nullable, required
    - `sid` string, required
    - `team_sid` string, required
    - `name` string, required
    - `description` string, nullable, required
    - `status` 'draft' | 'active' | 'paused' | 'archived', required
    - `published_version_sid` string, nullable, required: wf_vr_… [ro] - written ONLY by publish; null until first publish. Old versions are never repointed back to (no rollback verb - republish the old steps instead).
    - `draft_steps` union[], required: Working copy of the step document (chain-and-blocks v2); step ids always present on read (server-assigned). Not executable until published. Max 100 steps incl. nested / 512 KB.
      - union
        - object
          - `type` 'linkedin_send_connection_request', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `message` string, nullable: Invite note - max 300 RENDERED chars (server-checked); null/omitted = blank invite. Template namespaces: {{people.*}} {{company.*}} {{record.<slug>}} {{ai.<key>}}; per-variable fallback {{record.slug|text}}.
          - `required_fields` object, nullable
            - `refs` string[], required: 0..20 bare template refs required on top of the text's own variables (people.first_name, company.name, record.<slug>, company.record.<slug>, ai.<key>).
            - `on_missing` 'fail' | 'skip', required: fail | skip - what to do when a value the text needs is empty at task time; no policy (null) sends the text as it renders.
        - object
          - `type` 'linkedin_send_message', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `message` string, required: Message template (same namespaces as all templates).
          - `required_fields` object, nullable
            - `refs` string[], required: 0..20 bare template refs required on top of the text's own variables (people.first_name, company.name, record.<slug>, company.record.<slug>, ai.<key>).
            - `on_missing` 'fail' | 'skip', required: fail | skip - what to do when a value the text needs is empty at task time; no policy (null) sends the text as it renders.
        - object
          - `type` 'linkedin_send_inmail', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `subject` string, nullable
          - `message` string, required
          - `required_fields` object, nullable
            - `refs` string[], required: 0..20 bare template refs required on top of the text's own variables (people.first_name, company.name, record.<slug>, company.record.<slug>, ai.<key>).
            - `on_missing` 'fail' | 'skip', required: fail | skip - what to do when a value the text needs is empty at task time; no policy (null) sends the text as it renders.
        - object
          - `type` 'linkedin_visit_profile', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
        - object
          - `type` 'linkedin_endorse_skills', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `skills_count` integer, required: Endorse the person's top N listed skills, 1..10.
        - object
          - `type` 'linkedin_withdraw_connection_request', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
        - object
          - `type` 'linkedin_like_latest_post', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
        - object
          - `type` 'linkedin_comment_latest_post', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `comment` string, required: Comment template on the person's latest post, 1..1250 rendered; same namespaces as message templates.
          - `required_fields` object, nullable
            - `refs` string[], required: 0..20 bare template refs required on top of the text's own variables (people.first_name, company.name, record.<slug>, company.record.<slug>, ai.<key>).
            - `on_missing` 'fail' | 'skip', required: fail | skip - what to do when a value the text needs is empty at task time; no policy (null) sends the text as it renders.
        - object
          - `type` 'email_send', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `subject` string, required
          - `body` string, required: HTML body template.
          - `required_fields` object, nullable
            - `refs` string[], required: 0..20 bare template refs required on top of the text's own variables (people.first_name, company.name, record.<slug>, company.record.<slug>, ai.<key>).
            - `on_missing` 'fail' | 'skip', required: fail | skip - what to do when a value the text needs is empty at task time; no policy (null) sends the text as it renders.
          - `email_field` string, nullable: Which address the send uses: any | work_email | personal_email; omitted or any = the best one.
          - `cc` string[], nullable: Up to 5 literal copy addresses.
          - `bcc` string[], nullable: Up to 5 literal blind-copy addresses.
          - `attachments` object[], nullable: Up to 5 library files ({sid: wf_at_…, name}) sent with the email.
            - `sid` string, required
            - `name` string, required
          - `reply_in_thread` boolean, nullable
          - `track_opens` boolean, nullable
          - `track_clicks` boolean, nullable
          - `unsubscribe_link` boolean, nullable
        - object
          - `type` 'wait', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `for` string, required: Duration string: ^[1-9][0-9]*(m|h|d)$ - e.g. "45m", "12h", "14d". Publish lints the range 1m..365d.
        - object
          - `type` 'wait_for_event', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `event` 'linkedin_connection_request_accepted' | 'email_opened' | 'email_link_clicked', required: Event observed via the activities ingest.
          - `timeout` object, required: Required since 2026-10-11: a wait always has a time limit. Bundled: a timeout path cannot exist without a duration.
            - `after` string, required: Duration string: ^[1-9][0-9]*(m|h|d)$ - e.g. "45m", "12h", "14d". Publish lints the range 1m..365d.
            - `steps` object, required: Recursive node: the same object shape repeats here. A static OpenAPI document cannot express the cycle, so this level is documented as an open object.
        - object
          - `type` 'condition', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `branches` object[], required: 1..9 branches, evaluated in order, first match wins.
            - `id` integer: Server-assigned - SAME id-space as step ids; omit on new branches, carry on kept ones. The fired branch id lands on the condition task result.
            - `label` string, nullable: Editor caption.
            - `when` object, required: NON-nullable filter AST, LOOSE mode (predicate-only, never compiled to ES): leaf paths people.* / company.* / record.<slug> / ai.<output_key> / api.<output_key>.<path>; a path missing from the record's playtable makes the leaf false.
            - `steps` object, required: Recursive node: the same object shape repeats here. A static OpenAPI document cannot express the cycle, so this level is documented as an open object.
          - `else` object, required: Recursive node: the same object shape repeats here. A static OpenAPI document cannot express the cycle, so this level is documented as an open object.
        - object
          - `type` 'ab_test', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `branches` object[], required: 2..9 weighted arms; the record is assigned deterministically by hashing its sid into the weight distribution. No `else`.
            - `id` integer: Server-assigned - SAME id-space as step ids; omit on new arms, carry on kept ones. The assigned arm id lands on the ab_test task result.
            - `label` string, nullable: Editor caption ("Variant A").
            - `weight` integer, required: Relative weight (int >= 1; need not sum to 100). Record assigned by crc32(record.sid) mod sum(weights) - see workflow_versions.md ab_test algorithm.
            - `steps` object, required: Recursive node: the same object shape repeats here. A static OpenAPI document cannot express the cycle, so this level is documented as an open object.
        - object
          - `type` 'end', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
        - object
          - `type` 'api_call', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `method` 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', required
          - `url` string, required: https-only (publish check); a template - variables may stand in the path and the query.
          - `headers` object, nullable
          - `query` object, nullable
          - `body` string, nullable: JSON body template, variables inside quotes; null = the fixed payload; must be null on GET / DELETE.
          - `provider_connection_sid` string, nullable: A `custom` provider connection whose auth the integration platform injects at call time; null = no auth.
          - `timeout_secs` integer, nullable
          - `retry_on_error` boolean, nullable
          - `remove_empty_values` boolean, nullable
          - `output_key` string, nullable: The answer's name for later steps (2026-10-11): a slug, unique within the version with the ask_ai keys; later steps read {{api.<output_key>.<path>}} and condition leaves filter on api.<output_key>.<path>; null = the answer is not kept.
        - object
          - `type` 'ask_ai', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `prompt` string, required: Same variable namespaces as message templates.
          - `output_key` string, required: Slug, unique within the version (publish check) - later steps read {{ai.<output_key>}} / condition leaves on ai.<output_key>.
          - `preset_sid` string, nullable: gtm-api preset for the AI call; null = team default.
        - object
          - `type` 'manual_task', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `title` string, required
          - `instructions` string, nullable: Rendered into the human todo; the journey waits until the task is completed.
        - object
          - `type` 'move_to_workflow', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `target_workflow_sid` string, required: Enroll the person into this campaign (wf_wf_…); reuses the existing record per the re-entry policy. THIS record then completes - terminal-for-this-record.
          - `mode` 'from_start' | 'from_current', nullable: Re-entry mode when the person is ALREADY in the target (workflow_records.md, Re-entry): from_start (default; reset to the entry step) | from_current (resume the pinned step, else the entry step). A re-entry is loop-guarded at re_entered_count 10.
        - object
          - `type` 'add_tag', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `tag_sids` string[], required: 1..20 CRM tags (cr_tg_…) attached to the person mid-flow, then falls through.
        - object
          - `type` 'remove_tag', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `tag_sids` string[], required: 1..20 CRM tags (cr_tg_…) detached from the person mid-flow, then falls through.
        - object
          - `type` 'set_field', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `field_slug` string, required: Playtable field slug to write (pipeline_stage is a scalar_select field).
          - `value` union, required: Scalar cell value (one cell; no arrays/objects); null clears the field.
            - string
            - number
            - boolean
            - unknown
            - unknown
        - object
          - `type` 'change_sender', required
          - `id` integer: Server-assigned (workflows.step_id_seq). OMIT on new steps, CARRY verbatim on kept steps; always present on read.
          - `name` string, nullable: Editor label.
          - `sender_profile_sid` string, nullable: Explicit forward-only re-pin (ub_sp_…). Exactly one of sender_profile_sid / rotation (publish check). NOT a restart-from-top.
          - `rotation` 'fair' | 'random' | 'new_sender' | 'prior_engagement', nullable: Re-pick the sender from the workflow pool by this strategy instead of a fixed sid. Exactly one of sender_profile_sid / rotation (publish check).
    - `schedule` object, nullable, required: The campaign's sending windows, the one send-window gate: null = any time (gtm-api's per-account limits still pace the sends). A sender profile carries no window of its own (founder 2026-09-22).
      - `timeblocks` object[], required
        - `dow` integer, required: Day of week, 0=Sunday … 6=Saturday (legacy Carbon dayOfWeek convention).
        - `start_minute` integer, required: Minutes from local midnight (legacy key: min).
        - `end_minute` integer, required: Minutes from local midnight; must be > start_minute (server-validated). Legacy key: max.
      - `timezone` string, required: IANA tz name - base timezone for the blocks.
      - `use_people_timezone` boolean, required: Resolve blocks in the person's timezone when known, falling back to `timezone`.
    - `on_reply` 'autopilot' | 'copilot' | 'stop' | 'continue', required: What a reply does (default copilot): autopilot = the records pause and the AI SDR answers within its caps; copilot = the records pause and the AI SDR drafts for approval; stop = the records pause and the thread is the human's; continue = the records keep sending (nurture), the agent stays out. Reply scope = all the person's live records. Frozen onto each thread the workflow acts on as its automation_ref mode (autopilot | copilot | off).
    - `agent_sid` string, nullable, required: The AI SDR picked for this workflow (ub_ag_…, block 10 step 10.2): the agent that works the threads it starts, every setting on the agent itself (update_agent). Null = its home table's pick, else the team's default. The campaign's own setup of before (ai_sdr_setup, 2026-10-12 to 2026-10-30) became an agent of its own in step 10.3.
    - `sender_profile_sids` string[], required: ub_sp_… each - the sender pool; a record is pinned to ONE sender at enrollment by rotation_strategy.
    - `rotation_strategy` 'fair' | 'random' | 'new_sender' | 'prior_engagement', required
    - `priority` integer, required: 1..3 (low / medium / high) - the workflow's SHARE of a sender profile it shares with other workflows (three actions of high for one of low), never a rank (see ./workflow_tasks.md).
    - `created_by` object, required
      - `actor_type` 'user' | 'support' | 'api_key' | 'system' | 'agent', required
      - `actor_sid` string, nullable, required
      - `team_sid` string, required
      - `actor_name` string, nullable: The OAuth client that acted ("Claude", "n8n"); null for a user, an API key or a system job.
      - `oauth_client_sid` string, nullable: The acting OAuth client (id_oc_*); null off the OAuth path.
      - `reason` string, nullable: Why a system actor wrote the row (snapshot_capture_job, ...); null otherwise.
      - `permissions` object, required: Internal audit context: the grant set the actor held at write time. Not a contract.
      - `cluster_id` integer, nullable: Internal audit context: the cluster that served the write.
      - `trace_id` string, nullable: Internal audit context: the trace id of the request that wrote the row.
    - `deleted_by` object, nullable, required
      - `actor_type` 'user' | 'support' | 'api_key' | 'system' | 'agent', required
      - `actor_sid` string, nullable, required
      - `team_sid` string, required
      - `actor_name` string, nullable: The OAuth client that acted ("Claude", "n8n"); null for a user, an API key or a system job.
      - `oauth_client_sid` string, nullable: The acting OAuth client (id_oc_*); null off the OAuth path.
      - `reason` string, nullable: Why a system actor wrote the row (snapshot_capture_job, ...); null otherwise.
      - `permissions` object, required: Internal audit context: the grant set the actor held at write time. Not a contract.
      - `cluster_id` integer, nullable: Internal audit context: the cluster that served the write.
      - `trace_id` string, nullable: Internal audit context: the trace id of the request that wrote the row.
    - `created_at` string, required
    - `updated_at` string, required
    - `deleted_at` string, nullable, required
  - `result` object, required
    - `tasks_released_count` integer, required: Held scheduled tasks eligible for dispatch again.
  - `meta` object, required
    - `trace_id` string, required: UUID v7; same 128-bit value as the X-Trace-Id header.
    - `span_id` string, required: 16 hex chars, root span of this request.
    - `timestamp` string, required: ISO 8601 UTC (Y-m-dTH:i:sZ), response time.
    - `duration_ms` integer, required: Server-side wall clock.
    - `team_sid` string, nullable: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends.
    - `actor_type` string, nullable: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends.

## Other responses

- `4XX`: MCP error envelope. `error.code` is one of validation_failed, nothing_to_update, not_found, relation_not_found, invalid_transition, limit_exceeded, payment_required, duplicate_rejected, conflict, delete_blocked, unauthorized, forbidden, rate_limited, not_implemented.
- `5XX`: MCP error envelope with `error.code` internal_error or service_unavailable.

---

[API](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-playtables.md) - [All operations](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-playtables/llms.txt) - [OpenAPI document](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-playtables/revisions/aa394506515d?raw)
