Create a drip workflow
Creates a managed drip-workflow definition from a .flow source. key
is the stable org-unique handle other calls reference the workflow by
and is immutable afterwards; a key already in use returns
409 conflict.
A source that does not validate is still a 201. The row is stored
with status: invalid and invalid_reason, and the response carries
the stored source with every positioned finding in diagnostics — so a
definition is never silently dropped, and a caller can see both that its
work landed and why it cannot run yet. 422 is reserved for a request
that could not be stored at all (no key, no name, no source, a key
that is not a slug).
A created workflow is a draft: it enrols nobody and sends nothing
until POST /v1/workflows/{id}/activate. Do not write SendFlow from
memory — the grammar is published at
https://www.sendlang.com/docs/reference/grammar, and a guessed
definition usually nearly parses.
Request body
Content type: application/json
key string required The stable org-unique handle other calls reference this workflow by — a lowercase slug (letters, digits, hyphen, underscore) starting with a letter, up to 63 characters. Immutable afterwards.
name string required Human-readable name.
description string optional What this journey is for
source string required The definition in SendFlow (.flow). Full text, not a patch. The grammar is published at https://www.sendlang.com/docs/reference/grammar — a definition written from memory usually nearly parses.
Responses
Errors follow the RFC 7807 problem format — see the error reference.
draft or invalid application/json 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.
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