Journey management endpoints
Journeys API requests are authenticated by passing your workspace API key as a Bearer token in theAuthorization header: Authorization: Bearer <your-api-key>. Requests without this header are rejected.
Journey-scoped notification template endpoints
Each journey manages its own notification templates, created and managed exclusively within that journey’s scope. These endpoints mirror the workspace-level/notifications endpoints but are nested under a specific journey.
Journey structure: triggers, nodes, and the DAG
A journey is a directed acyclic graph (DAG) of nodes. Each node performs one action (send a notification, wait, branch, fetch data, etc.), and the sequence defines execution order.Triggers
Every journey starts with a trigger node that determines how runs begin. See Triggers for what each type does.- API invoke (
trigger_type: "api-invoke"): Starts a run when you callPOST /journeys/{templateId}/invoke. You can optionally attach a JSON Schema to the trigger via theschemafield. Courier validates thedatapayload against it when the trigger node executes. - Twilio Segment (
trigger_type: "segment"): Starts a run when a matching Twilio Segment event arrives. Requiresrequest_type(identify,group, ortrack) and optionally anevent_id.
Node types
Nodes are the building blocks of your journey. Each node has atype field that determines its behavior, and some node types have a secondary discriminator. Here’s the full set:
Conditions
Several node types support aconditions field for conditional execution. Conditions are expressed as tuples:
- Binary (3 elements):
[path, operator, value], for example["data.plan", "is equal", "pro"] - Unary (2 elements):
[path, operator], for example["data.email", "exists"]
is equal, is not equal, contains, does not contain, starts with, ends with, greater than, greater than or equal, less than, less than or equal.
Available operators for unary conditions: exists, does not exist.
Conditions can be grouped with AND / OR logic, and groups can be nested for complex expressions.
Standard workflow: create → template → wire → publish → invoke
Here’s the typical process for building a journey programmatically. Each step builds on the previous one, so follow them in order:- Create the journey shell with
POST /journeys. Pass anameand at least one node (typically a trigger). This gives you a journey ID and a draft you can build on. - Create journey-scoped templates with
POST /journeys/{id}/templates. These are the notification templates your send nodes will reference. Define the content using Elemental format and specify thechannel(email, sms, push, etc.). - Wire templates into the journey with
PUT /journeys/{id}. Replace the draft with your full node graph, including send nodes that reference the template IDs you created in step 2. - Publish with
POST /journeys/{id}/publish. This locks in the current draft as a versioned snapshot. All new runs execute against the published version. - Invoke with
POST /journeys/{id}/invoke. Pass auser_id(orprofilewith contact info) and optionaldatapayload. Courier starts a run that walks through the DAG.
Example: building a welcome journey
Let’s walk through creating a “Welcome Journey” that sends a welcome email when invoked via the API.Step 1: Create the journey shell
id from the response. You’ll use it in every subsequent request.
Step 2: Create a scoped email template
id from the response as your template ID.
Step 3: Wire the template into the journey
Step 4: Publish the journey
Step 5: Invoke the journey
runId immediately (HTTP 202). Courier processes the run asynchronously, validating data against the trigger’s JSON Schema and walking through the DAG.
Journey-scoped templates vs. workspace templates
These two template types serve different purposes and are managed differently. Journey-scoped templates are created underPOST /journeys/{id}/templates and live exclusively within that journey. They use Elemental format for content, can’t be shared across journeys, and can’t be referenced from the Send API. They’re published either alongside the journey (when you publish the journey itself) or independently via their own publish endpoint.
Workspace templates are the ones you create at POST /notifications or in Design Studio. They live at the workspace level, can be used from the Send API, and aren’t tied to any specific journey.
If you need a template that’s reusable across multiple journeys or callable from the Send API, use a workspace template. If the template is specific to one journey’s logic, keep it scoped to the journey.
Node types reference
Here’s a detailed reference for each node type with their discriminator fields and required properties.Merge strategies for fetch nodes
When a fetch node receives a response, themerge_strategy field determines how it’s incorporated into the journey run state:
Experiments on send nodes
A send node can run an A/B test instead of sending a single template. In place ofmessage.template, the node carries an experiment object that defines 2 to 10 weighted variants, each with its own template. Every recipient is deterministically assigned to one variant and stays in it. A send node uses either a single template or an experiment, not both. See Experiments for how this works in the UI.
message stays required but can be empty when an experiment is present. message.to, message.delay, and message.data still apply to whichever variant is chosen; only message.template is replaced by the variant’s template.
Experiment fields
Variant fields
Behavior
- Weights are relative. They don’t have to add up to 100.
[6, 3, 1]splits traffic 60/30/10. Weights must be zero or greater, and the total must be above zero. A variant weighted0gets no traffic. - Assignments are sticky. A recipient keeps the same variant across sends. Changing a variant’s weight, template, or name doesn’t reassign anyone. Adding or removing a variant, or changing the bucketing key, resets the experiment and re-buckets everyone.
- Metrics are per variant. Courier tracks sent, delivered, opened, clicked, and error or undeliverable counts for each variant.
- Promoting replaces the experiment with the promoted variant’s template and ends the test. Runs already in progress finish on their current version; only new entrants get the promoted template. The other variants are removed from the node.
experiment on the send node in place of message.template: