Skip to main content
POST
/
api
/
campaigns
/
{id}
/
generate-content
Generate campaign content suggestions with AI
curl --request POST \
  --url https://app.puffle.ai/api/campaigns/{id}/generate-content \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "node_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "message": "<string>",
  "sample_leads": [
    {
      "first_name": "<string>",
      "last_name": "<string>",
      "email": "<string>",
      "company": "<string>",
      "position": "<string>",
      "custom_fields": {}
    }
  ],
  "sequence_context": {}
}
'
{
  "session_id": "44444444-4444-4444-4444-444444444444",
  "assistant_message": "Here's a framework targeting ops leaders at mid-market SaaS companies. Let me know what you'd like to adjust.",
  "skeleton": {
    "subject": "Quick thought on %company%'s onboarding",
    "body": "Hi %first_name%,\n\n&&2-sentence pain hook tied to %position%.&&\n\n&&Specific example of how we help.&&\n\nWorth a 10-min chat?\n\n%sender_first%",
    "blocks": [
      {
        "name": "Opener",
        "strategy": "Reference their role and company",
        "personalization": {
          "fields": [
            "first_name",
            "position",
            "company"
          ],
          "approach": "Single-line, no flattery",
          "fallback": "Generic intro tied to role"
        }
      }
    ],
    "exampleSubject": "Quick thought on Acme's onboarding",
    "exampleBody": "Hi Sarah,\n\nMost heads of ops we talk to burn 6+ hours/week on Zapier glue. We replaced that glue at 40+ SaaS companies.\n\nWorth a 10-min chat?\n\nSid"
  },
  "skeleton_updated": true
}

Overview

Runs a single turn of the framework-chat agent against a specific AI sequence node. Finds (or creates) the ai_chat_sessions row keyed by (user, campaign, node), appends the user’s chat message, runs the Claude multi-step agent with updateSkeleton + searchWeb tools, persists any new skeleton, and returns the assistant reply + the latest skeleton inline. Requirements:
  • Campaign must be in draft status
  • node_id must belong to this campaign AND have type: "ai"
  • The session must not already be processing another turn (single-writer)
If the caller omits sample_leads, the server auto-samples up to 5 prospects from the campaign so the agent can ground its examples in real data.

AI agent notes

Session identity. Sessions are keyed by (user_id, campaign_id, node_id) — there is at most one persistent chat per AI node. The server creates it lazily on the first call; subsequent calls continue the same thread. Store session_id from the response if you want to cross-reference the ai_chat_sessions row directly.Skeleton lifecycle. skeleton_updated: true means the agent produced a new framework during this turn. When that happens, any previously cached generated_examples on the session are cleared server-side — you’ll need to regenerate examples before the skeleton can be used to seed prospect drafts.Quality gate. The agent’s updateSkeleton tool runs a judge pass (em-dash check + rules validator) before accepting the skeleton. If the judge fails, the tool returns status: "failed" to the agent so it can self-correct within the same turn; the final skeleton returned to you is always the last one that passed (or null if none ever did).Single-writer — 409 means try again. A concurrent call on the same session returns 409 with Session is busy (chat_processing). Wait and retry — the session releases when the turn completes or errors out.Follow-ups. After a satisfactory skeleton, call POST /api/campaigns/{id}/ai-chat-sessions/{id}/generate-examples to produce per-prospect example emails, then POST /api/campaigns/{id}/generate-messages with action: "resolve" to seed draft messages for launch.

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

A single chat turn. The server appends it to the persisted conversation, runs the framework-chat agent synchronously, and returns the result.

node_id
string<uuid>
required

UUID of the sequence node being drafted. Must belong to this campaign and have type === 'ai'.

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

User-authored chat turn — natural-language instructions to the framework-chat agent.

Minimum string length: 1
sample_leads
object[]

Optional ground-truth examples for the AI. When omitted, the server pulls up to 5 prospects from the campaign.

sequence_context
object

Optional structured context about sibling nodes, used by the prompt to avoid duplication.

Response

Chat turn complete. The session row has been updated and the returned skeleton is the latest version.

Synchronous framework-chat result. The ai_chat_sessions row has already been updated with the new turn + skeleton.

session_id
string<uuid>
required

The ai_chat_sessions row identifier. Persist this to continue the same conversation on future turns.

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

Plain-text assistant reply. Render as the AI's chat message.

skeleton
object
required

Structured email framework produced by the AI. Persisted on the ai_chat_sessions row and returned verbatim.

skeleton_updated
boolean
required

True iff the agent produced a new skeleton during this turn. When true, any previously cached generated_examples on the session are cleared server-side.