Skip to main content
PUT
/
api
/
campaigns
/
{id}
/
sequence
/
nodes
/
{nodeId}
Update a sequence node
curl --request PUT \
  --url https://app.puffle.ai/api/campaigns/{id}/sequence/nodes/{nodeId} \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "subject": "<string>",
  "body": "<string>",
  "include_message": true,
  "is_reply": true,
  "delay_days": 4503599627370495,
  "delay_hours": 4503599627370495,
  "ai_node": {
    "purpose": "<string>",
    "output_type": "email"
  }
}
'
{
  "node": {
    "id": "bbbb1111-1111-1111-1111-111111111111",
    "campaign_id": "8c2b2d4e-e29b-41d4-a716-446655440000",
    "position": 0,
    "type": "email",
    "subject": "Following up on {{company}}",
    "body": "Hi {{first_name}}, just wanted to check in...",
    "ai_node": null
  }
}

Overview

Partial update of a single sequence node. Only fields present in the body are applied. Draft campaigns only — on any other status the server returns 400 without writing. Immutable fields:
  • type — to change a node’s kind, delete it and add a new one with addSequenceNode.
  • position — to reorder, use the PATCH verb on the collection (moveSequenceNode).
  • campaign_id — set at creation.

Editable fields by node type

FieldApplies to
subjectemail
bodyconnection_request, message, email
include_messageconnection_request
is_replyemail
delay_days / delay_hoursdelay
ai_node.purpose / ai_node.output_typeai
AI extension fields are silently ignored unless the existing node type === "ai". If the extension row doesn’t exist yet, the server creates it; otherwise it’s updated in place.

AI agent notes

Two-step AI node setup. After addSequenceNode with { type: "ai" }, immediately PUT the same node with ai_node: { purpose, output_type } to populate the extension row. The builder UI does this in one motion; agents should do the same.Delay validation happens at launch. This endpoint does not enforce the “total delay must be 1 minute to 30 days” rule — that’s checked when you call POST /api/campaigns/{id}/launch. Fail-fast validation for a full sequence is available via GET /api/campaigns/{id}/validate.Read-after-write. The response includes the fully-refreshed node with its AI extension flattened into node.ai_node. No separate GET needed.

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Path Parameters

id
string<uuid>
required

Campaign UUID

Pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
nodeId
string<uuid>
required

Sequence node UUID

Pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$

Body

application/json

All fields optional. Only keys present in the body are applied. type, position, and campaign_id are immutable — use moveSequenceNode to reorder.

subject
string

Email subject template. email nodes only.

body
string

Message body template — supports %first_name%, %company%, %sender_first%, and other variables resolved at launch time.

include_message
boolean

connection_request only. If true, the invite includes a message body (<=300 chars).

is_reply
boolean

email only. If true, the email is sent as a reply on the previous email's thread.

delay_days
integer

delay only. Days to wait before advancing.

Required range: 0 <= x <= 9007199254740991
delay_hours
integer

delay only. Hours to wait before advancing.

Required range: 0 <= x <= 9007199254740991
ai_node
object

AI extension patch. Only applied when the existing node type === 'ai'. Creates the extension row if it doesn't exist yet, otherwise updates it.

Response

Node updated. Returns the refreshed node.

node
object
required

The updated node with its AI extension (if any) flattened from the 1:1 join.