Add mailboxes in bulk
Connect one or multiple mailboxes to your account by providing SMTP/IMAP credentials along with optional sending configurations. This endpoint enables you to set up the connection but you can also specify additional settings, including daily sending limits, tracking domains or footers.
You can use this endpoint to connect Gmail and Outlook mailboxes in bulk as well, provided you are using dedicated app passwords instead of regular credentials.
Request
Endpoint
POST https://api.woodpecker.co/rest/v2/mailboxes/manual_connection/bulk
Headers
x-api-key: {YOUR_API_KEY}
Content-type: application/json
For details on how to authenticate your requests, please see the authentication guide.
Body
The request body contains a mailboxes array where each object represents a mailbox with its SMTP/IMAP connection credentials and optional settings (like sending limits, tracking domains, and notification preferences). You can provide one or multiple mailbox configurations in a single request.
You can connect up to 200 mailboxes in a single request.
- All available fields
- Only required fields
{
"mailboxes": [
{
"smtp_email": "smtp@mail.com",
"smtp_login": "smtp-username",
"smtp_password": "secret-smtp-password",
"smtp_server": "smtp.server.io",
"smtp_port": 465,
"smtp_from_name": "smtp-from-name",
"imap_email": "imap@mail.com",
"imap_password": "secret-imap-password",
"imap_server": "imap.server.io",
"imap_port": 993,
"footer": "<div>Best regards,</div><div>John</div>",
"bcc": "email-to@bcc.com",
"open_tracking_domain": "open-domain.com",
"click_tracking_domain": "click-domain.com",
"unsubscribe_tracking_domain": "unsubscribe-domain.com",
"sending_wait_time_from": 10,
"sending_wait_time_to": 20,
"daily_sending_limit": 100
}
],
"completion_notification_types": ["MAIL", "IN_APP"]
}
{
"mailboxes": [
{
"smtp_email": "jared@getpiedpiper.com",
"smtp_password": "secret-smtp-password",
"smtp_server": "smtp.server.io",
"smtp_port": 465,
"imap_email": "jared@getpiedpiper.com",
"imap_password": "secret-imap-password",
"imap_server": "imap.server.io",
"imap_port": 993
}
]
}
Body schema
| Field | Type | Required | Description |
|---|---|---|---|
mailboxes | array | Yes | Array of mailbox configurations |
└─smtp_email | string | Yes | Email address of the sending mail |
└─smtp_login | string | No | Username for SMTP authentication. Use if it's different than the email |
└─smtp_password | string | Yes | Password for SMTP authentication |
└─smtp_server | string | Yes | SMTP server hostname |
└─smtp_port | integer | Yes | SMTP server port number |
└─smtp_from_name | string | No | Sender's display name shown to recipients |
└─imap_email | string | Yes | Email address of the receiving mail |
└─imap_password | string | Yes | Password for IMAP authentication |
└─imap_server | string | Yes | IMAP server hostname |
└─imap_port | integer | Yes | IMAP server port number |
└─footer | string | No | Signature added to sent emails, in HTML format. Supports the {{UNSUBSCRIBE}} snippet, which generates an unsubscribe link. Remember to wrap it in an <a href> tag |
└─bcc | string | No | Email address to be added as BCC in all outgoing emails. Useful with CRMs |
└─open_tracking_domain | string | No | Custom domain used to track email opens |
└─click_tracking_domain | string | No | Custom domain used to track link clicks |
└─unsubscribe_tracking_domain | string | No | Custom domain handling unsubscribe requests |
└─sending_wait_time_from | integer | No* | Minimum pause between emails in seconds (range: 10-9999) |
└─sending_wait_time_to | integer | No* | Maximum pause between emails in seconds (range: 20-9999) |
└─daily_sending_limit | integer | No | Maximum number of sent emails allowed per day (range: 1-5500) |
completion_notification_types | string[] | No | How to notify you when batch processing completes: via email MAIL and/or in-app notification IN_APP |
*Note: If either sending_wait_time_from or sending_wait_time_to is provided, both become required and from must be less than to.
Request samples
Connect a mailbox
- cURL
- Python
- Java
- Node.js
- PHP
curl --request POST \
--url "https://api.woodpecker.co/rest/v2/mailboxes/manual_connection/bulk" \
--header "x-api-key: {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"mailboxes": [
{
"smtp_email": "jared@getpiedpiper.com",
"smtp_password": "secret-smtp-password",
"smtp_server": "smtp.server.io",
"smtp_port": 465,
"imap_email": "jared@getpiedpiper.com",
"imap_password": "secret-imap-password",
"imap_server": "imap.server.io",
"imap_port": 993
}
]
}'
import requests
def connect_mailboxes():
url = "https://api.woodpecker.co/rest/v2/mailboxes/manual_connection/bulk"
headers = {
"x-api-key": "{YOUR_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"mailboxes": [
{
"smtp_email": "jared@getpiedpiper.com",
"smtp_password": "secret-smtp-password",
"smtp_server": "smtp.server.io",
"smtp_port": 465,
"imap_email": "jared@getpiedpiper.com",
"imap_password": "secret-imap-password",
"imap_server": "imap.server.io",
"imap_port": 993
}
]
}
try:
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
print("POST response:", response.json())
else:
raise Exception(f"POST request failed: {response.status_code}, {response.text}")
except Exception as e:
print("Error:", e)
if __name__ == "__main__":
connect_mailboxes()
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class WoodpeckerApiClient {
public static void main(String[] args) {
String apiKey = "YOUR_API_KEY";
String endpoint = "https://api.woodpecker.co/rest/v2/mailboxes/manual_connection/bulk";
String jsonInputString = "{"
+ "\"mailboxes\": ["
+ "{"
+ "\"smtp_email\": \"jared@getpiedpiper.com\","
+ "\"smtp_password\": \"secret-smtp-password\","
+ "\"smtp_server\": \"smtp.server.io\","
+ "\"smtp_port\": 465,"
+ "\"imap_email\": \"jared@getpiedpiper.com\","
+ "\"imap_password\": \"secret-imap-password\","
+ "\"imap_server\": \"imap.server.io\","
+ "\"imap_port\": 993"
+ "}"
+ "]"
+ "}";
try {
URL url = new URL(endpoint);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("x-api-key", apiKey);
connection.setRequestProperty("Content-Type", "application/json");
connection.setDoOutput(true);
try (OutputStream os = connection.getOutputStream()) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED) {
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println("Response: " + response.toString());
} else {
System.out.println("POST request failed with response code: " + responseCode);
}
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
const axios = require("axios");
const apiKey = "YOUR_API_KEY";
const url = "https://api.woodpecker.co/rest/v2/mailboxes/manual_connection/bulk";
const data = {
mailboxes: [
{
smtp_email: "jared@getpiedpiper.com",
smtp_password: "secret-smtp-password",
smtp_server: "smtp.server.io",
smtp_port: 465,
imap_email: "jared@getpiedpiper.com",
imap_password: "secret-imap-password",
imap_server: "imap.server.io",
imap_port: 993,
},
],
};
axios
.post(url, data, {
headers: {
"x-api-key": apiKey,
"Content-Type": "application/json",
},
})
.then((response) => {
console.log("Response:", response.data);
})
.catch((error) => {
console.error("Error:", error.response ? error.response.data : error.message);
});
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
$client = new Client([
'base_uri' => 'https://api.woodpecker.co/rest/v2/',
'headers' => [
'x-api-key' => getenv('WOODPECKER_API_KEY'),
'Content-Type' => 'application/json',
],
]);
try {
$response = $client->post('mailboxes/manual_connection/bulk', [
'json' => [
'mailboxes' => [
[
'smtp_email' => 'jared@getpiedpiper.com',
'smtp_password' => 'secret-smtp-password',
'smtp_server' => 'smtp.server.io',
'smtp_port' => 465,
'imap_email' => 'jared@getpiedpiper.com',
'imap_password' => 'secret-imap-password',
'imap_server' => 'imap.server.io',
'imap_port' => 993,
],
],
],
]);
echo $response->getStatusCode(), "\n";
echo $response->getBody(), "\n";
} catch (RequestException $e) {
echo "Error: ", $e->getMessage(), "\n";
if ($e->hasResponse()) {
echo $e->getResponse()->getBody(), "\n";
}
}
Response
Response examples
- 202
- 400
- 401
- 404
- 409
- 500
The response includes a batch_id that allows you to monitor the progress and results of the submitted mailbox connections using rest/v2/mailboxes/manual_connection/bulk/{batch_id}/summary endpoint.
{
"batch_id": 123456
}
Body schema
| Field | Type | Description |
|---|---|---|
batch_id | string | ID of the submitted email batch. Use it to review the connection status. |
Invalid request or malformed request syntax. Please review the request body and field requirements.
Each validation error will be included in the details array. For example, mailbox[3].daily_sending_limit indicates the issue encountered while validating the daily_sending_limit for the fourth mailbox in your request. Below are a few additional examples.
{
"code": "VALIDATION_FAILURE",
"details": [
"mailbox[0].smtp_password must not be blank",
"mailbox[0].imap_port must be provided and in range <1,65535>",
"mailbox[0].sending_wait_time_from if provided must be present along with and lower than sending_wait_time_to and be in range <10,9999>",
"mailbox[0].sending_wait_time_to if provided must be present along with and higher than sending_wait_time_from and be in range <20,9999>",
"mailbox[0].open_tracking_domain if provided must be a valid domain"
]
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Code that outlines the request issue. VALIDATION_FAILURE or UNKNOWN |
details[] | array | Array of specific error messages. If the code is VALIDATION_FAILURE, every field with an issue will be returned as mailbox[index].filed message |
An issue with authorization. Please review the authorization guide
{
"title": "Unauthorized",
"status": 401,
"detail": "Invalid api key",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
detail | string | A detailed message explaining the error |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
Please review the request URL
{
"title": "Not Found",
"status": 404,
"detail": "Requested resource does not exist",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
detail | string | A detailed message explaining the error |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
Conflict
{
"code": "Conflict",
"details": [
"string"
]
}
Unexpected error, please try again later
{
"title": "Internal server error",
"status": 500,
"detail": "An unexpected error has occurred. Please try again later.",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
detail | string | A detailed message explaining the error |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |