Interactive documentation · Developer portal · OpenAPI specification · EN · FR · DE · IT · PT · ES
Base URL : https://api.mailpro.com/v2
The Mailpro™ Email API v2 lets you programmatically manage contact lists, email templates, sender addresses, and campaigns — and send both bulk and transactional emails.
Every endpoint accepts three authentication methods. Use whichever fits your stack.
POST /token, not scoped.> Legacy Bearer JWT and IdClient+ApiKey keep working unchanged and are not > scope-gated. Only OAuth 2.0 access tokens are subject to the per-endpoint > scope checks shown on each operation below.
Mailpro runs one OAuth 2.0 authorization server for all of its APIs, at a single version-neutral surface: https://api.mailpro.com/oauth/. There is no per-version OAuth endpoint — you obtain a token from /oauth/token and the same token works on this Email v2 API, on CRM v3 and on SMS. Which endpoints it may call is governed by the token's scopes, not the API base URL. Issuer (iss claim): https://api.mailpro.com/oauth/.
| Endpoint | URL |
|---|---|
| Authorize | GET https://api.mailpro.com/oauth/authorize |
| Token / Refresh | POST https://api.mailpro.com/oauth/token |
| JWKS | GET https://api.mailpro.com/oauth/.well-known/jwks |
| Discovery | GET https://api.mailpro.com/oauth/.well-known/openid-configuration |
Grants: authorization_code + PKCE (S256, mandatory), client_credentials, refresh_token (rotation). Access-token TTL 20 min; refresh token 30 days (sliding, single-use); auth code 60 s.
Quickstart — get a token and call this API (replace placeholders; never commit a real secret):
curl -X POST https://api.mailpro.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=email:send email:read"
Then call this API with Authorization: Bearer ACCESS_TOKEN. The same token works on the CRM v3 and SMS APIs. A token missing an endpoint's required scope gets 403 insufficient_scope. Full flow walk-through and scope catalog: see the CRM v3 API docs › Authentication. Email-relevant scopes: email:send, email:read, email:templates:read, email:templates:write, contacts:read, contacts:write, account:read.
Authorization: Bearer <access_token>
Get a token via POST /token (form-encoded, OAuth2 password grant):
POST https://api.mailpro.com/v2/token
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=YOUR_USERNAME&password=YOUR_API_KEY
The same token is also valid on the CRM API v3 and the SMS API — authenticate once, call any API. Tokens are valid for 30 days by default; use grant_type=refresh_token to renew without re-authenticating. Not scoped — prefer OAuth 2.0 for installable integrations.
Two credentials:
| Parameter | Type | Description |
|---|---|---|
IdClient | integer | Your numeric Mailpro client ID, found in Settings → API access |
ApiKey | string | Your API key, found in Settings → API access |
Send them in either of these places — pick one:
IdClient: 12345 and ApiKey: a1b2c3d4-…. URLs are logged by web servers, CDNs and proxies, so a key in the query string can leak into access logs. Headers are not logged.?IdClient=12345&ApiKey=…. Kept for backward compatibility with existing integrations. Switch to headers as soon as you can.IdClient=12345&ApiKey=… in application/x-www-form-urlencoded.If you provide credentials in more than one place, the header wins.
For OAuth-issued tokens, each endpoint group requires a scope (legacy auth is exempt). Each operation lists its scope under Authorization. Summary:
| Endpoints | Required scope |
|---|---|
/send/* (campaigns, transactional, scheduling, test — except /send/{IdSend}/status) | email:send |
/stats/* (except /stats/del), /history/list, /send/{IdSend}/status | email:read |
/message/*, /senderEmail/* (read) | email:templates:read |
/message/*, /senderEmail/* (add/edit/del), /stats/del | email:templates:write |
/list/list, /email/list | contacts:read |
/list/{add,edit,del}, /email/{add,addSingle,edit,del}, /import/upload | contacts:write |
/account/* | account:read |
Scope rejection returns 403 with { "error": "insufficient_scope", "required_scope": "…" }.
All endpoints support json (default) and xml. Specify the format in the URL:
/email/list.json/email/list.xmlSame rule on the three Mailpro APIs (Email v2, CRM v3, SMS): 5,000 items per page, by default and at most.
numberOfRecords — items per page. Omitted, 0, or above 5,000 = 5,000.pageNumber — 1-based page number (default 1).pageNumber until a page comes back with fewer items.bodyHTML=true) return 10 per page./email/list adds X-Total-Count (the real total) and X-Truncated: true when contacts remain beyond the page.Unknown parameters are ignored, never rejected. When a request carries a parameter the API does not recognise, the response includes X-Ignored-Parameters (the names) and X-Documentation (a link to this page), so a typo does not go unnoticed.
| HTTP | Meaning |
|---|---|
| 400 | Bad request — missing or invalid required parameter |
| 401 | Unauthorized — invalid IdClient or ApiKey |
| 403 | Forbidden — account limit reached (e.g. max lists) |
| 404 | Not found — the requested resource does not exist |
| 500 | Internal server error |
Errors from business rules:
"Not enough credit available." → 403 — your account has no remaining email credits"Email address unsubscribed." → 400 — the recipient has unsubscribedEvery single email you send (POST /send/sendmail, the CRM API v3, the SMTP relay or an automation) gets an identifier. It can be followed until the recipient's server accepts or rejects it. Campaigns are not covered here: use the statistics instead.
POST /send/sendmail.{format} contains the identifier of the send.email.delivered and email.bounced webhook events push the final status to your server as soon as it is known, with no polling at all.GET /send/{IdSend}/status.{format} returns the current status (table below).status | Meaning | Final? |
|---|---|---|
queued | Accepted, waiting to be sent. | no |
scheduled | Planned for later (DatePlanned). | no |
sent | Handed over by our servers, no answer from the recipient server yet. | no |
delivered | Accepted by the recipient server (DiagCode = its SMTP reply). | yes |
bounced | Rejected. BounceType: hard = invalid address or permanent refusal; soft = refused after retries (mailbox full, server unreachable…). | yes |
not_found | Unknown id, or an id that does not belong to your account. | yes |
Polling without looping
delivered appears about 5 minutes after the handover.Retry-After header (the number of seconds to wait), and the call does not count against your quota.scheduled: no need to poll before DatePlanned.Statuses are kept for 365 days.
Mailpro™ can notify your server in real time when certain events occur on your account (an email is delivered, a campaign finishes, a contact bounces, etc.). Stripe-style — your URL receives an HMAC-SHA256 signed POST as soon as the event fires.
Each event sent to your webhook counts as 1 request against your API request quota.
POST /webhooks. The response contains a secret shown once (Stripe / GitHub style) — store it.secret (snippets below).> ⏱ Activation delay — after you create, update or delete a webhook, the > change takes up to 10 minutes to propagate across the Mailpro API > servers. During that window events may not yet match your new subscription > (or may still match a just-removed one). This is the time required to > refresh the in-memory subscription cache on every API instance.
All events share a common envelope:
{
"id": "evt_8f3a9b2c4d1e5f6789abcdef01234567",
"type": "email.delivered",
"created": 1748194800,
"apiVersion": "v2",
"account": { "id": 12345, "name": "ACME Corp" },
"data": { "messageId": "...", "email": "[email protected]", "...": "..." }
}
| Field | Format | Description |
|---|---|---|
id | evt_ + 32 hex | Unique event ID — use as an idempotency key on your side. |
type | <domain>.<verb> | Canonical event type. |
created | Unix seconds UTC | Included in the HMAC signature. |
apiVersion | "v2" | Which Mailpro API published the event. |
account.id | int | Your Mailpro client ID (= IdClient). |
account.name | string | Your account name (handy for client-side debug). |
data | object | Event payload — shape depends on type (see catalog below). |
| Event | Trigger | data shape |
|---|---|---|
ping | Test via POST /webhooks/{id}/test | { message } |
email.processed | Send accepted by pipeline | { messageId, email, campaignId, processedAt } |
email.delivered | A single email was accepted by the recipient server — API v2/v3, SMTP or Automation (not campaigns). Emitted from the PowerMTA delivery feed. | { messageId, email, idSending, sendingType, diagCode, deliveredAt } |
email.opened | Tracking pixel hit | { messageId, email, campaignId, openedAt, ip, userAgent } |
email.clicked | Click on tracked link | { messageId, email, campaignId, clickedAt, url, ip, userAgent } |
email.bounced | Hard or soft bounce | { messageId, email, campaignId, bouncedAt, bounceType, reason } |
email.spam_report | FBL complaint | { messageId, email, campaignId, reportedAt } |
email.unsubscribed | Opt-out link clicked | { messageId, email, campaignId, unsubscribedAt } |
email.dropped | Refused before send | { messageId, email, campaignId, droppedAt, reason } |
campaign.started | Dispatch begins | { campaignId, name, totalRecipients, startedAt } |
campaign.finished | All recipients processed | { campaignId, name, totalSent, totalFailed, finishedAt } |
credits.low | Email credit balance under your alert threshold | { remaining, threshold } |
Subscribe to all events of this API by passing ["*"] in the events array.
Every POST carries an X-Mailpro-Signature header:
X-Mailpro-Signature: t=1748194800,v1=7f9ac2b6e4d1c8a3...
X-Mailpro-Event-Id: evt_8f3a9b2c4d1e5f6789abcdef01234567
X-Mailpro-Event-Type: email.delivered
X-Mailpro-Attempt: 1
To verify:
t and v1 from the header.|now - t| > 300 (5-minute replay protection window).expected = hex(HMAC_SHA256(secret, t + "." + raw_body)).v1 with expected (crypto.timingSafeEqual, hash_equals, CryptographicOperations.FixedTimeEquals) — never ==.const crypto = require('crypto');
function verifyMailproSignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
if (Math.abs(Date.now() / 1000 - parseInt(parts.t)) > 300) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(parts.t + '.' + rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(parts.v1, 'hex'),
Buffer.from(expected, 'hex'));
}
function verifyMailproSignature(string $rawBody, string $header, string $secret): bool {
parse_str(str_replace(',', '&', $header), $p);
if (abs(time() - (int)$p['t']) > 300) return false;
$expected = hash_hmac('sha256', $p['t'] . '.' . $rawBody, $secret);
return hash_equals($expected, $p['v1']);
}
import hmac, hashlib, time
def verify_mailpro_signature(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split('=') for p in header.split(','))
t = int(parts['t'])
if abs(time.time() - t) > 300:
return False
expected = hmac.new(
secret.encode(),
f"{t}.".encode() + raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, parts['v1'])
using System.Security.Cryptography;
using System.Text;
static bool VerifyMailproSignature(string rawBody, string header, string secret) {
var parts = header.Split(',')
.Select(p => p.Split('='))
.ToDictionary(a => a[0], a => a[1]);
var t = long.Parse(parts["t"]);
if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - t) > 300) return false;
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes($"{t}.{rawBody}"));
var expected = Convert.ToHexString(hash).ToLowerInvariant();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expected),
Encoding.UTF8.GetBytes(parts["v1"]));
}
| Attempt | Delay before next try |
|---|---|
| 1 → 2 | 10 seconds |
| 2 → 3 | 1 minute |
| 3 → 4 | 5 minutes |
| 4 → 5 | 30 minutes |
| 5 → 6 | 2 hours |
| 6 → 7 | 6 hours |
| 7 | abandoned (delivery marked failed) |
After 10 consecutive failures on the same webhook, it is automatically disabled (isActive: false) and you receive a notification email. Re-enable it with PUT /webhooks/{id} once your endpoint is fixed.
We treat your webhook as successful only if you return a status in 200–299. Any 3xx/4xx/5xx or a timeout (> 10 s) counts as a failure and triggers retries. If your endpoint is permanently dead, return 410 Gone explicitly — retries still happen but the failure reason is clearer in your delivery log.
| Limit | Value |
|---|---|
| Webhooks per account | 20 |
| Subscribed events per webhook | 50 |
| Max payload size | 256 KB |
| HTTP timeout per attempt | 10 seconds |
| Delivery history retention | 30 days |
Obtain a Bearer JWT token to authenticate all other API calls. The v2 /token endpoint is a 307 redirect to the canonical /v3/token issuer — your HTTP client follows the redirect transparently (POST method and body are preserved).
/tokenGet access token (307 redirect to /v3/token)
Obtains a Bearer JWT token using Mailpro client credentials. Pass the returned access_token as Authorization: Bearer <token> in all subsequent requests. The same token is also valid on the CRM API v3 and the SMS API.
> ⚙ Implementation detail — POST /v2/token is a 307 Temporary > Redirect to POST /v3/token (configured at the IIS web.config > rewrite layer). HTTP 307 preserves both the method (POST) and > the request body, so any standards-compliant HTTP client follows > the redirect transparently — you keep posting to /v2/token and get > a valid JWT back. There is no duplicated token-issuance logic; the > Email v2 API never issues tokens itself.
| Grant type | Use case |
|---|---|
password | Standard integration — username + API key |
refresh_token | Renew an expiring access token without re-authenticating |
client_credentials | Server-to-server with client ID only |
Use grant_type=refresh_token to renew the access token without re-authenticating. Store tokens server-side (Vault, env vars); never embed in browser JS or mobile app binaries. See the CRM API v3 /token doc for the full security note.
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
grant_type | string: password, refresh_token, client_credentials | yes | |
username | string | Your Mailpro username (email). Required for password grant. | |
password | string | Your Mailpro API key (GUID format). Required for password grant. | |
refresh_token | string | Previous refresh token. Required for refresh_token grant. | |
client_id | string | Your Mailpro client ID (integer). Required for client_credentials grant. |
Responses
| Code | Description |
|---|---|
200 | Token issued successfully. Returned by POST /v3/token after the
transparent 307 follow — your HTTP client sees a normal 200 with
the same body shape it would receive directly from the v3 token
endpoint.
|
307 | Temporary Redirect to POST /v3/token, issued by the IIS rewrite
rule in web.config. Standards-compliant HTTP clients follow this
automatically and preserve the original POST method + form body.
Visible only to clients that explicitly disable redirect following
(e.g. curl --max-redirs 0).
|
400 | Invalid credentials or unsupported grant type |
Example 200
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 31535999,
"userName": "12345",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"application": null
}
Example 400
{
"error": "invalid_grant",
"error_description": "The username or password is incorrect."
}
Check your email credit balance, monthly API quota, and subscription status.
/account/credits.{format}Get email credit balance
Returns the remaining email credit balance for your Mailpro account. Each email sent consumes one credit. Credits do not expire with the subscription, but are only usable while the subscription is active.
OAuth scope : account:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Responses
| Code | Description |
|---|---|
200 | Credit balance returned successfully |
401 | Invalid credentials |
Example 200
{
"Credits": 48500
}
/account/subscriptionEndDate.{format}Get subscription end date
Returns the expiry date of the current Mailpro subscription. After the expiry date, sends are blocked until the subscription is renewed.
OAuth scope : account:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Responses
| Code | Description |
|---|---|
200 | Subscription end date returned successfully |
401 | Invalid credentials |
Example 200
{
"SubscriptionEndDate": "2027-01-15T00:00:00"
}
/account/limits.{format}Get monthly API quota
Returns the monthly API request quota for the authenticated client.
Quota calculation: the limit is calibrated from the email subscription credits (typically subscription_credits × 5). For a 10,000-email/month plan, this yields a 50,000-request monthly budget. Reset on the subscription anniversary date (not the 1st of the calendar month).
What consumes credits:
v2, v3 or sms endpoints.What is FREE (does not consume credits):
/health endpoints.401, 403 or 429.The quota is shared across all 3 Mailpro APIs (v2, v3, sms) — one client = one monthly budget across the whole platform.
OAuth scope : account:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Responses
| Code | Description |
|---|---|
200 | Quota snapshot |
401 | Invalid credentials |
404 | No quota record found (newly created account; row will be created on the first authenticated request). |
Example 200
{
"MonthlyLimit": 50000,
"Remaining": 32847,
"Used": 17153,
"IsFree": false,
"RemainedSendsForFree": 0
}
Manage lists (contact lists). A list groups contacts for bulk campaigns. Maximum 1,000 lists per account (same limit on the three Mailpro APIs and in the app). Type 0 = standard list. Type 1 = blacklist (used for unsubscribe management).
/list/list.{format}List all lists
Returns all lists for the account, ordered by creation date (newest first) unless sortByName=true is set. Use type=0 for regular lists or type=1 for blacklists.
> Legacy alias: /addressbook/list.{format} — same endpoint, kept for backward > compatibility. New integrations should use /list/*.
OAuth scope : contacts:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
titleFilter | query | string | Filter by title (partial, case-insensitive match) | |
type | query | integer: 0, 1 | 0 = standard lists only, 1 = blacklists only. Omit to return all. | |
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. | |
sortByName | query | boolean | Set to true to sort results alphabetically by title |
Responses
| Code | Description |
|---|---|
200 | Lists returned successfully.
> The response also contains a legacy duplicate field AddressBookList (identical content)
> and each item has a legacy duplicate AddressBookID (identical to ListID). New
> integrations should ignore them.
|
401 | Invalid credentials |
Example 200
{
"Lists": [
{
"ListID": 101,
"Title": "Newsletter — France",
"Type": 0,
"NumberEmails": 4521,
"CreationDate": "2025-01-15T10:30:00"
},
{
"ListID": 102,
"Title": "Unsubscribers",
"Type": 1,
"NumberEmails": 312,
"CreationDate": "2025-01-15T10:31:00"
}
]
}
/list/add.{format}Create a list
Creates a new list (contact list). The list starts empty — add contacts with /email/add or /email/addSingle. Maximum 1,000 lists per account (same limit on the three Mailpro APIs and in the app). Returns HTTP 422 if the limit is reached.
> Legacy alias: /addressbook/add.{format} — same endpoint, kept for backward compatibility.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
Title | string | yes | Display name for the new list |
Type | integer: 0, 1 | 0 = standard list (default), 1 = blacklist |
Responses
| Code | Description |
|---|---|
200 | List created successfully.
> A legacy duplicate field AddressBookList is also returned (identical content).
|
400 | Missing Title |
401 | Invalid credentials |
422 | List limit (1,000) reached |
Example 200
{
"Lists": [
{
"ListID": 103,
"Title": "Spring Campaign 2026",
"Type": 0,
"NumberEmails": 0,
"CreationDate": "2026-04-14T12:00:00"
}
]
}
/list/edit.{format}Rename a list
Updates the title of an existing list. The contacts inside are not affected.
> Legacy alias: /addressbook/edit.{format} — same endpoint, kept for backward compatibility. > The AddressBookID parameter name is also accepted as a legacy alias for ListID.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
ListID | integer | yes | ID of the list to rename (from /list/list). Legacy alias AddressBookID is also accepted. |
Title | string | yes | New title for the list |
Responses
| Code | Description |
|---|---|
200 | List renamed successfully |
400 | Missing ListID or Title |
401 | Invalid credentials |
404 | List not found |
/list/del.{format}Delete a list
Permanently deletes a list and all the contacts it contains. This action cannot be undone.
> Legacy alias: /addressbook/del.{format} — same endpoint, kept for backward compatibility. > The AddressBookID parameter name is also accepted as a legacy alias for ListID.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
ListID | integer | yes | ID of the list to delete. Legacy alias AddressBookID is also accepted. |
Responses
| Code | Description |
|---|---|
200 | List deleted successfully |
400 | Missing ListID |
401 | Invalid credentials |
404 | List not found |
Example 200
{
"Result": "OK"
}
Manage individual email contacts. Contacts belong to one or more lists. Use bulk import (/email/add) for large batches, or single-contact add (/email/addSingle) for individual sign-ups with custom field values.
/email/list.{format}List contacts
Returns a paginated list of email contacts for the account. Filter by list or by email pattern. Results are ordered alphabetically when sortByName=true, otherwise by insertion order.
OAuth scope : contacts:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
emailFilter | query | string | Filter by email address (partial, case-insensitive match) | |
listId | query | integer | Return only contacts in this list. Legacy alias addressBookId is also accepted. | |
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. | |
sortByName | query | boolean | Set to true to sort results alphabetically by email address |
Responses
| Code | Description |
|---|---|
200 | Contact list returned successfully |
401 | Invalid credentials |
Example 200
{
"Emails": [
{
"IdEmail": 10001,
"Email": "[email protected]",
"Field1": "Alice",
"Field2": "Dupont"
}
]
}
/email/add.{format}Import contacts (bulk)
Imports one or more email addresses into a list in a single request. Provide addresses in EmailList separated by semicolons.
Example: [email protected];[email protected];[email protected]
This endpoint only imports email addresses. To also set custom field values, use /email/addSingle for each contact instead.
Previously unsubscribed addresses are silently skipped unless Force=1 is set.
List selection: provide ListId (or its legacy alias AddressBookId). At least one is required.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
ListId | integer | ID of the destination list. Legacy alias AddressBookId is also accepted. | |
EmailList | string | yes | Semicolon-separated email addresses to import |
Force | integer: 0, 1 | Set to 1 to re-import addresses that were previously unsubscribed.
Default: 0 (unsubscribed addresses are skipped).
|
Responses
| Code | Description |
|---|---|
200 | Contacts imported successfully |
400 | Missing EmailList |
401 | Invalid credentials |
Example 200
{
"Result": "3 contacts imported"
}
/email/addSingle.{format}Add a single contact
Adds one contact to a list with optional custom field values.
Field1 through Field25 map to the custom column definitions of your list. The mapping depends on the order columns were defined — check your list settings in the Mailpro interface to see which field number corresponds to which column (e.g. Field1 = FirstName, Field2 = LastName, Field3 = City…).
List selection: provide ListId (or its legacy alias AddressBookId).
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
ListId | integer | ID of the destination list. Legacy alias AddressBookId is also accepted. | |
Email | string | yes | Email address of the new contact |
Force | integer: 0, 1 | Set to 1 to re-add a previously unsubscribed address | |
Field1 | string | Value for custom field 1 (column mapping depends on your list) | |
Field2 | string | Value for custom field 2 | |
Field3 | string | Value for custom field 3 | |
Field4 | string | Value for custom field 4 | |
Field5 | string | Value for custom field 5 |
Responses
| Code | Description |
|---|---|
200 | Contact added successfully |
400 | Missing Email |
401 | Invalid credentials |
Example 200
{
"EmailId": 10002,
"Email": "[email protected]",
"Field1": "Alice",
"Field2": "Dupont",
"ListID": 101
}
/email/edit.{format}Update a contact
Updates the email address and/or custom field values of an existing contact. Identify the contact by EmailId (from /email/list). Both EmailId and Email are required.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
EmailId | integer | yes | ID of the contact to update (from /email/list) |
Email | string | yes | New email address for the contact |
Field1 | string | New value for custom field 1 | |
Field2 | string | New value for custom field 2 | |
Field3 | string | New value for custom field 3 |
Responses
| Code | Description |
|---|---|
200 | Contact updated successfully |
400 | Missing EmailId or Email |
401 | Invalid credentials |
404 | Contact not found |
/email/del.{format}Delete a contact
Permanently deletes a contact by its unique identifier.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
EmailId | integer | yes | ID of the contact to delete (from /email/list) |
Responses
| Code | Description |
|---|---|
200 | Contact deleted successfully |
400 | Missing EmailId |
401 | Invalid credentials |
404 | Contact not found |
Manage HTML email templates. A message template stores your email subject, HTML body, and unsubscribe link configuration. Templates are reused across campaigns.
/message/list.{format}List message templates
Returns saved HTML email templates for the account. By default, the HTML body is not included in the response to keep payloads small. Set bodyHTML=true to include it (automatically limits results to 10 per page).
OAuth scope : email:templates:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
subjectFilter | query | string | Filter by subject (partial, case-insensitive match) | |
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. | |
bodyHTML | query | boolean | Set to true to include the full HTML body in each result.
When true, results are automatically limited to 10 per page regardless of numberOfRecords.
|
Responses
| Code | Description |
|---|---|
200 | Message templates returned successfully |
401 | Invalid credentials |
Example 200
{
"MessageList": [
{
"IDMessage": 501,
"Subject": "Spring Newsletter — April 2026",
"Language": "EN",
"LinkAlign": "center",
"LinkUp": 0,
"UpdateDate": "2026-03-10T14:22:00"
}
]
}
/message/add.{format}Create a message template
Creates a new HTML email template. The template stores the subject, HTML body, and unsubscribe link settings. Once created, use the returned IDMessage when launching a campaign.
OAuth scope : email:templates:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
Subject | string | yes | Email subject line (max 255 characters) |
BodyHTML | string | yes | Full HTML content of the email body |
Language | string | yes | Language code for unsubscribe link localization (e.g. EN, FR, DE) |
LinkAlign | string: left, center, right | yes | Horizontal alignment of the unsubscribe link |
LinkUp | integer: 0, 1 | yes | Position of the unsubscribe link — 0 = bottom of email, 1 = top |
Responses
| Code | Description |
|---|---|
200 | Message template created successfully |
400 | Missing required field |
401 | Invalid credentials |
Example 200
{
"IDMessage": 502,
"Subject": "Spring Newsletter — April 2026",
"Language": "EN",
"LinkAlign": "center",
"LinkUp": 0
}
/message/edit.{format}Update a message template
Updates the content and metadata of an existing message template.
OAuth scope : email:templates:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
MessageId | integer | yes | ID of the message template to update (from /message/list) |
Subject | string | yes | New email subject line |
BodyHTML | string | yes | New HTML content of the email body |
Language | string | yes | Language code for unsubscribe link localization |
LinkAlign | string: left, center, right | yes | |
LinkUp | integer: 0, 1 | yes |
Responses
| Code | Description |
|---|---|
200 | Message template updated successfully |
400 | Missing required field |
401 | Invalid credentials |
404 | Message template not found |
/message/del.{format}Delete a message template
Permanently deletes a message template by its unique identifier. Templates used by pending scheduled campaigns cannot be deleted.
OAuth scope : email:templates:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
MessageId | integer | yes | ID of the message template to delete (from /message/list) |
Responses
| Code | Description |
|---|---|
200 | Message template deleted successfully |
400 | Missing MessageId |
401 | Invalid credentials |
404 | Message template not found |
Send bulk email campaigns to a list and manage scheduled sends. A campaign uses a message template and a sender address.
/send/list.{format}List scheduled campaigns
Returns the list of scheduled and recently processed email campaigns. Results are ordered by planned send date (most recent first).
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
subjectMessageFilter | query | string | Filter by campaign subject (partial, case-insensitive match) | |
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. |
Responses
| Code | Description |
|---|---|
200 | Campaign list returned successfully |
401 | Invalid credentials |
Example 200
{
"sends": [
{
"IDSend": 3001,
"ListID": 101,
"SenderEmail": "[email protected]",
"SenderName": "Your Company Newsletter",
"DatePlanned": "2026-04-20T09:00:00",
"SubjectMessage": "Spring Newsletter — April 2026",
"NumberCredit": 4521
}
]
}
/send/campaign.{format}Launch a campaign
Schedules or immediately sends an email campaign to a list using a saved message template and a sender address.
Required:
IDMessage — the template to send (from /message/list)IDEmailExp — the sender address to use (from /senderEmail/list)ListId — the recipient list (from /list/list). Legacy alias IDAddressBook is also accepted.Scheduling: set DatePlanned to a future ISO 8601 date/time to schedule the send. Omit DatePlanned to send immediately.
Statistics tracking: set Campaign=1 and provide a TitleCampaign to enable open/click/bounce tracking in the Statistics section.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
ListId | integer | ID of the recipient list (from /list/list). Legacy alias IDAddressBook is also accepted. | |
IDMessage | integer | yes | ID of the message template to send (from /message/list) |
IDEmailExp | integer | yes | ID of the sender email address to use (from /senderEmail/list) |
IDUnsubscribeList | integer | List used to collect unsubscribed contacts (blacklist).
Legacy alias IDUnsubscribeAddressBook is also accepted.
| |
DatePlanned | string (date-time) | Scheduled send date/time (ISO 8601). Omit to send immediately. | |
Campaign | integer: 0, 1 | Set to 1 to track this send as a named campaign in statistics | |
TitleCampaign | string | Campaign title shown in statistics (required when Campaign=1) |
Responses
| Code | Description |
|---|---|
200 | Campaign scheduled or sent successfully.
> The response also returns legacy aliases AddressBookID and UnsubscribeAddressBookID
> (identical to ListID / UnsubscribeListID). New integrations should ignore them.
|
400 | Missing IDMessage or IDEmailExp |
401 | Invalid credentials |
403 | Not enough email credits |
404 | List, message, or sender not found |
Example 200
{
"send": {
"IDSend": 3002,
"ListID": 101,
"UnsubscribeListID": 102,
"IDCampaign": 8001,
"SenderName": "Your Company Newsletter",
"SenderEmail": "[email protected]",
"DatePlanned": "2026-04-20T09:00:00",
"SubjectMessage": "Spring Newsletter — April 2026",
"NumberCredit": 4521
}
}
/send/del.{format}Cancel or delete a campaign
Cancels a pending scheduled campaign send, or deletes a completed one. Only future-scheduled sends can be cancelled — sends in progress cannot be stopped.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
IdSend | integer | yes | ID of the campaign send to cancel or delete (from /send/list) |
Responses
| Code | Description |
|---|---|
200 | Campaign cancelled or deleted successfully |
400 | Missing IdSend |
401 | Invalid credentials |
404 | Campaign not found |
Example 200
{
"Result": "Campaign Deleted"
}
/send/scheduled.{format}List my scheduled email campaigns
Returns the email campaigns and single sends scheduled for a future date that have not yet been dispatched. Useful before calling /cancel or /reschedule. Records are sorted by DatePlanned ascending.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. |
Responses
| Code | Description |
|---|---|
200 | List of scheduled campaigns / sends.
> Each record also includes legacy aliases AddressBookID and
> UnsubscribeAddressBookID (identical to ListID / UnsubscribeListID).
|
401 | Invalid credentials |
Example 200
{
"sends": [
{
"IDSend": 5001,
"ListID": 101,
"UnsubscribeListID": 102,
"IDCampaign": 8001,
"SenderName": "Your Company Newsletter",
"SenderEmail": "[email protected]",
"DatePlanned": "2026-05-01T09:00:00",
"SubjectMessage": "Spring Sale 2026",
"NumberCredit": 4521
}
]
}
/send/{IdSend}/cancel.{format}Cancel a scheduled campaign / send
Cancels a scheduled email send (campaign or single) that has not yet been dispatched. If the send has already gone out (or doesn't exist / belongs to another client), returns 409 Conflict.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
IdSend | path | integer | yes |
Responses
| Code | Description |
|---|---|
200 | Send cancelled |
401 | Invalid credentials |
409 | Already dispatched or not found |
Example 200
{
"Result": "OK",
"Cancelled": 5001
}
/send/{IdSend}/reschedule.{format}Reschedule a scheduled campaign / send
Updates the DatePlanned of a scheduled email send. New date must be in the future and within 90 days.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
IdSend | path | integer | yes |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
PlanDate | string (date-time) | yes |
Responses
| Code | Description |
|---|---|
200 | Send rescheduled |
400 | Missing or invalid PlanDate |
401 | Invalid credentials |
409 | Already dispatched, not found, or new date out of range (must be future and within 90 days) |
Example 200
{
"Result": "OK",
"IdSend": 5001,
"NewPlanDate": "2026-12-31T14:30:00"
}
/send/sendingTest.{format}Send a test email
Sends a preview of a message template to a specified recipient for review before launching the real campaign. The sender, template, and test recipient are all identified by their IDs.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
MessageId | integer | yes | ID of the message template to preview (from /message/list) |
IDEmailExpFrom | integer | yes | ID of the sender address to use (from /senderEmail/list) |
IDEmailExpTo | integer | yes | ID of the recipient sender address — the test email is sent to this address |
Responses
| Code | Description |
|---|---|
200 | Test email sent successfully |
400 | Missing required field |
401 | Invalid credentials |
Example 200
{
"SendingTest": {
"IdSendingTest": 7001,
"ToEmail": "[email protected]",
"FromEmail": "[email protected]",
"SenderName": "Your Company Newsletter"
}
}
Send one-off transactional emails to individual recipients. Unlike campaigns, transactional emails do not require a list — you pass the recipient email and HTML body directly.
/send/mail/scheduled.{format}List scheduled single emails
Returns the single emails sent with /send/sendmail and a future date that have not left yet. Campaigns are listed by /send/scheduled.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Responses
| Code | Description |
|---|---|
200 | Scheduled emails returned |
401 | Invalid credentials |
Example 200
{
"scheduledMails": [
{
"IdSingleSend": 880123,
"Email": "[email protected]",
"Subject": "Your invoice",
"DatePlanned": "2026-12-01T09:00:00"
}
]
}
/send/mail/{IdSingleSend}/cancel.{format}Cancel a scheduled single email
Cancels the email as long as it has not left.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
IdSingleSend | path | integer (int64) | yes | IdSingleSend returned by /send/sendmail |
Responses
| Code | Description |
|---|---|
200 | Email cancelled |
401 | Invalid credentials |
409 | Send not found, or already dispatched |
Example 200
{
"Result": "OK",
"Cancelled": 880123
}
/send/mail/{IdSingleSend}/reschedule.{format}Reschedule a scheduled single email
Changes the send date. The new date must be in the future and within 90 days. PATCH is also accepted.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
IdSingleSend | path | integer (int64) | yes | IdSingleSend returned by /send/sendmail |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
PlanDate | string (date-time) | yes | New send date (ISO 8601) |
Responses
| Code | Description |
|---|---|
200 | Email rescheduled |
400 | Missing or invalid date |
401 | Invalid credentials |
409 | Send not found, already dispatched, or date out of range |
Example 200
{
"Result": "OK",
"IdSingleSend": 880123,
"NewPlanDate": "2026-12-15T09:00:00"
}
/send/sendmail.{format}Send a transactional email
Sends a one-off transactional email to one or more recipients. Unlike campaigns, this endpoint does not require a list — the recipient address and HTML body are passed directly.
Required:
IDEmailExp — the sender address ID (from /senderEmail/list)EmailData — recipient in email,field1,field2,...,field25 format (comma-separated)BodyHTML — the HTML bodySubject — the subject lineEmailData format: [email protected],Alice,Dupont,Paris The first value is the email address; values 2–26 map to Field1–Field25 for personalization variables in your HTML template.
Multiple recipients: separate multiple EmailData entries with semicolons: [email protected],Alice;[email protected],Bob
Scheduling: set DatePlanned to a future ISO 8601 date/time, or omit to send immediately.
OAuth scope : email:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (multipart/form-data)
| Name | Type | Required | Description |
|---|---|---|---|
IDEmailExp | integer | yes | ID of the sender email address to use (from /senderEmail/list) |
EmailData | string | yes | Recipient in email,field1,field2,...,field25 format (comma-separated).
The first value is the email address; remaining values are personalization
fields mapped to Field1–Field25 in your HTML template.
Local part of the email (before @) accepts letters, digits and
. _ - + — including RFC 5322 sub-addressing (user+tag@domain,
e.g. Gmail, Fastmail, Proton). Other special characters RFC 5322
permits (e.g. ! # $ % & ' * / = ? ^ { | } ~) and quoted local
parts are not accepted.
|
BodyHTML | string | yes | Full HTML content of the email body |
Subject | string | yes | Email subject line (max 255 characters) |
attachments | array of string (binary) | Optional file attachments — send the request as multipart/form-data and add one
file part per attachment (any field name). Up to 5 files, 10 MB per file,
15 MB total. Accepted types: PDF, Word, Excel, PowerPoint, images (JPG, PNG, GIF,
WebP), ZIP, CSV, TXT — executable and script files are rejected. Billing: the send keeps its usual 1 credit, plus 1 credit per 512 KB of
attachments beyond the first 512 KB (attachments up to ~512 KB add nothing). Every other field of
this endpoint (ReplyTo, ActivateStatistics, CustomHeaders, …) works exactly as
in the form-urlencoded variant.
| |
KeepInMyFiles | boolean | false (default): the attachments are used for this send only — they are stored outside
your account, do not count toward your storage quota and are deleted automatically after
35 days. true: the attachments are also kept in your account's files (folder
Secure/Files/Sending/api) and count toward your storage quota.
|
Responses
| Code | Description |
|---|---|
200 | Transactional email sent or scheduled successfully |
400 | Missing required field or invalid email format |
401 | Invalid credentials |
403 | Not enough email credits |
404 | Sender not found or email address unsubscribed |
Example 200
{
"SingleSend": {
"IdSingleSend": 8001,
"IdEmailExp": 201,
"DatePlanned": "2026-04-15T14:30:00",
"Subject": "Your order #12345 is confirmed",
"Email": "[email protected]",
"Field1": "Alice",
"Field2": "Dupont",
"UnsubscriptionLink": false,
"ActivateStatistics": true
}
}
/send/{IdSend}/status.{format}Get delivery status of a transactional email
Returns the current delivery status of a transactional email sent with /send/sendmail. Pass the IdSingleSend that call returned.
Possible values: delivered (accepted by the recipient server, see DiagCode), bounced (rejected — BounceType: hard invalid address or permanent refusal, soft refused after retries), sent (handed to the mail server, no answer yet), scheduled (not sent yet, DatePlanned in the future) and queued (waiting to be dispatched). delivered and bounced are final: stop polling. Every email has a final status at most 48 hours after sending; poll every 15 minutes at most (each call counts against your API quota) or use the email.delivered / email.bounced webhooks. Only the client that sent the email can read its status — any other id returns 404.
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
IdSend | path | integer | yes | The IdSingleSend returned by /send/sendmail. |
Responses
| Code | Description |
|---|---|
200 | Current delivery status |
401 | Invalid credentials |
404 | No transactional send found with this id for this client |
Example 200
{
"status": {
"IdSingleSend": 8001,
"Email": "[email protected]",
"Status": "delivered",
"DatePlanned": "2026-04-15T14:30:00",
"StatusDate": "2026-04-15T14:30:07",
"DiagCode": "2.0.0 (success)",
"BounceType": null
}
}
List and manage validated sender email addresses. Only verified addresses can be used as the From address in campaigns.
/senderEmail/list.{format}List validated sender addresses
Returns the list of sender email addresses configured for the account. Only addresses with IsValid: true can be used when sending campaigns. New addresses are added with /senderEmail/add and become valid once the link in the validation email is clicked.
OAuth scope : email:templates:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
emailFilter | query | string | Filter by email address (partial, case-insensitive match) | |
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. | |
sortByName | query | boolean | Set to true to sort results by sender name alphabetically |
Responses
| Code | Description |
|---|---|
200 | Sender addresses returned successfully |
401 | Invalid credentials |
Example 200
{
"ExpEmailList": [
{
"IDEmailExp": 201,
"Email": "[email protected]",
"SenderName": "Your Company Newsletter",
"IsValid": true,
"CreationDate": "2025-06-01T09:00:00"
},
{
"IDEmailExp": 202,
"Email": "[email protected]",
"SenderName": "Your Company Support",
"IsValid": true,
"CreationDate": "2025-06-01T09:05:00"
}
]
}
/senderEmail/add.{format}Add a sender address
Adds the address with the same checks as the app and sends it a validation email. It can be used once the link in that email is clicked. An account can have up to 25 senders, validated or pending.
OAuth scope : email:templates:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
Email | string | yes | Sender address |
SenderName | string | yes | Name shown to recipients |
Responses
| Code | Description |
|---|---|
200 | Address added. DomainHasMx is false when the domain has no MX record. |
400 | Email or SenderName missing |
401 | Invalid credentials |
422 | Address refused: invalid, already among the senders of the account, public mail domain, forbidden name, or limit of 25 reached. The message gives the reason. |
503 | Temporarily unavailable, please try again |
Example 200
{
"Result": "OK",
"ConfirmationSent": true,
"DomainHasMx": true
}
/senderEmail/{IdSenderEmail}/resendValidation.{format}Send the validation email again
For an address still waiting for validation.
OAuth scope : email:templates:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
IdSenderEmail | path | integer | yes | ID of the sender, from /senderEmail/list |
Responses
| Code | Description |
|---|---|
200 | Email sent again |
401 | Invalid credentials |
404 | Sender not found |
409 | This sender is already validated |
503 | Temporarily unavailable, please try again |
Example 200
{
"Result": "OK",
"ConfirmationSent": true
}
/senderEmail/{IdSenderEmail}/authentication.{format}Check the domain of a sender (SPF, DKIM, DMARC, MX)
Checks the domain live in DNS and returns the records to publish. Read-only. DKIM is activated in the app: DkimSettingsUrl opens the right screen.
OAuth scope : email:templates:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
IdSenderEmail | path | integer | yes | ID of the sender, from /senderEmail/list |
Responses
| Code | Description |
|---|---|
200 | Status of the domain. Spf.RecommendedRecord is built on your current record: publish it only when Spf.ChangeNeeded is true. |
401 | Invalid credentials |
404 | Sender not found |
503 | Temporarily unavailable, please try again |
Example 200
{
"Domain": "example.com",
"Spf": {
"Valid": true,
"Record": "v=spf1 include:spf.mailpro.com -all",
"RecommendedRecord": "v=spf1 include:spf.mailpro.com -all",
"ChangeNeeded": false
},
"Dkim": {
"Valid": true,
"Records": [
{
"Host": "key._domainkey.example.com",
"Value": "v=DKIM1;k=rsa;p=MIIBIjANBgkq...",
"Validated": true
}
]
},
"Dmarc": {
"Valid": true,
"Record": "v=DMARC1; p=quarantine"
},
"Mx": {
"Valid": true,
"Record": "mail.example.com"
},
"DkimSettingsUrl": "https://app.mailpro.com/dkim/list?d=example.com"
}
/senderEmail/del.{format}Remove a sender address
Removes a validated sender email address from the account. Sender addresses used by scheduled campaigns cannot be deleted.
OAuth scope : email:templates:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
idEmailExp | integer | yes | ID of the sender address to remove (from /senderEmail/list) |
Responses
| Code | Description |
|---|---|
200 | Sender address removed successfully |
400 | Missing idEmailExp |
401 | Invalid credentials |
Example 200
{
"Result": "EmailSender Deleted"
}
Access per-campaign performance metrics: opens, clicks, bounces, and more. Detailed breakdowns by hour, day, country, OS, and email client are available.
/stats/list.{format}List campaign statistics
Returns per-campaign performance metrics: sent, opens, clicks, bounces (BadMail), unsubscribes, complaints, spam-trap hits and the deliverability score. Filter by date range or campaign title. Results are ordered by send date (most recent first).
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
titleCampaignFilter | query | string | Filter by campaign title (partial, case-insensitive match) | |
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. | |
dateStart | query | string (date-time) | Start of date range filter (ISO 8601) | |
dateEnd | query | string (date-time) | End of date range filter (ISO 8601) |
Responses
| Code | Description |
|---|---|
200 | Campaign statistics returned successfully |
401 | Invalid credentials |
Example 200
{
"Campaigns": [
{
"IdCampaign": 8001,
"DateCampaign": "2026-04-20T09:00:01",
"TitleCampaign": "Spring Newsletter April 2026",
"NumberEmailSend": 4521,
"NumberEmailOpen": 1823,
"NumberEmailClick": 342,
"BadMail": 12,
"NumberUnsubscribers": 8,
"NumberComplaints": 1,
"SpamTrap": 0,
"DeliverabilityScore": 98
}
]
}
/stats/byHour.{format}Statistics by hour
Returns the hourly open and click breakdown for a specific campaign. Useful for understanding the optimal send time.
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
idCampaign | query | integer | yes | ID of the campaign (from /stats/list) |
Responses
| Code | Description |
|---|---|
200 | Hourly statistics returned successfully |
401 | Invalid credentials |
Example 200
{
"ByHour": [
{
"Hour": 9,
"Opens": 823,
"Clicks": 142
},
{
"Hour": 10,
"Opens": 512,
"Clicks": 98
}
]
}
/stats/byDay.{format}Statistics by day
Returns the daily open and click breakdown for a specific campaign.
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
idCampaign | query | integer | yes | ID of the campaign (from /stats/list) |
Responses
| Code | Description |
|---|---|
200 | Daily statistics returned successfully |
401 | Invalid credentials |
/stats/del.{format}Delete campaign statistics
Permanently deletes all statistics records for a given campaign.
OAuth scope : email:templates:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
IdCampaign | integer | yes | ID of the campaign whose statistics to delete (from /stats/list) |
Responses
| Code | Description |
|---|---|
200 | Campaign statistics deleted successfully |
400 | Missing IdCampaign |
401 | Invalid credentials |
404 | Campaign not found |
Example 200
{
"Result": "Statistic Deleted"
}
/stats/country.{format}Geolocation breakdown
Returns the geolocation breakdown of campaign opens — how many opens came from each country.
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
idCampaign | query | integer | yes |
Responses
| Code | Description |
|---|---|
200 | Country breakdown returned |
401 | Invalid credentials |
/stats/open.{format}Open events log
Returns the per-recipient log of open events for a campaign (one row per open). Use emailFilter to search for a specific recipient (partial, case-insensitive).
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
idCampaign | query | integer | yes | |
emailFilter | query | string | ||
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. |
Responses
| Code | Description |
|---|---|
200 | Open events returned |
401 | Invalid credentials |
/stats/click.{format}Click events log
Returns the per-recipient log of click events for a campaign — including which link was clicked and when.
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
idCampaign | query | integer | yes | |
emailFilter | query | string | ||
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. |
Responses
| Code | Description |
|---|---|
200 | Click events returned |
401 | Invalid credentials |
/stats/system.{format}Operating system breakdown
Returns the breakdown of opens by operating system (Windows, macOS, iOS, Android, Linux, etc.) for a given campaign.
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
idCampaign | query | integer | yes |
Responses
| Code | Description |
|---|---|
200 | OS breakdown returned |
401 | Invalid credentials |
/stats/emailSoftware.{format}Email client breakdown
Returns the breakdown of opens by email client (Gmail, Outlook, Apple Mail, Thunderbird, etc.) for a given campaign.
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
idCampaign | query | integer | yes |
Responses
| Code | Description |
|---|---|
200 | Email client breakdown returned |
401 | Invalid credentials |
/stats/bounce.{format}Bounce log
Returns the list of bounced recipients (hard bounces, soft bounces, spam complaints) for a campaign, with the failure reason for each address.
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
idCampaign | query | integer | yes | |
emailFilter | query | string | ||
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. |
Responses
| Code | Description |
|---|---|
200 | Bounce events returned |
401 | Invalid credentials |
Example 200
{
"ListBadMail": [
{
"Email": "[email protected]",
"Date": "2026-04-20T09:05:12",
"Error": "550 5.1.1 recipient rejected",
"AddressbookId": 1813318
}
]
}
Browse the complete log of all past email sends.
/history/list.{format}List sending history
Returns a paginated log of all past email sends. Filter by campaign ID or by subject. Set bodyHTML=true to include the HTML body (automatically limits to 10 per page).
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
subjectMessageFilter | query | string | Filter by campaign subject (partial, case-insensitive match) | |
idCampaign | query | integer | Return history for a specific campaign only | |
numberOfRecords | query | integer | Items per page — 5,000 by default and at most (see Pagination). | |
pageNumber | query | integer | Page number (1-based). Default is 1. | |
bodyHTML | query | boolean | The MessageBody field is returned by default. Set to false to omit it
— useful to cut the response size when you only need the metrics.
|
Responses
| Code | Description |
|---|---|
200 | History returned successfully |
401 | Invalid credentials |
Example 200
{
"HistoricList": [
{
"IDHistoric": 9001,
"SubjectMessage": "Spring Newsletter — April 2026",
"DateSend": "2026-04-20T09:00:01",
"NumberSend": 4521,
"NumberOpen": 1823,
"NumberClick": 342
}
]
}
Upload a CSV or XLSX file to import email contacts into a list in bulk. The file is queued for asynchronous processing — track completion via the ImportJobId returned or via the optional WebhookUrl callback.
/import/upload.{format}Upload a contacts file (CSV / XLSX) for bulk import
Upload a CSV or XLSX file to import email contacts into a list. The file is stored and queued for asynchronous processing — the endpoint returns an ImportJobId immediately.
Request type: multipart/form-data
Required form fields:
IdClient (integer) — your Mailpro client IDApiKey (string) — your API keyListId (integer) — target list ID. Legacy alias AddressBookId is also accepted.file (binary) — the CSV or XLSX file to uploadOptional:
WebhookUrl (string) — HTTPS URL called when processing finishes. Receives a JSON payload with the final status (success / failed) and counts of imported / skipped / invalid rows.File format:
Email — case-insensitive)OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (multipart/form-data)
| Name | Type | Required | Description |
|---|---|---|---|
ListId | integer | yes | ID of the destination list. Legacy alias AddressBookId is also accepted. |
file | string (binary) | yes | CSV or XLSX file (UTF-8, max 50 MB) |
WebhookUrl | string (uri) | Callback URL invoked when processing finishes |
Responses
| Code | Description |
|---|---|
200 | File accepted and queued |
400 | No file uploaded or missing ListId |
401 | Invalid credentials |
500 | Server or storage error |
Example 200
{
"Result": "OK",
"ImportJobId": 13,
"InternalName": "230575-20260420140221-contacts.csv"
}
Subscribe your server to real-time event notifications. Mailpro POSTs an HMAC-signed JSON payload to your URL as soon as the subscribed event happens. See the "Webhooks" section in the introduction for the full event catalog, payload envelope, signature verification snippets, and retry policy.
/webhooksList webhooks
Returns every webhook configured on the authenticated account.
Responses
| Code | Description |
|---|---|
200 | Webhooks of the account |
401 | Invalid credentials |
Example 200
{
"items": [
{
"id": 14,
"url": "https://example.com/hooks/mailpro",
"description": "Slack integration",
"is_active": true,
"created_at": "2026-07-01T21:09:52.43",
"updated_at": "2026-07-21T18:56:08.02",
"last_delivery_at": "2026-07-21T18:56:07.653",
"last_delivery_status": 200,
"consecutive_failures": 0,
"events": [
"email.delivered",
"email.bounced"
]
}
],
"total": 1
}
/webhooksCreate a webhook
Creates a new webhook subscription. The response contains a secret that is shown only once — store it immediately, server-side. Use it to verify the HMAC signature of every incoming payload.
Limits: max 20 webhooks per account, max 50 events per webhook.
Request body (application/json)
| Name | Type | Required | Description |
|---|---|---|---|
url | string (uri) | yes | HTTPS endpoint (HTTP refused in production) |
events | array of string | yes | List of event types to subscribe to (see catalog above). Use ["*"] to subscribe to all events of this API. |
description | string |
Example
{
"url": "https://example.com/hooks/mailpro",
"events": [
"email.delivered",
"email.bounced"
],
"description": "Slack integration"
}
Responses
| Code | Description |
|---|---|
201 | Webhook created — secret is returned only here |
400 | Invalid URL, events list, or limit exceeded |
401 | Invalid credentials |
Example 201
{
"id": 42,
"url": "https://example.com/hooks/mailpro",
"events": [
"email.delivered",
"email.bounced"
],
"description": "Slack integration",
"isActive": true,
"consecutiveFailures": 0,
"autoDisabledAt": null,
"autoDisabledReason": null,
"createdAt": "2026-05-25T10:00:00Z",
"updatedAt": "2026-05-25T10:00:00Z",
"secret": "whsec_AbcD1234EfgH5678IjkL9012MnoP3456QrsT"
}
/webhooks/{id}Get a webhook
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer (int64) | yes |
Responses
| Code | Description |
|---|---|
200 | The webhook |
404 | Webhook not found |
/webhooks/{id}Update a webhook
Partial update — only the sent fields are modified.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer (int64) | yes |
Request body (application/json)
| Name | Type | Required | Description |
|---|---|---|---|
url | string (uri) | ||
events | array of string | ||
description | string | ||
isActive | boolean | Set true to re-enable an auto-disabled webhook once your endpoint is fixed. |
Responses
| Code | Description |
|---|---|
200 | Updated webhook |
400 | Invalid payload |
404 | Webhook not found |
/webhooks/{id}Delete a webhook
Permanently deletes the webhook and its full delivery history.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer (int64) | yes |
Responses
| Code | Description |
|---|---|
204 | Deleted |
404 | Webhook not found |
/webhooks/{id}/testSend a test event
Queues an immediate ping event (or any other event type via the eventType body field) to your endpoint. Useful to verify reachability and signature verification before relying on real events.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer (int64) | yes |
Request body (application/json)
| Name | Type | Required | Description |
|---|---|---|---|
eventType | string |
Example
{
"eventType": "ping"
}
Responses
| Code | Description |
|---|---|
202 | Event queued for dispatch |
404 | Webhook not found |
Example 202
{
"deliveryId": 1234,
"eventId": "evt_8f3a9b2c4d1e5f6789abcdef01234567",
"eventType": "ping"
}
/webhooks/{id}/rotate-secretRotate the HMAC secret
Generates a new HMAC secret and invalidates the old one immediately. The new secret is returned once in the response — store it. Every delivery from this point on will be signed with the new secret.
Use this if you suspect your secret has been compromised.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer (int64) | yes |
Responses
| Code | Description |
|---|---|
200 | New secret (visible once) |
404 | Webhook not found |
Example 200
{
"secret": "whsec_XyzA9876BcdE5432FghI1098JklM7654NopQ"
}
/webhooks/{id}/deliveriesList delivery attempts
Returns the history of dispatch attempts for this webhook (30-day retention). Useful for debugging — which events were sent, status codes received, retry counts, error messages.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer (int64) | yes | |
limit | query | integer | ||
cursor | query | integer (int64) | Pagination cursor (= last delivery ID seen). Returns deliveries with id < cursor. |
Responses
| Code | Description |
|---|---|
200 | Array of delivery attempts (newest first) |
404 | Webhook not found |
/webhooks/{id}/deliveries/{deliveryId}/replayReplay a delivery
Queues a fresh delivery attempt with the same payload as the original. Useful to re-trigger a failed delivery after fixing your endpoint. A NEW Webhook_Delivery row is created (the original is preserved for audit). The replay goes through the standard retry policy (same backoff schedule, same 10-failure auto-disable rule).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer (int64) | yes | |
deliveryId | path | integer (int64) | yes |
Responses
| Code | Description |
|---|---|
202 | Replay queued |
404 | Webhook or delivery not found |
Bounces and unsubscribers of the account: list, add, remove.
/suppression/bounces.{format}List the bounces of the account
Most recent first. The total is in the X-Total-Count header.
OAuth scope : email:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
type | query | string: hard, soft | hard or soft | |
source | query | string | Where the bounce comes from: list, contact, api or smtp | |
email | query | string | Exact address | |
dateFrom | query | string (date-time) | From this date | |
dateTo | query | string (date-time) | Up to this date | |
numberOfRecords | query | integer | ||
pageNumber | query | integer |
Responses
| Code | Description |
|---|---|
200 | Bounces returned |
400 | type must be hard or soft |
401 | Invalid credentials |
Example 200
{
"bounces": [
{
"Id": 5521,
"Source": "contact",
"BounceType": "hard",
"Email": "[email protected]",
"Date": "2026-10-12T08:30:00",
"DiagCode": "550 5.1.1 User unknown",
"IdList": 101
}
]
}
/suppression/bounces/{Id}/del.{format}Remove a bounce
Source and Type are required, as returned by /suppression/bounces: an Id alone is not unique.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Id | path | integer (int64) | yes | Id of the bounce |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
Source | string | yes | list, contact, api or smtp |
Type | string: hard, soft | yes | hard or soft |
Responses
| Code | Description |
|---|---|
200 | Bounce removed |
400 | Source or Type missing |
401 | Invalid credentials |
404 | Bounce not found |
Example 200
{
"Result": "OK",
"Id": 5521,
"Restored": false
}
/suppression/bounces/{Id}/restore.{format}Put a contact with a hard bounce back in its lists
Only for a hard bounce with the source contact.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Id | path | integer (int64) | yes | Id of the bounce |
Responses
| Code | Description |
|---|---|
200 | Contact put back |
401 | Invalid credentials |
404 | Hard bounce of a contact not found, or address no longer active |
Example 200
{
"Result": "OK",
"Id": 5521,
"Restored": true
}
/suppression/unsubscribers.{format}List the unsubscribers of the account
Most recent first. The total is in the X-Total-Count header.
OAuth scope : contacts:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
source | query | string | Where the unsubscribe comes from: contact, list, smtp, api or api3 | |
email | query | string | Exact address | |
dateFrom | query | string (date-time) | From this date | |
dateTo | query | string (date-time) | Up to this date | |
numberOfRecords | query | integer | ||
pageNumber | query | integer |
Responses
| Code | Description |
|---|---|
200 | Unsubscribers returned |
401 | Invalid credentials |
Example 200
{
"unsubscribers": [
{
"Id": 9012,
"Source": "api",
"Email": "[email protected]",
"Date": "2026-10-20T16:05:00",
"ListName": null,
"Comment": "Asked by phone"
}
]
}
/suppression/unsubscribers/add.{format}Add an unsubscriber
Without ListId, the address is unsubscribed for the whole account. With it, from that unsubscribe list only.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
Email | string | yes | Address to unsubscribe |
Comment | string | Free comment | |
ListId | integer (int64) | Unsubscribe list of the account (optional) |
Responses
| Code | Description |
|---|---|
200 | Done. AlreadyUnsubscribed is true when the address already was. |
400 | A valid email is required |
401 | Invalid credentials |
404 | ListId is not an unsubscribe list of this account |
Example 200
{
"Result": "OK",
"AlreadyUnsubscribed": false
}
/suppression/unsubscribers/{Id}/del.{format}Remove an unsubscriber
Source is required, as returned by /suppression/unsubscribers: an Id alone is not unique.
OAuth scope : contacts:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Id | path | integer (int64) | yes | Id of the unsubscriber |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
Source | string | yes | contact, list, smtp, api or api3 |
Responses
| Code | Description |
|---|---|
200 | Unsubscriber removed |
400 | Source is required |
401 | Invalid credentials |
404 | Unsubscriber not found |
Example 200
{
"Result": "OK",
"Removed": 9012
}