Skip to main content
DELETE
/
api
/
campaigns
/
{id}
/
sequence
/
nodes
Delete a sequence node
curl --request DELETE \
  --url https://app.puffle.ai/api/campaigns/{id}/sequence/nodes \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "nodeId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'
{
  "sequence_nodes": [
    {
      "id": "bbbb1111-1111-1111-1111-111111111111",
      "campaign_id": "8c2b2d4e-e29b-41d4-a716-446655440000",
      "position": 0,
      "type": "email",
      "subject": "Quick question about {{company}}",
      "body": "Hi {{first_name}}, ..."
    },
    {
      "id": "bbbb3333-1111-1111-1111-111111111111",
      "campaign_id": "8c2b2d4e-e29b-41d4-a716-446655440000",
      "position": 1,
      "type": "end"
    }
  ]
}

Overview

Removes a node from the sequence by id. Draft campaigns only — every mutation on this collection is gated on status === "draft". The nodeId travels in the JSON body, not the URL — the collection endpoint serves all three mutation verbs (POST add, PATCH move, DELETE remove) and keys off the body. The backing Postgres RPC holds a row-level lock while deleting and re-packing remaining nodes to contiguous 0-indexed positions, so concurrent mutations can’t corrupt the order.

AI agent notes

Don’t delete the end node. Every campaign needs a terminal end marker. Removing it will leave the campaign unlaunchable — GET /api/campaigns/{id}/validate will flag it.Positions re-pack automatically. After deletion, remaining nodes shift down so positions stay contiguous (0, 1, 2, …). You don’t need to follow up with moveSequenceNode calls.Read-back fallback. Normal response is { sequence_nodes: [...] }. If the post-mutation read-back fails, the server returns { mutated: true }; in that case refetch via GET /api/campaigns/{id}.Irreversible. There is no soft-delete. Confirm with the human before deleting if the node has been hand-authored content (subject, body, AI purpose).

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)$

Body

application/json

Draft-only. Remaining nodes are re-packed to contiguous 0-indexed positions.

nodeId
string<uuid>
required

UUID of the node to delete. Must belong to this campaign.

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)$

Response

Node deleted. Returns the fresh ordered list of sequence nodes. Falls back to { mutated: true } if the post-mutation read-back fails.

Successful mutation response. The server re-fetches the full sequence and returns it so clients don't have to round-trip a GET.

sequence_nodes
object[]
required

Fresh ordered list of nodes after the mutation. Absent (replaced by mutated: true + optional node_id) only if the server's best-effort read-back after the mutation failed.