> For the complete documentation index, see [llms.txt](https://run.emailchaser.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://run.emailchaser.com/api-reference/campaigns.md).

# Campaigns

## List campaigns

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

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.ListCampaignsResponse":{"type":"object","properties":{"campaigns":{"type":"array","items":{"$ref":"#/components/schemas/models.CampaignListItem"}},"hasMore":{"type":"boolean"},"limit":{"type":"integer"},"page":{"type":"integer"},"total":{"type":"integer"}}},"models.CampaignListItem":{"type":"object","properties":{"createdAt":{"type":"string"},"emoji":{"type":"string"},"flow":{"type":"string"},"id":{"type":"integer"},"name":{"type":"string"},"stats":{"$ref":"#/components/schemas/models.CampaignStats"},"status":{"type":"string"},"updatedAt":{"type":"string"}}},"models.CampaignStats":{"type":"object","properties":{"bouncedLeadsCount":{"type":"integer"},"contactedLeadsCount":{"type":"integer"},"emailsSentCount":{"type":"integer"},"leadsRespondedPositivelyCount":{"type":"integer"},"repliedLeadsCount":{"type":"integer"},"senderEmailsConnected":{"type":"integer"},"senderEmailsDisconnected":{"type":"integer"},"senderEmailsTotal":{"type":"integer"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/campaigns":{"get":{"description":"Lists all campaigns for the authenticated user's space with optional filtering by status and folder. Supports pagination.","tags":["Campaigns"],"summary":"List campaigns","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Page number (default: 1)","name":"page","in":"query"},{"schema":{"type":"integer"},"description":"Page size (default: 20, maximum: 200)","name":"limit","in":"query"},{"schema":{"type":"string","enum":["not_started","running","completed","paused","draft"]},"description":"Filter by status","name":"status","in":"query"},{"schema":{"type":"integer"},"description":"Filter by folder ID","name":"folderId","in":"query"}],"responses":{"200":{"description":"List of campaigns","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ListCampaignsResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"500":{"description":"Failed to retrieve campaigns","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}}}}}}
```

## Create a campaign

> Creates a campaign in the API key's workspace. The campaign starts in draft status and is placed in the workspace's default folder. A campaign has no emails until a sequence is written to it with PUT /campaigns/{id}/sequence, and it cannot be launched until then.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.CreateCampaignResponse":{"type":"object","properties":{"campaign":{"$ref":"#/components/schemas/models.CreatedCampaign"},"message":{"type":"string"}}},"models.CreatedCampaign":{"type":"object","properties":{"createdAt":{"type":"string"},"emoji":{"type":"string"},"flow":{"type":"string"},"id":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string"},"timezone":{"type":"string"},"updatedAt":{"type":"string"}}},"models.ErrorInvalidRequest":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorFailedToCreateCampaign":{"type":"object","properties":{"error":{"type":"string"}}},"models.CreateCampaignRequest":{"type":"object","required":["name"],"properties":{"allowNonBusinessEmails":{"type":"boolean"},"dailyLimit":{"description":"DailyLimit caps the total emails (initial + follow-ups) this campaign may\nschedule per calendar day in the campaign timezone. Omit for no\ncampaign-level cap; per-inbox limits still apply either way.","type":"integer","maximum":10000,"minimum":1},"emoji":{"type":"string","maxLength":10,"minLength":1},"flow":{"description":"Flow defaults to multiple_leads_scheduled, which is what the app creates,\nso a campaign made through the API behaves identically to one made in the\nUI. The api flow skips lead validation and the processing-leads guard at\nlaunch; pass it only if you want that.","type":"string","enum":["multiple_leads_scheduled","api"]},"ignoreOutOfOfficeReplies":{"type":"boolean"},"isEnabledCatchallValidated":{"type":"boolean"},"isEnabledEmailVerifier":{"type":"boolean"},"isEnabledIgnoreHardBouncedLeads":{"type":"boolean"},"isEnabledIgnoreLeadsWhoAlreadyResponded":{"type":"boolean"},"isEnabledLlm":{"description":"Settings - all optional, mirroring UpdateCampaignRequest","type":"boolean"},"isEnabledSkipLeadIfAlreadyExists":{"type":"boolean"},"isEnabledStopFollowUpsForSameCompany":{"type":"boolean"},"isEnabledStopFollowUpsOnReply":{"type":"boolean"},"maximumSendingLimitPerSenderEmail":{"type":"integer","maximum":10000,"minimum":1},"maximumSendingLimitPerSenderEmailVariation":{"type":"integer","maximum":100,"minimum":0},"maximumTimeBetweenEmails":{"type":"integer","maximum":30,"minimum":2},"minimumTimeBetweenEmails":{"type":"integer","maximum":30,"minimum":2},"name":{"type":"string","maxLength":255,"minLength":1},"timezone":{"type":"string"}}}}},"paths":{"/campaigns":{"post":{"description":"Creates a campaign in the API key's workspace. The campaign starts in draft status and is placed in the workspace's default folder. A campaign has no emails until a sequence is written to it with PUT /campaigns/{id}/sequence, and it cannot be launched until then.","tags":["Campaigns"],"summary":"Create a campaign","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true}],"responses":{"201":{"description":"Campaign created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.CreateCampaignResponse"}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidRequest"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"500":{"description":"Failed to create campaign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorFailedToCreateCampaign"}}}}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.CreateCampaignRequest"}}},"description":"Campaign to create","required":true}}}}}
```

## Get a campaign by ID

> Retrieves detailed information about a specific campaign including settings, schedule, statistics, and email counts.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.GetCampaignResponse":{"type":"object","properties":{"createdAt":{"type":"string"},"emails":{"$ref":"#/components/schemas/models.CampaignEmailCounts"},"emoji":{"type":"string"},"flow":{"type":"string"},"id":{"type":"integer"},"launchAt":{"type":"string"},"name":{"type":"string"},"schedule":{"$ref":"#/components/schemas/models.CampaignSchedule"},"sendAt":{"type":"string"},"settings":{"$ref":"#/components/schemas/models.CampaignSettings"},"stats":{"$ref":"#/components/schemas/models.CampaignStats"},"status":{"type":"string"},"timezone":{"type":"string"},"updatedAt":{"type":"string"}}},"models.CampaignEmailCounts":{"type":"object","properties":{"blocked":{"type":"integer"},"bounced":{"type":"integer"},"canceled":{"type":"integer"},"catchAll":{"type":"integer"},"deferred":{"type":"integer"},"delivered":{"type":"integer"},"draft":{"type":"integer"},"dropped":{"type":"integer"},"errorOnSent":{"type":"integer"},"followUp":{"type":"integer"},"followUpCanceled":{"type":"integer"},"followUpDraft":{"type":"integer"},"invalid":{"type":"integer"},"pending":{"type":"integer"},"replied":{"type":"integer"},"scheduled":{"type":"integer"},"sent":{"type":"integer"},"spamReport":{"type":"integer"},"total":{"type":"integer"},"waitingForReschedule":{"type":"integer"}}},"models.CampaignSchedule":{"type":"object","properties":{"cronSchedule":{"type":"string"},"daysSchedule":{"type":"array","items":{"type":"string"}},"endSchedule":{"type":"string"},"everySchedule":{"type":"integer"},"maximumTimeBetweenEmails":{"type":"integer"},"minimumTimeBetweenEmails":{"type":"integer"},"startSchedule":{"type":"string"}}},"models.CampaignSettings":{"type":"object","properties":{"allowNonBusinessEmails":{"type":"boolean"},"dailyLimit":{"description":"DailyLimit is the campaign-level cap on total emails scheduled per\ncalendar day (campaign timezone). null means no campaign-level cap.","type":"integer"},"ignoreOutOfOfficeReplies":{"type":"boolean"},"isEnabledCatchallValidated":{"type":"boolean"},"isEnabledEmailVerifier":{"type":"boolean"},"isEnabledIgnoreHardBouncedLeads":{"type":"boolean"},"isEnabledIgnoreLeadsWhoAlreadyResponded":{"type":"boolean"},"isEnabledLlm":{"type":"boolean"},"isEnabledSkipLeadIfAlreadyExists":{"type":"boolean"},"isEnabledStopFollowUpsAcrossCampaigns":{"type":"boolean"},"isEnabledStopFollowUpsForSameCompany":{"type":"boolean"},"isEnabledStopFollowUpsOnReply":{"type":"boolean"},"maximumSendingLimitPerSenderEmail":{"type":"integer"},"maximumSendingLimitPerSenderEmailVariation":{"type":"integer"}}},"models.CampaignStats":{"type":"object","properties":{"bouncedLeadsCount":{"type":"integer"},"contactedLeadsCount":{"type":"integer"},"emailsSentCount":{"type":"integer"},"leadsRespondedPositivelyCount":{"type":"integer"},"repliedLeadsCount":{"type":"integer"},"senderEmailsConnected":{"type":"integer"},"senderEmailsDisconnected":{"type":"integer"},"senderEmailsTotal":{"type":"integer"}}},"models.ErrorInvalidCampaignID":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/campaigns/{id}":{"get":{"description":"Retrieves detailed information about a specific campaign including settings, schedule, statistics, and email counts.","tags":["Campaigns"],"summary":"Get a campaign by ID","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Campaign details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.GetCampaignResponse"}}}},"400":{"description":"Invalid campaign ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidCampaignID"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"500":{"description":"Failed to retrieve campaign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}}}}}}
```

## Update a campaign by ID

> Updates campaign properties such as name, emoji, timezone, and various settings. Only provided fields will be updated.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.UpdateCampaignResponse":{"type":"object","properties":{"campaign":{"type":"object","properties":{"emoji":{"type":"string"},"id":{"type":"integer"},"name":{"type":"string"}}},"message":{"type":"string"}}},"models.ErrorInvalidCampaignID":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorFailedToUpdateCampaign":{"type":"object","properties":{"error":{"type":"string"}}},"models.UpdateCampaignRequest":{"type":"object","properties":{"allowNonBusinessEmails":{"type":"boolean"},"dailyLimit":{"description":"DailyLimit caps the total emails (initial + follow-ups) this campaign may\nschedule per calendar day in the campaign timezone. Omit to leave the cap\nunchanged; it cannot be cleared through this endpoint.","type":"integer","maximum":10000,"minimum":1},"emoji":{"type":"string","maxLength":10,"minLength":1},"ignoreOutOfOfficeReplies":{"type":"boolean"},"isEnabledCatchallValidated":{"type":"boolean"},"isEnabledEmailVerifier":{"type":"boolean"},"isEnabledIgnoreHardBouncedLeads":{"type":"boolean"},"isEnabledIgnoreLeadsWhoAlreadyResponded":{"type":"boolean"},"isEnabledLlm":{"description":"Settings - all optional boolean flags","type":"boolean"},"isEnabledSkipLeadIfAlreadyExists":{"type":"boolean"},"isEnabledStopFollowUpsAcrossCampaigns":{"type":"boolean"},"isEnabledStopFollowUpsForSameCompany":{"type":"boolean"},"isEnabledStopFollowUpsOnReply":{"type":"boolean"},"maximumSendingLimitPerSenderEmail":{"type":"integer","maximum":10000,"minimum":1},"maximumSendingLimitPerSenderEmailVariation":{"type":"integer","maximum":100,"minimum":0},"maximumTimeBetweenEmails":{"type":"integer","maximum":30,"minimum":2},"minimumTimeBetweenEmails":{"type":"integer","maximum":30,"minimum":2},"name":{"type":"string","maxLength":255,"minLength":1},"timezone":{"type":"string"}}}}},"paths":{"/campaigns/{id}":{"put":{"description":"Updates campaign properties such as name, emoji, timezone, and various settings. Only provided fields will be updated.","tags":["Campaigns"],"summary":"Update a campaign by ID","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Campaign updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.UpdateCampaignResponse"}}}},"400":{"description":"Invalid campaign ID or request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidCampaignID"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"500":{"description":"Failed to update campaign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorFailedToUpdateCampaign"}}}}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.UpdateCampaignRequest"}}},"description":"Campaign fields to update","required":true}}}}}
```

## Delete a campaign

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

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.DeleteCampaignResponse":{"type":"object","properties":{"id":{"type":"integer"},"message":{"type":"string"}}},"models.ErrorInvalidCampaignID":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorFailedToDeleteCampaign":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/campaigns/{id}":{"delete":{"description":"Deletes a campaign and all associated data including emails, sequences, and scheduled tasks.","tags":["Campaigns"],"summary":"Delete a campaign","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Campaign deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.DeleteCampaignResponse"}}}},"400":{"description":"Invalid campaign ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidCampaignID"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"500":{"description":"Failed to delete campaign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorFailedToDeleteCampaign"}}}}}}}}}
```

## Move a lead to another campaign

> 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.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.MoveLeadResponse":{"type":"object","properties":{"leadId":{"type":"integer"},"message":{"type":"string"},"sourceCampaignId":{"type":"integer"},"targetCampaignId":{"type":"integer"}}},"models.ErrorInvalidCampaignID":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorForbidden":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorFailedToMoveLead":{"type":"object","properties":{"error":{"type":"string"}}},"models.MoveLeadRequest":{"type":"object","required":["targetCampaignId"],"properties":{"targetCampaignId":{"type":"integer"}}}}},"paths":{"/campaigns/{id}/leads/{leadId}/move":{"post":{"description":"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.","tags":["Campaigns"],"summary":"Move a lead to another campaign","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Source campaign ID","name":"id","in":"path","required":true},{"schema":{"type":"integer"},"description":"Lead ID","name":"leadId","in":"path","required":true}],"responses":{"200":{"description":"Lead moved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.MoveLeadResponse"}}}},"400":{"description":"Invalid campaign ID, lead ID or request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidCampaignID"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"403":{"description":"Forbidden - lead does not belong to your space","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorForbidden"}}}},"404":{"description":"Campaign, target campaign or lead not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"500":{"description":"Failed to move lead","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorFailedToMoveLead"}}}}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.MoveLeadRequest"}}},"description":"Target campaign","required":true}}}}}
```

## Pause a campaign

> Pauses a running campaign and cancels all scheduled emails.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.PauseCampaignResponse":{"type":"object","properties":{"campaign":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string"}}},"message":{"type":"string"}}},"models.ErrorInvalidCampaignID":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorFailedToPauseCampaign":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/campaigns/{id}/pause":{"post":{"description":"Pauses a running campaign and cancels all scheduled emails.","tags":["Campaigns"],"summary":"Pause a campaign","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Campaign paused successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.PauseCampaignResponse"}}}},"400":{"description":"Invalid campaign ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidCampaignID"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"500":{"description":"Failed to pause campaign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorFailedToPauseCampaign"}}}}}}}}}
```

## Launch or resume a campaign

> Launches a new campaign or resumes a paused campaign. Validates campaign configuration before launching.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.LaunchCampaignResponse":{"type":"object","properties":{"campaign":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string"}}},"message":{"type":"string"}}},"models.ErrorCampaignValidation":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorFailedToLaunchCampaign":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/campaigns/{id}/resume":{"post":{"description":"Launches a new campaign or resumes a paused campaign. Validates campaign configuration before launching.","tags":["Campaigns"],"summary":"Launch or resume a campaign","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Campaign launched/resumed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.LaunchCampaignResponse"}}}},"400":{"description":"Campaign validation failed (missing configuration)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignValidation"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"409":{"description":"Campaign is currently processing leads","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}},"500":{"description":"Failed to launch/resume campaign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorFailedToLaunchCampaign"}}}}}}}}}
```

## Update campaign schedule

> Updates the sending schedule for a campaign including timezone, days, time windows, and frequency.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.UpdateCampaignScheduleResponse":{"type":"object","properties":{"campaign":{"type":"object","properties":{"daysSchedule":{"type":"array","items":{"type":"string"}},"endSchedule":{"type":"string"},"everySchedule":{"type":"integer"},"id":{"type":"integer"},"maximumTimeBetweenEmails":{"type":"integer"},"minimumTimeBetweenEmails":{"type":"integer"},"name":{"type":"string"},"startSchedule":{"type":"string"},"timezone":{"type":"string"}}},"message":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.UpdateCampaignScheduleRequest":{"type":"object","required":["timezone"],"properties":{"daysSchedule":{"description":"DaysSchedule is the set of weekdays the campaign may send on. Accepts\nweekday numbers as strings, where Sunday is 0 (\"1\" is Monday), or weekday\nnames such as \"monday\". Names are matched case-insensitively and common\nshort forms (\"mon\", \"tues\") work. Values are stored and returned as\nnumbers, so a request sending names reads back as [\"1\",\"2\",\"3\",\"4\",\"5\"].\n\nFor multiple leads scheduled campaigns (flow 3) and API campaigns.","type":"array","items":{"type":"string"}},"endSchedule":{"type":"string"},"everySchedule":{"type":"integer","minimum":1},"maximumTimeBetweenEmails":{"type":"integer","maximum":30,"minimum":2},"minimumTimeBetweenEmails":{"type":"integer","maximum":30,"minimum":2},"sendAt":{"description":"For single lead scheduled campaigns (flow 2)","type":"string"},"startSchedule":{"type":"string"},"timezone":{"description":"Common field for all flows","type":"string"}}}}},"paths":{"/campaigns/{id}/schedule":{"put":{"description":"Updates the sending schedule for a campaign including timezone, days, time windows, and frequency.","tags":["Campaigns"],"summary":"Update campaign schedule","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Campaign schedule updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.UpdateCampaignScheduleResponse"}}}},"400":{"description":"Invalid request body or schedule configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"500":{"description":"Failed to update campaign schedule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.UpdateCampaignScheduleRequest"}}},"description":"Schedule configuration","required":true}}}}}
```

## Attach sender emails to a campaign

> 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.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.AttachCampaignSendersResponse":{"type":"object","properties":{"alreadyAttached":{"description":"AlreadyAttached lists the requested sender emails that were attached\nbefore this call (the call is idempotent: they are left untouched).","type":"array","items":{"type":"integer"}},"attached":{"description":"Attached lists the sender emails newly attached by this call.","type":"array","items":{"type":"integer"}},"campaignId":{"type":"integer"},"message":{"type":"string"},"senderEmailIds":{"description":"SenderEmailIDs lists every sender email attached to the campaign after\nthis call.","type":"array","items":{"type":"integer"}}}},"models.ErrorInvalidCampaignID":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorSenderEmailNotConnected":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}},"models.AttachCampaignSendersRequest":{"type":"object","required":["senderEmailIds"],"properties":{"senderEmailIds":{"description":"SenderEmailIDs are the sender emails to attach. Every ID must be a\nconnected sender email in your workspace; the call is refused whole if\nany is not.","type":"array","minItems":1,"items":{"type":"integer"}}}}}},"paths":{"/campaigns/{id}/sender-emails":{"post":{"description":"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.","tags":["Campaigns"],"summary":"Attach sender emails to a campaign","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Sender emails attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.AttachCampaignSendersResponse"}}}},"400":{"description":"Invalid campaign ID or request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidCampaignID"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign or sender email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"409":{"description":"Sender email is not connected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorSenderEmailNotConnected"}}}},"500":{"description":"Failed to attach sender emails","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.AttachCampaignSendersRequest"}}},"description":"Sender emails to attach","required":true}}}}}
```

## Detach a sender email from a campaign

> 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.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.DetachCampaignSenderResponse":{"type":"object","properties":{"campaignId":{"type":"integer"},"detached":{"description":"Detached is false when the sender email was not attached to the\ncampaign (the call is idempotent and does nothing in that case).","type":"boolean"},"message":{"type":"string"},"senderEmailIds":{"description":"SenderEmailIDs lists every sender email still attached to the campaign\nafter this call.","type":"array","items":{"type":"integer"}}}},"models.ErrorInvalidCampaignID":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/campaigns/{id}/sender-emails/{senderEmailId}":{"delete":{"description":"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.","tags":["Campaigns"],"summary":"Detach a sender email from a campaign","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true},{"schema":{"type":"integer"},"description":"Sender Email ID","name":"senderEmailId","in":"path","required":true}],"responses":{"200":{"description":"Sender email detached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.DetachCampaignSenderResponse"}}}},"400":{"description":"Invalid campaign or sender email ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidCampaignID"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign or sender email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"500":{"description":"Failed to detach sender email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}}}}}}
```

## Get a campaign's sequence

> 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.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.GetSequenceResponse":{"type":"object","properties":{"campaignId":{"type":"integer"},"signature":{"type":"string"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/models.SequenceStep"}},"total":{"type":"integer"}}},"models.SequenceStep":{"type":"object","properties":{"body":{"type":"string"},"contentType":{"type":"string"},"delayDays":{"type":"integer"},"order":{"type":"integer"},"subject":{"type":"string"},"useSameThread":{"type":"boolean"},"variants":{"type":"array","items":{"$ref":"#/components/schemas/models.SequenceVariant"}}}},"models.SequenceVariant":{"type":"object","properties":{"body":{"type":"string"},"label":{"type":"string"},"subject":{"type":"string"}}},"models.ErrorInvalidCampaignID":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorFailedToRetrieveSequence":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/campaigns/{id}/sequence":{"get":{"description":"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.","tags":["Campaigns"],"summary":"Get a campaign's sequence","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Campaign sequence","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.GetSequenceResponse"}}}},"400":{"description":"Invalid campaign ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidCampaignID"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"500":{"description":"Failed to retrieve campaign sequence","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorFailedToRetrieveSequence"}}}}}}}}}
```

## Replace a campaign's 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}. The two-brace {{first\_name}} form other tools use is rejected with a 400, because it renders down to a stray brace and would send as "Hi },".

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.ReplaceSequenceResponse":{"type":"object","properties":{"campaignId":{"type":"integer"},"message":{"type":"string"},"signature":{"type":"string"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/models.SequenceStep"}},"total":{"type":"integer"}}},"models.SequenceStep":{"type":"object","properties":{"body":{"type":"string"},"contentType":{"type":"string"},"delayDays":{"type":"integer"},"order":{"type":"integer"},"subject":{"type":"string"},"useSameThread":{"type":"boolean"},"variants":{"type":"array","items":{"$ref":"#/components/schemas/models.SequenceVariant"}}}},"models.SequenceVariant":{"type":"object","properties":{"body":{"type":"string"},"label":{"type":"string"},"subject":{"type":"string"}}},"models.ErrorInvalidSequence":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnsupportedCampaignFlow":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorFailedToReplaceSequence":{"type":"object","properties":{"error":{"type":"string"}}},"models.ReplaceSequenceRequest":{"type":"object","required":["steps"],"properties":{"signature":{"description":"Signature omitted keeps the campaign's current signature; pass an empty\nstring to clear it. Editing one body should not silently drop a\nsignature the caller never asked about.","type":"string"},"steps":{"type":"array","maxItems":20,"minItems":1,"items":{"$ref":"#/components/schemas/models.SequenceStepInput"}}}},"models.SequenceStepInput":{"type":"object","required":["body","subject"],"properties":{"body":{"type":"string","minLength":1},"contentType":{"type":"string","enum":["html","text"]},"delayDays":{"type":"integer","maximum":365,"minimum":0},"order":{"type":"integer","minimum":1},"subject":{"type":"string","maxLength":998,"minLength":1},"useSameThread":{"type":"boolean"},"variants":{"type":"array","maxItems":25,"items":{"$ref":"#/components/schemas/models.SequenceVariantInput"}}}},"models.SequenceVariantInput":{"type":"object","required":["label","subject"],"properties":{"body":{"type":"string"},"label":{"type":"string","maxLength":2,"minLength":1},"subject":{"type":"string","maxLength":998,"minLength":1}}}}},"paths":{"/campaigns/{id}/sequence":{"put":{"description":"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}. The two-brace {{first_name}} form other tools use is rejected with a 400, because it renders down to a stray brace and would send as \"Hi },\".","tags":["Campaigns"],"summary":"Replace a campaign's sequence","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Campaign sequence replaced successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ReplaceSequenceResponse"}}}},"400":{"description":"Invalid campaign ID or sequence payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidSequence"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"422":{"description":"Campaign flow does not support sequences","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnsupportedCampaignFlow"}}}},"500":{"description":"Failed to replace campaign sequence","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorFailedToReplaceSequence"}}}}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ReplaceSequenceRequest"}}},"description":"Sequence steps","required":true}}}}}
```

## Get campaign performance stats

> 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.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.GetCampaignStatsResponse":{"type":"object","properties":{"campaignId":{"type":"integer"},"daily":{"type":"array","items":{"$ref":"#/components/schemas/models.CampaignStatsDay"}},"steps":{"type":"array","items":{"$ref":"#/components/schemas/models.CampaignStatsStep"}},"totals":{"$ref":"#/components/schemas/models.CampaignStatsTotals"},"variants":{"type":"array","items":{"$ref":"#/components/schemas/models.CampaignStatsVariant"}}}},"models.CampaignStatsDay":{"type":"object","properties":{"date":{"type":"string"},"positive":{"type":"integer"},"replied":{"type":"integer"},"sent":{"type":"integer"}}},"models.CampaignStatsStep":{"type":"object","properties":{"index":{"type":"integer"},"replied":{"type":"integer"},"sent":{"type":"integer"}}},"models.CampaignStatsTotals":{"type":"object","properties":{"bounced":{"type":"integer"},"meetings":{"type":"integer"},"positive":{"type":"integer"},"replied":{"type":"integer"},"sent":{"type":"integer"}}},"models.CampaignStatsVariant":{"type":"object","properties":{"label":{"type":"string"},"positive":{"type":"integer"},"replied":{"type":"integer"},"sent":{"type":"integer"}}},"models.ErrorInvalidCampaignID":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorCampaignNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/campaigns/{id}/stats":{"get":{"description":"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.","tags":["Campaigns"],"summary":"Get campaign performance stats","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Campaign ID","name":"id","in":"path","required":true}],"responses":{"200":{"description":"Campaign performance stats","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.GetCampaignStatsResponse"}}}},"400":{"description":"Invalid campaign ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorInvalidCampaignID"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"Campaign not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorCampaignNotFound"}}}},"500":{"description":"Failed to retrieve campaign stats","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}}}}}}
```
