Skip to main content
POST
/
api
/
campaigns
/
{id}
/
generate-messages
Generate Emails
curl --request POST \
  --url https://app.puffle.ai/api/campaigns/{id}/generate-messages \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "action": "resolve"
}
'
{
  "messages": [
    {
      "id": "<string>",
      "campaign_id": "<string>",
      "prospect_id": "<string>",
      "node_id": "<string>",
      "subject": "<string>",
      "body_text": "<string>",
      "manually_edited": true,
      "created_at": "2023-11-07T05:31:56Z",
      "body_html": "<string>"
    }
  ]
}
CLI:
puffle campaign generate-message --id <id>

Overview

One POST, two actions, discriminated by the action field in the body:
  • resolve — synchronously resolves %variables% for every non-AI sendable node across every lead and upserts campaign_prospect_messages rows as draft. Also copies pre-generated AI examples onto matching leads (matched by lead name). Manually-edited rows are preserved; template changes flow through to unedited rows.
  • generate_ai — queues the generate-ai-messages Trigger.dev task for a specific AI node. Charges AI_MESSAGE_GENERATION credits per lead after the task is queued. The pre-flight credit check returns 402 without destroying any existing messages.
Both actions require the campaign to be in draft status.

AI agent notes

resolve is safe to re-run. It upserts on (campaign_id, prospect_id, node_id) and excludes manually-edited rows from the upsert — template changes propagate without clobbering user customizations. If the “find edited messages” query itself fails, the server falls back to ignoreDuplicates: true for the whole batch to avoid silently overwriting edits.AI seeding matches on lead name. When resolve seeds AI drafts from generated_examples, it normalizes first_name + last_name and matches against leads. Duplicate names yield multiple rows (one per lead); unmatched examples are logged and skipped. After adding new leads or regenerating the skeleton, re-run resolve to pick up the new matches.generate_ai credit flow. Credits are previewed before any write — a 402 response means no existing messages were deleted and no task was queued. Credits are only charged AFTER the task is successfully enqueued. For single-lead regeneration (prospect_ids provided), the server deletes the existing draft for that (node, lead) pair first so the task can produce a fresh version.Related endpoints. For reading the draft outbox or inline-editing a single message, see GET and PATCH on the same path. For building the framework before calling resolve, see generateCampaignContent.

Authorizations

Authorization
string
header
required

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

Path Parameters

id
string
required

Campaign UUID

Body

application/json

Discriminated by action. resolve synchronously re-aggregates draft messages; generate_ai fires a background task and returns immediately.

action
enum<string>
required
Available options:
resolve

Response

Messages were resolved or AI generation was queued.

messages
object[]
required