curl -X POST "https://api.tracklysms.com/api/v2/journeys" \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"name":"Welcome","steps":[{"stepId":"done","stepType":"exit"}],"status":"draft"}'
{
"success": true,
"message": "Journey created successfully",
"journey": {
"id": "664f1a2b3c4d5e6f7a8b9c0d",
"accountId": 101,
"name": "Welcome",
"trigger": {
"operator": "AND",
"conditions": [],
"groups": []
},
"priority": 0,
"steps": [
{
"stepId": "done",
"name": "Exit",
"stepType": "exit",
"config": {},
"nextStepId": null
}
],
"startStepId": "done",
"status": "draft",
"pauseReason": null,
"createdAt": "2026-09-21T10:00:00",
"updatedAt": "2026-09-21T10:00:00"
}
}
Journeys (v2)
Create Journey
Create an SMS welcome journey with triggers and steps.
POST
/
v2
/
journeys
curl -X POST "https://api.tracklysms.com/api/v2/journeys" \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"name":"Welcome","steps":[{"stepId":"done","stepType":"exit"}],"status":"draft"}'
{
"success": true,
"message": "Journey created successfully",
"journey": {
"id": "664f1a2b3c4d5e6f7a8b9c0d",
"accountId": 101,
"name": "Welcome",
"trigger": {
"operator": "AND",
"conditions": [],
"groups": []
},
"priority": 0,
"steps": [
{
"stepId": "done",
"name": "Exit",
"stepType": "exit",
"config": {},
"nextStepId": null
}
],
"startStepId": "done",
"status": "draft",
"pauseReason": null,
"createdAt": "2026-09-21T10:00:00",
"updatedAt": "2026-09-21T10:00:00"
}
}
Creates a journey for your account. Start with a draft, then use Update Journey Status to activate the journey after reviewing its steps.
Declares the
Retrieve eligible lists, creatives, audiences, offers, and double opt-in configurations with Get Journey Options. References must belong to your account. Step routes must resolve and cannot form cycles.
Both 409 responses include
journeys.write scope. Authenticate with X-Api-Key; see Authentication.
API Key Requirements
Creating, duplicating, editing, or activating journeys requires a live key with no send-confirmation requirement, no daily send cap, and no sending-list restriction. These requirements also apply to drafts. Account or key-owner policy can require confirmation even when the key does not. Restricted keys retain reads, custom-field refresh, and valid stop operations, subject to their scopes. A detail update is a stop operation only when its entire body is{"status": "draft"}, {"status": "paused"}, or {"status": "archived"}. Archive and the dedicated status endpoint also support stopping.
Empty permission lists, api.full, exact journey scopes, and matching wildcard permissions follow the existing scope rules. Account and key-owner scope ceilings can restrict access even for full-access keys.
Body Parameters
string
required
Required name after trimming whitespace; maximum 255 characters.
object
Enrollment group with operator AND or OR, conditions, and nested groups. Conditions use conditionType: list, custom_field, audience, or any. List conditions use integer listIds; audience conditions use audienceId. Omitted or empty triggers become an empty AND group.
integer
Routing priority; higher values win when several journeys match. Default: 0.
array of objects
Ordered step definitions. Each has stepId, stepType, optional name, config, and nextStepId. Types: send_message, send_contact_card, wait, wait_until, condition, percentage_split, set_custom_field, unsubscribe, double_optin, exit. See Welcome Journeys for step configuration.
string
Starting step ID. When steps exist and this field is omitted, the first step becomes the start.
string
Initial status; defaults to draft. Accepts draft, active, paused, or archived. Creating an active journey requires a nonempty steps array and a start step contained in it, the same checks the status endpoint applies.
Example
curl -X POST "https://api.tracklysms.com/api/v2/journeys" \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"name":"Welcome","steps":[{"stepId":"done","stepType":"exit"}],"status":"draft"}'
Response
Thejourney object uses camelCase: id, integer accountId, name, trigger, priority, steps, startStepId, status, pauseReason, createdAt, and updatedAt. The trigger can be null. Each step contains stepId, name, stepType, config, and nullable nextStepId. Pause reasons and absent timestamps can be null.
{
"success": true,
"message": "Journey created successfully",
"journey": {
"id": "664f1a2b3c4d5e6f7a8b9c0d",
"accountId": 101,
"name": "Welcome",
"trigger": {
"operator": "AND",
"conditions": [],
"groups": []
},
"priority": 0,
"steps": [
{
"stepId": "done",
"name": "Exit",
"stepType": "exit",
"config": {},
"nextStepId": null
}
],
"startStepId": "done",
"status": "draft",
"pauseReason": null,
"createdAt": "2026-09-21T10:00:00",
"updatedAt": "2026-09-21T10:00:00"
}
}
Errors and Retry
| HTTP | Code | Meaning |
|---|---|---|
| 400 | May be absent | Request body is not a JSON object, or name is not a string; read error. |
| 400 | May be absent | Invalid name, trigger, steps, or routing; read error. |
| 400 | May be absent | status is not draft, active, paused, or archived; read error. |
| 400 | May be absent | status is active and the journey has no steps, or no start step contained in those steps; read error. |
| 401 | invalid_credentials | Missing or invalid key. |
| 403 | insufficient_scope | The key or account policy does not permit this operation. |
| 403 | confirmation_required | A send-confirmation policy prevents journey authoring. |
| 403 | journey_not_allowed_for_capped_key | The key has a daily send cap. |
| 403 | list_not_allowed_for_key | The key has a sending-list restriction. |
| 403 | sandbox_read_only | The key is a sandbox key. |
| 403 | May be absent | A step references a partner offer and that product is not enabled for the account; read error. |
| 409 | journey_write_conflict | Another change won; read journeyId and reconcile before resubmitting. |
| 409 | journey_write_outcome_unknown | The write may have committed. Inspect journeyId before another write. |
| 503 | send_policy_unavailable | The send policy could not be checked or changed during the write. |
journeyId and retryable: false. After journey_write_outcome_unknown on a request that included status, inspect the journey; if it shows the status you sent, send the same status once more through the status endpoint, which re-applies the holds for that status. A successful response confirms the journey write.
Idempotency
Send anIdempotency-Key header with create and duplicate requests, and reuse the same key when a lost response leaves the outcome ambiguous. The first 201 is cached for 24 hours and replayed verbatim, so the retry returns the journey the first request created instead of creating a second one. A journey_write_outcome_unknown 409 is cached the same way, so a retry replays that verdict rather than attempting the write again.
Reusing a key with a different request body returns 409 idempotency_conflict, and a retry arriving while the first request is still running returns 409 idempotency_in_progress with a Retry-After header. Duplicate binds its key to the source journey path as well, so the same key aimed at another journey conflicts instead of replaying the earlier copy. Ordinary 4xx results release the key for reuse. Without the header the request runs unprotected: inspect your journeys before repeating it after a lost response.