openapi: 3.1.0 info: description: Manage email marketing campaigns and retrieve their performance statistics version: 2.0.0 title: Email Campaigns contact: name: Mailtrap url: "https://docs.mailtrap.io" email: support@mailtrap.io license: name: Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) url: "https://creativecommons.org/licenses/by-sa/4.0/" security: - HeaderAuth: [] - BearerAuth: [] servers: - description: Mailtrap API url: "https://mailtrap.io" tags: - name: Email Campaigns x-page-title: Email Campaigns x-page-description: List, create, retrieve, and delete email campaigns description: Manage email campaigns - name: Campaign Stats x-page-title: Campaign Stats x-page-description: Retrieve aggregated performance metrics for a campaign description: Retrieve campaign statistics paths: "/api/email_campaigns": get: operationId: getEmailCampaigns summary: Get a list of email campaigns description: |- Returns a paginated list of the account's email campaigns, newest first. Pagination is page-token based. Use the `token` query parameter together with the `token`, `next_token`, and `prev_token` values returned in the `pagination` object to navigate between pages. tags: - Email Campaigns parameters: - $ref: "#/components/parameters/token" - $ref: "#/components/parameters/per_page" - $ref: "#/components/parameters/search" responses: "200": $ref: "#/components/responses/EmailCampaignsResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL" source: | curl -X GET "https://mailtrap.io/api/email_campaigns?per_page=50&token=1" \ -H 'Api-Token: YOUR_API_KEY' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); const list = await client.emailCampaigns.getList({ per_page: 50, token: 1 }); console.log(`Campaigns: ${list.data.length}`, list.pagination); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->getEmailCampaigns(perPage: 50, token: 1); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns page = campaigns.get_list(mt.EmailCampaignListParams(per_page=50, token=1)) print(page.data, page.pagination) - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) list = email_campaigns.list(per_page: 50, token: 1) puts list.data.map(&:name) puts list.pagination - lang: csharp label: .NET source: | using Mailtrap; using Mailtrap.EmailCampaigns.Models; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); var page = await client .EmailCampaigns() .GetAll(new EmailCampaignListFilter { PerPage = 50, Token = 1 }); Console.WriteLine($"Campaigns: {page.Data.Count}"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; import io.mailtrap.api.emailcampaigns.EmailCampaignListFilter; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); var page = campaigns.getEmailCampaigns( EmailCampaignListFilter.builder() .perPage(50) .token(1) .build()); System.out.println(page.getData()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } page, _, err := client.EmailCampaigns.List(context.Background(), &mailtrap.EmailCampaignListOptions{ PerPage: 50, Search: "newsletter", }) if err != nil { log.Fatal(err) } for _, campaign := range page.Data { fmt.Printf("%d %s (%s)\n", campaign.ID, campaign.Name, campaign.CurrentState) } // To walk every page instead of one, use the iterator, which follows // pagination.next_token for you: // // for campaign, err := range client.EmailCampaigns.All(context.Background(), nil) { // if err != nil { // log.Fatal(err) // } // fmt.Println(campaign.Name) // } } post: operationId: createEmailCampaign summary: Create an email campaign description: |- Creates a new email campaign. The campaign must reference an existing sending domain via `domain_id`, and include a template `subject` within `template_attributes`. Create accepts the same fields as update — pick the audience with `contact_list_ids`/`contact_segment_ids`, set delivery options, and add the design via `template_attributes.body_html`. The campaign is always created in the `draft` state; scheduling and starting are separate actions (see the `schedule` and `start` endpoints). tags: - Email Campaigns requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/EmailCampaignCreateRequest" responses: "201": $ref: "#/components/responses/EmailCampaignResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "422": $ref: "#/components/responses/VALIDATION_ERROR" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL — create a draft" source: | curl -X POST "https://mailtrap.io/api/email_campaigns" \ -H 'Api-Token: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "name": "Spring Sale", "domain_id": 4321, "from_display_name": "Acme Marketing", "from_local_part": "news", "reply_to": { "display_name": "Acme Support", "local_part": "support", "domain": "acme.com" }, "template_attributes": { "subject": "Spring is here — 30% off" } }' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); const created = await client.emailCampaigns.create({ name: "Spring Sale", domain_id: 4321, from_display_name: "Acme Marketing", from_local_part: "news", reply_to: { display_name: "Acme Support", local_part: "support", domain: "acme.com" }, template_attributes: { subject: "Spring is here — 30% off" } }); console.log(`Campaign created: ${created.data.id} (${created.data.current_state})`); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->createEmailCampaign(new CreateEmailCampaign( name: 'Spring Sale', domainId: 4321, fromLocalPart: 'news', templateAttributes: new TemplateAttributes(subject: 'Spring is here — 30% off'), fromDisplayName: 'Acme Marketing', replyTo: new ReplyTo( displayName: 'Acme Support', localPart: 'support', domain: 'acme.com', ), )); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns created = campaigns.create( mt.CreateEmailCampaignParams( name="Spring Sale", domain_id=4321, from_display_name="Acme Marketing", from_local_part="news", reply_to=mt.EmailCampaignReplyTo( display_name="Acme Support", local_part="support", domain="acme.com" ), template_attributes=mt.CreateEmailCampaignTemplateAttributes( subject="Spring is here — 30% off" ) ) ) print(f"Campaign created: {created.id} ({created.current_state})") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) campaign = email_campaigns.create( name: 'Spring Sale', domain_id: 4321, from_display_name: 'Acme Marketing', from_local_part: 'news', reply_to: { display_name: 'Acme Support', local_part: 'support', domain: 'acme.com' }, template_attributes: { subject: 'Spring is here — 30% off' } ) puts "Campaign created: #{campaign.id} (#{campaign.current_state})" - lang: csharp label: .NET source: | using Mailtrap; using Mailtrap.EmailCampaigns.Models; using Mailtrap.EmailCampaigns.Requests; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); var request = new CreateEmailCampaignRequest { Name = "Spring Sale", DomainId = 4321, FromDisplayName = "Acme Marketing", FromLocalPart = "news", ReplyTo = new ReplyTo { DisplayName = "Acme Support", LocalPart = "support", Domain = "acme.com" }, TemplateAttributes = new EmailCampaignTemplateAttributes { Subject = "Spring is here — 30% off" } }; var campaign = await client.EmailCampaigns().Create(request); Console.WriteLine($"Campaign created: {campaign.Id} ({campaign.CurrentState})"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; import io.mailtrap.model.request.emailcampaigns.CreateEmailCampaign; import io.mailtrap.model.request.emailcampaigns.TemplateAttributes; import io.mailtrap.model.response.emailcampaigns.ReplyTo; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); var created = campaigns.createEmailCampaign( CreateEmailCampaign.builder() .name("Spring Sale") .domainId(4321L) .fromDisplayName("Acme Marketing") .fromLocalPart("news") .replyTo(ReplyTo.builder() .displayName("Acme Support") .localPart("support") .domain("acme.com") .build()) .templateAttributes(TemplateAttributes.builder() .subject("Spring is here — 30% off") .build()) .build()); System.out.println("Campaign created: " + created.getData().getId()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } // A campaign is always created as a draft; scheduling and starting it are // separate actions. campaign, _, err := client.EmailCampaigns.Create(context.Background(), &mailtrap.CreateEmailCampaignRequest{ Name: "March newsletter", DomainID: 1000001, FromDisplayName: "Acme Corp", FromLocalPart: "news", ReplyTo: &mailtrap.EmailCampaignReplyTo{ DisplayName: "Acme Support", LocalPart: "support", Domain: "mail.example.com", }, TemplateAttributes: &mailtrap.EmailCampaignTemplateAttributes{ Subject: "Hi {{first_name}}, here is March", BodyHTML: "

Hello {{first_name}}

Unsubscribe", MergeTags: []string{"first_name"}, }, DeliveryMode: mailtrap.EmailCampaignDeliveryModeGradual, DeliveryOptions: &mailtrap.EmailCampaignDeliveryOptions{EmailsPerHour: mailtrap.Ptr(5000)}, ContactListIDs: []int64{1, 2}, }) if err != nil { log.Fatal(err) } fmt.Printf("created campaign %d (%s)\n", campaign.ID, campaign.CurrentState) } "/api/email_campaigns/{email_campaign_id}": get: operationId: getEmailCampaign summary: Get an email campaign by ID description: Returns a single email campaign. tags: - Email Campaigns parameters: - $ref: "#/components/parameters/email_campaign_id" responses: "200": $ref: "#/components/responses/EmailCampaignResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "404": $ref: "#/components/responses/NOT_FOUND" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL" source: | curl -X GET "https://mailtrap.io/api/email_campaigns/4567" \ -H 'Api-Token: YOUR_API_KEY' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); const campaign = await client.emailCampaigns.get(4567); console.log(`${campaign.data.name}: ${campaign.data.current_state}`); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->getEmailCampaign(4567); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns campaign = campaigns.get_by_id(email_campaign_id=4567) print(f"{campaign.name}: {campaign.current_state}") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) campaign = email_campaigns.get(4567) puts "#{campaign.name}: #{campaign.current_state}" - lang: csharp label: .NET source: | using Mailtrap; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); var campaign = await client.EmailCampaign(4567).GetDetails(); Console.WriteLine($"{campaign.Name}: {campaign.CurrentState}"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); var campaign = campaigns.getEmailCampaign(4567L); System.out.println(campaign.getData().getCurrentState()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } campaign, _, err := client.EmailCampaigns.Get(context.Background(), 1000001) if err != nil { log.Fatal(err) } fmt.Printf("%s: %s, subject %q\n", campaign.Name, campaign.CurrentState, campaign.Template.Subject) } patch: operationId: updateEmailCampaign summary: Update an email campaign description: |- Updates an existing `draft` campaign. Only the provided attributes are changed. To edit the template (subject or design), pass `template_attributes` — the campaign's template is always edited in place, and only the template sub-fields you provide change (see the `template_attributes` schema for the partial-update rules). Update accepts the same fields as create, including the audience (`contact_list_ids`/`contact_segment_ids`). The typical flow is to create a draft, add its design and audience over one or more updates, then schedule or start it via the dedicated `schedule`/`start` action endpoints. Only `draft` campaigns can be updated — editing a campaign that is already scheduled/sending returns `422`. tags: - Email Campaigns parameters: - $ref: "#/components/parameters/email_campaign_id" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/EmailCampaignUpdateRequest" responses: "200": $ref: "#/components/responses/EmailCampaignResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "404": $ref: "#/components/responses/NOT_FOUND" "422": $ref: "#/components/responses/VALIDATION_ERROR" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL — edit a draft (subject + design)" source: | curl -X PATCH "https://mailtrap.io/api/email_campaigns/4567" \ -H 'Api-Token: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "name": "Spring Sale (updated)", "template_attributes": { "subject": "New subject", "body_html": "

Hi {{first_name}}!

Unsubscribe

" } }' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); const updated = await client.emailCampaigns.update(4567, { name: "Spring Sale (updated)", template_attributes: { subject: "New subject", body_html: '

Hi {{first_name}}!

Unsubscribe

', merge_tags: ["first_name"] }, delivery_mode: "gradual", delivery_options: { emails_per_hour: 1000 }, contact_list_ids: [55, 56], contact_segment_ids: [12] }); console.log(`Campaign updated: ${updated.data.name}`); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->updateEmailCampaign(4567, new UpdateEmailCampaign( name: 'Spring Sale (updated)', deliveryMode: EmailCampaignInterface::DELIVERY_MODE_GRADUAL, deliveryOptions: ['emails_per_hour' => 1000], templateAttributes: new TemplateAttributes( subject: 'New subject', bodyHtml: '

Hi {{first_name}}!

' . '

Unsubscribe

', mergeTags: ['first_name'], ), contactListIds: [55, 56], contactSegmentIds: [12], )); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns updated = campaigns.update( email_campaign_id=4567, campaign_params=mt.UpdateEmailCampaignParams( name="Spring Sale (updated)", delivery_mode="gradual", delivery_options=mt.EmailCampaignDeliveryOptions(emails_per_hour=1000), contact_list_ids=[55, 56], contact_segment_ids=[12], template_attributes=mt.EmailCampaignTemplateAttributes( subject="New subject", body_html=( "

Hi {{first_name}}!

" '

Unsubscribe

' ), merge_tags=["first_name"] ) ) ) print(f"Campaign updated: {updated.name}") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) campaign = email_campaigns.update( 4567, name: 'Spring Sale (updated)', template_attributes: { subject: 'New subject', body_html: '

Hi {{first_name}}!

' \ '

Unsubscribe

', merge_tags: ['first_name'] }, delivery_mode: 'gradual', delivery_options: { emails_per_hour: 1000 }, contact_list_ids: [55, 56], contact_segment_ids: [12] ) puts "Campaign updated: #{campaign.name}" - lang: csharp label: .NET source: | using Mailtrap; using Mailtrap.EmailCampaigns.Models; using Mailtrap.EmailCampaigns.Requests; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); var request = new UpdateEmailCampaignRequest { Name = "Spring Sale (updated)", DeliveryMode = DeliveryMode.Gradual, DeliveryOptions = new EmailCampaignDeliveryOptions { EmailsPerHour = 1000 }, TemplateAttributes = new EmailCampaignTemplateAttributes { Subject = "New subject", BodyHtml = "

Hi {{first_name}}!

" + "

Unsubscribe

", MergeTags = ["first_name"] }, ContactListIds = [55, 56], ContactSegmentIds = [12] }; var campaign = await client.EmailCampaign(4567).Update(request); Console.WriteLine($"Campaign updated: {campaign.Name}"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; import io.mailtrap.model.DeliveryMode; import io.mailtrap.model.request.emailcampaigns.TemplateAttributes; import io.mailtrap.model.request.emailcampaigns.UpdateEmailCampaign; import io.mailtrap.model.response.emailcampaigns.DeliveryOptions; import java.util.List; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); var updated = campaigns.updateEmailCampaign(4567L, UpdateEmailCampaign.builder() .name("Spring Sale (updated)") .templateAttributes(TemplateAttributes.builder() .subject("New subject") .bodyHtml("

Hi {{first_name}}!

" + "

Unsubscribe

") .mergeTags(List.of("first_name")) .build()) .deliveryMode(DeliveryMode.GRADUAL) .deliveryOptions(DeliveryOptions.builder().emailsPerHour(1000).build()) .contactListIds(List.of(55L, 56L)) .contactSegmentIds(List.of(12L)) .build()); System.out.println("Campaign updated: " + updated.getData().getName()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } // Only a draft campaign can be updated, and only the fields you set change. // The list and segment fields are pointers: nil leaves them unchanged, while a // pointer to an empty slice clears them. campaign, _, err := client.EmailCampaigns.Update(context.Background(), 1000001, &mailtrap.UpdateEmailCampaignRequest{ Name: "March newsletter (final)", TemplateAttributes: &mailtrap.EmailCampaignTemplateAttributes{ Subject: "Hi {{first_name}}, March is here", }, ContactListIDs: mailtrap.Ptr([]int64{3}), ContactSegmentIDs: mailtrap.Ptr([]int64{}), }) if err != nil { log.Fatal(err) } fmt.Printf("updated campaign %d: %s\n", campaign.ID, campaign.Name) } delete: operationId: deleteEmailCampaign summary: Delete an email campaign description: |- Soft-deletes an email campaign. Returns `204 No Content` on success. Only `draft` campaigns can be deleted — deleting a campaign that is already scheduled/sending returns `422`. A campaign that has been started never returns to `draft`, so it can no longer be deleted. A `scheduled` campaign can be returned to `draft` first via the `cancel` or `reset` action endpoints. tags: - Email Campaigns parameters: - $ref: "#/components/parameters/email_campaign_id" responses: "204": description: The email campaign was deleted. No response body. "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "404": $ref: "#/components/responses/NOT_FOUND" "422": $ref: "#/components/responses/VALIDATION_ERROR" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL" source: | curl -X DELETE "https://mailtrap.io/api/email_campaigns/4567" \ -H 'Api-Token: YOUR_API_KEY' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); await client.emailCampaigns.delete(4567); console.log("Campaign deleted"); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->deleteEmailCampaign(4567); var_dump($response->getStatusCode()); // 204 - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns deleted = campaigns.delete(email_campaign_id=4567) print(f"Campaign deleted: {deleted.id}") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) email_campaigns.delete(4567) puts 'Campaign deleted' - lang: csharp label: .NET source: | using Mailtrap; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); await client.EmailCampaign(4567).Delete(); Console.WriteLine("Campaign deleted"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); campaigns.deleteEmailCampaign(4567L); System.out.println("Campaign deleted"); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } // Only a campaign in the draft state can be deleted. if _, err := client.EmailCampaigns.Delete(context.Background(), 1000001); err != nil { log.Fatal(err) } fmt.Println("campaign deleted") } "/api/email_campaigns/{email_campaign_id}/start": post: operationId: startEmailCampaign summary: Start an email campaign description: |- Starts sending a `draft` campaign immediately. Runs full sending validation (the template must have a `body_html` design, the audience and verified sending domain must be set, billing within limits); on failure the request returns `422` and the campaign stays a `draft`. The campaign must be in the `draft` state — starting from any other state returns `422`. tags: - Email Campaigns parameters: - $ref: "#/components/parameters/email_campaign_id" responses: "200": $ref: "#/components/responses/EmailCampaignResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "404": $ref: "#/components/responses/NOT_FOUND" "422": $ref: "#/components/responses/ACTION_VALIDATION_ERROR" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL" source: | curl -X POST "https://mailtrap.io/api/email_campaigns/4567/start" \ -H 'Api-Token: YOUR_API_KEY' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); const started = await client.emailCampaigns.start(4567); console.log(`State after start: ${started.data.current_state}`); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->startEmailCampaign(4567); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns started = campaigns.start(email_campaign_id=4567) print(f"State after start: {started.current_state}") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) campaign = email_campaigns.start(4567) puts "State after start: #{campaign.current_state}" - lang: csharp label: .NET source: | using Mailtrap; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); var campaign = await client.EmailCampaign(4567).Start(); Console.WriteLine($"State after start: {campaign.CurrentState}"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); var started = campaigns.startEmailCampaign(4567L); System.out.println("State after start: " + started.getData().getCurrentState()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } campaign, _, err := client.EmailCampaigns.Start(context.Background(), 1000001) if err != nil { log.Fatal(err) } fmt.Printf("campaign %d is %s\n", campaign.ID, campaign.CurrentState) } "/api/email_campaigns/{email_campaign_id}/schedule": post: operationId: scheduleEmailCampaign summary: Schedule an email campaign description: |- Schedules a `draft` campaign to start sending at a future time. Runs full sending validation (the template must have a `body_html` design, the audience and verified sending domain must be set, billing within limits); on failure the request returns `422` and the campaign stays a `draft`. The campaign must be in the `draft` state — scheduling from any other state returns `422`. After scheduling, the time is reported back in `current_state_metadata.scheduled_at`. tags: - Email Campaigns parameters: - $ref: "#/components/parameters/email_campaign_id" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/EmailCampaignScheduleRequest" responses: "200": $ref: "#/components/responses/EmailCampaignResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "404": $ref: "#/components/responses/NOT_FOUND" "422": $ref: "#/components/responses/ACTION_VALIDATION_ERROR" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL" source: | # Must be in the future and no more than 1 month ahead. DATETIME=$(date -u -d '+1 day' +%Y-%m-%dT%H:%M:%S.000Z 2>/dev/null || date -u -v+1d +%Y-%m-%dT%H:%M:%S.000Z) curl -X POST "https://mailtrap.io/api/email_campaigns/4567/schedule" \ -H 'Api-Token: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d "{ \"datetime\": \"$DATETIME\" }" - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); // Must be in the future and no more than 1 month ahead. const scheduledAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); const scheduled = await client.emailCampaigns.schedule(4567, { datetime: scheduledAt }); console.log(`Scheduled for: ${scheduled.data.current_state_metadata.scheduled_at}`); - lang: php label: PHP source: | emailCampaigns($accountId); // Must be in the future and no more than 1 month ahead. $response = $emailCampaigns->scheduleEmailCampaign( 4567, new DateTimeImmutable('+1 day', new DateTimeZone('UTC')) ); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | from datetime import datetime, timedelta, timezone import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns # Must be in the future and no more than 1 month ahead. send_at = datetime.now(timezone.utc) + timedelta(days=1) scheduled = campaigns.schedule( email_campaign_id=4567, schedule_params=mt.ScheduleEmailCampaignParams( datetime=send_at.isoformat(timespec="milliseconds").replace("+00:00", "Z") ) ) print(f"Scheduled for: {scheduled.current_state_metadata.scheduled_at}") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) # Must be in the future and no more than 1 month ahead. schedule_at = (Time.now.utc + 86_400).strftime('%Y-%m-%dT%H:%M:%S.000Z') campaign = email_campaigns.schedule(4567, schedule_at) puts "State after schedule: #{campaign.current_state}" - lang: csharp label: .NET source: | using Mailtrap; using Mailtrap.EmailCampaigns.Requests; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); // Must be in the future and no more than 1 month ahead. var request = new ScheduleEmailCampaignRequest(DateTimeOffset.UtcNow.AddDays(1)); var campaign = await client.EmailCampaign(4567).Schedule(request); Console.WriteLine($"State after schedule: {campaign.CurrentState}"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; import io.mailtrap.model.request.emailcampaigns.ScheduleEmailCampaignRequest; import java.time.OffsetDateTime; import java.time.ZoneOffset; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); // Must be in the future and no more than 1 month ahead. var scheduled = campaigns.scheduleEmailCampaign(4567L, new ScheduleEmailCampaignRequest(OffsetDateTime.now(ZoneOffset.UTC).plusDays(1))); System.out.println(scheduled.getData().getCurrentStateMetadata().getScheduledAt()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } // The datetime is ISO 8601, must be in the future and no more than a month ahead. campaign, _, err := client.EmailCampaigns.Schedule(context.Background(), 1000001, "2026-10-01T09:00:00Z") if err != nil { log.Fatal(err) } fmt.Printf("campaign %d is %s for %s\n", campaign.ID, campaign.CurrentState, campaign.CurrentStateMetadata.ScheduledAt) } "/api/email_campaigns/{email_campaign_id}/cancel": post: operationId: cancelEmailCampaign summary: Cancel a scheduled email campaign description: |- Cancels a scheduled campaign, removing the pending send job and returning the campaign to the `draft` state. The campaign must be in the `scheduled` state — cancelling from any other state returns `422` (`"Campaign is not scheduled"`). tags: - Email Campaigns parameters: - $ref: "#/components/parameters/email_campaign_id" responses: "200": $ref: "#/components/responses/EmailCampaignResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "404": $ref: "#/components/responses/NOT_FOUND" "422": $ref: "#/components/responses/ACTION_VALIDATION_ERROR" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL" source: | curl -X POST "https://mailtrap.io/api/email_campaigns/4567/cancel" \ -H 'Api-Token: YOUR_API_KEY' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); const cancelled = await client.emailCampaigns.cancel(4567); console.log(`State after cancel: ${cancelled.data.current_state}`); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->cancelEmailCampaign(4567); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns cancelled = campaigns.cancel(email_campaign_id=4567) print(f"State after cancel: {cancelled.current_state}") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) campaign = email_campaigns.cancel(4567) puts "State after cancel: #{campaign.current_state}" - lang: csharp label: .NET source: | using Mailtrap; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); var campaign = await client.EmailCampaign(4567).Cancel(); Console.WriteLine($"State after cancel: {campaign.CurrentState}"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); var cancelled = campaigns.cancelEmailCampaign(4567L); System.out.println("State after cancel: " + cancelled.getData().getCurrentState()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } // Cancel returns a scheduled campaign to the draft state. campaign, _, err := client.EmailCampaigns.Cancel(context.Background(), 1000001) if err != nil { log.Fatal(err) } fmt.Printf("campaign %d is %s\n", campaign.ID, campaign.CurrentState) } "/api/email_campaigns/{email_campaign_id}/terminate": post: operationId: terminateEmailCampaign summary: Terminate a sending email campaign description: |- Terminates a campaign that is currently sending, aborting the in-flight send. The campaign must be in a sending state (`started`, `queued`, or `paused`) — terminating from any other state returns `422`. tags: - Email Campaigns parameters: - $ref: "#/components/parameters/email_campaign_id" responses: "200": $ref: "#/components/responses/EmailCampaignResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "404": $ref: "#/components/responses/NOT_FOUND" "422": $ref: "#/components/responses/ACTION_VALIDATION_ERROR" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL" source: | curl -X POST "https://mailtrap.io/api/email_campaigns/4567/terminate" \ -H 'Api-Token: YOUR_API_KEY' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); const terminated = await client.emailCampaigns.terminate(4567); console.log(`State after terminate: ${terminated.data.current_state}`); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->terminateEmailCampaign(4567); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns terminated = campaigns.terminate(email_campaign_id=4567) print(f"State after terminate: {terminated.current_state}") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) campaign = email_campaigns.terminate(4567) puts "State after terminate: #{campaign.current_state}" - lang: csharp label: .NET source: | using Mailtrap; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); var campaign = await client.EmailCampaign(4567).Terminate(); Console.WriteLine($"State after terminate: {campaign.CurrentState}"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); var terminated = campaigns.terminateEmailCampaign(4567L); System.out.println("State after terminate: " + terminated.getData().getCurrentState()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } // Terminate aborts a campaign that is started, queued or paused. campaign, _, err := client.EmailCampaigns.Terminate(context.Background(), 1000001) if err != nil { log.Fatal(err) } fmt.Printf("campaign %d is %s\n", campaign.ID, campaign.CurrentState) } "/api/email_campaigns/{email_campaign_id}/reset": post: operationId: resetEmailCampaign summary: Reset an email campaign to draft description: |- Resets a campaign back to the `draft` state. Allowed only from the `scheduled` state; resetting from any other state (e.g. a sending or terminal state) returns `422`. tags: - Email Campaigns parameters: - $ref: "#/components/parameters/email_campaign_id" responses: "200": $ref: "#/components/responses/EmailCampaignResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "404": $ref: "#/components/responses/NOT_FOUND" "422": $ref: "#/components/responses/ACTION_VALIDATION_ERROR" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL" source: | curl -X POST "https://mailtrap.io/api/email_campaigns/4567/reset" \ -H 'Api-Token: YOUR_API_KEY' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); const reset = await client.emailCampaigns.reset(4567); console.log(`State after reset: ${reset.data.current_state}`); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->resetEmailCampaign(4567); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns reset = campaigns.reset(email_campaign_id=4567) print(f"State after reset: {reset.current_state}") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) campaign = email_campaigns.reset(4567) puts "State after reset: #{campaign.current_state}" - lang: csharp label: .NET source: | using Mailtrap; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); var campaign = await client.EmailCampaign(4567).Reset(); Console.WriteLine($"State after reset: {campaign.CurrentState}"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); var reset = campaigns.resetEmailCampaign(4567L); System.out.println("State after reset: " + reset.getData().getCurrentState()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } // Reset returns a scheduled campaign to the draft state. campaign, _, err := client.EmailCampaigns.Reset(context.Background(), 1000001) if err != nil { log.Fatal(err) } fmt.Printf("campaign %d is %s\n", campaign.ID, campaign.CurrentState) } "/api/email_campaigns/{email_campaign_id}/stats": get: operationId: getEmailCampaignStats summary: Get email campaign statistics description: |- Returns aggregated performance metrics for a single campaign: counts and rates for deliveries, opens, clicks, bounces, spam complaints, and unsubscriptions. If the campaign has never been started, all counts and rates are returned as `0`. Statistics are aggregated asynchronously and may be slightly delayed for recently sent campaigns. By default, statistics are aggregated over the whole period since the campaign was last started. Use the optional `start_date` and `end_date` query parameters to narrow the aggregation window. tags: - Campaign Stats parameters: - $ref: "#/components/parameters/email_campaign_id" - $ref: "#/components/parameters/start_date" - $ref: "#/components/parameters/end_date" responses: "200": $ref: "#/components/responses/EmailCampaignStatsResponse" "401": $ref: "#/components/responses/UNAUTHENTICATED" "403": $ref: "#/components/responses/PERMISSION_DENIED" "404": $ref: "#/components/responses/NOT_FOUND" "429": $ref: "#/components/responses/RATE_LIMITED" x-codeSamples: - lang: shell label: "cURL" source: | curl -X GET "https://mailtrap.io/api/email_campaigns/4567/stats?start_date=2026-05-01&end_date=2026-05-31" \ -H 'Api-Token: YOUR_API_KEY' - lang: javascript label: Node.js source: | import { MailtrapClient } from "mailtrap"; const client = new MailtrapClient({ token: "YOUR_API_KEY" }); const stats = await client.emailCampaigns.getStats(4567, { start_date: "2026-05-01", end_date: "2026-05-31" }); console.log(`Delivered: ${stats.data.delivery_count}, opened: ${stats.data.open_count}`); - lang: php label: PHP source: | emailCampaigns($accountId); $response = $emailCampaigns->getEmailCampaignStats( 4567, startDate: '2026-05-01', endDate: '2026-05-31' ); var_dump(ResponseHelper::toArray($response)); - lang: python label: Python source: | import mailtrap as mt client = mt.MailtrapClient(token="YOUR_API_KEY") campaigns = client.email_campaigns_api.email_campaigns stats = campaigns.get_stats( email_campaign_id=4567, params=mt.EmailCampaignStatsParams(start_date="2026-05-01", end_date="2026-05-31") ) print(f"Delivered: {stats.delivery_count}, opened: {stats.open_count}") - lang: ruby label: Ruby source: | require 'mailtrap' client = Mailtrap::Client.new(api_key: 'YOUR_API_KEY') email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) stats = email_campaigns.stats(4567, start_date: '2026-05-01', end_date: '2026-05-31') puts "Delivered: #{stats.delivery_count}, opened: #{stats.open_count}" - lang: csharp label: .NET source: | using Mailtrap; using Mailtrap.EmailCampaigns.Models; using var mailtrapFactory = new MailtrapClientFactory("YOUR_API_KEY"); var client = mailtrapFactory.CreateClient(); var filter = new EmailCampaignStatsFilter { StartDate = "2026-05-01", EndDate = "2026-05-31" }; var stats = await client.EmailCampaign(4567).GetStats(filter); Console.WriteLine($"Delivered: {stats.DeliveryCount}, opened: {stats.OpenCount}"); - lang: java label: Java source: | import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; import io.mailtrap.api.emailcampaigns.EmailCampaignStatsFilter; var config = new MailtrapConfig.Builder() .token("YOUR_API_KEY") .build(); var client = MailtrapClientFactory.createMailtrapClient(config); var campaigns = client.emailCampaignsApi().emailCampaigns(); var stats = campaigns.getEmailCampaignStats(4567L, EmailCampaignStatsFilter.builder() .startDate("2026-05-01") .endDate("2026-05-31") .build()); System.out.println(stats.getData()); - lang: go label: Go source: | package main import ( "context" "fmt" "log" "os" "github.com/mailtrap/mailtrap-go" ) func main() { client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) if err != nil { log.Fatal(err) } // Pass nil for the default window, which is the period since the campaign was // last started. stats, _, err := client.EmailCampaigns.Stats(context.Background(), 1000001, &mailtrap.EmailCampaignStatsOptions{ StartDate: "2026-03-01", EndDate: "2026-03-31", }) if err != nil { log.Fatal(err) } fmt.Printf("delivered %d (%.2f), opened %d (%.2f), clicked %d (%.2f)\n", stats.DeliveryCount, stats.DeliveryRate, stats.OpenCount, stats.OpenRate, stats.ClickCount, stats.ClickRate) } components: securitySchemes: HeaderAuth: type: apiKey description: Pass the API token in the Api-Token header in: header name: Api-Token BearerAuth: type: http description: Pass the API token as a Bearer token in the Authorization header scheme: bearer parameters: email_campaign_id: name: email_campaign_id description: Unique identifier of the email campaign in: path required: true schema: type: integer format: int64 minimum: 1 example: 4567 token: name: token description: Page number to retrieve (page-token pagination). Defaults to `1`. in: query required: false schema: type: integer minimum: 1 default: 1 example: 1 per_page: name: per_page description: Number of campaigns per page. Defaults to `50`, maximum `100`. in: query required: false schema: type: integer minimum: 1 maximum: 100 default: 50 example: 50 search: name: search description: Filter campaigns by name (case-insensitive partial match). in: query required: false schema: type: string example: spring start_date: name: start_date description: |- Start of the aggregation window (inclusive), in `YYYY-MM-DD` format. Defaults to the day the campaign was last started. Values earlier than the account creation date are clamped to it. in: query required: false schema: type: string format: date example: "2026-05-01" end_date: name: end_date description: End of the aggregation window (inclusive), in `YYYY-MM-DD` format. Defaults to the current date. in: query required: false schema: type: string format: date example: "2026-05-31" schemas: EmailCampaignWritableAttributes: type: object description: |- Campaign attributes. Both create and update accept the same fields. Create and update only manage attributes and audience — the campaign always stays in `draft`; scheduling and starting are performed via the dedicated lifecycle action endpoints. properties: name: type: string description: Campaign name. example: Spring Sale domain_id: type: integer format: int64 description: ID of the verified sending domain used for the campaign, as returned by the Sending Domains endpoints. example: 4321 from_display_name: type: string description: Display name shown in the From header. example: Acme Marketing from_local_part: type: string description: Local part (before the @) of the From address. example: news reply_to: $ref: "#/components/schemas/ReplyTo" template_attributes: $ref: "#/components/schemas/TemplateAttributes" delivery_mode: type: string description: |- How the campaign is delivered. `rapid` sends as fast as possible; `gradual` throttles sending to `delivery_options.emails_per_hour`. example: rapid enum: - rapid - gradual delivery_options: type: object description: Delivery throttling options. Applies when `delivery_mode` is `gradual`. properties: emails_per_hour: type: [integer, "null"] example: 1000 contact_list_ids: type: array description: |- IDs of contact lists to send to. Treated as the full set of included lists — lists not listed are removed. Combine with `contact_segment_ids` to target both. items: type: integer format: int64 example: [55, 56] contact_segment_ids: type: array description: IDs of contact segments to send to. Treated as the full set of included segments. items: type: integer format: int64 example: [12] EmailCampaignCreateRequest: description: Campaign attributes. `name`, `domain_id`, `from_local_part` and a template `subject` are required. allOf: - $ref: "#/components/schemas/EmailCampaignWritableAttributes" - type: object required: - name - domain_id - from_local_part - template_attributes properties: template_attributes: type: object required: - subject EmailCampaignUpdateRequest: description: Attributes to update. All fields optional; only provided fields change. allOf: - $ref: "#/components/schemas/EmailCampaignWritableAttributes" EmailCampaignScheduleRequest: type: object description: When to start sending the campaign. required: - datetime properties: datetime: type: string format: date-time description: |- When to send the campaign (ISO 8601). Must be in the future and no more than 1 month ahead, otherwise the request is rejected with `422`. example: "2026-10-01T09:00:00.000Z" ReplyTo: type: object description: Reply-To address parts. properties: display_name: type: string example: Acme Support local_part: type: string example: support domain: type: string example: acme.com TemplateAttributes: type: object description: |- Inline email template — the campaign's subject and design. Each campaign has exactly one template, created together with it using the `template_attributes` fields. On update, the campaign's template is always edited in place. Updates are partial: only the sub-fields you provide change; sub-fields that are omitted or sent as `null` keep their current value. properties: subject: type: string maxLength: 255 description: |- Email subject line. Required when creating a campaign. Supports merge tags, e.g. `Hi {{first_name}}`. example: "Spring is here — 30% off" body_html: type: string maxLength: 10000000 description: |- HTML body of the email (the design). Optional for a draft; required before the campaign can be scheduled or started. Include an unsubscribe link via an anchor whose `href` contains the `__unsubscribe_url__` placeholder — a design without one is accepted, but flagged with an "Unsubscribe link is missing" warning in the Mailtrap UI. Supports `{{tag_name}}` merge tags. example: '

Hi {{first_name}}!

Unsubscribe

' body_text: type: [string, "null"] maxLength: 10000000 description: |- Optional plain-text alternative of the email body. Supports the same `__unsubscribe_url__` placeholder and `{{tag_name}}` merge tags as `body_html`. example: "Hi {{first_name}}! Unsubscribe: __unsubscribe_url__" merge_tags: type: array description: |- Bare names of the merge tags referenced in the subject/body, without the `{{ }}` delimiters — e.g. `["first_name"]` when the content contains `{{first_name}}`. Lets Mailtrap track which contact fields the campaign uses. Replaced as a whole when provided; defaults to an empty array. items: type: string example: ["first_name"] EmailCampaign: type: object properties: id: type: integer format: int64 example: 4567 domain_id: type: integer format: int64 description: ID of the sending domain used for the campaign, as returned by the Sending Domains endpoints. example: 4321 domain_name: type: string example: acme.com name: type: string example: Spring Sale from_local_part: type: string example: news from_display_name: type: string example: Acme Marketing reply_to: $ref: "#/components/schemas/ReplyTo" current_state: type: string description: Current state of the campaign in its lifecycle. example: draft enum: - draft - scheduled - started - queued - paused - terminating - under_review - finished - failed - failed_immediately current_state_metadata: type: object description: Metadata about the most recent state transition. Which keys are present depends on the state. properties: reason: type: string error: type: string description: Last error message recorded for a failed campaign. errors: type: array description: Per-recipient errors recorded when sending failed. items: type: object properties: message: type: string example: Invalid recipient address rcpt_index: type: integer example: 0 scheduled_at: type: string format: date-time description: When the campaign is scheduled to send (ISO 8601). Present in the `scheduled` state. example: "2026-06-01T09:00:00.000Z" created_at: type: string format: date-time example: "2026-05-01T10:15:00.000Z" updated_at: type: string format: date-time example: "2026-05-02T09:00:00.000Z" last_started_at: type: [string, "null"] format: date-time example: "2026-05-03T12:00:00.000Z" last_started_at_date: type: string format: date description: Date the campaign was last started. Present only when the campaign has been started. example: "2026-05-03" recipient_total_count: type: [integer, "null"] description: Total number of recipients targeted by the campaign. `null` until the audience is resolved. example: 1500 contact_list_ids: type: array description: IDs of the contact lists included in the campaign's audience. items: type: integer format: int64 example: [55, 56] contact_segment_ids: type: array description: IDs of the contact segments included in the campaign's audience. items: type: integer format: int64 example: [12] delivery_mode: type: string description: |- How the campaign is delivered. `rapid` sends as fast as possible; `gradual` throttles sending to `delivery_options.emails_per_hour`. example: rapid enum: - rapid - gradual delivery_options: type: object description: Delivery throttling options. Applies when `delivery_mode` is `gradual`. properties: emails_per_hour: type: [integer, "null"] example: 1000 template: type: object description: |- The campaign's template. `body_html` and `body_text` are returned only on single-campaign responses (create, update, get-by-ID); the list endpoint omits them to keep the payload small. properties: id: type: integer format: int64 example: 789 subject: type: string example: "Spring is here — 30% off" merge_tags: type: array items: type: string example: ["first_name"] body_html: type: [string, "null"] description: HTML body (the design). Returned only on single-campaign responses. example: '

Hi {{first_name}}!

Unsubscribe

' body_text: type: [string, "null"] description: Plain-text body. Returned only on single-campaign responses. example: null EmailCampaignStats: type: object description: Aggregated campaign performance metrics. Counts and rates are `0` when the campaign has not been started. properties: delivery_count: type: integer example: 1450 delivery_rate: type: number format: float description: Share of sent messages that were delivered (0–1). example: 0.9667 bounce_count: type: integer example: 30 bounce_rate: type: number format: float example: 0.02 open_count: type: integer example: 820 open_rate: type: number format: float example: 0.5655 click_count: type: integer example: 310 click_rate: type: number format: float example: 0.2138 spam_count: type: integer example: 5 spam_rate: type: number format: float example: 0.0033 unsubscription_count: type: integer example: 12 unsubscription_rate: type: number format: float example: 0.0083 sent_count: type: integer example: 1500 Pagination: type: object description: Page-token pagination metadata. properties: token: type: integer description: Current page number. example: 1 prev_token: type: [integer, "null"] description: Previous page number, or `null` on the first page. example: null next_token: type: [integer, "null"] description: Next page number, or `null` on the last page. example: 2 first_url: type: string format: uri example: "https://mailtrap.io/api/email_campaigns?per_page=50&token=1" prev_url: type: [string, "null"] format: uri example: null current_url: type: string format: uri example: "https://mailtrap.io/api/email_campaigns?per_page=50&token=1" next_url: type: [string, "null"] format: uri example: "https://mailtrap.io/api/email_campaigns?per_page=50&token=2" RateLimitedResponse: type: object # NOTE: the shared global throttle (Rack::Attack) returns the legacy singular `error` key. # This diverges from the plural-`errors` convention; kept here to match the real response. properties: error: type: string example: Throttled UnauthenticatedResponse: type: object properties: error: type: string example: Incorrect API token PermissionDeniedResponse: type: object properties: errors: type: string example: Account access forbidden NotFoundResponse: type: object properties: error: type: string example: Not Found ValidationErrorResponse: type: object properties: errors: type: object additionalProperties: type: array items: type: string example: name: - can't be blank from_local_part: - can't be blank template.subject: - can't be blank ActionValidationErrorResponse: type: object description: |- Validation error returned by the lifecycle action endpoints. `errors` is either a single message (an invalid state transition or an invalid `datetime`) or a list of messages (sending-validation failures). properties: errors: oneOf: - type: string example: "Cannot transition from 'started' to 'scheduled'" - type: array items: type: string example: - Campaign design can't be blank responses: EmailCampaignsResponse: description: A paginated list of email campaigns. content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/EmailCampaign" pagination: $ref: "#/components/schemas/Pagination" EmailCampaignResponse: description: A single email campaign. content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/EmailCampaign" EmailCampaignStatsResponse: description: Aggregated campaign statistics. content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/EmailCampaignStats" UNAUTHENTICATED: description: Authentication failed — missing or invalid API token. content: application/json: schema: $ref: "#/components/schemas/UnauthenticatedResponse" PERMISSION_DENIED: description: The API token does not have permission to access this resource. content: application/json: schema: $ref: "#/components/schemas/PermissionDeniedResponse" NOT_FOUND: description: The requested email campaign was not found. content: application/json: schema: $ref: "#/components/schemas/NotFoundResponse" VALIDATION_ERROR: description: The request was rejected due to validation errors. content: application/json: schema: $ref: "#/components/schemas/ValidationErrorResponse" ACTION_VALIDATION_ERROR: description: |- The lifecycle action was rejected — the campaign was in a state that does not allow the action, the `datetime` was invalid, or sending validation failed. content: application/json: schema: $ref: "#/components/schemas/ActionValidationErrorResponse" RATE_LIMITED: description: |- Too many requests. The public API is rate limited to 150 requests per 10 seconds per API token. Retry after the window resets (see the `x-ratelimit-reset` header). headers: x-ratelimit-limit: description: Maximum number of requests allowed within the window. schema: type: integer example: 150 x-ratelimit-remaining: description: Requests remaining in the current window. schema: type: integer example: 0 x-ratelimit-reset: description: ISO 8601 timestamp when the current rate-limit window resets. schema: type: string format: date-time example: "2026-06-15T10:00:10.000000Z" content: application/json: schema: $ref: "#/components/schemas/RateLimitedResponse"