Skip to main content

Klucze API - Zarządzanie i bezpieczeństwo

Klucze API pozwalają na programistyczny dostęp do platformy Mailist. Automatyzuj tworzenie kontaktów, wysyłkę kampanii i zarządzanie danymi z własnych aplikacji.

Czym jest klucz API?

Klucz API to unikalny identyfikator używany do uwierzytelniania żądań do REST API Mailist. Działa jak hasło - pozwala Twojej aplikacji na dostęp do danych i funkcji platformy.
Bezpieczeństwo: Traktuj klucze API jak hasła. Nie udostępniaj ich publicznie ani nie commituj do repozytoriów Git.

Dostęp do kluczy API

1

Otwórz sekcję Integracje

W menu głównym przejdź do Integracje API
2

Przegląd kluczy

Zobaczysz dashboard z:
  • Wszystkie klucze (całkowita liczba)
  • Aktywne klucze
  • Nieaktywne klucze (odwołane/wygasłe)
  • Całkowite żądania API (z wszystkich kluczy)

Tworzenie nowego klucza API

1

Kliknij 'Utwórz nowy klucz'

Przycisk w prawym górnym rogu sekcji Integracje
2

Wypełnij formularz

Podstawowe informacje

Nazwa klucza: "Formularz kontaktowy - Strona główna"
Opis (opcjonalnie): "Klucz dla produkcyjnego środowiska"
Dobra nazwa: Opisz gdzie i jak klucz będzie używany. To pomoże Ci później zidentyfikować klucze.Przykłady:
  • “Landing Page - Newsletter signup”
  • “Mobile App - iOS Production”
  • “Webhook - Abandoned Cart”
  • “Internal CRM Integration”

Wybór uprawnień

Zaznacz uprawnienia, których potrzebuje Twoja integracja:
UprawnienieOpisUżycie
contacts.readOdczyt kontaktówPobieranie listy kontaktów, szczegóły kontaktu
contacts.writeTworzenie/edycja kontaktówDodawanie nowych subskrybentów, aktualizacja danych
contacts.deleteUsuwanie kontaktówPermanentne usuwanie kontaktów
lists.readOdczyt listPobieranie listy wszystkich list
lists.writeTworzenie/edycja listTworzenie nowych list, dodawanie kontaktów do list
campaigns.readOdczyt kampaniiPobieranie statystyk kampanii
campaigns.writeTworzenie/edycja kampaniiTworzenie i edycja kampanii
campaigns.sendWysyłka kampaniiUruchamianie wysyłki kampanii
automation.readOdczyt automatyzacjiPobieranie workflow
automation.writeTworzenie/edycja automatyzacjiZarządzanie workflow
*Pełny dostępWszystkie uprawnienia (używaj ostrożnie!)
Principle of Least Privilege: Nadawaj tylko te uprawnienia, których faktycznie potrzebuje Twoja integracja.Przykład: Formularz zapisu do newslettera potrzebuje tylko contacts.write + lists.read, nie wymaga campaigns.send.
3

Utwórz klucz

Kliknij Utwórz klucz API
4

Skopiuj klucz

WAŻNE! To jedyny raz, kiedy zobaczysz pełny klucz. Skopiuj go i przechowuj bezpiecznie.
Modal wyświetli:
Nazwa: Formularz kontaktowy
Klucz API: ml_prod_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Uprawnienia: contacts.write, lists.read
Kliknij Kopiuj i zapisz klucz w bezpiecznym miejscu (np. password manager).

Używanie klucza API

Uwierzytelnianie

Dodaj klucz do nagłówka Authorization jako Bearer token:
curl -X GET https://api.mailist.com/v1/contacts \
  -H "Authorization: Bearer ml_prod_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json"

Przykłady użycia

const apiKey = 'ml_prod_...';

const response = await fetch('https://api.mailist.com/v1/contacts', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    email: '[email protected]',
    firstName: 'Jan',
    lastName: 'Kowalski',
    listId: 'list_abc123',
    customFields: {
      company: 'ABC Sp. z o.o.',
      city: 'Warszawa'
    }
  })
});

const contact = await response.json();
console.log('Utworzono kontakt:', contact.id);
Wymagane uprawnienie: contacts.write
const campaignId = 'campaign_xyz789';

const response = await fetch(
  `https://api.mailist.com/v1/campaigns/${campaignId}/stats`,
  {
    headers: {
      'Authorization': `Bearer ${apiKey}`
    }
  }
);

const stats = await response.json();
console.log('Open rate:', stats.openRate);
console.log('Click rate:', stats.clickRate);
Wymagane uprawnienie: campaigns.read
const contacts = [
  { email: '[email protected]', firstName: 'Anna' },
  { email: '[email protected]', firstName: 'Piotr' },
  // ... do 1000 kontaktów per request
];

const response = await fetch('https://api.mailist.com/v1/contacts/bulk', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    listId: 'list_abc123',
    contacts: contacts,
    skipDuplicates: true
  })
});

const result = await response.json();
console.log(`Zaimportowano ${result.created} kontaktów`);
console.log(`Pominięto ${result.skipped} duplikatów`);
Wymagane uprawnienie: contacts.write
// Express.js endpoint
app.post('/webhook/abandoned-cart', async (req, res) => {
  const { userEmail, cartItems, cartValue } = req.body;

  // Dodaj do listy "Abandoned Cart"
  await fetch('https://api.mailist.com/v1/contacts', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.MAILIST_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      email: userEmail,
      listId: 'list_abandoned_cart',
      customFields: {
        cartValue: cartValue,
        lastCartUpdate: new Date().toISOString()
      }
    })
  });

  // Trigger automatyzacji (jeśli jest workflow na TAG_ADDED)
  await fetch(`https://api.mailist.com/v1/events`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.MAILIST_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      email: userEmail,
      event: 'cart_abandoned',
      data: { cartValue, items: cartItems.length }
    })
  });

  res.json({ success: true });
});
Wymagane uprawnienia: contacts.write, automation.write

Zarządzanie kluczami

Przegląd kluczy

W tabeli kluczy API zobaczysz:
KolumnaOpis
NazwaNazwa klucza + liczba uprawnień
Klucz APIZamaskowany klucz (ml_prod_a1b2c3…******) + przycisk kopiuj
StatusAktywny / Odwołany / Wygasły
ŻądaniaCałkowita liczba wywołań API tym kluczem
Ostatnie użycieData i godzina ostatniego żądania
Data utworzeniaKiedy klucz został wygenerowany
AkcjeMenu z opcjami zarządzania

Filtrowanie kluczy

Szukaj po:
  • Nazwie klucza
  • Fragmencie klucza API
Wpisz: "Landing" → Znajdzie "Landing Page - Newsletter"
Wpisz: "ml_prod_a1b" → Znajdzie klucz zaczynający się od ml_prod_a1b

Akcje na kluczach

Kiedy używać: Tymczasowe wyłączenie klucza bez usuwaniaJak:
  1. Menu akcji → Dezaktywuj (lub Aktywuj)
  2. Status zmienia się na “Odwołany”
  3. Wszystkie żądania z tym kluczem będą odrzucane (HTTP 401)
Użycie:
  • Debugowanie integracji
  • Rotacja kluczy (wyłącz stary, włącz nowy)
  • Podejrzenie compromisu bezpieczeństwa
Co robi: Generuje nowy klucz, stary przestaje działaćKiedy używać:
  • Klucz został przypadkowo udostępniony publicznie
  • Regularny rotation (best practice: co 90 dni)
  • Zmiana środowiska (dev → production)
Proces:
  1. Menu akcji → Wygeneruj ponownie
  2. Potwierdź: “Stary klucz przestanie działać”
  3. Skopiuj nowy klucz (pokazany raz!)
  4. Zaktualizuj klucz w swojej aplikacji
Uwaga: Stary klucz natychmiast przestaje działać. Aplikacja używająca starego klucza zacznie dostawać błędy 401.Strategia zero-downtime:
  1. Stwórz nowy klucz (osobny, nie regeneruj)
  2. Wdróż aplikację z nowym kluczem
  3. Poczekaj kilka dni (upewnij się że stary nie jest używany)
  4. Usuń stary klucz
Kopiuje pełny klucz API do schowka.Uwaga: Klucze są zamaskowane w interfejsie (ml_prod_a1b2...******), ale przycisk kopiuje pełny klucz.
Permanentnie dezaktywuje klucz (nie można cofnąć)Różnica vs dezaktywacja:
  • Dezaktywacja: Można ponownie aktywować
  • Odwołanie: Nie można aktywować (tylko usunąć)
Użycie: Kompromis bezpieczeństwa - klucz wyciekł publicznie
Permanentnie usuwa klucz z systemu.
Nieodwracalne! Klucz znika z listy, wszystkie statystyki zostają usunięte.
Kiedy usuwać:
  • Projekt został zamknięty
  • Integracja nie jest już używana
  • Czyszczenie starych/testowych kluczy

Statystyki i monitoring

Top Endpoints

Platforma pokazuje najpopularniejsze endpointy API używane przez Twoje klucze:
1. POST /v1/contacts - 15,234 wywołań
2. GET /v1/campaigns/{id}/stats - 8,921 wywołań
3. GET /v1/contacts - 5,432 wywołań
4. POST /v1/events - 3,210 wywołań
...
Użycie:
  • Zidentyfikuj najczęściej używane endpointy
  • Optymalizuj rate limiting
  • Znajdź potencjalne bottlenecki

Per-Key Statistics

Każdy klucz pokazuje:
  • Całkowite żądania: Suma wszystkich wywołań API
  • Ostatnie użycie: Kiedy klucz był ostatnio używany (pomaga znaleźć nieużywane klucze)

Bezpieczeństwo - Best Practices

Przechowywanie kluczy

✅ Dobrze:
  • Zmienne środowiskowe (.env file, nie commitowane)
  • Secrets manager (AWS Secrets, Azure Key Vault)
  • Password manager (1Password, LastPass)
❌ Źle:
  • Hardcoded w kodzie
  • Commitowane do Git
  • Przesyłane plaintext emailem
  • Zapisane w logs

Rotacja kluczy

Zalecane: Co 90 dniAutomatyzacja:
// Sprawdź wiek klucza
const keyAge = Date.now() - keyCreatedAt;
const ninetyDays = 90 * 24 * 60 * 60 * 1000;

if (keyAge > ninetyDays) {
  console.warn('Klucz API starszy niż 90 dni - rozważ rotację');
}

Minimalne uprawnienia

Nadawaj tylko te uprawnienia, których potrzebujesz:
# ✅ Dobrze - tylko co potrzebne
Formularz zapisu:
  - contacts.write
  - lists.read

# ❌ Źle - za dużo uprawnień
Formularz zapisu:
  - * (full access)

Monitoring

Regularnie sprawdzaj:
  • Nieużywane klucze (ostatnie użycie > 90 dni)
  • Nieoczekiwane spike’i w requestach
  • 401 errors (potencjalna próba włamania)
Akcja: Usuń nieużywane klucze

Rate Limiting

Mailist ma limity żądań API:
PlanRate LimitBurst
Free60 req/min10 req/s
Starter300 req/min50 req/s
Business1,200 req/min100 req/s
EnterpriseNieograniczonyNieograniczony
Co się stanie przy przekroczeniu:
HTTP/1.1 429 Too Many Requests
{
  "error": "Rate limit exceeded",
  "retryAfter": 42,
  "limit": 300,
  "remaining": 0
}
Jak obsłużyć:
const response = await fetch(url, { headers });

if (response.status === 429) {
  const retryAfter = response.headers.get('Retry-After');
  console.log(`Rate limit - czekaj ${retryAfter}s`);

  await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));

  // Retry request
  return fetch(url, { headers });
}

Błędy uwierzytelniania

{
  "error": "Unauthorized",
  "message": "Invalid or missing API key"
}
Przyczyny:
  • Brak nagłówka Authorization
  • Nieprawidłowy format (brak “Bearer ”)
  • Klucz odwołany/wygasły
  • Klucz nie istnieje (literówka)
Rozwiązanie:
// ✅ Poprawnie
headers: {
  'Authorization': 'Bearer ml_prod_...'
}

// ❌ Źle
headers: {
  'Authorization': 'ml_prod_...' // brak "Bearer "
}
{
  "error": "Forbidden",
  "message": "Insufficient permissions: requires contacts.write"
}
Przyczyny:
  • Klucz nie ma wymaganego uprawnienia
  • Próba dostępu do zasobu poza zakresem uprawnień
Rozwiązanie:
  1. Sprawdź uprawnienia klucza w Integracje → [Twój klucz]
  2. Edytuj klucz → Dodaj brakujące uprawnienie
  3. LUB stwórz nowy klucz z odpowiednimi uprawnieniami
{
  "error": "Rate limit exceeded",
  "limit": 300,
  "remaining": 0,
  "resetAt": "2025-01-07T10:15:00Z"
}
Rozwiązanie:
  • Implementuj exponential backoff
  • Cache’uj odpowiedzi gdzie możliwe
  • Batch requests (np. bulk import zamiast pojedynczych)
  • Upgrade plan dla wyższych limitów

Środowiska: Development vs Production

Zalecane jest używanie osobnych kluczy dla różnych środowisk:
Nazwa: "Dev - Landing Page"
Opis: "Klucz dla lokalnego developmentu"
Uprawnienia: Minimum required (contacts.write, lists.read)
Przechowywanie:
# .env (local, NOT committed)
MAILIST_API_KEY=ml_dev_abc123...

FAQ

Zależy od planu:
  • Free: 2 klucze
  • Starter: 5 kluczy
  • Business: 20 kluczy
  • Enterprise: Nieograniczone
Strategie przy limicie:
  • Jeden klucz per aplikacja/środowisko
  • Usuń nieużywane klucze
  • Grupuj podobne integracje (jeśli bezpieczne)
Klucze są pokazane tylko raz przy tworzeniu.Jeśli zgubiłeś:
  1. Nie możesz go odzyskać - klucze są haszowane w bazie
  2. Wygeneruj nowy klucz (regeneruj istniejący lub stwórz nowy)
  3. Zaktualizuj aplikację z nowym kluczem
  4. Usuń stary klucz
Zapobieganie:
  • Zapisuj klucze w password managerze od razu
  • Używaj .env files dla lokalnego developmentu
  • Dokumentuj gdzie każdy klucz jest używany
Nie, klucze API nie wygasają automatycznie (chyba że ręcznie ustawisz datę wygaśnięcia w przyszłości).Best practice: Rotuj klucze co 90 dni dla bezpieczeństwa.
Tak, ale niezalecane ze względów bezpieczeństwa.Lepiej:
  • Osobny klucz per środowisko (dev, staging, prod)
  • Osobny klucz per aplikacja (jeśli masz wiele)
Dlaczego:
  • Łatwiejszy audit (wiesz, która aplikacja wywołała API)
  • Łatwiejsza rotacja (jeden kompromis nie wpływa na inne)
  • Lepszy monitoring (per-key statistics)

Następne kroki

Bezpieczeństwo - Zgłaszanie problemów

Jeśli znajdziesz lukę bezpieczeństwa lub podejrzewasz kompromis klucza:

Security Team

Email: [email protected]Zgłoś:
  • Opis problemu
  • Kompromitowany klucz (jeśli dotyczy)
  • Timestamp incydentu
Odpowiedź: W ciągu 24 godzin