diff --git a/README.md b/README.md index e080ab5..e57046e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ The specifications are located in the `specs/` directory: | ----------------------------- | ----------------------------------------------- | --------------------------------- | | Account Management | `specs/account-management.openapi.yml` | `https://mailtrap.io` | | Contacts | `specs/contacts.openapi.yml` | `https://mailtrap.io` | +| Email Campaigns | `specs/email-campaigns.openapi.yml` | `https://mailtrap.io` | | Email Sending | `specs/email-sending.openapi.yml` | `https://mailtrap.io` | | Email Sending (Bulk) | `specs/email-sending-bulk.openapi.yml` | `https://bulk.api.mailtrap.io` | | Email Sending (Transactional) | `specs/email-sending-transactional.openapi.yml` | `https://send.api.mailtrap.io` | diff --git a/specs/email-campaigns.openapi.yml b/specs/email-campaigns.openapi.yml new file mode 100644 index 0000000..45dde52 --- /dev/null +++ b/specs/email-campaigns.openapi.yml @@ -0,0 +1,1027 @@ +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_TOKEN' + post: + operationId: createEmailCampaign + summary: Create an email campaign + description: |- + Creates a new email campaign. The campaign must reference an existing sending domain via + `mailsend_domain_id`, and a `template` with a `subject` provided inline. + + 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_TOKEN' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "Spring Sale", + "mailsend_domain_id": "d2313359-acb4-4b87-bce6-f5774f6a1e37", + "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" } + }' + "/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_TOKEN' + 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), include `template_attributes` with the existing template `id` + so it is modified in place rather than replaced. + + 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_TOKEN' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "Spring Sale (updated)", + "template_attributes": { + "id": 789, + "subject": "New subject", + "body_html": "

Hi {{first_name}}!

Unsubscribe

" + } + }' + delete: + operationId: deleteEmailCampaign + summary: Delete an email campaign + description: |- + Soft-deletes an email campaign. The campaign must not be in a sending state. + Returns `204 No Content` on success. + 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_TOKEN' + "/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_TOKEN' + "/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: | + curl -X POST "https://mailtrap.io/api/email_campaigns/4567/schedule" \ + -H 'Api-Token: YOUR_API_TOKEN' \ + -H 'Content-Type: application/json' \ + -d '{ "datetime": "2026-06-01T09:00:00.000Z" }' + "/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_TOKEN' + "/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_TOKEN' + "/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 from states that permit a transition + back to `draft` (e.g. `scheduled`, `failed`); resetting from a state that does not allow it + (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_TOKEN' + "/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_TOKEN' +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 + mailsend_domain_id: + type: string + format: uuid + description: UUID of the verified sending domain used for the campaign. + example: "d2313359-acb4-4b87-bce6-f5774f6a1e37" + 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: + type: object + description: |- + Inline email template — subject and design. On create, a new template is built from + these fields (any `id` is ignored). On update, pass the existing template `id` so the + template is edited in place instead of replaced. + + `body_html` holds the campaign design and is **required before the campaign can be + scheduled or started** via the `schedule`/`start` action endpoints (those return `422` + (`"Campaign design can't be blank"`) when it is missing). Add an unsubscribe link by + placing the `__unsubscribe_url__` placeholder in an anchor `href`, and personalize + content with merge tags written as `{{tag_name}}`. + properties: + id: + type: integer + format: int64 + description: Existing template ID. Pass on update to edit the template in place; ignored on create. + example: 789 + subject: + type: string + description: Email subject line. Supports merge tags, e.g. `Hi {{first_name}}`. + example: "Spring is here — 30% off" + body_html: + type: string + description: |- + HTML body of the email (the design). Required before the campaign can be scheduled + or started. Include an unsubscribe link via the `__unsubscribe_url__` placeholder. + example: "

Hi {{first_name}}!

Unsubscribe

" + body_text: + type: [string, "null"] + description: Optional plain-text alternative of the email body. + 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), used for personalization tracking. Defaults to an empty array. + items: + type: string + example: ["first_name"] + 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`, `mailsend_domain_id`, `from_local_part` and a template `subject` are required. + allOf: + - $ref: "#/components/schemas/EmailCampaignWritableAttributes" + - type: object + required: + - name + - mailsend_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-06-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 + EmailCampaign: + type: object + properties: + id: + type: integer + format: int64 + example: 4567 + type: + type: string + description: |- + Resource type discriminator. `ContactsEmailCampaign` targets contact lists/segments; + `RecipientsEmailCampaign` targets an uploaded recipients list. + example: ContactsEmailCampaign + enum: + - ContactsEmailCampaign + - RecipientsEmailCampaign + mailsend_domain_id: + type: string + format: uuid + example: "d2313359-acb4-4b87-bce6-f5774f6a1e37" + mailsend_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 + open_count: + type: integer + example: 820 + click_count: + type: integer + example: 310 + bounce_count: + type: integer + example: 30 + unsubscription_count: + type: integer + example: 12 + sent_count: + type: integer + example: 1500 + spam_count: + type: integer + example: 5 + message_count: + type: integer + example: 1500 + reject_count: + type: integer + example: 20 + delivery_rate: + type: number + format: float + description: Share of sent messages that were delivered (0–1). + example: 0.9667 + open_rate: + type: number + format: float + example: 0.5655 + click_rate: + type: number + format: float + example: 0.2138 + bounce_rate: + type: number + format: float + example: 0.02 + spam_rate: + type: number + format: float + example: 0.0033 + unsubscription_rate: + type: number + format: float + example: 0.0083 + 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"