> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.brevo.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.brevo.com/_mcp/server.

# Create an email campaign

POST https://api.brevo.com/v3/emailCampaigns
Content-Type: application/json

Create a new email campaign. The campaign requires at minimum a name and sender details, and is created in draft status by default. You must provide email content via one of three mutually exclusive options: htmlContent (inline HTML), htmlUrl (remote URL), or templateId (existing template); additionally, A/B testing can be enabled by setting abTesting to true with subjectA and subjectB, but this is incompatible with sendAtBestTime.

Reference: https://developers.brevo.com/reference/create-email-campaign

## Authentication

- `api-key` header (required) — The API key should be passed in the request headers as `api-key` for authentication.

## Request

### Body (application/json)

This endpoint expects an object.

- `name` (string, required) — Name of the campaign
- `sender` (object, required) — Sender details including id or email and name (optional). Only one of either Sender’s email or Sender’s ID shall be passed in one request at a time. Passing both `email` and `id` will result in an error. For example: **\{"name":"xyz", "email":"[example@abc.com](mailto:example@abc.com)"}** or **\{"name":"xyz", "id":123}**
  - `email` (string, optional) — Sender email. Must be a valid email address. Cannot be used together with `id` in the same request.
  - `id` (long, optional) — Select the sender for the campaign on the basis of sender id. Cannot be used together with `email` in the same request. _In order to select a sender with a specific pool of IPs, dedicated IP users shall pass id (instead of email)._
  - `name` (string, optional) — Sender Name
- `abTesting` (boolean, optional, default: false) — Status of A/B Test. abTesting = false means it is disabled & abTesting = true means it is enabled. **subjectA, subjectB, splitRule, winnerCriteria & winnerDelay** will be considered when abTesting is set to true. subjectA & subjectB are mandatory together & subject if passed is ignored. **Can be set to true only if sendAtBestTime is false**. You will be able to set up two subject lines for your campaign and send them to a random sample of your total recipients. Half of the test group will receive version A, and the other half will receive version B
- `attachmentUrl` (string, optional) — Absolute url of the attachment (no local file). Extension allowed: #### xlsx, xls, ods, docx, docm, doc, csv, pdf, txt, gif, jpg, jpeg, png, tif, tiff, rtf, bmp, cgm, css, shtml, html, htm, zip, xml, ppt, pptx, tar, ez, ics, mobi, msg, pub and eps
- `emailExpirationDate` (object, optional) — To reduce your carbon footprint, set an expiration date for your email. If supported, it will be automatically deleted from the recipient’s inbox, saving storage space and energy. Learn more about setting an email expiration date. For reference , ``https://help.brevo.com/hc/en-us/articles/4413566705298-Create-an-email-campaign``
  - `duration` (long, optional) — Duration of the email expiry. maximum duration can be 3600 days or 480 weeks or 120 months.
  - `unit` (enum, optional) — unit of the duration
    - Allowed values: `days`, `weeks`, `months`
- `footer` (string, optional) — Footer of the email campaign
- `header` (string, optional) — Header of the email campaign
- `htmlContent` (string, optional) — **Mandatory if htmlUrl and templateId are empty.** Body of the message (HTML). Must have more than 10 characters and be less than 1MB in size. Cannot be used together with `htmlUrl` or `templateId`.
- `htmlUrl` (string, optional) — **Mandatory if htmlContent and templateId are empty.** URL to the message (HTML). Cannot be used together with `htmlContent` or `templateId`. For example: **https://html.domain.com**
- `increaseRate` (long, optional) — **Mandatory if ipWarmupEnable is set to true**. Set a percentage increase rate for warming up your ip. We recommend you set the increase rate to 30% per day. If you want to send the same number of emails every day, set the daily increase value to 0%.
- `initialQuota` (long, optional) — **Mandatory if ipWarmupEnable is set to true**. Set an initial quota greater than 1 for warming up your ip. We recommend you set a value of 3000.
- `inlineImageActivation` (boolean, optional, default: false) — Use true to embedded the images in your email. Final size of the email should be less than **4MB**. Campaigns with embedded images can _not be sent to more than 5000 contacts_
- `ipWarmupEnable` (boolean, optional, default: false) — **Available for dedicated ip clients**. Set this to true if you wish to warm up your ip.
- `mirrorActive` (boolean, optional) — Use true to enable the mirror link
- `params` (map from string to any, optional) — Pass the set of attributes to customize the type classic campaign. For example: **\{"FNAME":"Joe", "LNAME":"Doe"}**. Only available if **type** is **classic**. It's considered only if campaign is in *New Template Language format*. The New Template Language is dependent on the values of **subject, htmlContent/htmlUrl, sender.name & toField**
- `previewText` (string, optional) — Preview text or preheader of the email campaign
- `recipients` (object, optional) — Segment ids and List ids to include/exclude from campaign
  - `exclusionListIds` (list of long, optional) — List ids to exclude from the campaign
  - `exclusionSegmentIds` (list of long, optional) — Segment ids which have to be excluded from a campaign.
  - `listIds` (list of long, optional) — **Mandatory if scheduledAt is not empty**. List Ids to send the campaign to
  - `segmentIds` (list of long, optional) — **Mandatory if listIds are not used**. Segment ids to send the campaign to.
- `replyTo` (string, optional) — Email on which the campaign recipients will be able to reply to
- `scheduledAt` (string, optional) — Sending UTC date-time (YYYY-MM-DDTHH:mm:ss.SSSZ). **Prefer to pass your timezone in date-time format for accurate result**. If sendAtBestTime is set to true, your campaign will be sent according to the date passed (ignoring the time part). For example: **2017-06-01T12:30:00+02:00**
- `sendAtBestTime` (boolean, optional, default: false) — Set this to true if you want to send your campaign at best time.
- `splitRule` (long, optional) — Add the size of your test groups. **Mandatory if abTesting = true & 'recipients' is passed**. We'll send version A and B to a random sample of recipients, and then the winning version to everyone else
- `subject` (string, optional) — Subject of the campaign. **Mandatory if abTesting is false**. Ignored if abTesting is true.
- `subjectA` (string, optional) — Subject A of the campaign. **Mandatory if abTesting = true**. subjectA & subjectB should have unique value
- `subjectB` (string, optional) — Subject B of the campaign. **Mandatory if abTesting = true**. subjectA & subjectB should have unique value
- `tag` (string, optional) — Tag of the campaign
- `templateId` (long, optional) — **Mandatory if htmlContent and htmlUrl are empty.** Id of the transactional email template with status _active_. Used to copy only its content fetched from htmlContent/htmlUrl to an email campaign for RSS feature. Cannot be used together with `htmlContent` or `htmlUrl`.
- `toField` (string, optional) — To personalize the **To** Field. If you want to include the first name and last name of your recipient, add **\{FNAME} \{LNAME}**. These contact attributes must already exist in your Brevo account. If input parameter **params** used please use **\{\{contact.FNAME}} \{\{contact.LNAME}}** for personalization
- `unsubscriptionPageId` (string, optional) — Enter an unsubscription page id. The page id is a 24 digit alphanumeric id that can be found in the URL when editing the page. If not entered, then the default unsubscription page will be used.
- `updateFormId` (string, optional) — **Mandatory if templateId is used containing the \{\{ update\_profile }} tag**. Enter an update profile form id. The form id is a 24 digit alphanumeric id that can be found in the URL when editing the form. If not entered, then the default update profile form will be used.
- `utmCampaign` (string, optional) — Customize the utm_campaign value. If this field is empty, the campaign name will be used. Only alphanumeric characters and spaces are allowed
- `utmContent` (string, optional) — Customize the utm_content value. Appears on outgoing tracking links alongside utm_campaign. Only alphanumeric characters and spaces are allowed
- `utmTerm` (string, optional) — Customize the utm_term value. Appears on outgoing tracking links alongside utm_campaign. Only alphanumeric characters and spaces are allowed
- `winnerCriteria` (enum, optional) — Choose the metrics that will determinate the winning version. **Mandatory if *splitRule* >= 1 and \< 50**. If splitRule = 50, `winnerCriteria` is ignored if passed
  - Allowed values: `open`, `click`
- `winnerDelay` (long, optional) — Choose the duration of the test in hours. Maximum is 7 days, pass 24\*7 = 168 hours. The winning version will be sent at the end of the test. **Mandatory if *splitRule* >= 1 and \< 50**. If splitRule = 50, `winnerDelay` is ignored if passed

## Response

### 201

successfully created

- `id` (long, required) — ID of the object created

## Errors

### 400 Bad Request Error

bad request

- `code` (enum, required) — Error code displayed in case of a failure
  - Allowed values: `invalid_parameter`, `missing_parameter`, `out_of_range`, `campaign_processing`, `campaign_sent`, `document_not_found`, `not_enough_credits`, `permission_denied`, `duplicate_parameter`, `duplicate_request`, `method_not_allowed`, `unauthorized`, `account_under_validation`, `not_acceptable`, `bad_request`, `unprocessable_entity`, `Domain does not exist`, `Contact email not found`, `Attribute not found`, `Category id not found`, `Invalid parameters passed`, `Record(s) for identifier not found`, `Returned when query params are invalid`, `Returned when invalid data posted`, `Feed not found`, `Campaign ID not found`, `api-key not found`, `DMARC policy requires domain authentication`, `DNS records not properly configured`, `Invalid OTP code provided`, `OTP code has expired`, `Domain already exists in your account`, `The sum of all IP weights must equal 100`, `Authentication failed`, `Insufficient credits`, `Request already processed`
- `message` (string, required) — Readable message associated to the failure

### 405 Method Not Allowed Error

Method not allowed. Returned when using sendAtBestTime or abTesting features without the required premium plan.

- `message` (string, required) — Readable message associated to the failure
- `code` (string, optional) — Error code displayed in case of a failure

## Examples

**Request**

```json
{
  "name": "Newsletter - May 2017"
}
```

**Response**

```json
{
  "id": 5
}
```

**SDK Code**

```typescript
import { BrevoClient } from "@getbrevo/brevo";

async function main() {
    const client = new BrevoClient({
        apiKey: "YOUR_API_KEY_HERE",
    });
    await client.emailCampaigns.createEmailCampaign({
        name: "Newsletter - May 2017",
    });
}
main();

```

```python
from brevo import Brevo

client = Brevo(
    api_key="YOUR_API_KEY_HERE",
)

client.email_campaigns.create_email_campaign(
    name="Newsletter - May 2017",
)

```

```php
<?php

namespace Example;

use Brevo\Brevo;
use Brevo\EmailCampaigns\Requests\CreateEmailCampaignRequest;

$client = new Brevo(
    apiKey: 'YOUR_API_KEY_HERE',
);
$client->emailCampaigns->createEmailCampaign(
    new CreateEmailCampaignRequest([
        'name' => 'Newsletter - May 2017',
    ]),
);

```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.brevo.com/v3/emailCampaigns"

	payload := strings.NewReader("{\n  \"name\": \"Newsletter - May 2017\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("api-key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.brevo.com/v3/emailCampaigns")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["api-key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"name\": \"Newsletter - May 2017\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.brevo.com/v3/emailCampaigns")
  .header("api-key", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"name\": \"Newsletter - May 2017\"\n}")
  .asString();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.brevo.com/v3/emailCampaigns");
var request = new RestRequest(Method.POST);
request.AddHeader("api-key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"name\": \"Newsletter - May 2017\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "api-key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = ["name": "Newsletter - May 2017"] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.brevo.com/v3/emailCampaigns")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```