Create workflow
Creates a workflow as a draft (version 1). The definition is validated strictly; see Workflows for the
node, edge, trigger, condition and action reference. A draft does not run yet: call Publish workflow
to activate it. Nested group and team member IDs are sent as public IDs (gid_..., sub_...) and
checked against your account. An account can have at most 100 workflows.
Base URL:
POST: https://api.smsgatewayapi.com/v1/flows
| Parameter | Input | Description | |
|---|---|---|---|
| client_id | API client ID | Developer › API & Webhooks › API credentials | Required |
| client_secret | API client secret | A cs_ credential, shown once when you create it under Developer › API & Webhooks › API credentials | Required |
| name | name of the workflow | 1 to 255 characters | Required |
| nodes | list of node objects | 1 to 200 nodes; a draft may have zero or one trigger node | Required |
| edges | list of edge objects | 0 to 400 edges; always sent together with nodes | Required |
| viewport | { x, y, zoom } | Position and zoom of the builder canvas | Optional |
| meta | object | Free-form data that is stored with the version | Optional |
<?php //PHP - cURL $ch = curl_init(); $url = "https://api.smsgatewayapi.com/v1/flows"; $client_id = "XXX"; // Your API client ID (required) $client_secret = "YYY"; // Your API client secret (required) $data = [ 'name' => "STOP = opt-out", //Name of the workflow (required) 'nodes' => [ //Nodes (required) ["id" => "trigger", "type" => "triggerNode", "position" => ["x" => 0, "y" => 0], "data" => ["title" => "Inbound message", "config" => ["triggerType" => "inbound_message", "inboxId" => 0]]], ["id" => "is_stop", "type" => "conditionNode", "position" => ["x" => 0, "y" => 150], "data" => ["title" => "Message is STOP?", "config" => ["conditionType" => "if", "field" => "message.body", "operator" => "equals", "value" => "STOP"]]], ["id" => "opt_out", "type" => "actionNode", "position" => ["x" => 0, "y" => 300], "data" => ["title" => "Opt out", "config" => ["actionType" => "opt_out", "editMethod" => "opt_out"]]] ], 'edges' => [ //Edges (required, together with nodes) ["id" => "e1", "source" => "trigger", "sourceHandle" => "out", "target" => "is_stop", "targetHandle" => "in"], ["id" => "e2", "source" => "is_stop", "sourceHandle" => "true", "target" => "opt_out", "targetHandle" => "in"] ]
]; curl_setopt($ch, CURLOPT_URL, "$url"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_VERBOSE, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "X-Client-Id: $client_id", "X-Client-Secret: $client_secret", "Content-Type: application/json", ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $response = curl_exec($ch); ?>
Example request:
{
"name": "STOP = opt-out",
"nodes": [
{
"id": "trigger",
"type": "triggerNode",
"position": { "x": 0, "y": 0 },
"data": {
"title": "Inbound message",
"config": { "triggerType": "inbound_message", "inboxId": 0 }
}
},
{
"id": "is_stop",
"type": "conditionNode",
"position": { "x": 0, "y": 150 },
"data": {
"title": "Message is STOP?",
"config": { "conditionType": "if", "field": "message.body", "operator": "equals", "value": "STOP" }
}
},
{
"id": "opt_out",
"type": "actionNode",
"position": { "x": 0, "y": 300 },
"data": {
"title": "Opt out",
"config": { "actionType": "opt_out", "editMethod": "opt_out" }
}
}
],
"edges": [
{ "id": "e1", "source": "trigger", "sourceHandle": "out", "target": "is_stop", "targetHandle": "in" },
{ "id": "e2", "source": "is_stop", "sourceHandle": "true", "target": "opt_out", "targetHandle": "in" }
]
}
Example success response (http status 201):
{
"id": "flw_5d41402abc4b2a76b9719d911017c592",
"uuid": "c0a8012e-4f5b-4d6c-8e9f-1a2b3c4d5e6f",
"name": "STOP = opt-out",
"status": "draft",
"trigger_type": "inbound_message",
"trigger_config": {
"triggerType": "inbound_message",
"inboxId": 0
},
"published_version_number": null,
"draft_version_number": 1,
"has_draft_changes": true,
"published_at": null,
"updated_at": "2026-09-08T10:15:00+02:00",
"nodes": [
{
"id": "trigger",
"type": "triggerNode",
"position": { "x": 0, "y": 0 },
"data": {
"title": "Inbound message",
"config": { "triggerType": "inbound_message", "inboxId": 0 }
}
},
{
"id": "is_stop",
"type": "conditionNode",
"position": { "x": 0, "y": 150 },
"data": {
"title": "Message is STOP?",
"config": { "conditionType": "if", "field": "message.body", "operator": "equals", "value": "STOP" }
}
},
{
"id": "opt_out",
"type": "actionNode",
"position": { "x": 0, "y": 300 },
"data": {
"title": "Opt out",
"config": { "actionType": "opt_out", "editMethod": "opt_out", "groupid": null }
}
}
],
"edges": [
{ "id": "e1", "source": "trigger", "sourceHandle": "out", "target": "is_stop", "targetHandle": "in" },
{ "id": "e2", "source": "is_stop", "sourceHandle": "true", "target": "opt_out", "targetHandle": "in" }
],
"viewport": null,
"meta": null,
"version_number": 1,
"version_status": "draft"
}
The nodes and edges come back normalised: defaults are filled in and builder-only keys are stripped.
Example error response:
{
"error": 164,
"errorMsg": "Invalid flow definition: nodes.1.data.config.operator: must be one of equals, not_equals, contains, not_contains, starts_with, ends_with, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, in, not_in, is_empty, is_not_empty, is_true, is_false (+1 more, see error_info)",
"error_info": [
{
"path": "nodes.1.data.config.operator",
"message": "must be one of equals, not_equals, contains, not_contains, starts_with, ends_with, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, in, not_in, is_empty, is_not_empty, is_true, is_false"
},
{
"path": "edges.1.target",
"message": "must be the id of a node in the flow"
}
]
}