Документація
Публічне API та MCP-сервер
Створюй агентів, CLI та бек-офіс інтеграції на основі qmailing. PLUS та вище відкриває аутентифікацію через Bearer-токен на кожному публічному ендпоінті, а також готовий MCP-сервер, який можна підключити до Claude Desktop, Cursor чи будь-якого іншого сумісного клієнта.
Початок роботи
- 1. Перейди на PLUS — публічне API доступне на
PLUS,PRO,PRO_PLUSчиPRO_MAX. Виклики з FREE отримують402 Payment Requiredнезалежно від токена. - 2. Створи токен — перейди в Налаштування → Розробники, натисни Новий токен, обери потрібні скоупи та скопіюй значення
qm_live_…, коли воно зʼявиться. Токени показуються лише один раз. - 3. Зроби свій перший виклик:
curl https://qmailing.com/api/v1/pub/mailboxes \ -H "Authorization: Bearer qm_live_your_token_here"
Аутентифікація
Кожен виклик публічного API містить заголовок Authorization: Bearer qm_live_<…>. Формат токена має префікс (qm_live_ з 32 випадковими символами), щоб витоки в логах і репозиторіях одразу впадали в око.
Токени зберігаються у вигляді хешу (SHA-256). Відкритий текст показується рівно один раз під час створення і більше ніколи — якщо ти його втратиш, створюй новий. Кожен запит виконує порівняння хешу з постійним часом; last_used_at оновлюється у разі успіху, тож UI розробників може показувати застарілі облікові дані.
apiAccess на кожному підписаному запиті — немає потреби відкликати токени окремо при зміні плану.Скоупи
Кожен токен містить один або більше рядків скоупу. Ендпоінти оголошують потрібні їм скоупи; відсутні скоупи призводять до 403 InsufficientScope. Підстановка (*) надає все — вмикай лише тоді, коли це справді потрібно.
| Скоуп | Дозволяє |
|---|---|
| mailboxes:read | Переглядати та інспектувати поштові скриньки |
| mailboxes:write | Створювати, змінювати, видаляти поштові скриньки |
| domains:read | Переглядати домени та DNS-записи |
| domains:write | Додавати, верифікувати, видаляти власні домени (лише через FE у v1) |
| email:read | Читати та переглядати листи у скриньках |
| email:send | Надсилати вихідні листи через API |
| webhooks:read | Переглядати ендпоінти вебхуків |
| webhooks:write | Реєструвати, переглядати та відкликати ендпоінти вебхуків |
| * | Підстановка — надає все. Використовуй обачно. |
Поштові скриньки
Список
GET /api/v1/pub/mailboxes
# scope: mailboxes:readОтримати одну
GET /api/v1/pub/mailboxes/{id}
# scope: mailboxes:readСтворити
POST /api/v1/pub/mailboxes
Content-Type: application/json
# scope: mailboxes:write
{
"localPart": "support",
"domain": "yourbrand.com",
"displayName": "Support team",
"forwardTo": "ops@yourbrand.com"
}Поле domain необовʼязкове — пропусти його, і скриньку буде створено на qmailing.com. На власному домені він має бути одночасно claimed ТА fullyVerified; ще не готовий домен повертає 400 замість того, щоб мовчки створити нероботящу скриньку.
Домени та DNS
Список власних доменів
GET /api/v1/pub/domains
# scope: domains:readПерелік DNS-записів
GET /api/v1/pub/domains/{id}/dns-records
# scope: domains:readПовертає повний перелік з 8 записів (challenge TXT, MX, SPF, три DKIM CNAME, DMARC, опційний _amazonses) із status для кожного рядка: PENDING / FOUND / MISMATCH / NOT_REQUIRED. Агент може використати це, щоб підказати користувачу, що саме опублікувати у свого DNS-провайдера.
Електронна пошта
Читай вміст скриньок та надсилай вихідні листи. Пагінація за папками збігається з внутрішнім API фронтенду — агенти, що мігрують з cookie-JWT на Bearer, не побачать жодних змін у формі відповіді.
Список за папкою
GET /api/v1/pub/email?folder=INBOX&offset=0&limit=25
# scope: email:read
# Optional query params:
# mailboxId filter to one mailbox (omit for all-mailboxes view)
# folder INBOX | SENT | DRAFTS | TRASH | STARRED | SPAM
# offset 0-based index, default 0
# limit 1..100, default 25folder за замовчуванням INBOX; допустимі значення: INBOX, SENT, DRAFTS, TRASH, STARRED, SPAM. mailboxId необовʼязковий — без нього список повертається з усіх скриньок викликача. Пагінація через offset + limit; limit обмежено діапазоном 1–100 (за замовчуванням 25).
Отримати один
GET /api/v1/pub/email/{id}
# scope: email:readПовертає повний EmailDetailDto: HTML / текст листа, список ярликів, метадані вкладень. Самі вкладення не вкладаються в тіло — кожне завантажуй наведеним нижче GET /{emailId}/attachments/{index}, який стрімить сирі байти з оригінальною назвою файла та content-type.
Завантаження вкладення
GET /api/v1/pub/email/{emailId}/attachments/{index}
# scope: email:read
# response: streamed bytes, Content-Type from the attachmentСтрімить сирі байти вкладення (індекс від нуля у списку вкладень листа). Той самий формат, що й сесійний ендпоінт: pass-through Content-Type, Content-Disposition за RFC 6266 з оригінальною назвою файла (UTF-8 percent-encoded для Unicode). MCP-агенти, які звертаються через @qmailing/mcp-server, використовують інструмент qmailing_get_attachment, що повертає байти в base64 (ліміт 5 MiB).
Надіслати
Ендпоінт надсилання. Тіло multipart/form-data з однією JSON-частиною command, що містить отримувачів / тему / тіло, та з нулем або більше частинами-файлами attachments:
POST /api/v1/pub/email/send
Content-Type: multipart/form-data
# scope: email:send
--boundary
Content-Disposition: form-data; name="command"
Content-Type: application/json
{
"mailboxId": "11111111-2222-3333-4444-555555555555",
"to": ["alice@example.com"],
"cc": [],
"bcc": [],
"subject": "Order #1428 confirmed",
"bodyText": "Plain-text body",
"bodyHtml": "<p>Rich body</p>",
"replyToId": null
}
--boundary
Content-Disposition: form-data; name="attachments"; filename="receipt.pdf"
Content-Type: application/pdf
<binary bytes>
--boundary--Структура JSON command:
{
"mailboxId": "uuid (required) — must be SES-verified",
"to": "string[] (required, max 50)",
"cc": "string[] (optional, max 50)",
"bcc": "string[] (optional, max 50)",
"subject": "string (max 998 chars)",
"bodyText": "string — plain-text body",
"bodyHtml": "string — HTML body",
"replyToId": "uuid | null — set for in-thread replies"
}Кожен виклик враховується у щоденному ліміті відправки за планом, у квоті відправки SES для скриньки та у квоті сховища для копії в SENT. Адреси отримувачів перевіряються за RFC 5322; невалідні повертають 400 з конкретною адресою. Скринька, на яку посилається mailboxId, має бути верифікованою в SES — інакше отримаєш 409 MailboxNotVerified з підказкою, яка веде на сторінку доменів.
multipart/form-data, тож формат на дроті залишається ідентичним.filename на command-частині не обовʼязковий — Node fetch автоматично штампує filename="blob" на кожен Blob, браузерні та Node-клієнти можуть передавати command.json, а коректні curl --form клієнти можуть взагалі його опустити. Усі три варіанти парсяться однаково; значення має лише Content-Type: application/json на байтах частини.Скоупи: email:read для списку та get-one, email:send для надсилання.
Вебхуки
Зареєструй HTTPS-ендпоінт, і QMailing POSTитиме підписаний JSON-конверт на нього щоразу, коли спрацьовує одна з підписаних подій. Доставка жива — воркер опитує per-event чергу кожні 10 с, підписує кожен запит HMAC-SHA256 і повторює невдачі за експоненційним backoff-ом (1 хв / 5 хв / 15 хв / 1 год / 6 год), після чого паркує рядок у DLQ для ручного огляду.
Зареєструвати ендпоінт
POST /api/v1/pub/webhooks
Content-Type: application/json
# scope: webhooks:write
{
"url": "https://your-server.example.com/qmailing-events",
"label": "Production listener",
"events": ["email.received", "email.bounced", "domain.verified"]
}
# Response (201):
{
"endpoint": {
"id": "uuid",
"url": "https://your-server.example.com/qmailing-events",
"label": "Production listener",
"events": ["email.received", "email.bounced", "domain.verified"],
"secretPrefix": "whk_AbCd",
"createdAt": "2026-05-04T20:30:00Z",
"active": true
},
"plaintext": "whk_AbCd…<32 random chars>" // shown ONCE
}url має бути HTTPS-ендпоінтом (HTTP дозволено лише для localhost у dev). Хост перевіряється за allow-list проти SSRF — приватні / loopback / cloud-metadata адреси відхиляються вже на реєстрації. У відповіді plaintext приходить рівно один раз; збережи його на сервері та використовуй для перевірки HMAC-підпису вхідних подій.
Каталог подій
| Подія | Спрацьовує коли |
|---|---|
| email.received | Вхідний лист прийшов в одну з твоїх скриньок. |
| email.sent | Вихідний лист прийнято SES. |
| email.bounced | Вихідний лист повернувся з помилкою (hard або soft bounce). |
| domain.verified | Власний домен повністю пройшов DNS-верифікацію. |
Список ендпоінтів
GET /api/v1/pub/webhooks
# scopes: webhooks:read OR webhooks:writeПовертає активні та відкликані ендпоінти, нові спочатку. Доступ на читання працює і з webhooks:read, і з webhooks:write — інспекторський токен може уникати ширшого write-скоупу.
Формат доставки
Кожна доставка — це один POST на твій ендпоінт із Content-Type: application/json. Тіло — конверт події з полями event (імʼя події), occurred_at (ISO-таймштамп) і блоком data, форма якого залежить від типу події (див. таблицю вище). Три хедери несуть routing і verification-метадані:
POST https://your-server.example.com/qmailing-events
Content-Type: application/json
User-Agent: qmailing-webhook/1 (+https://qmailing.com)
X-Qmailing-Event: email.received
X-Qmailing-Delivery: 5f3b2a91-7c4d-4d52-9c3e-aa1bcd8a4f12
X-Qmailing-Signature: t=1685120800,v1=abc123def…
{
"event": "email.received",
"occurred_at": "2026-05-23T19:46:40Z",
"data": {
"email_id": "uuid",
"mailbox_id": "uuid",
"from": "alice@example.com",
"subject": "Hello",
"preview_text": "Just checking in…"
}
}Схема підпису
# Receiver side (Node example)
const crypto = require("node:crypto");
function verify(headers, body, secret) {
const sig = headers["x-qmailing-signature"]; // "t=1685120800,v1=abc123…"
const m = /t=(\d+),v1=([0-9a-f]+)/.exec(sig);
if (!m) return false;
const ts = Number(m[1]);
if (Math.abs(Date.now() / 1000 - ts) > 300) return false; // 5-min replay window
const expected = crypto
.createHmac("sha256", secret)
.update(`${ts}.${body}`)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}Відхиляй запит, якщо timestamp старший за 5 хвилин (вікно проти replay) або якщо обчислений HMAC не збігається з v1 hex — наш BE-підписувач точно повторює конвенції Stripe / GitHub, тож будь-яка стандартна бібліотека для верифікації працюватиме з коробки.
Політика повторів
Не-2xx відповіді (або transport-помилки — DNS, TLS, connection refused, 10с timeout) інкрементують attempt_count і зсувають next_attempt_at на наступний слот у 1 хв / 5 хв / 15 хв / 1 год / 6 год. Після пʼятої невдачі доставка переходить у FAILED (DLQ) — видно на сторінці /settings/developers, де розробник може повторити її вручну. Ідемпотентність: кожен retry несе той самий UUID у X-Qmailing-Delivery — приймачі дедуплікують за цим хедером.
Список останніх доставок
GET /api/v1/pub/webhooks/{endpointId}/deliveries?limit=100
# scopes: webhooks:read OR webhooks:writeПовертає limit найсвіжіших доставок для одного ендпоінту (default 100, max 100), нові спочатку. Кожен рядок містить статус, лічильник спроб, останній HTTP-код, рядок помилки і оригінальний JSON payload — все, що потрібно для history-вʼю на дашборді розробника.
Надіслати тестову доставку
POST /api/v1/pub/webhooks/{endpointId}/test
# scope: webhooks:write
# Response 202: WebhookDeliveryDto (enqueued, picked up next worker tick)Випускає синтетичну подію webhook.test на ендпоінт — той самий шлях dispatch-у, що й справжні події; корисно для перевірки підпису + TLS перш ніж підписатись на живий трафік.
Повторити доставку
POST /api/v1/pub/webhooks/deliveries/{deliveryId}/retry
# scope: webhooks:write
# Response 200: WebhookDeliveryDto (status=PENDING, attempt=MAX-1)Вручну повторно додає рядок доставки в чергу. Не можна повторити вже-DELIVERED рядок (409). Повтор дає рівно одну додаткову спробу (лічильник скидається до MAX-1), тож постійно зламаний ендпоінт не зациклитися від багаторазових кліків.
Видалити доставку з історії
DELETE /api/v1/pub/webhooks/deliveries/{deliveryId}
# scope: webhooks:write
# Response: 204 No ContentHard-delete рядка доставки з таблиці історії. Compliance audit-log зберігає сам факт видалення, тож traceability не втрачається, навіть якщо рядка більше нема.
Відкликати ендпоінт
DELETE /api/v1/pub/webhooks/{id}
# scope: webhooks:write
# Response: 204 No ContentІдемпотентно — відкликання вже відкликаного завершується тихим успіхом. Рядок зберігається (виставляється revoked-at), щоб журнали аудиту не втрачали посилання.
409 WebhookEndpointLimitExceeded / 400 InvalidWebhookEvent.Помилки
Кожна відповідь з кодом, відмінним від 2xx, є RFC-7807 ProblemDetail зі стабільним полем code, на яке твій агент може реагувати:
| HTTP | code | Коли |
|---|---|---|
| 400 | InvalidApiTokenScope | Невідомий рядок скоупу під час створення. |
| 400 | InvalidEmailAddress | Один з отримувачів не пройшов перевірку синтаксису RFC 5322. |
| 400 | InvalidWebhookEvent | Список зареєстрованих подій містить невідому назву. |
| 401 | — | Bearer-заголовок відсутній, некоректний, або токен відкликано / прострочено. |
| 402 | PlanFeatureRequired | Викликач на плані без доступу до API — підвищ план. |
| 403 | InsufficientScope | Токен не має жодного з потрібних скоупів. |
| 404 | ApiTokenNotFound | Ціль для відкликання не існує або належить іншому акаунту. |
| 409 | MailboxNotVerified | Домен скриньки ще не повністю верифікований — заверши DNS у /settings/domains. |
| 409 | ApiTokenLimitExceeded | Акаунт містить максимум (20) активних токенів; спочатку відклич один. |
| 409 | WebhookEndpointLimitExceeded | На акаунті вже 10 активних ендпоінтів вебхуків; спочатку відклич один. |
| 429 | RateLimitExceeded | Токен перевищив 300 запитів за хвилину. Retry-After у секундах. |
Обмеження частоти
Кожен токен має власну корзину на 300 запитів за хвилину. Багатоагентні налаштування (CLI + cron + плагін IDE), що використовують різні токени для одного акаунта, працюють незалежно — вони ніколи не борються за одну спільну корзину. 429 RateLimitExceeded містить заголовок Retry-After у секундах.
MCP-сервер
Пакет @qmailing/mcp-server працює локально та надає публічне API як інструменти Model Context Protocol. Підключи його до Claude Desktop, Cursor, Continue або будь-якого MCP-сумісного клієнта.
@qmailing/mcp-server@0.2.0), якщо не хочеш автоматичних оновлень.{
"mcpServers": {
"qmailing": {
"command": "npx",
"args": ["-y", "@qmailing/mcp-server"],
"env": {
"QMAILING_API_TOKEN": "qm_live_your_token_here"
}
}
}
}Вбудовані інструменти: qmailing_list_mailboxes, qmailing_get_mailbox, qmailing_create_mailbox, qmailing_list_domains, qmailing_get_dns_records, qmailing_list_emails, qmailing_get_email, qmailing_get_attachment, qmailing_send_email, qmailing_register_webhook, qmailing_list_webhooks, qmailing_delete_webhook. Каталог зростає в міру появи нових ендпоінтів.