Interactive documentation · Developer portal · OpenAPI specification · EN · FR · DE · IT · PT · ES
Base URL : https://api.mailpro.com/sms
The Mailpro™ SMS API lets you programmatically manage phone contact lists, phone numbers, and send bulk or individual SMS messages.
Every endpoint accepts three authentication methods. Use whichever fits your stack.
POST /token, not scoped.> Legacy Bearer JWT and id_client+api_key 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 SMS API, on CRM v3 and on Email v2. 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=sms:send sms:read"
Then call this API with Authorization: Bearer ACCESS_TOKEN. The same token works on the CRM v3 and Email v2 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. SMS-relevant scopes: sms:send, sms:read, sms:contacts:read, sms:contacts:write, account:read.
Authorization: Bearer <access_token>
Get a token via POST /token (form-encoded, OAuth2 password grant):
POST https://api.mailpro.com/sms/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 Email v2 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 |
|---|---|---|
id_client | integer | Your numeric Mailpro client ID, found in Settings → API access |
api_key | string | Your API key (GUID format), found in Settings → API access |
Send them in either of these places — pick one:
id_client: 12345 and api_key: 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.?id_client=12345&api_key=…. Kept for backward compatibility with existing integrations. Switch to headers as soon as you can.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/* (bulk, single, scheduling) | sms:send |
/history/list, /credit/get | sms:read |
/phone/list, /list/list | sms:contacts:read |
/phone/{add,edit,del}, /list/{edit,del}, /import/upload | sms:contacts:write |
/account/limits | account:read |
> POST /list/add (create a list) is currently reachable by any > valid token — it is not yet scope-gated. Scope rejection on the gated > endpoints returns 403 with > { "error": "insufficient_scope", "required_scope": "…" }.
All endpoints support json (default) and xml. Specify the format in the URL:
/credit/get.json/credit/get.xmlField names in responses are PascalCase (TotalCredits, FirstName, …), while request parameters are snake_case (id_client, address_book_id, …). List endpoints wrap their rows in an Items array.
Same 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.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.
One SMS costs country credits × number of parts.
Country credits — each destination country has a price of 1, 2 or 3 credits, based on the phone's international prefix. Sending to a number whose prefix we don't price is rejected with 400.
Number of parts — a message longer than one SMS is split by the operator and each part is charged. The limit depends on the characters used:
| Alphabet | Single message | Per part when split |
|---|---|---|
| GSM-7 (standard latin) | 160 chars | 153 chars |
| Unicode (accented beyond GSM-7, Cyrillic, emoji, …) | 70 chars | 67 chars |
A single character outside GSM-7 switches the whole message to Unicode, so one emoji can take a 160-character message from 1 part to 3. The characters ^ { } [ ] ~ | \ and € count as two each, even though they are GSM-7.
*Example:* a 300-character message in standard latin = 2 parts; sent to a 2-credit country, it costs 2 × 2 = 4 credits per recipient.
Phone numbers are normalized automatically. You can send them in many formats — spaces, dashes, dots, slashes, parentheses, underscores and non-breaking spaces are stripped before validation. Any other character (letters, apostrophes, =, ;, etc.) makes the number invalid.
| Valid | Invalid |
|---|---|
+41794849980 — E.164 international | 0612345678 — national format (leading 0) |
0041 79 484 99 80 — 00-prefix + spaces | 41'794'849'80 — apostrophes (not stripped) |
+33 6-12-34-56-78 — + with dashes | tel=0041794849980 — extra characters |
41794849980 — bare digits | 1234567 — too short (< 8 digits) |
Rules:
+, 00, or no prefix0)-, dot ., slash /, parentheses ( ), underscore _, NBSP=, ;, + inside the number, etc.) → rejected41794849980)| HTTP | Code | Meaning |
|---|---|---|
| 400 | 103 | Malformed input — missing or invalid required parameter |
| 202 | 202 | Insufficient SMS credits |
| 401 | Invalid id_client / api_key | |
| 500 | 500 | Internal error |
Mailpro™ can notify your server in real time when SMS events happen on your account (an SMS is delivered, an SMS fails, your credit balance is low, 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": "sms.queued",
"created": 1748194800,
"apiVersion": "sms",
"account": { "id": 12345, "name": "ACME Corp" },
"data": {
"messageId": 12345,
"to": "41791234567",
"queuedAt": "2026-05-27T14:32:12.0123456Z",
"scheduledAt": "2026-05-27T14:32:12.0123456Z",
"parts": 1
}
}
| 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 | "sms" for events emitted by POST /sms/send/add_single; "v3" for events emitted by POST /v3/Sms/SingleSMS; "watcher" for DLR-driven events emitted by the worker. | Which Mailpro component published the event. |
account.id | int | Your Mailpro client ID (= id_client). |
account.name | string | Your account name (handy for client-side debug). |
data | object | Event payload — shape depends on type (see catalog below). |
Only the events listed below are actually emitted. The shapes are the literal JSON sent by the platform — match these field names exactly when parsing.
| Event | Trigger | data shape |
|---|---|---|
ping | Test via POST /webhooks/{id}/test | { message } |
sms.queued | POST /sms/send/add_single or POST /v3/Sms/SingleSMS accepted into the Mailpro send queue | { messageId: int, to: string, queuedAt: ISO-8601, scheduledAt: ISO-8601, parts: int } |
sms.delivered | DLR with carrier code 004 or 008 (success codes) | { messageId: int, phone: string, stateCode: string, dlrDate: datetime } |
sms.failed | DLR with any other carrier code (timeout, undelivered, rejected, blacklisted, …) | { messageId: int, phone: string, stateCode: string, dlrDate: datetime } |
credits.sms.low | SMS credit balance fell under your alert threshold (worker CreditsLowWatcher) | { remaining: int, threshold: int, alertedAt: ISO-8601 } |
> Note — there is no sms.sent event. The carrier DLR is the only feedback > the platform receives between "queued" and "delivered/failed", so the next > event you receive after sms.queued is either sms.delivered (success) or > sms.failed (error). Most messages reach the carrier within seconds and a > DLR within minutes; mobile-network conditions can occasionally extend this > to hours.
> Field naming — sms.queued uses to (the destination as submitted by > the API caller, after normalization). sms.delivered / sms.failed use > phone (the destination as returned by the carrier on the DLR). They will > be the same digits in practice, but the names differ because the events > originate from different code paths.
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: sms.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 SMS /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 Email v2 API.
> ⚙ Implementation detail — POST /sms/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 /sms/token and get > a valid JWT back. There is no duplicated token-issuance logic; the > SMS 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 SMS credit balance, monthly API quota, and account status.
/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. SMS sends additionally consume the dedicated SMS credit balance (GET /credit/get.{format}).
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": null
}
Send SMS messages — either a single transactional SMS to one recipient, or a bulk campaign to an entire contact list.
/send/add.{format}Send bulk SMS to a contact list
Sends an SMS message to all phone numbers in a contact list. Supports scheduling: set scheduled_time to a future date to send later, or omit it to send immediately.
Credit consumption: credits are deducted at send time. Long messages (>160 GSM-7 chars) are split into multiple parts, each costing additional credits.
The response returns { "Error": "Insufficient Credit", "Code": 202 } if the account has insufficient credits.
OAuth scope : sms: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 |
|---|---|---|---|
id_client | integer | yes | Your Mailpro client ID |
api_key | string | yes | Your API key (GUID) |
message | string | yes | SMS message text. Keep under 160 characters for a single-part SMS. Characters outside the GSM-7 alphabet (e.g. Chinese, Arabic, emoji) switch the encoding to Unicode, reducing the limit to 70 characters per part. |
to | integer | yes | ID of the contact list to send to (from /list/list) |
scheduled_time | string (date-time) | Schedule the send for a future date/time (ISO 8601). Omit or leave empty to send immediately. | |
shorten_urls | boolean | Set to true to replace each link of the message with a short link https://mailp.ro/m/...: fewer characters, so sometimes fewer billed SMS parts. A link that cannot be shortened stays as is. |
Responses
| Code | Description |
|---|---|
200 | Bulk SMS send created successfully |
202 | Insufficient SMS credits |
400 | Missing required field |
401 | Invalid credentials |
Example 200
{
"Id": 6001,
"Message": "Flash sale today only — 30% off all orders. Shop now at mailpro.com/shop",
"ScheduledTime": "2026-04-20T10:00:00",
"To": [
"42"
]
}
Example 202
{
"Error": "Insufficient Credit",
"Code": 202
}
Example 400
{
"Error": "Malformed Input",
"Code": 103
}
/send/add_single.{format}Send a single SMS (GET)
Sends a single SMS to one recipient via a GET request. Useful for simple integrations where POST is not available. Credentials and message are passed as query parameters.
Note: avoid using GET for sensitive messages — query parameters appear in server logs. Prefer POST /send/add_single instead.
OAuth scope : sms:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
to | query | string | yes | Recipient phone number. Accepts +41…, 0041… or 41… with or without separators. See Phone number format in the API description. |
message | query | string | yes | SMS message text (max 160 chars for standard encoding) |
scheduled_time | query | string (date-time) | Schedule the send (ISO 8601). Omit to send immediately. | |
shorten_urls | query | boolean | Set to true to replace each link of the message with a short link https://mailp.ro/m/...: fewer characters, so sometimes fewer billed SMS parts. A link that cannot be shortened stays as is. |
Responses
| Code | Description |
|---|---|
200 | SMS sent (or scheduled) successfully |
202 | Insufficient SMS credits |
400 | Missing required parameter |
401 | Invalid credentials |
Example 200
{
"Id": 5001,
"Message": "Your appointment is confirmed for tomorrow at 10:00.",
"ScheduledTime": "2026-04-15T09:00:00",
"To": "0041791234567",
"Charge": "1"
}
/send/add_single.{format}Send a single SMS
Sends a single SMS message to one recipient phone number. Supports scheduling via scheduled_time.
Credit consumption: credits are deducted at send time. The Charge field in the response shows how many credits were consumed.
The response returns { "Error": "Insufficient Credit", "Code": 202 } if the account has insufficient credits.
OAuth scope : sms: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 |
|---|---|---|---|
id_client | integer | yes | Your Mailpro client ID |
api_key | string | yes | Your API key (GUID) |
message | string | yes | SMS message text |
to | string | yes | Recipient phone number. Accepts +41…, 0041… or 41… with or without separators. See Phone number format in the API description. |
scheduled_time | string (date-time) | Schedule the send for a future time. Omit to send immediately. | |
shorten_urls | boolean | Set to true to replace each link of the message with a short link https://mailp.ro/m/...: fewer characters, so sometimes fewer billed SMS parts. A link that cannot be shortened stays as is. |
Responses
| Code | Description |
|---|---|
200 | SMS sent (or scheduled) successfully |
202 | Insufficient SMS credits |
400 | Missing required field |
401 | Invalid credentials |
Example 200
{
"Id": 5001,
"Message": "Your verification code is 8472. Valid for 10 minutes.",
"ScheduledTime": "2026-04-15T14:30:00",
"To": "0041791234567",
"Charge": "1"
}
Example 202
{
"Error": "Insufficient Credit",
"Code": 202
}
Example 400
{
"Error": "Malformed Input",
"Code": 103
}
/send/list.{format}List SMS campaigns
Returns the list of SMS campaigns (bulk sends) for the account. Pagination via NumberOfRecords and PageNumber headers.
Use type_to to filter by send type:
0 (default) — address-book bulk sends1 — single individual sendsOAuth scope : sms:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
type_to | query | integer: 0, 1 | Send type filter:
- 0 (default) — bulk sends to a contact list
- 1 — individual single-number sends
|
Responses
| Code | Description |
|---|---|
200 | Pending sends returned successfully (a plain array, empty when there is nothing waiting). Only sends still scheduled in the future are listed. |
401 | Invalid credentials |
Example 200
[
{
"Id": 5001,
"Message": "Your verification code is 8472",
"ScheduledTime": "2026-04-15T09:00:00",
"To": "41791234567"
}
]
/send/del.{format}Cancel or delete an SMS send
Cancels a pending scheduled SMS send, or deletes a completed one from history. Only future-scheduled sends can be cancelled. Sends in progress cannot be stopped.
OAuth scope : sms: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 |
|---|---|---|---|
id_client | integer | yes | Your Mailpro client ID |
api_key | string | yes | Your API key (GUID) |
id_send | integer | yes | ID of the SMS send to cancel or delete (from /send/list) |
Responses
| Code | Description |
|---|---|
200 | SMS send cancelled or deleted successfully |
400 | Missing id_send |
401 | Invalid credentials |
500 | Delete failed (send not found or already processed) |
Example 200
{
"Result": "OK"
}
Example 500
{
"Error": "Delete failed",
"Code": 500
}
/send/scheduled.{format}List my scheduled SMS sends
Returns the SMS sends (single or campaign) scheduled for a future date that have not yet been dispatched. Useful before calling /cancel or /reschedule.
Records are sorted by scheduled_time ascending.
OAuth scope : sms:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
id_client | query | integer | yes | |
api_key | query | string | yes | |
NumberOfRecords | header | integer | Items per page — 5,000 by default and at most (see Pagination). HTTP header or query parameter. | |
PageNumber | header | integer | 1-based page number for paginated responses. Sent as HTTP header. |
Responses
| Code | Description |
|---|---|
200 | List of scheduled SMS sends |
401 | Invalid credentials |
Example 200
[
{
"Id": 9001,
"Message": "Black Friday: 30% off until midnight.",
"ScheduledTime": "2026-11-27T12:00:00",
"To": "+33671234567"
}
]
/send/{id_send}/cancel.{format}Cancel a scheduled SMS send
Cancels a scheduled SMS that has not yet been dispatched. If the send has already been processed (or doesn't exist / belongs to another client), returns 409 Conflict.
OAuth scope : sms:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
id_send | path | integer | yes |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
id_client | integer | yes | |
api_key | string | yes |
Responses
| Code | Description |
|---|---|
200 | Send cancelled |
401 | Invalid credentials |
409 | Already dispatched or not found |
Example 200
{
"Result": "OK",
"Cancelled": 9001
}
Example 409
{
"error": "conflict",
"message": "Send not found, not owned by client, or already dispatched."
}
/send/{id_send}/reschedule.{format}Reschedule a scheduled SMS send
Updates the planned date of a scheduled SMS that has not yet been dispatched. New date must be in the future and within 90 days.
Date field name: the API accepts scheduled_time (primary) as well as the legacy aliases PlanDate, date_planned, and DatePlanned. Provide any one of them — the first non-null is used.
OAuth scope : sms:send
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
id_send | path | integer | yes |
Request body (application/x-www-form-urlencoded)
| Name | Type | Required | Description |
|---|---|---|---|
id_client | integer | yes | |
api_key | string | yes | |
scheduled_time | string (date-time) | Primary field name. | |
PlanDate | string (date-time) | Legacy alias accepted for backward compatibility. | |
date_planned | string (date-time) | Legacy alias accepted for backward compatibility. | |
DatePlanned | string (date-time) | Legacy alias accepted for backward compatibility. |
Responses
| Code | Description |
|---|---|
200 | Send rescheduled |
400 | Missing or invalid scheduled_time |
401 | Invalid credentials |
409 | Already dispatched, not found, or new date out of range |
Example 200
{
"Result": "OK",
"Id": 9001,
"NewScheduledTime": "2026-12-31T14:30:00"
}
Example 409
{
"error": "conflict",
"message": "Reschedule failed — send not found, already dispatched, or new date out of range."
}
Manage SMS contact lists. A contact list groups phone numbers for bulk sending. You can create up to 1,000 lists per account (same limit on the three Mailpro APIs and in the app).
/list/list.{format}List phone contact lists
Returns all SMS contact lists for the account. Use the returned Id values when sending bulk SMS campaigns or adding phone numbers.
OAuth scope : sms:contacts:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Responses
| Code | Description |
|---|---|
200 | Phone lists returned successfully |
401 | Invalid credentials |
Example 200
{
"Items": [
{
"AddressBookId": 42,
"Name": "Newsletter subscribers",
"TotalNumberPhones": 1547
},
{
"AddressBookId": 43,
"Name": "Loyalty program",
"TotalNumberPhones": 823
}
]
}
/list/add.{format}Create a contact list
Creates a new SMS contact list with the given name. The list starts empty — add phone numbers with POST /phone/add. 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.
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 |
|---|---|---|---|
id_client | integer | yes | Your Mailpro client ID |
api_key | string | yes | Your API key (GUID) |
name | string | yes | Display name for the new contact list |
Responses
| Code | Description |
|---|---|
200 | Contact list created successfully |
400 | Missing required field (name) |
401 | Invalid credentials |
Example 200
{
"Id": 44,
"Name": "Spring campaign 2026",
"NumberPhones": 0
}
Example 400
{
"Error": "Malformed Input",
"Code": 103
}
/list/edit.{format}Rename a contact list
Updates the display name of an existing SMS contact list. The phone numbers in the list are not affected.
OAuth scope : sms: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 |
|---|---|---|---|
id_client | integer | yes | Your Mailpro client ID |
api_key | string | yes | Your API key (GUID) |
address_book_id | integer | yes | ID of the contact list to rename (from /list/list) |
name | string | yes | New display name for the contact list |
Responses
| Code | Description |
|---|---|
200 | Contact list renamed successfully |
400 | Missing required field |
401 | Invalid credentials |
/list/del.{format}Delete a contact list
Permanently deletes an SMS contact list and all the phone numbers it contains. This action cannot be undone.
OAuth scope : sms: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 |
|---|---|---|---|
id_client | integer | yes | Your Mailpro client ID |
api_key | string | yes | Your API key (GUID) |
address_book_id | integer | yes | ID of the contact list to delete |
Responses
| Code | Description |
|---|---|
200 | Contact list deleted successfully |
400 | Missing address_book_id |
401 | Invalid credentials |
Manage individual phone numbers within a contact list — add, update, and remove entries.
/phone/list.{format}List phone numbers in a contact list
Returns a paginated list of phone numbers in a specific contact list. Use address_book_id to specify which list to retrieve. Pagination: NumberOfRecords and PageNumber (5,000 per page by default, page 1).
OAuth scope : sms:contacts:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
address_book_id | query | integer | yes | ID of the contact list to retrieve phone numbers from |
NumberOfRecords | header | integer | Items per page — 5,000 by default and at most (see Pagination). HTTP header or query parameter. | |
PageNumber | header | integer | 1-based page number for paginated responses. Sent as HTTP header. | |
SortByName | header | boolean | Set to true to sort results alphabetically by name. |
Responses
| Code | Description |
|---|---|
200 | Phone numbers returned successfully |
401 | Invalid credentials |
Example 200
{
"Items": [
{
"Id": 1001,
"Number": "41791234567",
"FirstName": "Alice",
"LastName": "Dupont"
},
{
"Id": 1002,
"Number": "33612345678",
"FirstName": "Bob",
"LastName": "Martin"
}
]
}
/phone/add.{format}Add phone number(s) to a list
Adds one or more phone numbers to a contact list.
Single number: use the number field.
Bulk import: use the phone_list field with numbers separated by semicolons: +41791234567;+33612345678;0049301234567
Phone numbers accept +, 00 or bare format — see Phone number format in the API description for the full list of accepted / rejected inputs. Duplicate numbers are silently skipped unless force=1 is set.
OAuth scope : sms: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 |
|---|---|---|---|
id_client | integer | yes | Your Mailpro client ID |
api_key | string | yes | Your API key (GUID) |
address_book_id | integer | yes | ID of the contact list to add the number(s) to |
number | string | Single phone number. Accepts +41…, 0041… or 41… with or without
separators (spaces, dashes, dots, slashes, parentheses). See
Phone number format in the API description.
Use this OR phone_list — not both.
| |
phone_list | string | Semicolon-separated list of phone numbers for bulk import.
Use this OR number — not both.
Example: 0041791234567;0033612345678
| |
firstname | string | First name of the contact (optional, single number only) | |
lastname | string | Last name of the contact (optional, single number only) | |
force | integer: 0, 1 | Set to 1 to add a number even if it was previously unsubscribed or already exists.
Default: 0 (duplicates are skipped).
|
Responses
| Code | Description |
|---|---|
200 | Phone number(s) added successfully |
400 | Missing required field |
401 | Invalid credentials |
Example 200
{
"Id": 1003,
"Number": "0041791234567",
"FirstName": "Alice",
"LastName": "Dupont"
}
/phone/edit.{format}Update a phone number entry
Updates the phone number and/or contact details of an existing entry. Use number_id (from /phone/list) to identify the entry to update. Only fields provided in the request are updated.
OAuth scope : sms: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 |
|---|---|---|---|
id_client | integer | yes | Your Mailpro client ID |
api_key | string | yes | Your API key (GUID) |
number_id | integer | yes | ID of the phone number entry to update (from /phone/list) |
number | string | New phone number. Accepts +41…, 0041… or 41… with or without separators. See Phone number format in the API description. | |
firstname | string | New first name for the contact | |
lastname | string | New last name for the contact |
Responses
| Code | Description |
|---|---|
200 | Phone number entry updated successfully |
400 | Missing number_id |
401 | Invalid credentials |
/phone/del.{format}Remove a phone number from a list
Permanently removes a specific phone number entry from its contact list. Use number_id (from /phone/list) to identify the entry.
OAuth scope : sms: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 |
|---|---|---|---|
id_client | integer | yes | Your Mailpro client ID |
api_key | string | yes | Your API key (GUID) |
number_id | integer | yes | ID of the phone number entry to delete (from /phone/list) |
Responses
| Code | Description |
|---|---|
200 | Phone number removed successfully |
400 | Missing number_id |
401 | Invalid credentials |
Check the remaining SMS credit balance for your account (separate from the monthly API request quota — see /account/limits).
/credit/get.{format}Get SMS credit balance
Returns the current SMS credit balance for your account. SMS credits are consumed at send time. One standard SMS (≤160 GSM-7 characters) costs 1 credit. Longer messages or messages with special characters may cost more.
> Note: this returns your SMS send credit balance, not your API request quota. > For the API quota see GET /account/limits.{format}.
OAuth scope : sms: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
{
"TotalCredits": 5000
}
Browse the complete log of past SMS campaigns.
/history/list.{format}List SMS sending history
Returns a paginated log of all past SMS campaigns sent from this account. Results are ordered by date descending (most recent first). Pagination: NumberOfRecords and PageNumber (headers or query, 5,000 per page by default).
OAuth scope : sms:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
format | path | string: json, xml | yes | Response format |
Responses
| Code | Description |
|---|---|
200 | History list returned successfully |
401 | Invalid credentials |
Example 200
{
"Items": [
{
"Id": 583294,
"DateSent": "2026-07-17T18:56:39.813",
"Message": "Your verification code is 8472",
"Charge": 3,
"Parts": 1
}
]
}
Upload a CSV or XLSX file to import phone numbers into a contact list in bulk. The file is queued for asynchronous processing — track completion via the ImportJobId returned or via the optional webhook_url callback.
/import/upload.{format}Upload a phone-numbers file (CSV / XLSX) for bulk import
Upload a CSV or XLSX file to import phone numbers into a contact list. The file is stored and queued for asynchronous processing — the endpoint returns an ImportJobId immediately.
Request type: multipart/form-data
Required form fields:
id_client (integer) — your Mailpro client IDapi_key (string) — your API keyid_list (integer) — target contact list IDfile (binary) — the CSV or XLSX file to uploadOptional:
webhook_url (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:
Phone or PhoneNumber — case-insensitive)+41791234567OAuth scope : sms: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 |
|---|---|---|---|
id_list | integer (int64) | yes | ID of the destination contact list |
file | string (binary) | yes | CSV or XLSX file (UTF-8, max 50 MB) |
webhook_url | string (uri) | Callback URL invoked when processing finishes |
Responses
| Code | Description |
|---|---|
200 | File accepted and queued |
400 | No file uploaded or missing id_list |
401 | Invalid credentials |
500 | Server or storage error |
Example 200
{
"Result": "OK",
"ImportJobId": 15,
"InternalName": "230575-20260420142616-phones.csv"
}
Subscribe your server to real-time SMS 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": [
"sms.delivered",
"sms.failed"
]
}
],
"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": [
"sms.delivered",
"sms.failed"
],
"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": [
"sms.delivered",
"sms.failed"
],
"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 |