Preview

ReferenceUpdated June 14, 2026

Preview

Preview a campaign's emails against 5 synthetic leads that match its Ideal Customer Profile (ICP), using the same generation engine as a real run. This is the pre-flight step in the generation workflow: you set up your lead structure and options (the same ones you'd use for a batch run), preview the output to validate tone, structure, and personalization, then process the full CSV with Generate Batch.

Requires the campaigns:write scope.

Billing: Every call costs 5 credits. If some synthetic leads fail to generate, you are refunded for the failed leads; if all fail, you are fully refunded. The credit cost is fixed at 5 per call (there is no free first preview on the API).

Readiness: The campaign's email instructions must be built first (after saving campaign strategy). If they aren't ready yet, the request returns 409 NOT_READY — wait about a minute and retry. No credits are charged when this happens.


Generate a preview

POST /api/v1/campaigns/:id/preview · scope campaigns:write

Path parameters

ParameterTypeDescription
idstring (UUID)Campaign ID.

Headers

HeaderRequiredDescription
AuthorizationYesBearer <API_KEY> (or X-API-Key).
Content-TypeYesapplication/json.
Idempotency-KeyNoA unique key to safely retry. A replayed key returns the original result without re-charging. A failed run releases the key so you can retry.

Request body

FieldTypeRequiredDescription
sample.headersstring[]YesYour lead CSV column headers (≥ 1). The synthetic leads mirror this structure.
sample.rowsstring[][]NoA few sample data rows to anchor realism.
sample.field_mappingobject | stringNoMaps logical fields (e.g. email, firstName, companyName) to your header names.
options.email_countnumber | stringYesEmails per lead. Integer 1–4.
options.email_signaturestringYes*Sender signature, inline. Required unless signature_id is provided. Multi-line: use \n between lines. See Signature format.
options.signature_idstring (UUID)Yes*ID of a saved signature (from GET /api/v1/signatures) to use instead of email_signature. Provide exactly one of the two.
options.additional_detailsstringNoFree-text campaign context passed to the writer ("Added Campaign Details").
options.approved_ctasstring | string[]NoApproved calls-to-action. Defaults to Question CTA, Lead Magnet CTA, Direct Meeting CTA.
options.enrichment_enabledbooleanNoPreview enriched (true) vs. non-enriched (false, default) output. See note below.
options.enrichment_mappingobjectNoAccepted for parity with a real run, but not required for preview.
options.assistant_idstringNoOverride the assistant used for this preview (advanced; defaults to the campaign's).
options.vector_idstringNoOverride the knowledge-base vector store used for this preview (advanced; defaults to the campaign's).

* Exactly one of email_signature or signature_id is required (providing both is a 400). Preview uses the same required config as a real run so the output faithfully reflects what generation will produce (including the real signature). campaign_angle is a deprecated alias of additional_details.

Enrichment in preview. Set enrichment_enabled to true or false to preview the campaign with or without enriched output. Preview uses synthetic leads and simulates the enrichment columns, so no per-lead data or enrichment_mapping is required, and the cost stays a flat 5 credits.

Example request

curl -X POST "https://app.machfive.io/api/v1/campaigns/CAMPAIGN_ID/preview" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1b8e2a-..." \
  -d '{
    "sample": {
      "headers": ["First Name", "Company Name", "Title", "Email"],
      "rows": [["Jane", "Acme Inc", "VP Marketing", "jane@acme.com"]],
      "field_mapping": { "email": "Email", "firstName": "First Name", "companyName": "Company Name" }
    },
    "options": {
      "email_count": 2,
      "email_signature": "Sam Rivera\nMachFive | Founder\n500 Market St, San Francisco, CA 94105"
    }
  }'

Response (200 OK)

{
  "preview_id": "uuid",
  "status": "success",
  "credits_charged": 5,
  "success_count": 5,
  "fail_count": 0,
  "headers": ["#", "Subject Line 1", "Email 1", "Subject Line 2", "Email 2", "First Name", "Company Name", "Title"],
  "rows": [["1", "...", "...", "...", "...", "Jane", "Acme Inc", "VP Marketing"]],
  "synthetic_leads": [ { "First Name": "Jane", "email_address": "jane@acme.com" } ],
  "generated_emails": [
    {
      "lead_index": 0,
      "lead_data": { "First Name": "Jane" },
      "emails": [ { "subject": "...", "body": "..." } ],
      "status": "success"
    }
  ]
}
FieldTypeDescription
preview_idstring (UUID)Stored preview ID.
statusstringsuccess (all leads) or partial (some leads failed).
credits_chargednumberNet credits charged after any partial-failure refunds.
success_count / fail_countnumberPer-lead generation outcome counts.
headers / rowsarrayCSV-shaped table data for display.
synthetic_leadsarrayThe generated synthetic leads.
generated_emailsarrayPer-lead generated emails (and any per-lead errors).

Error responses

StatusErrorDescription
400BAD_REQUESTInvalid body (e.g. missing sample.headers); email_count missing or not 1–4; or no signature (neither email_signature nor signature_id) or both provided.
401UNAUTHORIZEDMissing or invalid API key.
402INSUFFICIENT_CREDITSNot enough balance or PAYG headroom for 5 credits. detail includes required, available, payg_enabled, pct_of_cap.
403FORBIDDENKey missing the campaigns:write scope.
404NOT_FOUNDCampaign not found or not in this workspace, or signature_id not found in this workspace.
409CONFLICTAn Idempotency-Key was reused with a different body, or a request with the same key is still in flight.
409NOT_READYCampaign email instructions aren't built yet; retry shortly. No credits charged.
429RATE_LIMITEDPer-key rate limit exceeded; honor Retry-After. (This write fails closed when the limiter is unavailable.)
500INTERNAL_ERRORGeneration failed. Any charged credits are refunded.