MailerBasis

Dla programistów

Dokumentacja API

REST-owe API do integracji własnych i nietypowych sklepów. Jeśli Twój sklep nie jest jednym z tych, dla których mamy gotową integrację, to jest droga, którą podłączysz go w godzinę.

Uwierzytelnianie

Każde żądanie wymaga klucza API w nagłówku Authorization. Klucz wygenerujesz w panelu: Ustawienia → API. Pokazujemy go jeden raz — my przechowujemy tylko jego skrót, więc zgubionego klucza nie odzyskamy, trzeba wystawić nowy.

curl https://mailerbasis.com/api/v1/me \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ"

Adres bazowy: https://mailerbasis.com/api. Wersja jest częścią ścieżki (/v1) — zmiany łamiące zgodność trafią do /v2, a /v1 będzie działać dalej. Integrację w sklepie pisze się raz na kilka lat i nie może przestać działać po naszym wdrożeniu.

Zasady i limity

  • Limit żądań: 120 na minutę na klucz. Po przekroczeniu dostaniesz 429 — odczekaj i ponów.
  • Idempotencja: zapis kontaktu jest idempotentny po adresie e-mail. Powtórzone wywołanie aktualizuje kontakt, nie tworzy duplikatu i nie wysyła drugiej prośby o potwierdzenie.
  • Format: wyłącznie JSON, kodowanie UTF-8. Pola w odpowiedziach są w snake_case, daty w ISO 8601 (UTC).
  • Paginacja: parametry page i limit; odpowiedź zawiera obiekt pagination z polem hasMore.
  • Synchronizacja przyrostowa: zamiast przepytywać całą bazę, używaj updated_since (kontakty) i created_since (wykluczenia).

Obsługa błędów

Błąd ma zawsze ten sam kształt, z maszynowym kodem w error.code. Nie parsuj treści komunikatu — ta może się zmienić, kod nie.

{
  "error": {
    "code": "validation_error",
    "message": "Podaj poprawny adres e-mail",
    "field": "email"
  }
}
KodHTTPZnaczenie
unauthorized401Brak klucza API albo klucz unieważniony.
forbidden403Klucz poprawny, ale operacja niedozwolona.
not_found404Zasób nie istnieje w Twoim koncie.
conflict409Konflikt stanu — np. zasób już istnieje.
validation_error422Dane wejściowe nie przeszły walidacji. Pole wskazuje `field`.
plan_limit402Limit planu wyczerpany (kontakty, listy, funkcja niedostępna).
subscription_inactive402Subskrypcja nieaktywna — wysyłka wstrzymana.
rate_limited429Przekroczono 120 żądań na minutę na klucz.
server_error500Błąd po naszej stronie. Ponów żądanie.

Konto

Punkt wyjścia każdej integracji: sprawdzenie klucza i limitów, zanim zaczniesz wysyłać dane.

GET/v1/meInformacje o koncie i limitach

Zwraca dane konta, aktualne zużycie i limity planu. Wywołaj to jako pierwsze — potwierdzisz, że klucz działa, i dowiesz się, ile kontaktów oraz wiadomości jeszcze się zmieści.

Przykład

curl -X GET https://mailerbasis.com/api/v1/me \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ"

Odpowiedź (200)

{
  "account": {
    "id": "clx2f8a9b0000",
    "name": "Kawiarnia Aurora",
    "plan": "PRO",
    "subscription_status": "ACTIVE",
    "sender_email": "newsletter@aurora.pl",
    "sender_domain_verified": true,
    "double_opt_in": true
  },
  "limits": {
    "contacts_used": 1840,
    "contacts_limit": 25000,
    "emails_used_this_month": 4200,
    "emails_left_this_month": 45800,
    "emails_limit_monthly": 50000,
    "requests_per_minute": 120
  },
  "features": { "automations": true, "integrations": true }
}

Listy odbiorców

Kontakt zapisuje się na listę, a nie „do bazy”. Identyfikatory list potrzebne są przy każdym zapisie.

GET/v1/listsPobierz listy

Zwraca listy odbiorców wraz z licznikami. Domyślnie pomija zarchiwizowane.

Parametry zapytania

archivedboolean`true` dołącza listy zarchiwizowane.

Przykład

curl -X GET https://mailerbasis.com/api/v1/lists \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ"

Odpowiedź (200)

{
  "data": [
    {
      "id": "clx3n1p2q0001",
      "name": "Newsletter",
      "public_name": "Newsletter Aurory",
      "description": "Nowości raz w tygodniu.",
      "members_count": 1840,
      "subscribed_count": 1792,
      "archived": false,
      "created_at": "2026-03-04T09:12:00.000Z"
    }
  ]
}
POST/v1/listsUtwórz listę

Przydatne przy pierwszym uruchomieniu integracji — sklep może założyć własną listę bez wchodzenia do panelu.

Pola żądania

namewymaganestringNazwa widoczna w panelu (2–120 znaków).
public_namestringNazwa pokazywana odbiorcy w centrum preferencji.
descriptionstringCzego dotyczy lista i jak często piszecie.

Przykład

curl -X POST https://mailerbasis.com/api/v1/lists \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Klienci sklepu",
  "public_name": "Informacje o zamówieniach",
  "description": "Wiadomości dla osób, które kupiły w sklepie."
}'

Odpowiedź (201)

{
  "id": "clx3n1p2q0002",
  "name": "Klienci sklepu",
  "public_name": "Informacje o zamówieniach",
  "description": "Wiadomości dla osób, które kupiły w sklepie.",
  "members_count": 0,
  "subscribed_count": 0,
  "archived": false,
  "created_at": "2026-08-11T07:40:00.000Z"
}

Kontakty

Serce integracji. Zapis jest idempotentny po adresie e-mail: ponowne wywołanie aktualizuje kontakt, zamiast tworzyć duplikat i wysyłać drugą prośbę o potwierdzenie.

POST/v1/contactsZapisz lub zaktualizuj kontakt

Tworzy kontakt albo aktualizuje istniejący. Pole `consent_source` jest wymagane — zapisujemy je przy kontakcie jako dowód podstawy wysyłki i to ono pokazuje się, gdy ktoś zapyta, skąd macie ten adres.

Pola żądania

emailwymaganestringAdres e-mail. Klucz tożsamości kontaktu.
consent_sourcewymaganestringSkąd pochodzi zgoda, np. „checkbox w koszyku”. Min. 3 znaki.
first_namestringImię — używane w personalizacji `{{ imie }}`.
last_namestringNazwisko.
phonestringTelefon.
citystringMiasto.
countrystringKraj (kod lub nazwa).
tagsstring[]Tagi — mogą wyzwalać scenariusze.
fieldsobjectPola własne: `{ "klucz": "wartość" }`. Dostępne w personalizacji.
list_idsstring[]Listy, do których dopisujemy kontakt.
skip_confirmationbooleanPomija double opt-in, gdy zgodę zebraliście wcześniej. Odpowiedzialność za dowód jest wtedy po Waszej stronie.

Przykład

curl -X POST https://mailerbasis.com/api/v1/contacts \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "anna@example.com",
  "first_name": "Anna",
  "consent_source": "checkbox w koszyku, zamówienie 10021",
  "list_ids": ["clx3n1p2q0001"],
  "tags": ["klient"],
  "fields": { "ulubiona_kawa": "espresso" }
}'

Odpowiedź (201)

{
  "id": "clx4a1b2c0003",
  "email": "anna@example.com",
  "first_name": "Anna",
  "last_name": null,
  "status": "PENDING",
  "source": "API",
  "tags": ["klient"],
  "fields": { "ulubiona_kawa": "espresso" },
  "consent": { "at": null, "source": "checkbox w koszyku, zamówienie 10021" },
  "list_ids": ["clx3n1p2q0001"],
  "status_detail": "pending_confirmation",
  "created_at": "2026-08-11T07:41:00.000Z",
  "updated_at": "2026-08-11T07:41:00.000Z"
}
GET/v1/contactsPobierz kontakty

Zwraca kontakty z paginacją. Parametr `updated_since` pozwala dociągać wyłącznie zmiany — przy synchronizacji nocnej nie musisz przepytywać całej bazy.

Parametry zapytania

pageintegerNumer strony, od 1.
limitintegerWielkość strony, maks. 100 (domyślnie 50).
statusstring`SUBSCRIBED`, `PENDING`, `UNSUBSCRIBED`, `BOUNCED`, `COMPLAINED`.
list_idstringTylko kontakty należące do tej listy.
tagstringTylko kontakty z tym tagiem.
querystringFragment adresu e-mail.
updated_sincestring (ISO 8601)Tylko kontakty zmienione po tej dacie.

Przykład

curl -X GET https://mailerbasis.com/api/v1/contacts \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ"

Odpowiedź (200)

{
  "data": [ { "id": "clx4a1b2c0003", "email": "anna@example.com", "status": "SUBSCRIBED" } ],
  "pagination": { "page": 1, "limit": 50, "total": 1840, "totalPages": 37, "hasMore": true }
}
GET/v1/contacts/{identyfikator}Pobierz jeden kontakt

Identyfikatorem może być adres e-mail (zakodowany w URL) albo nasze `id`. Adres jest pełnoprawnym kluczem — sklepy rzadko przechowują cudze identyfikatory.

Przykład

curl -X GET https://mailerbasis.com/api/v1/contacts/anna%40example.com \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ"

Odpowiedź (200)

{
  "id": "clx4a1b2c0003",
  "email": "anna@example.com",
  "status": "SUBSCRIBED",
  "tags": ["klient"],
  "engagement": {
    "last_email_at": "2026-08-08T06:00:00.000Z",
    "last_open_at": "2026-08-08T07:12:00.000Z",
    "last_click_at": null
  }
}
PATCH/v1/contacts/{identyfikator}Zaktualizuj kontakt

Aktualizuje wskazane pola. Pola własne są **scalane**, nie podmieniane — integracja zmieniająca jedno pole nie skasuje pozostałych, których nie zna.

Pola żądania

first_namestringImię.
last_namestringNazwisko.
tagsstring[]Podmienia cały zestaw tagów.
fieldsobjectScalane z istniejącymi polami własnymi.
add_to_listsstring[]Dopisz do tych list.
remove_from_listsstring[]Usuń z tych list.

Przykład

curl -X PATCH https://mailerbasis.com/api/v1/contacts/anna%40example.com \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ" \
  -H "Content-Type: application/json" \
  -d '{
  "fields": { "ostatnie_zamowienie": "2026-08-10" },
  "add_to_lists": ["clx3n1p2q0002"]
}'

Odpowiedź (200)

{
  "id": "clx4a1b2c0003",
  "email": "anna@example.com",
  "status": "SUBSCRIBED",
  "list_ids": ["clx3n1p2q0001", "clx3n1p2q0002"]
}
DELETE/v1/contacts/{identyfikator}Wypisz lub usuń kontakt

Domyślnie **wypisuje** kontakt i dokłada adres do listy wykluczeń. Parametr `purge=true` usuwa kontakt razem z historią (prawo do bycia zapomnianym) — adres i tak zostaje na liście wykluczeń, żeby ponowny import nie wznowił wysyłki.

Parametry zapytania

purgeboolean`true` kasuje kontakt i historię zamiast go wypisywać.
reasonstringPowód rezygnacji zapisywany przy kontakcie.

Przykład

curl -X DELETE https://mailerbasis.com/api/v1/contacts/anna%40example.com \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ"

Odpowiedź (200)

{ "status": "unsubscribed", "email": "anna@example.com" }
POST/v1/contacts/bulkZapisz wiele kontaktów naraz

Do 500 kontaktów jednym żądaniem — do migracji z innego systemu i do nocnej synchronizacji. Odpowiedź jest per wiersz, a nie „wszystko albo nic”: kilka błędnych adresów nie wywraca reszty, a Ty wiesz dokładnie, które odpadły i dlaczego.

Pola żądania

contactswymaganeobject[]Tablica kontaktów (1–500), pola jak w `POST /v1/contacts`.
consent_sourcewymaganestringWspólne źródło zgody dla całej paczki.
list_idsstring[]Listy dla wszystkich kontaktów z paczki.
skip_confirmationbooleanPomija double opt-in dla całej paczki.

Przykład

curl -X POST https://mailerbasis.com/api/v1/contacts/bulk \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ" \
  -H "Content-Type: application/json" \
  -d '{
  "consent_source": "migracja z poprzedniego systemu, zgody z lat 2024–2026",
  "list_ids": ["clx3n1p2q0001"],
  "contacts": [
    { "email": "anna@example.com", "first_name": "Anna" },
    { "email": "piotr@example.com", "first_name": "Piotr" }
  ]
}'

Odpowiedź (200)

{
  "summary": { "received": 2, "created": 1, "updated": 1, "skipped": 0, "errors": 0 },
  "results": [
    { "email": "anna@example.com", "status": "created" },
    { "email": "piotr@example.com", "status": "updated" }
  ]
}

Zdarzenia

Zgłoszenie zdarzenia uruchamia scenariusz — porzucony koszyk, zrealizowane zamówienie, koniec gwarancji. Nazwę zdarzenia ustalasz sam; nie narzucamy słownika, bo nietypowy sklep ma nietypowe zdarzenia.

POST/v1/eventsZgłoś zdarzenie

Uruchamia scenariusze, których wyzwalaczem jest zdarzenie o tej nazwie. Wartości z `properties` zapisujemy w polach własnych kontaktu, więc możesz ich użyć w treści wiadomości.

Pola żądania

eventwymaganestringNazwa zdarzenia, ta sama co w wyzwalaczu scenariusza.
emailwymaganestringAdres kontaktu, który musi już istnieć w bazie.
propertiesobjectDane zdarzenia trafiające do pól własnych kontaktu.

Przykład

curl -X POST https://mailerbasis.com/api/v1/events \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ" \
  -H "Content-Type: application/json" \
  -d '{
  "event": "porzucony_koszyk",
  "email": "anna@example.com",
  "properties": { "wartosc_koszyka": "149,00 zł", "link_koszyka": "https://sklep.pl/koszyk/abc" }
}'

Odpowiedź (200)

{ "status": "accepted", "event": "porzucony_koszyk", "contact_id": "clx4a1b2c0003" }

Wykluczenia

Adresy, do których nie wysyłamy nigdy. Najważniejszy endpoint po kontaktach: pozwala uzgodnić rezygnacje z własnym systemem, żeby sklep nie pisał do kogoś, kto wypisał się u nas.

GET/v1/suppressionsPobierz listę wykluczeń

Zwraca wykluczone adresy z powodem. Parametr `created_since` służy do dociągania nowych rezygnacji przy okresowej synchronizacji.

Parametry zapytania

pageintegerNumer strony.
limitintegerWielkość strony, maks. 500.
created_sincestring (ISO 8601)Tylko wykluczenia dodane po tej dacie.

Przykład

curl -X GET https://mailerbasis.com/api/v1/suppressions \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ"

Odpowiedź (200)

{
  "data": [
    { "email": "piotr@example.com", "reason": "unsubscribed", "note": null, "created_at": "2026-08-09T18:20:00.000Z" }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 12, "totalPages": 1, "hasMore": false }
}
POST/v1/suppressionsDodaj adres do wykluczeń

Użyj, gdy klient zgłosił rezygnację poza systemem — telefonicznie albo w sklepie. Jeśli adres jest u nas aktywnym kontaktem, zostanie przy okazji wypisany.

Pola żądania

emailwymaganestringAdres do wykluczenia.
notestringNotatka, np. skąd zgłoszenie.

Przykład

curl -X POST https://mailerbasis.com/api/v1/suppressions \
  -H "Authorization: Bearer mb_TWOJ_KLUCZ" \
  -H "Content-Type: application/json" \
  -d '{ "email": "piotr@example.com", "note": "rezygnacja zgłoszona telefonicznie" }'

Odpowiedź (201)

{ "status": "suppressed", "email": "piotr@example.com" }

Webhooki

API pozwala Ci mówić nam, co się dzieje w sklepie. Webhooki są drugą stroną tej rozmowy: zgłaszamy Ci zdarzenia z naszej strony, przede wszystkim rezygnacje. Bez nich Twoja baza żyje w nieświadomości i przy kolejnym imporcie próbuje wskrzesić kontakt, który się wypisał.

Adres i subskrybowane zdarzenia ustawisz w panelu: Ustawienia → API. Wysyłamy żądanie POST z ciałem JSON. Odpowiedz kodem 2xx — każda inna odpowiedź oznacza ponowienie: siedem prób rozłożonych na dobę (po 1, 5, 15 minutach, potem 1, 3, 8 i 24 godzinach).

ZdarzenieKiedy
contact.createdNowy kontakt trafił do bazy (dowolnym źródłem).
contact.subscribedKontakt potwierdził zapis albo wrócił do subskrypcji — od tej chwili dostaje wiadomości.
contact.unsubscribedKontakt zrezygnował. To zdarzenie musisz obsłużyć, żeby nie pisać do niego z własnego systemu.
contact.bouncedAdres trwale odrzuca wiadomości i trafił na listę wykluczeń.
contact.complainedOdbiorca zgłosił wiadomość jako spam.
campaign.finishedWysyłka kampanii dobiegła końca — payload zawiera podsumowanie wyników.

Weryfikacja podpisu

Każde zgłoszenie podpisujemy sekretem webhooka. Zweryfikuj podpis — bez tego każdy, kto zna Twój adres, może podać się za nas i wypisać Ci pół bazy.

X-MailerBasis-Event: contact.unsubscribed
X-MailerBasis-Delivery: clx8d9e0f0001
X-MailerBasis-Timestamp: 1786512000
X-MailerBasis-Signature: sha256=8f2a…

Podpis to HMAC-SHA256 z ciągu timestamp + "." + surowe_ciało, liczony sekretem webhooka. Porównuj go metodą stałoczasową i odrzucaj zgłoszenia starsze niż kilka minut.

<?php
// PHP — weryfikacja podpisu webhooka
$body      = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_MAILERBASIS_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_MAILERBASIS_SIGNATURE'] ?? '';

$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, MAILERBASIS_WEBHOOK_SECRET);

if (!hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) {
    http_response_code(400);
    exit('nieprawidlowy podpis');
}

$event = json_decode($body, true);
if ($event['event'] === 'contact.unsubscribed') {
    // oznacz adres jako wypisany we własnej bazie
}
http_response_code(200);

Zgoda i RODO

Jedna rzecz, której API nie zrobi za Ciebie: nie oceni, czy masz prawo pisać do tych osób. Dlatego consent_source jest polem wymaganym — zapisujemy je przy kontakcie i to ono odpowiada na pytanie „skąd macie ten adres”, gdy je usłyszysz.

  • Domyślnie każdy zapis przez API przechodzi double opt-in — kontakt dostaje wiadomość z linkiem i do czasu kliknięcia nie wejdzie do żadnej wysyłki.
  • skip_confirmation: true pomija ten krok. Używaj go tylko wtedy, gdy zgodę zebrałeś wcześniej i potrafisz ją wykazać — odpowiedzialność przechodzi wtedy na Ciebie.
  • Zakup w sklepie nie jest sam w sobie zgodą na newsletter.
  • Adresy z listy wykluczeń nie wracają do bazy przez API. Zapis takiego adresu zwróci 200 ze statusem suppressed — to poprawny stan, nie błąd integracji.

Potrzebujesz pomocy przy integracji?

Napisz na hello@mailerbasis.com — odpowiadamy w dni robocze, zwykle tego samego dnia. Jeśli budujesz integrację dla platformy, z której korzysta więcej sklepów, odezwij się: chętnie dodamy ją do gotowych.