Skip to main content
POST
/
api
/
campaigns
/
ai-chat-sessions
Create a campaign AI chat session
curl --request POST \
  --url https://app.puffle.ai/api/campaigns/ai-chat-sessions \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "campaign_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "node_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'
{
  "session": {
    "id": "d1d1d1d1-1111-1111-1111-111111111111",
    "user_id": "11111111-1111-1111-1111-111111111111",
    "campaign_id": "8c2b2d4e-e29b-41d4-a716-446655440000",
    "node_id": "bbbb1111-1111-1111-1111-111111111111",
    "title": null,
    "messages": null,
    "draft_subject": null,
    "draft_body": null,
    "skeleton": null,
    "skeleton_history": null,
    "draft_history": null,
    "framework_version": null,
    "generated_examples": null,
    "task_status": null,
    "created_at": "2026-04-22T10:00:00Z",
    "updated_at": "2026-04-22T10:00:00Z"
  }
}

Overview

A campaign AI chat session is a back-and-forth conversation with the framework LLM that drafts the content for one email (one campaign_sequence_nodes row). Each session owns:
  • messages — the chat transcript
  • skeleton — the structural framework (hook, value prop, CTA, …) the conversation has converged on
  • draft_subject / draft_body — the current email draft, using %variable% template syntax
  • generated_examples — per-lead AI-written sample emails derived from the skeleton
  • task_status — busy-flag while a /turn or /generate-examples Trigger.dev task is in flight
Creating a session seeds it empty — no LLM or Trigger.dev side effects, only a DB insert. Subsequent calls to runAiChatTurn build up the skeleton and draft, and generateAiChatExamples produces per-lead samples once a skeleton exists.
This operation shares the URL path /api/campaigns/ai-chat-sessions with the list verb. See List campaign AI chat sessions to enumerate sessions for a campaign.

AI agent notes

Typical flow.
  1. POST /api/campaigns/ai-chat-sessions with { campaign_id, node_id } — creates an empty session
  2. POST /api/campaigns/ai-chat-sessions/{id}/turn with a chat message — iteratively build up skeleton and draft_*
  3. Poll GET /api/campaigns/ai-chat-sessions/{id} until task_status returns to null after each turn
  4. Once skeleton is non-null, call POST .../generate-examples to produce sample emails
  5. Copy the finished draft_subject / draft_body onto the sequence node via updateCampaign
One session per sequence node. node_id must reference an existing campaign_sequence_nodes row — the session is FK-linked to it. Deleting the node does not cascade-delete the session.Ownership is checked on POST. The handler explicitly verifies campaign ownership and returns 404 on miss.Idle sessions are safe to create. Creation has no LLM or Trigger.dev side effects — only a DB insert. Burning sessions you never call /turn on costs nothing.

Authorizations

Authorization
string
header
required

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

Body

application/json

Minimal payload — the session is created empty (no messages, no skeleton, no draft). Content is built up via subsequent /turn and /generate-examples calls.

campaign_id
string<uuid>
required

Campaign this session belongs to. Must be owned by the caller — ownership is verified before insert.

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)$
node_id
string<uuid>
required

Sequence node (from campaign_sequence_nodes) this session drafts content for.

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

Session created. Returns the empty session row.

session
object
required

Canonical campaign AI chat session. One session drafts one email (one sequence node) through a back-and-forth chat with the framework LLM.