Campaigns

Create draft campaign

Creates a draft native campaign. Optional flow is normalized and validated on write. Optional recipient_esps and auto_esp_match choose which email providers get first emails. Optional follow_up_share sets how often a mailbox sends a due follow-up instead of a new lead, and max_new_per_day how many new leads it may start each day. The response carries warnings next to data. Supports idempotent retries with Idempotency-Key. See CampaignFlow and Campaign setup.

POST/v1/campaigns

Authorization

bearerAuth
AuthorizationBearer <token>

Use an account API key in the Authorization header: Authorization: Bearer f_....

In: header

Header Parameters

Idempotency-Key?string

Optional idempotency key for campaign create, lead create, bulk lead imports, and person enrichment. Retries with the same key and body return the cached response.

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

name?string
schedule?object

Send window. Omit to use Central 9–5 Mon–Fri. Pass null for 24/7.

timezonestring

IANA timezone, e.g. America/Chicago.

start_hourinteger

Inclusive start hour in the campaign timezone.

Range0 <= value <= 23
end_hourinteger

Exclusive end hour in the campaign timezone.

Range0 <= value <= 23
days_of_week?array<integer>

Allowed weekdays. Null/empty = every day.

start_minute?integer

Optional start minute. Defaults to 0 when omitted.

Range0 <= value <= 59
end_minute?integer

Optional end minute. Defaults to 0 when omitted.

Range0 <= value <= 59
sending_interval_seconds?number

Seconds between sends per mailbox. Omit to use 1440 (24 minutes; ~20 emails per mailbox per day on the default window).

lifecycle_schedule?object

Optional calendar start/pause dates. Omit to leave unset. start_at/pause_at are ignored on write.

time_zonestring

IANA timezone used to interpret start_on and pause_on.

start_onstring

First local sending date. Null means start as soon as the campaign is launched.

Formatdate
pause_onstring

Exclusive local pause date. Null means the campaign never auto-pauses.

Formatdate
start_at?string

Read-only UTC instant of local midnight on start_on. Ignored on write.

Formatdate-time
pause_at?string

Read-only UTC instant of local midnight on pause_on. Ignored on write.

Formatdate-time
mailbox_ids?array<string>
tag_ids?array<string>
flow?CampaignFlow

Directed acyclic graph of campaign steps (nodes + edges). See Email sequences for a plain-language overview, or expand the properties below.

nodesarray<FlowNode>
edgesarray<FlowEdge>
recipient_esps?array<string>

ESP filter: the email providers this campaign sends first emails to. Omit for every provider. Send one or more of google, microsoft and other, each at most once. other is every provider that is not Google or Microsoft. All three and null both mean every provider. An empty list is rejected. The filter applies only to a lead's first email. Follow-ups and replies always use the mailbox that sent the first email. When a filter applies, a lead whose email provider has not been checked yet waits for the check, usually about a minute.

Items1 <= items
auto_esp_match?boolean

Omit for false. When true, each mailbox prefers first emails to leads on its own email provider (the mailbox's sending_esp). It is best effort and never delays sending: a mailbox with no matching lead takes any lead the filters allow. The recipient_esps filters on the campaign and on its mailboxes always win over matching. Matching applies only to a lead's first email. Follow-ups and replies always use the mailbox that sent the first email.

follow_up_share?integer

Omit for 50. How often a mailbox sends a due follow-up instead of a new lead, in percent: 100 means follow-ups first, 0 new leads first. From 0 to 100 in steps of 10. The default is 50.

Multiple Of10
Range0 <= value <= 100
max_new_per_day?integer

Omit or send null for no limit. New leads per day: how many people who have not been emailed yet the campaign may start each day, counted by calendar day in the campaign's time zone. null means no limit and 0 means no new leads. Follow-ups are not limited, and unused room does not carry over. New leads go out in the order they were added. A lower number applies from the next open send slot, so slots already booked still send; pause the campaign to stop at once.

Range0 <= value <= 10000
curl -X POST "https://example.com/v1/campaigns" \  -H "Content-Type: application/json" \  -d '{    "name": "Q2 Pipeline",    "sending_interval_seconds": 1440,    "mailbox_ids": [      "c23da7b6-df4e-4d2f-b100-4bb07b7d38d7"    ],    "flow": {      "nodes": [        {          "id": "leadSource-1",          "type": "leadSource",          "position": {            "x": 0,            "y": 0          },          "data": {            "label": "Lead Bucket",            "customFieldKeys": [              "company"            ]          }        }      ],      "edges": []    }  }'
{  "data": {    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",    "account_id": "449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65",    "bucket_id": "8f7ac8aa-ed2b-4e16-81e0-0766bdb092b8",    "name": "string",    "status": "draft",    "display_status": "draft",    "completed_at": "2019-08-24T14:15:22Z",    "source": "manual",    "flow_data": {      "nodes": [        {          "id": "leadSource-1",          "type": "leadSource",          "position": {            "x": 0,            "y": 0          },          "data": {            "label": "Lead Bucket",            "customFieldKeys": [              "company"            ],            "mappedStandardFieldKeys": [              "email",              "first_name",              "last_name"            ],            "isRequired": true          },          "deletable": false        },        {          "id": "email-1",          "type": "email",          "position": {            "x": 220,            "y": 0          },          "data": {            "label": "Intro Email",            "priority": false,            "variants": [              {                "id": "11111111-1111-4111-8111-111111111111",                "label": "A",                "subject": "Quick question for {{first_name}}",                "template": "Hi {{first_name}} - reaching out about {{custom.company}}.",                "isActive": true,                "order": 0              },              {                "id": "11111111-1111-4111-8111-111111111112",                "label": "B",                "subject": "Following up for {{first_name}}",                "template": "Hi {{first_name}} - wanted to share a quick idea for {{custom.company}}.",                "isActive": true,                "order": 1              }            ]          }        },        {          "id": "waitTime-1",          "type": "waitTime",          "position": {            "x": 460,            "y": 0          },          "data": {            "label": "Wait 1 day",            "duration": "1",            "unit": "days",            "wait_duration_seconds": 86400          }        },        {          "id": "email-2",          "type": "email",          "position": {            "x": 700,            "y": 0          },          "data": {            "label": "Follow-up",            "priority": false,            "variants": [              {                "id": "22222222-2222-4222-8222-222222222221",                "label": "A",                "subject": "Bumping this for {{first_name}}",                "template": "Hi {{first_name}} - circling back in case this is relevant for {{custom.company}}.",                "isActive": true,                "order": 0              },              {                "id": "22222222-2222-4222-8222-222222222222",                "label": "B",                "subject": "Any thoughts, {{first_name}}?",                "template": "Hi {{first_name}} - should I close the loop or send more detail?",                "isActive": true,                "order": 1              }            ]          }        }      ],      "edges": [        {          "id": "e1",          "source": "leadSource-1",          "target": "email-1"        },        {          "id": "e2",          "source": "email-1",          "target": "waitTime-1"        },        {          "id": "e3",          "source": "waitTime-1",          "target": "email-2"        }      ]    },    "schedule": {      "timezone": "America/Chicago",      "start_hour": 9,      "end_hour": 17,      "days_of_week": [        1,        2,        3,        4,        5      ],      "start_minute": 0,      "end_minute": 0    },    "sending_interval_seconds": 0,    "recipient_esps": [      "google"    ],    "auto_esp_match": true,    "follow_up_share": 0,    "max_new_per_day": 0,    "lifecycle_schedule": {      "time_zone": "America/Chicago",      "start_on": "2026-09-01",      "pause_on": "2026-10-01",      "start_at": "2026-09-01T05:00:00.000Z",      "pause_at": "2026-10-01T05:00:00.000Z"    },    "created_at": "2019-08-24T14:15:22Z",    "updated_at": "2019-08-24T14:15:22Z",    "deleted_at": "2019-08-24T14:15:22Z",    "tags": [      {        "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",        "name": "string",        "color": "string",        "created_at": "2019-08-24T14:15:22Z"      }    ],    "mailbox_ids": [      "66b30d1e-447a-440c-8f92-4f8a34e9bbee"    ]  },  "warnings": [    {      "code": "esp_no_first_emails",      "severity": "error",      "message": "string",      "affected_campaigns": [        {          "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",          "name": "string"        }      ]    }  ]}
{  "error": {    "type": "invalid_request_error",    "code": "invalid_flow",    "message": "Flow validation failed"  },  "details": [    {      "path": "nodes[1].data.variants[0].id",      "code": "invalid_variant_id",      "message": "Email variants must have a stable UUID id."    }  ]}
{  "error": {    "type": "authentication_error",    "code": "invalid_api_key",    "message": "A valid Furnace API key is required"  }}
{  "error": {    "type": "rate_limit_error",    "code": "rate_limit_exceeded",    "message": "Rate limit exceeded for this account"  }}
{  "error": {    "type": "api_error",    "code": "internal_error",    "message": "Internal server error"  }}