Visual Builder API
The Visual Sequence Builder provides a drag-and-drop canvas for designing multi-step sequences and automations. These endpoints manage the canvas state, nodes, edges, and AI-powered generation.
All endpoints require authentication via Bearer token with the appropriate scope.
Canvas State
Load Canvas
GET /api/sequences/:sequenceId/canvasScope: sequences:read
Returns the full canvas state including all nodes and edges for a sequence.
Response (200)
{
"canvas": {
"sequence_id": "uuid",
"nodes": [
{
"id": "node-1",
"type": "email",
"position": { "x": 250, "y": 100 },
"data": {
"label": "Welcome Email",
"config": {
"subject": "Welcome aboard!",
"templateId": "uuid"
}
}
},
{
"id": "node-2",
"type": "delay",
"position": { "x": 250, "y": 250 },
"data": {
"label": "Wait 2 days",
"config": {
"delayDays": 2,
"delayHours": 0
}
}
}
],
"edges": [
{
"id": "edge-1",
"source": "node-1",
"target": "node-2",
"type": "default"
}
]
}
}Bulk Save Canvas
PUT /api/sequences/:sequenceId/canvasScope: sequences:write
Replaces the entire canvas state. This is a bulk operation that overwrites all nodes and edges.
Request Body
{
"nodes": [
{
"id": "node-1",
"type": "email",
"position": { "x": 250, "y": 100 },
"data": {
"label": "Welcome Email",
"config": {
"subject": "Welcome aboard!",
"templateId": "uuid"
}
}
},
{
"id": "node-2",
"type": "delay",
"position": { "x": 250, "y": 250 },
"data": {
"label": "Wait 2 days",
"config": {
"delayDays": 2,
"delayHours": 0
}
}
}
],
"edges": [
{
"id": "edge-1",
"source": "node-1",
"target": "node-2",
"type": "default"
}
]
}Response (200)
{
"saved": true,
"node_count": 2,
"edge_count": 1
}Error Codes
| Status | Description |
|---|---|
| 400 | Invalid node or edge structure |
| 404 | Sequence not found |
| 403 | Insufficient permissions |
Nodes CRUD
List Nodes
GET /api/sequences/:sequenceId/nodesScope: sequences:read
Returns all nodes for the specified sequence.
Response (200)
{
"nodes": [
{
"id": "node-1",
"sequence_id": "uuid",
"type": "email",
"position": { "x": 250, "y": 100 },
"data": {
"label": "Welcome Email",
"config": {
"subject": "Welcome aboard!",
"templateId": "uuid"
}
},
"created_at": "2026-03-28T10:00:00Z",
"updated_at": "2026-03-28T10:00:00Z"
}
]
}Create Node
POST /api/sequences/:sequenceId/nodesScope: sequences:write
Creates a new node on the canvas.
Request Body
{
"type": "email",
"position": { "x": 250, "y": 100 },
"data": {
"label": "Follow-Up Email",
"config": {
"subject": "Just checking in",
"templateId": "uuid"
}
}
}| Field | Required | Description |
|---|---|---|
type | Yes | Node type (e.g. email, delay, condition, webhook_trigger, http_request, slack_message, crm_update, create_record, data_transform, schedule_trigger, crm_event_trigger) |
position | Yes | Canvas position { x, y } |
data | Yes | Node data including label and config |
Response (201)
{
"node": {
"id": "node-3",
"sequence_id": "uuid",
"type": "email",
"position": { "x": 250, "y": 100 },
"data": {
"label": "Follow-Up Email",
"config": {
"subject": "Just checking in",
"templateId": "uuid"
}
},
"created_at": "2026-03-28T12:00:00Z",
"updated_at": "2026-03-28T12:00:00Z"
}
}Update Node
PATCH /api/sequences/:sequenceId/nodes/:nodeIdScope: sequences:write
Updates an existing node. Supports partial updates.
Request Body
{
"position": { "x": 300, "y": 150 },
"data": {
"label": "Updated Label",
"config": {
"subject": "New subject line"
}
}
}Response (200)
{
"node": {
"id": "node-1",
"sequence_id": "uuid",
"type": "email",
"position": { "x": 300, "y": 150 },
"data": {
"label": "Updated Label",
"config": {
"subject": "New subject line",
"templateId": "uuid"
}
},
"updated_at": "2026-03-28T14:00:00Z"
}
}Delete Node
DELETE /api/sequences/:sequenceId/nodes/:nodeIdScope: sequences:write
Deletes a node and all connected edges.
Response (200)
{
"deleted": true,
"edges_removed": 2
}Error Codes
| Status | Description |
|---|---|
| 400 | Invalid node data |
| 404 | Sequence or node not found |
| 403 | Insufficient permissions |
Edges CRUD
List Edges
GET /api/sequences/:sequenceId/edgesScope: sequences:read
Returns all edges (connections between nodes) for the sequence.
Response (200)
{
"edges": [
{
"id": "edge-1",
"sequence_id": "uuid",
"source": "node-1",
"target": "node-2",
"type": "default",
"label": null,
"created_at": "2026-03-28T10:00:00Z"
}
]
}Create Edge
POST /api/sequences/:sequenceId/edgesScope: sequences:write
Creates a connection between two nodes.
Request Body
{
"source": "node-1",
"target": "node-3",
"type": "default",
"label": "Yes"
}| Field | Required | Description |
|---|---|---|
source | Yes | ID of the source node |
target | Yes | ID of the target node |
type | No | Edge type (default, conditional). Defaults to default |
label | No | Display label for the edge (e.g. "Yes", "No" for condition branches) |
Response (201)
{
"edge": {
"id": "edge-2",
"sequence_id": "uuid",
"source": "node-1",
"target": "node-3",
"type": "default",
"label": "Yes",
"created_at": "2026-03-28T12:00:00Z"
}
}Delete Edge
DELETE /api/sequences/:sequenceId/edges/:edgeIdScope: sequences:write
Removes a connection between nodes.
Response (200)
{
"deleted": true
}Error Codes
| Status | Description |
|---|---|
| 400 | Invalid edge (source or target node not found) |
| 404 | Sequence or edge not found |
| 403 | Insufficient permissions |
AI Generation
Generate Flow from Prompt
POST /api/sequences/:sequenceId/generateScope: sequences:write
Uses AI to generate a complete node-and-edge flow from a natural language description. The generated flow replaces the current canvas state.
Request Body
{
"prompt": "Create a 3-step onboarding sequence: send a welcome email, wait 2 days, then send a follow-up with a meeting link"
}| Field | Required | Description |
|---|---|---|
prompt | Yes | Natural language description of the desired flow |
Response (200)
{
"nodes": [
{
"id": "gen-node-1",
"type": "email",
"position": { "x": 250, "y": 50 },
"data": {
"label": "Welcome Email",
"config": {
"subject": "Welcome to the team!",
"bodyHint": "Warm welcome with key resources"
}
}
},
{
"id": "gen-node-2",
"type": "delay",
"position": { "x": 250, "y": 200 },
"data": {
"label": "Wait 2 Days",
"config": {
"delayDays": 2,
"delayHours": 0
}
}
},
{
"id": "gen-node-3",
"type": "email",
"position": { "x": 250, "y": 350 },
"data": {
"label": "Follow-Up with Meeting Link",
"config": {
"subject": "Let's schedule a quick call",
"bodyHint": "Include calendar booking link"
}
}
}
],
"edges": [
{
"id": "gen-edge-1",
"source": "gen-node-1",
"target": "gen-node-2",
"type": "default"
},
{
"id": "gen-edge-2",
"source": "gen-node-2",
"target": "gen-node-3",
"type": "default"
}
]
}Error Codes
| Status | Description |
|---|---|
| 400 | Prompt is empty or too long |
| 404 | Sequence not found |
| 422 | AI could not generate a valid flow from the prompt |
| 429 | AI generation rate limited |
AI Optimize
Get Optimization Suggestions
POST /api/sequences/:sequenceId/optimizeScope: sequences:write
Analyzes the current canvas and returns AI-powered suggestions to improve the flow.
Request Body
No request body required. The endpoint reads the current canvas state.
Response (200)
{
"suggestions": [
{
"type": "add_step",
"title": "Add a condition check",
"description": "Consider adding an email-opened condition after the first email to branch between engaged and unengaged contacts.",
"priority": "high",
"target_node_id": "node-1"
},
{
"type": "adjust_timing",
"title": "Reduce delay to 1 day",
"description": "Data shows higher engagement when follow-ups are sent within 24 hours.",
"priority": "medium",
"target_node_id": "node-2"
},
{
"type": "improve_content",
"title": "Personalize subject line",
"description": "Adding the contact's first name to the subject line typically increases open rates by 20%.",
"priority": "low",
"target_node_id": "node-3"
}
]
}Suggestion Types
| Type | Description |
|---|---|
add_step | Suggests adding a new node to the flow |
remove_step | Suggests removing an unnecessary node |
adjust_timing | Suggests changing delay durations |
improve_content | Suggests content improvements for email/message nodes |
add_condition | Suggests adding a conditional branch |
reorder | Suggests reordering steps for better results |
Error Codes
| Status | Description |
|---|---|
| 404 | Sequence not found |
| 422 | Canvas is empty — nothing to optimize |
Sequence Fields for Visual Builder
When creating or updating sequences via the Sequences API, the following fields control builder behavior:
| Field | Type | Description |
|---|---|---|
builder_mode | "linear" | "visual" | Whether the sequence uses the classic step list (linear) or the drag-and-drop canvas (visual). Default: "linear" |
flow_type | "sequence" | "automation" | Whether this is a contact-enrollment sequence or an event-driven automation. Default: "sequence" |
These fields are returned in all sequence list and detail responses. See also:
- Sequences API for enrollment and listing endpoints
- Automations API for automation-specific trigger and run endpoints