Campaigns

Create, read, schedule, launch, pause and delete campaigns, and manage the emails and mailboxes each one uses.

List campaigns

get https://api.emailchaser.com/r/campaigns
Works with a read-only key

Lists all campaigns for the authenticated user's space with optional filtering by status and folder. Supports pagination.

  • page integer in query

    Page number (default: 1)

  • limit integer in query

    Page size (default: 20, maximum: 200)

  • status string in query

    Filter by status

    One of: "not_started", "running", "completed", "paused", "draft"
  • folderId integer in query

    Filter by folder ID

Fields in the 200 response
  • campaigns array of CampaignListItem
    8 fields inside campaigns
    • createdAt string
    • emoji string
    • flow string
    • id integer
    • name string
    • status string
    • updatedAt string
  • hasMore boolean
  • limit integer
  • page integer
  • total integer

Request

curl -X GET "https://api.emailchaser.com/r/campaigns" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY"

Response

{
  "campaigns": [
    {
      "createdAt": "string",
      "emoji": "string",
      "flow": "string",
      "id": 123,
      "name": "Jane Doe",
      "stats": {
        "bouncedLeadsCount": 123,
        "contactedLeadsCount": 123,
        "emailsSentCount": 123,
        "leadsRespondedPositivelyCount": 123,
        "repliedLeadsCount": 123,
        "senderEmailsConnected": 123,
        "senderEmailsDisconnected": 123,
        "senderEmailsTotal": 123
      },
      "status": "string",
      "updatedAt": "string"
    }
  ],
  "hasMore": true,
  "limit": 123,
  "page": 123,
  "total": 123
}

Create a campaign

post https://api.emailchaser.com/r/campaigns

Creates a campaign in the API key's workspace. The campaign starts in draft status and is placed in the workspace's default folder. Before it can be launched with POST /campaigns/{id}/resume, it needs four things: a sequence (PUT /campaigns/{id}/sequence), sending days (PUT /campaigns/{id}/schedule; there is no default), at least one email account (POST /campaigns/{id}/sender-emails), and at least one lead (POST /leads with campaignId). Campaigns with flow api can launch without leads.

  • allowNonBusinessEmails boolean
  • dailyLimit integer

    DailyLimit caps the total emails (initial + follow-ups) this campaign may schedule per calendar day in the campaign timezone. Omit for no campaign-level cap; per-inbox limits still apply either way.

    Minimum 1. Maximum 10000
  • emoji string
    At least 1 characters. At most 10 characters
  • flow string

    Flow defaults to multiple_leads_scheduled, which is what the app creates, so a campaign made through the API behaves identically to one made in the UI. The api flow skips lead validation and the processing-leads guard at launch; pass it only if you want that.

    One of: "multiple_leads_scheduled", "api"
  • ignoreOutOfOfficeReplies boolean
  • isEnabledCatchallValidated boolean
  • isEnabledEmailVerifier boolean
  • isEnabledIgnoreHardBouncedLeads boolean
  • isEnabledIgnoreLeadsWhoAlreadyResponded boolean
  • isEnabledLlm boolean

    Settings - all optional, mirroring UpdateCampaignRequest

  • isEnabledSkipLeadIfAlreadyExists boolean
  • isEnabledStopFollowUpsForSameCompany boolean
  • isEnabledStopFollowUpsOnReply boolean
  • maximumSendingLimitPerSenderEmail integer
    Minimum 1. Maximum 10000
  • maximumSendingLimitPerSenderEmailVariation integer
    Minimum 0. Maximum 100
  • maximumTimeBetweenEmails integer
    Minimum 2. Maximum 30
  • minimumHealthScore integer

    MinimumHealthScore, 1-100, is the lowest email account health score (see healthScore on the sender emails) that may send in this campaign. An account below it, or with no score yet, sends nothing here, first emails and follow-ups alike, until its score is back at or above it; its conversations wait for it and never move to another account. Omit or send 0 for no minimum.

    Minimum 0. Maximum 100. Example: 80
  • minimumTimeBetweenEmails integer
    Minimum 2. Maximum 30
  • name string required
    At least 1 characters. At most 255 characters
  • timezone string
Fields in the 201 response
  • campaign CreatedCampaign
    8 fields inside campaign
    • createdAt string
      Example: 2024-01-15T10:30:00Z
    • emoji string
      Example: 📥
    • flow string
      Example: multiple_leads_scheduled
    • id integer
      Example: 8589949820
    • name string
      Example: RB2B High Intent
    • status string
      Example: draft
    • timezone string
      Example: America/New_York
    • updatedAt string
      Example: 2024-01-15T10:30:00Z
  • message string
    Example: campaign created successfully

Request

curl -X POST "https://api.emailchaser.com/r/campaigns" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "allowNonBusinessEmails": true,
  "dailyLimit": 1,
  "emoji": "string",
  "flow": "multiple_leads_scheduled",
  "ignoreOutOfOfficeReplies": true,
  "isEnabledCatchallValidated": true,
  "isEnabledEmailVerifier": true,
  "isEnabledIgnoreHardBouncedLeads": true,
  "isEnabledIgnoreLeadsWhoAlreadyResponded": true,
  "isEnabledLlm": true,
  "isEnabledSkipLeadIfAlreadyExists": true,
  "isEnabledStopFollowUpsForSameCompany": true,
  "isEnabledStopFollowUpsOnReply": true,
  "maximumSendingLimitPerSenderEmail": 1,
  "maximumSendingLimitPerSenderEmailVariation": 123,
  "maximumTimeBetweenEmails": 2,
  "minimumHealthScore": 80,
  "minimumTimeBetweenEmails": 2,
  "name": "Jane Doe",
  "timezone": "America/New_York"
}'

Response

{
  "campaign": {
    "createdAt": "2024-01-15T10:30:00Z",
    "emoji": "📥",
    "flow": "multiple_leads_scheduled",
    "id": 8589949820,
    "name": "RB2B High Intent",
    "status": "draft",
    "timezone": "America/New_York",
    "updatedAt": "2024-01-15T10:30:00Z"
  },
  "message": "campaign created successfully"
}

Get a campaign by ID

get https://api.emailchaser.com/r/campaigns/{id}
Works with a read-only key

Retrieves detailed information about a specific campaign including settings, schedule, statistics, and email counts. schedule.daysSchedule is one comma-separated string of weekday numbers, such as "1,2,3,4,5", not an array. minimumHealth lists each connected email account on the campaign with its health score and whether the campaign's minimum health score (settings.minimumHealthScore) holds it back; when every account is held back, allHeldBack is true and message says the campaign is sending nothing.

  • id integer required in path

    Campaign ID

Fields in the 200 response
  • createdAt string
    Example: 2024-01-10T10:30:00Z
  • 20 fields inside emails
    • blocked integer
      Example: 2
    • bounced integer
      Example: 20
    • canceled integer
      Example: 15
    • catchAll integer
      Example: 12
    • deferred integer
      Example: 5
    • delivered integer
      Example: 280
    • draft integer
      Example: 20
    • dropped integer
      Example: 3
    • errorOnSent integer
      Example: 5
    • followUp integer
      Example: 50
    • followUpCanceled integer
      Example: 10
    • followUpDraft integer
      Example: 10
    • invalid integer
      Example: 8
    • pending integer
      Example: 100
    • replied integer
      Example: 50
    • scheduled integer
      Example: 500
    • sent integer
      Example: 300
    • spamReport integer
      Example: 2
    • total integer
      Example: 1000
    • waitingForReschedule integer
      Example: 5
  • emoji string
    Example: 🚀
  • flow string
    Example: multiple_leads_scheduled
  • id integer
    Example: 123
  • launchAt string

    "0001-01-01T00:00:00Z" until the campaign is first launched.

    Example: 2024-01-15T09:00:00Z
  • minimumHealth CampaignMinimumHealth

    MinimumHealth is what the campaign's minimum health score is doing: each connected account's score, which accounts it holds back and why. Null only when the scores could not be read this time.

    5 fields inside minimumHealth
    • accounts array of CampaignAccountHealth

      Accounts are the campaign's connected email accounts, the held-back ones first.

    • allHeldBack boolean

      AllHeldBack is true when a minimum is set and every connected account is below it, so the campaign sends nothing.

      Example: false
    • heldBackCount integer

      HeldBackCount is how many of those accounts the minimum holds back.

      Example: 1
    • message string

      Message says what the minimum is doing, in plain words. Null when it holds nothing back.

      Example: 1 of 3 email accounts is below this campaign's minimum health score of 80% and sends nothing in it until its score is back up.
    • minimumHealthScore integer

      MinimumHealthScore is the campaign's minimum, 1-100. Null when it has none.

      Example: 80
  • name string
    Example: Q1 Outreach Campaign
  • 7 fields inside schedule
    • cronSchedule string
      Example: 0 9 * * 1-5
    • daysSchedule string

      DaysSchedule is the sending days as one comma-separated string of weekday numbers, where Sunday is 0: "1,2,3,4,5" is Monday to Friday. It is not an array. Empty until days are set with PUT /campaigns/{id}/schedule.

      Example: 1,2,3,4,5
    • endSchedule string
      Example: 17:00
    • everySchedule integer
      Example: 30
    • maximumTimeBetweenEmails integer
      Example: 15
    • minimumTimeBetweenEmails integer
      Example: 5
    • startSchedule string
      Example: 09:00
  • sendAt string

    "0001-01-01T00:00:00Z" while not set.

    Example: 2024-01-15T10:00:00Z
  • 15 fields inside settings
    • allowNonBusinessEmails boolean
      Example: false
    • dailyLimit integer

      DailyLimit is the campaign-level cap on total emails scheduled per calendar day (campaign timezone). null means no campaign-level cap.

      Example: 200
    • ignoreOutOfOfficeReplies boolean
      Example: true
    • isEnabledCatchallValidated boolean
      Example: true
    • isEnabledEmailVerifier boolean
      Example: false
    • isEnabledIgnoreHardBouncedLeads boolean
      Example: true
    • isEnabledIgnoreLeadsWhoAlreadyResponded boolean
      Example: true
    • isEnabledLlm boolean
      Example: true
    • isEnabledSkipLeadIfAlreadyExists boolean
      Example: false
    • isEnabledStopFollowUpsAcrossCampaigns boolean
      Example: true
    • isEnabledStopFollowUpsForSameCompany boolean
      Example: false
    • isEnabledStopFollowUpsOnReply boolean
      Example: true
    • maximumSendingLimitPerSenderEmail integer
      Example: 50
    • maximumSendingLimitPerSenderEmailVariation integer
      Example: 10
    • minimumHealthScore integer

      MinimumHealthScore is the lowest email account health score that may send in this campaign, 1-100. null means no minimum.

      Example: 80
  • 5 fields inside stats
    • bouncedLeadsCount integer
    • contactedLeadsCount integer
    • emailsSentCount integer
    • leadsRespondedPositivelyCount integer
    • repliedLeadsCount integer
  • status string
    Example: running
  • timezone string
    Example: America/New_York
  • updatedAt string
    Example: 2024-01-15T10:30:00Z

Request

curl -X GET "https://api.emailchaser.com/r/campaigns/123" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY"

Response

{
  "createdAt": "2024-01-10T10:30:00Z",
  "emails": {
    "blocked": 2,
    "bounced": 20,
    "canceled": 15,
    "catchAll": 12,
    "deferred": 5,
    "delivered": 280,
    "draft": 20,
    "dropped": 3,
    "errorOnSent": 5,
    "followUp": 50,
    "followUpCanceled": 10,
    "followUpDraft": 10,
    "invalid": 8,
    "pending": 100,
    "replied": 50,
    "scheduled": 500,
    "sent": 300,
    "spamReport": 2,
    "total": 1000,
    "waitingForReschedule": 5
  },
  "emoji": "🚀",
  "flow": "multiple_leads_scheduled",
  "id": 123,
  "launchAt": "2024-01-15T09:00:00Z",
  "minimumHealth": {
    "accounts": [
      {
        "address": "john@example.com",
        "healthScore": 72,
        "heldBack": true,
        "reason": "Health score 72% is below this campaign's minimum of 80%.",
        "senderEmailId": 123
      }
    ],
    "allHeldBack": false,
    "heldBackCount": 1,
    "message": "1 of 3 email accounts is below this campaign's minimum health score of 80% and sends nothing in it until its score is back up.",
    "minimumHealthScore": 80
  },
  "name": "Q1 Outreach Campaign",
  "schedule": {
    "cronSchedule": "0 9 * * 1-5",
    "daysSchedule": "1,2,3,4,5",
    "endSchedule": "17:00",
    "everySchedule": 30,
    "maximumTimeBetweenEmails": 15,
    "minimumTimeBetweenEmails": 5,
    "startSchedule": "09:00"
  },
  "sendAt": "2024-01-15T10:00:00Z",
  "settings": {
    "allowNonBusinessEmails": false,
    "dailyLimit": 200,
    "ignoreOutOfOfficeReplies": true,
    "isEnabledCatchallValidated": true,
    "isEnabledEmailVerifier": false,
    "isEnabledIgnoreHardBouncedLeads": true,
    "isEnabledIgnoreLeadsWhoAlreadyResponded": true,
    "isEnabledLlm": true,
    "isEnabledSkipLeadIfAlreadyExists": false,
    "isEnabledStopFollowUpsAcrossCampaigns": true,
    "isEnabledStopFollowUpsForSameCompany": false,
    "isEnabledStopFollowUpsOnReply": true,
    "maximumSendingLimitPerSenderEmail": 50,
    "maximumSendingLimitPerSenderEmailVariation": 10,
    "minimumHealthScore": 80
  },
  "stats": {
    "bouncedLeadsCount": 123,
    "contactedLeadsCount": 123,
    "emailsSentCount": 123,
    "leadsRespondedPositivelyCount": 123,
    "repliedLeadsCount": 123
  },
  "status": "running",
  "timezone": "America/New_York",
  "updatedAt": "2024-01-15T10:30:00Z"
}

Update a campaign by ID

put https://api.emailchaser.com/r/campaigns/{id}

Updates campaign properties such as name, emoji, timezone, and various settings. Only provided fields will be updated. minimumHealthScore (1-100) stops any email account whose health score is below it, or that has no score yet, from sending in this campaign until its score is back up; its conversations wait for it and never move to another account. Send 0 to remove the minimum.

  • id integer required in path

    Campaign ID

  • allowNonBusinessEmails boolean
  • dailyLimit integer

    DailyLimit caps the total emails (initial + follow-ups) this campaign may schedule per calendar day in the campaign timezone. Omit to leave the cap unchanged; it cannot be cleared through this endpoint.

    Minimum 1. Maximum 10000
  • emoji string
    At least 1 characters. At most 10 characters
  • ignoreOutOfOfficeReplies boolean
  • isEnabledCatchallValidated boolean
  • isEnabledEmailVerifier boolean
  • isEnabledIgnoreHardBouncedLeads boolean
  • isEnabledIgnoreLeadsWhoAlreadyResponded boolean
  • isEnabledLlm boolean

    Settings - all optional boolean flags

  • isEnabledSkipLeadIfAlreadyExists boolean
  • isEnabledStopFollowUpsAcrossCampaigns boolean
  • isEnabledStopFollowUpsForSameCompany boolean
  • isEnabledStopFollowUpsOnReply boolean
  • maximumSendingLimitPerSenderEmail integer
    Minimum 1. Maximum 10000
  • maximumSendingLimitPerSenderEmailVariation integer
    Minimum 0. Maximum 100
  • maximumTimeBetweenEmails integer
    Minimum 2. Maximum 30
  • minimumHealthScore integer

    MinimumHealthScore, 1-100, is the lowest email account health score (see healthScore on the sender emails) that may send in this campaign. An account below it, or with no score yet, sends nothing here, first emails and follow-ups alike, until its score is back at or above it; its conversations wait for it and never move to another account. Send 0 to remove the minimum; omit to leave it unchanged. A running campaign re-plans its unsent emails at once.

    Minimum 0. Maximum 100. Example: 80
  • minimumTimeBetweenEmails integer
    Minimum 2. Maximum 30
  • name string
    At least 1 characters. At most 255 characters
  • timezone string
Fields in the 200 response
  • campaign object
    4 fields inside campaign
    • emoji string
      Example: 🚀
    • id integer
      Example: 123
    • minimumHealthScore integer

      MinimumHealthScore is the campaign's minimum email account health score after this update, null when it has none.

      Example: 80
    • name string
      Example: Q1 Outreach Campaign
  • message string
    Example: campaign updated successfully

Request

curl -X PUT "https://api.emailchaser.com/r/campaigns/123" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "allowNonBusinessEmails": true,
  "dailyLimit": 1,
  "emoji": "string",
  "ignoreOutOfOfficeReplies": true,
  "isEnabledCatchallValidated": true,
  "isEnabledEmailVerifier": true,
  "isEnabledIgnoreHardBouncedLeads": true,
  "isEnabledIgnoreLeadsWhoAlreadyResponded": true,
  "isEnabledLlm": true,
  "isEnabledSkipLeadIfAlreadyExists": true,
  "isEnabledStopFollowUpsAcrossCampaigns": true,
  "isEnabledStopFollowUpsForSameCompany": true,
  "isEnabledStopFollowUpsOnReply": true,
  "maximumSendingLimitPerSenderEmail": 1,
  "maximumSendingLimitPerSenderEmailVariation": 123,
  "maximumTimeBetweenEmails": 2,
  "minimumHealthScore": 80,
  "minimumTimeBetweenEmails": 2,
  "name": "Jane Doe",
  "timezone": "America/New_York"
}'

Response

{
  "campaign": {
    "emoji": "🚀",
    "id": 123,
    "minimumHealthScore": 80,
    "name": "Q1 Outreach Campaign"
  },
  "message": "campaign updated successfully"
}

Delete a campaign

delete https://api.emailchaser.com/r/campaigns/{id}

Deletes a campaign and all associated data including emails, sequences, and scheduled tasks.

  • id integer required in path

    Campaign ID

Fields in the 200 response
  • id integer
    Example: 123
  • message string
    Example: campaign deleted successfully

Request

curl -X DELETE "https://api.emailchaser.com/r/campaigns/123" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY"

Response

{
  "id": 123,
  "message": "campaign deleted successfully"
}

Move a lead to another campaign

post https://api.emailchaser.com/r/campaigns/{id}/leads/{leadId}/move

Removes a lead from the source campaign (deleting its unsent emails) and adds it to the target campaign. If the target campaign is running, emails for the lead are enqueued.

  • id integer required in path

    Source campaign ID

  • leadId integer required in path

    Lead ID

  • targetCampaignId integer required
Fields in the 200 response
  • leadId integer
    Example: 123
  • message string
    Example: lead moved successfully
  • sourceCampaignId integer
    Example: 1
  • targetCampaignId integer
    Example: 2

Request

curl -X POST "https://api.emailchaser.com/r/campaigns/123/leads/123/move" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "targetCampaignId": 123
}'

Response

{
  "leadId": 123,
  "message": "lead moved successfully",
  "sourceCampaignId": 1,
  "targetCampaignId": 2
}

Pause a campaign

post https://api.emailchaser.com/r/campaigns/{id}/pause

Pauses a running campaign and cancels all scheduled emails.

  • id integer required in path

    Campaign ID

Fields in the 200 response
  • campaign object
    3 fields inside campaign
    • id integer
      Example: 123
    • name string
      Example: Q1 Outreach Campaign
    • status string
      Example: paused
  • message string
    Example: campaign paused successfully

Request

curl -X POST "https://api.emailchaser.com/r/campaigns/123/pause" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY"

Response

{
  "campaign": {
    "id": 123,
    "name": "Q1 Outreach Campaign",
    "status": "paused"
  },
  "message": "campaign paused successfully"
}

Launch or resume a campaign

post https://api.emailchaser.com/r/campaigns/{id}/resume

Launches a new campaign or resumes a paused campaign. Validates campaign configuration before launching: the campaign needs sending days, at least one email account, a sequence, and, unless its flow is api, at least one lead. A missing one is refused with 400 and an error naming it.

  • id integer required in path

    Campaign ID

Fields in the 200 response
  • campaign object
    3 fields inside campaign
    • id integer
      Example: 123
    • name string
      Example: Q1 Outreach Campaign
    • status string
      Example: running
  • message string
    Example: campaign launched/resumed successfully

Request

curl -X POST "https://api.emailchaser.com/r/campaigns/123/resume" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY"

Response

{
  "campaign": {
    "id": 123,
    "name": "Q1 Outreach Campaign",
    "status": "running"
  },
  "message": "campaign launched/resumed successfully"
}

Update campaign schedule

put https://api.emailchaser.com/r/campaigns/{id}/schedule

Updates the sending schedule for a campaign including timezone, days, time windows, and frequency. daysSchedule is sent as an array of days, but the response returns it as one comma-separated string of weekday numbers, such as "1,2,3,4,5". GET /campaigns/{id} returns it the same way.

  • id integer required in path

    Campaign ID

  • daysSchedule array of string

    DaysSchedule is the set of weekdays the campaign may send on. Accepts weekday numbers as strings, where Sunday is 0 ("1" is Monday), or weekday names such as "monday". Names are matched case-insensitively and common short forms ("mon", "tues") work. It is sent as an array, but stored as numbers and returned as one comma-separated string: a request sending Monday to Friday by name reads back as "1,2,3,4,5".

    For multiple leads scheduled campaigns (flow 3) and API campaigns.

    Example: ["1","2","3","4","5"]
  • endSchedule string
  • everySchedule integer
    Minimum 1
  • maximumTimeBetweenEmails integer
    Minimum 2. Maximum 30
  • minimumTimeBetweenEmails integer
    Minimum 2. Maximum 30
  • sendAt string

    For single lead scheduled campaigns (flow 2)

  • startSchedule string
  • timezone string required

    Common field for all flows

Fields in the 200 response
  • campaign object
    9 fields inside campaign
    • daysSchedule string

      DaysSchedule comes back as one comma-separated string of weekday numbers, where Sunday is 0, even though the request sends an array.

      Example: 1,2,3,4,5
    • endSchedule string
      Example: 17:00
    • everySchedule integer
      Example: 30
    • id integer
      Example: 123
    • maximumTimeBetweenEmails integer
      Example: 15
    • minimumTimeBetweenEmails integer
      Example: 5
    • name string
      Example: Q1 Outreach Campaign
    • startSchedule string
      Example: 09:00
    • timezone string
      Example: America/New_York
  • message string
    Example: campaign schedule updated successfully

Request

curl -X PUT "https://api.emailchaser.com/r/campaigns/123/schedule" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "daysSchedule": [
    "1",
    "2",
    "3",
    "4",
    "5"
  ],
  "endSchedule": "string",
  "everySchedule": 1,
  "maximumTimeBetweenEmails": 2,
  "minimumTimeBetweenEmails": 2,
  "sendAt": "string",
  "startSchedule": "string",
  "timezone": "America/New_York"
}'

Response

{
  "campaign": {
    "daysSchedule": "1,2,3,4,5",
    "endSchedule": "17:00",
    "everySchedule": 30,
    "id": 123,
    "maximumTimeBetweenEmails": 15,
    "minimumTimeBetweenEmails": 5,
    "name": "Q1 Outreach Campaign",
    "startSchedule": "09:00",
    "timezone": "America/New_York"
  },
  "message": "campaign schedule updated successfully"
}

Attach sender emails to a campaign

post https://api.emailchaser.com/r/campaigns/{id}/sender-emails

Attaches connected sender emails from your workspace to a campaign. A running campaign only sends through the sender emails attached to it, so this call decides which mailboxes the campaign uses. Idempotent: already-attached senders are left untouched and only missing ones are added. The call is refused whole when any requested sender email is not yours (404) or not connected (409). Attaching to a running campaign triggers a reschedule so the new senders are picked up.

  • id integer required in path

    Campaign ID

  • senderEmailIds array of integer required

    SenderEmailIDs are the sender emails to attach. Every ID must be a connected sender email in your workspace; the call is refused whole if any is not.

    At least 1 item. Example: [1,2]
Fields in the 200 response
  • alreadyAttached array of integer

    AlreadyAttached lists the requested sender emails that were attached before this call (the call is idempotent: they are left untouched).

    Example: [3]
  • attached array of integer

    Attached lists the sender emails newly attached by this call.

    Example: [1,2]
  • campaignId integer
    Example: 10
  • message string
    Example: sender emails attached successfully
  • senderEmailIds array of integer

    SenderEmailIDs lists every sender email attached to the campaign after this call.

    Example: [1,2,3]

Request

curl -X POST "https://api.emailchaser.com/r/campaigns/123/sender-emails" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "senderEmailIds": [
    1,
    2
  ]
}'

Response

{
  "alreadyAttached": [
    3
  ],
  "attached": [
    1,
    2
  ],
  "campaignId": 10,
  "message": "sender emails attached successfully",
  "senderEmailIds": [
    1,
    2,
    3
  ]
}

Detach a sender email from a campaign

delete https://api.emailchaser.com/r/campaigns/{id}/sender-emails/{senderEmailId}

Detaches a sender email from a campaign. A running campaign only sends through the sender emails attached to it, so after this call the campaign stops sending through this mailbox: unsent follow-ups to emails this sender already sent are canceled, the sender is removed from the campaign's unsent emails, and a running campaign is rescheduled onto its remaining senders. Emails already sent are untouched. Idempotent: detaching a sender that is not attached is a no-op.

  • id integer required in path

    Campaign ID

  • senderEmailId integer required in path

    Sender Email ID

Fields in the 200 response
  • campaignId integer
    Example: 10
  • detached boolean

    Detached is false when the sender email was not attached to the campaign (the call is idempotent and does nothing in that case).

    Example: true
  • message string
    Example: sender email detached successfully
  • senderEmailIds array of integer

    SenderEmailIDs lists every sender email still attached to the campaign after this call.

    Example: [2,3]

Request

curl -X DELETE "https://api.emailchaser.com/r/campaigns/123/sender-emails/123" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY"

Response

{
  "campaignId": 10,
  "detached": true,
  "message": "sender email detached successfully",
  "senderEmailIds": [
    2,
    3
  ]
}

Get a campaign's sequence

get https://api.emailchaser.com/r/campaigns/{id}/sequence
Works with a read-only key

Returns the ordered emails that make up a campaign's sequence, including any A/B variants. delayDays on each step is counted from the previous step; the first step is always 0.

  • id integer required in path

    Campaign ID

Fields in the 200 response
  • campaignId integer
    Example: 8589949820
  • signature string
    Example: <p>Robby Frank</p>
  • steps array of SequenceStep
    7 fields inside steps
    • body string
      Example: <p>Hi {first_name},</p>
    • contentType string
      Example: html
    • delayDays integer
      Example: 0
    • order integer
      Example: 1
    • subject string
      Example: Quick question, {first_name}
    • useSameThread boolean
      Example: true
    • variants array of SequenceVariant
  • total integer
    Example: 3

Request

curl -X GET "https://api.emailchaser.com/r/campaigns/123/sequence" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY"

Response

{
  "campaignId": 8589949820,
  "signature": "<p>Robby Frank</p>",
  "steps": [
    {
      "body": "<p>Hi {first_name},</p>",
      "contentType": "html",
      "delayDays": 0,
      "order": 1,
      "subject": "Quick question, {first_name}",
      "useSameThread": true,
      "variants": [
        {
          "body": "<p>Hi {first_name},</p>",
          "label": "B",
          "subject": "Quick question about {company_name}"
        }
      ]
    }
  ],
  "total": 3
}

Replace a campaign's sequence

put https://api.emailchaser.com/r/campaigns/{id}/sequence

Replaces the campaign's whole sequence with the steps provided. The operation is idempotent, so sending the same payload twice leaves the same state and a retried request cannot duplicate steps. delayDays is counted from the previous step and the first step must be 0. Editing a running campaign is allowed and re-queues future sends; already-sent emails are untouched. Merge tags take ONE brace, e.g. {first_name}, {last_name}, {company_name}, {job_title}, plus any key set in a lead's customVariables; a fallback is {first_name}[there] or {first_name|there}. Placeholders written in another tool's syntax ({{first_name}}, [[first_name]], [First Name]) are rejected with a 400 that names the tag to write instead, because they are not substituted and would be sent exactly as typed ("Hi [First Name],").

  • id integer required in path

    Campaign ID

  • signature string

    Signature omitted keeps the campaign's current signature; pass an empty string to clear it. Editing one body should not silently drop a signature the caller never asked about.

  • steps array of SequenceStepInput required
    At least 1 item. At most 20 items
    7 fields inside steps
    • body string required
      At least 1 characters
    • contentType string
      One of: "html", "text"
    • delayDays integer
      Minimum 0. Maximum 365
    • order integer
      Minimum 1
    • subject string required
      At least 1 characters. At most 998 characters
    • useSameThread boolean
    • variants array of SequenceVariantInput
      At most 25 items
      3 fields inside variants
      • body string
      • label string required
        At least 1 characters. At most 2 characters
      • subject string required
        At least 1 characters. At most 998 characters
Fields in the 200 response
  • campaignId integer
    Example: 8589949820
  • message string
    Example: campaign sequence replaced successfully
  • signature string
    Example: <p>Robby Frank</p>
  • steps array of SequenceStep
    7 fields inside steps
    • body string
      Example: <p>Hi {first_name},</p>
    • contentType string
      Example: html
    • delayDays integer
      Example: 0
    • order integer
      Example: 1
    • subject string
      Example: Quick question, {first_name}
    • useSameThread boolean
      Example: true
    • variants array of SequenceVariant
  • total integer
    Example: 3

Request

curl -X PUT "https://api.emailchaser.com/r/campaigns/123/sequence" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "signature": "string",
  "steps": [
    {
      "body": "string",
      "contentType": "html",
      "delayDays": 123,
      "order": 1,
      "subject": "Quick question about Acme",
      "useSameThread": true,
      "variants": [
        {
          "body": "string",
          "label": "string",
          "subject": "Quick question about Acme"
        }
      ]
    }
  ]
}'

Response

{
  "campaignId": 8589949820,
  "message": "campaign sequence replaced successfully",
  "signature": "<p>Robby Frank</p>",
  "steps": [
    {
      "body": "<p>Hi {first_name},</p>",
      "contentType": "html",
      "delayDays": 0,
      "order": 1,
      "subject": "Quick question, {first_name}",
      "useSameThread": true,
      "variants": [
        {
          "body": "<p>Hi {first_name},</p>",
          "label": "B",
          "subject": "Quick question about {company_name}"
        }
      ]
    }
  ],
  "total": 3
}

Get campaign performance stats

get https://api.emailchaser.com/r/campaigns/{id}/stats
Works with a read-only key

Returns campaign performance in one call: totals, per-A/B-variant results, per-sequence-step results, and a daily (UTC) time series. Sent and bounced count outbound emails; replied and positive count distinct leads; totals.meetings counts the campaign's leads with a booked meeting recorded (see POST /leads/{id}/meeting). Variant sent counts initial emails only, while steps attribute replies to the exact email that was answered. There is no opens metric anywhere in this response: Emailchaser does not track opens, so its absence is deliberate, not an omission.

  • id integer required in path

    Campaign ID

Fields in the 200 response
  • campaignId integer
    Example: 123
  • daily array of CampaignStatsDay
    4 fields inside daily
    • date string
      Example: 2026-07-01
    • positive integer
      Example: 1
    • replied integer
      Example: 3
    • sent integer
      Example: 40
  • steps array of CampaignStatsStep
    3 fields inside steps
    • index integer
      Example: 0
    • replied integer
      Example: 45
    • sent integer
      Example: 600
  • 5 fields inside totals
    • bounced integer
      Example: 14
    • meetings integer
      Example: 7
    • positive integer
      Example: 32
    • replied integer
      Example: 85
    • sent integer
      Example: 1200
  • variants array of CampaignStatsVariant
    4 fields inside variants
    • label string
      Example: A
    • positive integer
      Example: 18
    • replied integer
      Example: 45
    • sent integer
      Example: 600

Request

curl -X GET "https://api.emailchaser.com/r/campaigns/123/stats" \
  -H "Authorization: Bearer $EMAILCHASER_API_KEY"

Response

{
  "campaignId": 123,
  "daily": [
    {
      "date": "2026-07-01",
      "positive": 1,
      "replied": 3,
      "sent": 40
    }
  ],
  "steps": [
    {
      "index": 0,
      "replied": 45,
      "sent": 600
    }
  ],
  "totals": {
    "bounced": 14,
    "meetings": 7,
    "positive": 32,
    "replied": 85,
    "sent": 1200
  },
  "variants": [
    {
      "label": "A",
      "positive": 18,
      "replied": 45,
      "sent": 600
    }
  ]
}

Generated from the Emailchaser API's own OpenAPI definition, so it always matches the running API.