Activate a drip workflow
Arms a draft or paused workflow. Any other status is
409 conflict, and the stored source must validate clean — an
invalid definition cannot enrol or step. On writes {id} resolves as
a UUID or, failing that, as the workflow's org-unique key.
mode is live (the default when the body is absent) or shadow.
Any other value is a 422 rather than being ignored. Classified
destructive and refused for a sk_test_ credential, because
enrolment cannot be undone: a contact enrolled into a journey is in
it, and pausing afterwards stops the journey stepping rather than
un-enrolling anybody.
mode: "shadow" is the staging state on real data. The workflow
goes active and enrols real contacts through the real triggers on the
real clock, but every externally visible effect — a send, a set, an
add to list — is RECORDED on the run timeline instead of performed.
Nothing leaves. cohort: {list: "<key>"} bounds who may enrol into the
rehearsal, and is a 422 with mode: "live": the two mean opposite
things about who is about to be enrolled. The send-approval gate is
neither read nor written for a shadow, because a rehearsal produces no
batch for anybody to approve. Read what it recorded with
GET /v1/workflows/{id}/enrollment-decisions and the funnel, then
POST /v1/workflows/{id}/go-live arms the same definition for real
with no re-authoring.
Two things happen besides the status change, and the response reports both:
- The send-approval gate. Forced ON when the stored source contains
a step that sends email or calls a webhook at any depth — a
sendor acall webhook, inside anif, asplit, arepeat, await … timeoutor afailed:arm — so every send and every call waits for a person. LEFT EXACTLY AS IT WAS when the source contains none: a flow whose only steps areadd to listorset attr.xreaches nothing outside SendOps and so produces nothing for anybody to approve, and forcing the gate would park it behind a queue nothing will ever drain.require_send_approvalandsend_stepsin the response say which half applied. - The
enroll existingbackfills.backfills[]lists one entry per trigger that will scan history, with the window it covers and an approximate count of what that window holds. An empty array means the workflow enrols forward only.
Path parameters
id string required The workflow's UUID or, when the value does not parse as a UUID, its org-unique key. Unknown handles read as 404 not_found. Accepted on every workflow route, read and write alike.
Request body
Content type: application/json
mode string enum optional live mails people. shadow enrols real contacts and steps them through the real flow on the real clock, but records every send, attribute write and list add instead of performing it. Any other value is refused with 422 rather than ignored.
One of: live, shadow
cohort object optional Bounds who may enrol while shadowing — a shadow over 40,000 contacts is the second run, the first is over five colleagues. Only valid with mode: "shadow"; with live it is a 422. An unknown key is a 422 unknown_reference carrying the org's list keys as candidates.
Fields of cohort:
list string required The list's org-unique key.
Responses
Errors follow the RFC 7807 problem format — see the error reference.
workflow object required A drip-workflow definition — metadata only. The .flow source, canvas
layout, content hash and profile versions are intentionally not exposed.
Fields of workflow:
id string<uuid> required name string required key string required The workflow's stable slug within the org.
description string optional status string required Lifecycle status: draft, active, paused, archived, or invalid.
invalid_reason string optional Present only when status is invalid.
require_send_approval boolean optional When true, sends are held for manual review instead of dispatching automatically.
send_mode string enum required shadow means the workflow is ACTIVE and enrolling real contacts but mailing nobody: every send, attribute write and list add is recorded on the run timeline instead of performed. A shadow workflow that looks broken is working as configured — POST /v1/workflows/{id}/go-live arms it for real.
One of: live, shadow
shadow_started_at string<date-time> optional When the current shadow began. Absent while live.
shadow_cohort_list_id string<uuid> optional The list bounding who may enrol while shadowing. Absent while live or when the shadow is unbounded.
shadow_cohort object optional That list resolved: which list, and how many people the rehearsal can reach. Absent while live or when the shadow is unbounded. The bare shadow_cohort_list_id stays beside it because it is the handle activate takes back.
current_version integer required The live definition version.
run_counts object required By-status run tally, e.g. active/completed → counts. Always present.
created_at string<date-time> required updated_at string<date-time> required source string optional The .flow program. Present only for a caller holding api.workflows.manage — the read scope withholds it — and on every response to a write, which returns what was stored.
content_hash string optional The optimistic-concurrency token. Pass it back as expected_content_hash on the next update and a concurrent edit is refused rather than overwritten. Present alongside source.
warnings array<string> optional The analyzer's non-fatal findings from the write that produced this row. A definition can be perfectly valid and still not do what somebody asked for, so these survive a successful save — read them.
diagnostics array<object> optional Every positioned finding, errors as well as warnings, from validating the source this call wrote. This is what makes a 201 with status: invalid actionable: the row was stored, and this says why it cannot run yet. Absent when none were gathered.
Each entry in diagnostics:
line integer | null optional column integer | null optional severity string required error blocks the definition from running; warning does not.
message string required requires array<object> optional Everything this definition still depends on, with each dependency's state. Present on the write responses and on the detail read (the polling surface); absent on the collection read. An empty array means nothing is outstanding — see V1Requirement.
Each entry in requires:
kind string enum required The namespace key is looked up in. Branch on this; never parse detail.
One of: template, segment, list, topic, identity, channel, attribute, activity_property, asset
key string required The identifier as the definition names it — a template name, a segment id or key, a from-address. Empty for a requirement about the ABSENCE of a reference: a send with no topic at all names no topic.
status string enum required ok — resolves and is usable now.
missing — nothing in this organization answers to that key. Correct
the reference, or create the thing.
not_ready — it exists, in a state that cannot be used yet. Usually
a wait, and retrying the identical request is the move. Treating this
as missing sends you hunting for a typo that is not there.
One of: ok, missing, not_ready
detail string required The state, in the words that say which state — deploy_state=pending, member_count=null (evaluation queued). For display and logs; do not parse it.
fix string required The call that clears this requirement, as an HTTP route where there is one and as a plain instruction where there is not (a template deploy is waited for, not called). Empty on an ok entry.
require_send_approval boolean required The gate as it stands AFTER this call — forced on for a flow that contains a step that sends email or calls a webhook, left untouched for one that does neither.
send_steps integer | null required The count the gate decision was made on. Null when the stored source could not be read, in which case the gate was forced on as the safe default.
backfills array<object> required Empty for a workflow that enrols forward only.
Each entry in backfills:
trigger_kind string required segment, event, activity or date_rel.
key string required The segment key
status string optional pending, running, done or failed.
window_start string<date-time> | null optional Null for an unbounded backfill — the whole back-catalogue.
window_end string<date-time> | null optional estimate integer<int64> | null optional APPROXIMATE — a count over the same window the backfill will scan, taken before it runs. Null when it could not be sized, which is not the same as zero.
mode string enum required What this activation armed. Always present, so a caller that sent no mode still learns which it got.
One of: live, shadow
cohort object optional The list bounding a shadow activation's enrolment, with its size. Absent on a live activation and on an unbounded shadow.
code: invalid_scope), or it is bound to the test environment and this operation is irreversible (code: test_environment_forbidden). Branch on code: the first is fixed by granting the scope, the second only by using a live credential. See the "Live and test credentials" section of the API description. application/problem+json code is conflict.
application/problem+json validation_failed Problem. When the refusal is about a
reference — a segment key, template slug, topic or attribute name the
organization does not have — it additionally carries validation_code,
field, line/column, missing, candidates and next_step, so a
client can correct the call without a second round of guessing. See
ValidationProblem.
application/problem+json code is internal_error. The
request_id field can be quoted to SendOps support to investigate.
application/problem+json