Dokumentacja REST API
PhishSpot udostępnia JSON:API przez HTTPS pod adresem
https://platform.phishspot.com/api/v1. Obejmuje praktycznie wszystko, co można zrobić w
aplikacji administracyjnej: budować, planować i analizować kampanie, zarządzać odbiorcami,
grupami, szablonami, kursami, mediami, domenami, autopilotami i webhookami oraz przesyłać
wyniki do własnych narzędzi.
Ten rozdział dokumentuje konwencje — jeden zestaw reguł, których przestrzega każdy punkt
końcowy — oraz kompletną mapę punktów końcowych. Celowo nie powtarza każdego atrybutu każdego
zasobu: API samo się opisuje, a gdy znasz konwencje, pojedyncze GET /campaigns powie Ci
resztę.
Model zdarzeń oparty na push opisano w Rozdziale 26 Webhooki; interfejs AI w języku naturalnym nad tymi samymi danymi — w Rozdziale 29 Serwer MCP.
27.1 Uwierzytelnianie
Dział zatytułowany „27.1 Uwierzytelnianie”Każde żądanie przenosi token API w nagłówku bearer:
Authorization: Bearer <token>Token można zdobyć na dwa sposoby.
Z panelu administracyjnego (zalecane). Ustawienia konta → Tokeny API → Nowy token (zob. Rozdział 14). Wartość jest pokazywana raz — skopiuj ją do menedżera sekretów.
Z API. Wyślij email + password (oraz otp_attempt, jeśli włączone jest 2FA) na
/auth:
curl -X POST https://platform.phishspot.com/api/v1/auth \ -H 'Content-Type: application/json' \ -d '{"email":"admin@example.com","password":"secret"}'{ "data": { "type": "api_tokens", "id": "token_9xKf2mQ1", "attributes": { "name": "API token", "token": "abc123…" } }}| Pole | Typ | Opis |
|---|---|---|
email | string | Wymagane. Adres e-mail użytkownika. |
password | string | Wymagane. Hasło użytkownika. |
otp_attempt | string | Wymagane tylko wtedy, gdy użytkownik ma włączone uwierzytelnianie dwuskładnikowe. |
Błędy: 401 invalid_credentials, 401 invalid_otp_attempt, 422 otp_attempt_required.
Token należy do jednego użytkownika i dziedziczy jego członkostwa w kontach. Traktuj go jak
hasło. Wszystkie przykłady poniżej zakładają, że $TOKEN zawiera prawidłowy token.
GET /me zwraca właściciela tokenu jako dokument users i nie wymaga wyboru konta —
przydatne jako test łączności.
27.2 Wybór konta
Dział zatytułowany „27.2 Wybór konta”API jest płaskie: nie ma ścieżek /accounts/:account_id/.... Użytkownik tokenu może
należeć do kilku kont, więc każde żądanie zakresowane do konta wskazuje jedno:
X-Account-Id: acct_0pAP5m3JOwak9HY7DbjLBKYgalbo — jeśli nagłówek jest niewygodny — ?account_id=acct_… w query stringu. Nagłówek ma
pierwszeństwo, gdy wysłane są oba. Oba akceptują identyfikator z prefiksem (acct_…) lub
surowy identyfikator całkowity.
Konta dostępne dla tokenu odkryjesz przez GET /accounts — listę tylko do odczytu, która
sama nie wymaga wyboru konta:
curl -s https://platform.phishspot.com/api/v1/accounts \ -H "Authorization: Bearer $TOKEN" \ -H 'Accept: application/vnd.api+json'Wskazanie konta, do którego użytkownik tokenu nie należy, to 400 account_required, a nie
403 — API nie potwierdzi, czy konto innego najemcy istnieje. Całkowite pominięcie wyboru
cofa się do niejawnego konta użytkownika, jeśli takie jest, a w przeciwnym razie zwraca
400 account_required. Zawsze wysyłaj wybór jawnie.
/auth, /users (rejestracja), /me, /password i /notification_tokens nie wymagają
konta.
27.3 Format dokumentu
Dział zatytułowany „27.3 Format dokumentu”- Adres bazowy:
https://platform.phishspot.com/api/v1. Wszystkie ścieżki poniżej są względem niego. - Typ MIME:
application/vnd.api+jsonw obu nagłówkachContent-TypeiAcceptdla punktów końcowych zasobów. BłędnyContent-Typeto415, błędnyAcceptto406. Zwykłe punkty końcowe poleceń (§27.9) akceptują takżeapplication/json. - Koperta: odpowiedzi to dokumenty JSON:API — element
data(obiekt lub tablica dla kolekcji), opcjonalneincludedorazmeta. - Klucze atrybutów są w
snake_case(email_subject,created_at), nie z myślnikami. - Czasy w formacie ISO-8601 UTC (
2026-05-20T14:22:33.000Z). Wejściowescheduled_atkampanii jest interpretowane w strefie czasowej konta. links.selfnigdy nie jest zwracane. Buduj adresy URL ztypeiiddokumentu.
Identyfikatory z prefiksem
Dział zatytułowany „Identyfikatory z prefiksem”Każdy publiczny identyfikator ma prefiks. Surowe klucze główne nigdy nie są ujawniane —
ani w data.id, ani w powiązaniach relacji, ani w filtrach, ani w adresach URL.
| Typ | Prefiks | Typ | Prefiks |
|---|---|---|---|
campaigns | camp_… | platform_domains | pdm_… |
contacts | cont_… | secured_domains | sdm_… |
groups | grp_… | events | evt_… |
deliverables | delv_… | results | res_… |
phishing_templates | tmpl_… | media | med_… |
phishing_template_categories | tcat_… | reported_messages | rep_… |
courses | course_… | webhook_endpoints | whep_… |
blocks | blk_… | webhook_events | whev_… |
autopilots | auto_… | accounts / users | acct_… / user_… |
deliverability_tests | dtst_… |
Identyfikator, którego nie da się zdekodować — zniekształcony prefiks albo prefiks innego typu — zwraca 404, nigdy 400.
Typowy dokument
Dział zatytułowany „Typowy dokument”curl -s "https://platform.phishspot.com/api/v1/campaigns/camp_0u1kQ9" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Account-Id: acct_0pAP5m3J" \ -H 'Accept: application/vnd.api+json'{ "data": { "type": "campaigns", "id": "camp_0u1kQ9", "attributes": { "name": "Test faktur Q2", "state": "in_progress", "email_subject": "Twoja faktura jest gotowa", "can_start": false, "can_pause": true, "statistics": { "total_contacts": 240, "total_deliverables": 240, "completion_percentage": 61 }, "created_at": "2026-05-20T14:22:33.000Z" }, "relationships": { "platform_domain": { "data": { "type": "platform_domains", "id": "pdm_7Yt3" } }, "course": { "data": { "type": "courses", "id": "course_1Aa9" } }, "groups": { "data": [{ "type": "groups", "id": "grp_44Zx" }] } }, "meta": { "html_url": "https://platform.phishspot.com/accounts/…/campaigns/camp_0u1kQ9" } }}27.4 Parametry zapytania
Dział zatytułowany „27.4 Parametry zapytania”Każdy punkt końcowy zwracający kolekcję przyjmuje te same cztery rodziny parametrów.
Stronicowanie
Dział zatytułowany „Stronicowanie”?page[number]=2&page[size]=50Domyślny rozmiar strony 25, maksymalny 200. Każda odpowiedź kolekcji zawiera sumy w
meta:
{ "data": [ … ], "meta": { "record_count": 412, "page_count": 9 } }Nie ma odpowiedzi z nieograniczoną listą.
Sortowanie
Dział zatytułowany „Sortowanie”?sort=-created_at # malejąco?sort=name,-created_at # wiele kluczySortować można tylko po rzeczywistych kolumnach (plus id, created_at, updated_at).
Atrybuty wyliczane, takie jak statistics, can_start czy full_name, nie są sortowalne.
Domyślnie created_at malejąco; kilka typów to nadpisuje — groups, courses,
platform_domains i phishing_templates sortują po name, a contacts po
last_name, first_name.
Filtrowanie
Dział zatytułowany „Filtrowanie”?filter[state]=in_progress # dowolna kolumna enum?filter[campaign]=camp_0u1kQ9 # dowolne belongs_to, po identyfikatorze z PREFIKSEM?filter[state]=draft,paused # przecinek = LUBKażda kolumna enum i każda relacja belongs_to są filtrowalne domyślnie. Kilka typów dodaje
własne filtry: phishing_templates przyjmuje filter[tab] (curated — domyślnie — albo
szablony Twojego konta), filter[search] i filter[category]; webhook_events przyjmuje
filter[event_type].
Dla kontaktów dostępne są: filter[search] (wyszukiwanie dosłownego tekstu w imieniu,
nazwisku i adresie e-mail), filter[department], filter[title], filter[location],
filter[risk_level] (low/medium/high/critical), filter[domain_status]
(verified/unverified), filter[group_id] (identyfikator grupy z prefiksem) oraz
filtry logiczne ever_clicked, ever_submitted, never_targeted, completed_training.
Wiele stanowisk podasz jako tablicę, np.
filter[title][]=Analityk&filter[title][]=Audytor; tak samo działają działy.
Wybory w jednym filtrze łączy LUB, a różne filtry — ORAZ. Filtrowanie poprzedza
stronicowanie. meta.filter_options w odpowiedzi zawiera dostępne działy,
stanowiska i lokalizacje z całego konta.
Zapis listy odbiorców kampanii
Dział zatytułowany „Zapis listy odbiorców kampanii”PATCH /campaigns/:id z relationships.recipient_contacts.data zastępuje całą
listę oczekujących odbiorców. Przekaż identyfikatory zasobów kontaktów. Opcjonalne
meta.group_origin_id przy każdym z nich wskazuje grupę pochodzenia. Pominięcie
tego pola zachowuje dotychczasową grupę, a null usuwa przypisanie. Kontakty i grupy
muszą należeć do wybranego konta; kampania musi pozwalać na edycję. Nie można usunąć
odbiorcy, któremu już wysłano tę kampanię.
{ "data": { "type": "campaigns", "id": "camp_example", "relationships": { "recipient_contacts": { "data": [{"type": "contacts", "id": "cont_example", "meta": {"group_origin_id": null}}] } } }, "meta": {"save_mode": "draft"}}Domyślny tryb draft pozwala zapisać pustą listę. Tryb continue wymaga co najmniej
jednego odbiorcy. Dane kampanii i lista zapisują się razem, a ponowienie identycznego
żądania nie powiela odbiorców. Żaden z tych trybów nie wysyła wiadomości.
Atrybut kampanii recipient_selection, dostępny tylko do odczytu, zwraca wybór wraz
z polami contact_id, group_origin_id i locked. W MCP odpowiednikiem zapisu jest
set_campaign_recipients, listę odczytasz przez get_campaign, a list_contacts
przyjmuje te same filtry kontaktów.
Dołączanie i rzadkie zestawy pól
Dział zatytułowany „Dołączanie i rzadkie zestawy pól”?include=groups,course # dołącz powiązane zasoby do `included`?fields[campaigns]=name,state # zwróć tylko te atrybuty27.5 Błędy
Dział zatytułowany „27.5 Błędy”Każde niepowodzenie — z dowolnego punktu końcowego, zwykłego czy JSON:API — to tablica
errors obiektów błędów JSON:API serwowana jako application/vnd.api+json:
{ "errors": [{ "status": "422", "code": "invalid_transition", "detail": "Transition 'start' is not applicable in the current state.", "meta": { "current_state": "done" } }]}code to stabilny kontrakt czytelny maszynowo — na nim buduj logikę. detail jest dla
człowieka i może się zmieniać lub być tłumaczone. Błędy walidacji zwracają po jednym obiekcie
na pole, każdy z source.pointer ustawionym na /data/attributes/<pole>.
code | HTTP | Znaczenie |
|---|---|---|
| (pusta treść) | 401 | Brakujący, nieznany lub wygasły token. |
invalid_credentials | 401 | Błędny e-mail/hasło w POST /auth. |
invalid_otp_attempt | 401 | Błędny kod 2FA. |
account_required | 400 | Nie wybrano konta albo wybrano konto, do którego użytkownik nie należy. |
parameter_missing | 400 | Brakuje wymaganego parametru najwyższego poziomu. |
bad_request | 400 | Zniekształcona wartość — najczęściej enum spoza dozwolonego zbioru. |
missing_records | 400 | POST /:type/bulk bez tablicy records. |
not_supported | 400 | Tworzenie masowe dla typu, który nie wspiera tworzenia. |
not_bulk | 400 | Zdarzenie przejścia nie ma formy zbiorczej. |
forbidden | 403 | Uwierzytelniono, ale rola użytkownika nie pozwala na tę akcję. |
read_only_relationship | 403 | Próba zapisu do relacji tylko do odczytu. |
not_found | 404 | Brak takiego rekordu w tym koncie — także odpowiedź dla identyfikatora, którego nie da się zdekodować. |
unknown_type | 404 | Segment :resource_type nie jest typem API. |
unknown_transition | 404 | Brak takiego zdarzenia dla tego typu; meta.allowed wymienia poprawne. |
otp_attempt_required | 422 | 2FA jest włączone, a kodu nie przesłano. |
unprocessable_content | 422 | Walidacja nie przeszła; zob. source.pointer. |
invalid_transition | 422 | Zdarzenie nie ma zastosowania w bieżącym stanie rekordu. |
missing_args | 422 | Przejście wymaga argumentów, których nie przesłano. |
preflight_failed | 422 | Kampania nie przeszła kontroli przed startem — po jednym obiekcie błędu na przyczynę. |
invalid_schedule | 422 | POST /campaigns/:id/schedule otrzymał nieużyteczny czas. |
quick_launch_failed | 422 | Szybkie uruchomienie szablonu nie znalazło zweryfikowanej domeny lub odbiorców. |
provision_failed | 422 | Provisioning BYOD odrzucony (pusta, zajęta lub nieprawidłowa domena). |
invalid_date | 422 | Data raportu nie była w formacie YYYY-MM-DD. |
Żądanie rekordu należącego do innego konta zwraca 404: API nigdy nie potwierdza, że dane innego najemcy istnieją.
27.6 Mapa punktów końcowych
Dział zatytułowany „27.6 Mapa punktów końcowych”Każdy typ poniżej wspiera wymienione akcje pod płaskimi ścieżkami — GET /campaigns,
GET /campaigns/:id, POST /campaigns, PATCH /campaigns/:id, DELETE /campaigns/:id —
w formacie dokumentu JSON:API.
| Typ | Akcje | Przejścia | Uwagi |
|---|---|---|---|
campaigns | index show create update destroy | start stop pause cancel cancel_schedule | groups to relacja zapisywalna; platform_domain i course to powiązania. statistics jest null, dopóki kampania nie wystartuje. |
contacts | index show create update destroy | — | groups to relacja zapisywalna. Tworzenie masowe wspierane. |
groups | index show create update destroy | — | Członkostwo przez …/relationships/contacts. |
deliverables | index show create update destroy | — | Powiązania do campaign i contact. Zapis wymaga administratora konta (lub administratora platformy) — zmienia to, co raport kampanii mówi o konkretnej osobie. Odczyt jest dostępny dla każdego członka. |
phishing_templates | index show | — | Tylko do odczytu. index pomija treści; pobierz pojedynczy szablon, aby je uzyskać. |
phishing_template_categories | index | — | Płaska lista tylko do odczytu; hierarchię odtworzysz przez relację parent. |
courses | index show create update destroy | — | blocks to relacja tylko do odczytu. |
blocks | index show create update destroy | — | Wgrywanie plików nie należy do powierzchni JSON:API — bloki video i file_download twórz przez POST /blocks/files (§27.9). |
autopilots | index show create update destroy | start pause stop | groups zapisywalne; campaigns tylko do odczytu. |
secured_domains | index show create destroy | verify_dns | Brak update. |
deliverability_tests | index show | confirm mark_not_received refresh_status | Historia testów dostarczalności, tylko do odczytu. Test tworzy POST /deliverability_tests (§27.9), który wysyła e-mail. Filtry: filter[secured_domain]=sdm_… i filter[state]=. Jednorazowy kod nigdy nie jest zwracany. Przejścia wymagają roli administratora lub edytora. |
platform_domains | index show create update destroy | check | Zapis wymaga administratora platformy, nie administratora konta. |
events | index show create update destroy | — | Powiązania do campaign i contact. Zapis wymaga administratora konta (lub administratora platformy), z tego samego powodu co deliverables. Odczyt jest dostępny dla każdego członka. |
results | index show create update destroy | — | Powiązania do block i contact. |
media | index show update destroy | — | Brak tworzenia w JSON:API — wgrywaj przez POST /media (§27.9). |
reported_messages | index show | — | Tylko do odczytu. Przyjmowanie przez POST /reported_messages (§27.9). |
webhook_endpoints | index show create update destroy | toggle | signing_secret jest ukryty w index, zwracany w show. |
webhook_events | index show | — | Dziennik dostarczeń, tylko do odczytu. |
accounts | index show | — | Tylko do odczytu; punkt odkrywania kont. Nie wymaga wyboru konta. |
Tworzenie masowe (§27.8) jest dostępne dla każdego typu, którego
akcje obejmują create.
Tworzenie i aktualizacja
Dział zatytułowany „Tworzenie i aktualizacja”curl -X POST https://platform.phishspot.com/api/v1/contacts \ -H "Authorization: Bearer $TOKEN" \ -H "X-Account-Id: acct_0pAP5m3J" \ -H 'Content-Type: application/vnd.api+json' \ -H 'Accept: application/vnd.api+json' \ -d '{ "data": { "type": "contacts", "attributes": { "first_name": "Ada", "last_name": "Lovelace", "email": "ada@example.com" }, "relationships": { "groups": { "data": [{ "type": "groups", "id": "grp_44Zx" }] } } } }'PATCH przyjmuje ten sam kształt plus data.id. DELETE zwraca 204.
Część atrybutów jest tylko do zapisu: można je ustawić, ale nigdy nie są zwracane —
landing_css, from_email, from_name, end_action_* i landing_page_enabled kampanii
oraz body bloku. Przesłanie nieznanego atrybutu jest błędem, a nie cichym pominięciem.
Relacje
Dział zatytułowany „Relacje”Powiązane rekordy są łączone identyfikatorami z prefiksem, a relacje „do wielu” mają własne punkty końcowe:
# Dodanie odbiorców do grupycurl -X POST https://platform.phishspot.com/api/v1/groups/grp_44Zx/relationships/contacts \ -H "Authorization: Bearer $TOKEN" -H "X-Account-Id: acct_0pAP5m3J" \ -H 'Content-Type: application/vnd.api+json' \ -d '{"data":[{"type":"contacts","id":"cont_9Qe1"},{"type":"contacts","id":"cont_9Qe2"}]}'
# Usunięciecurl -X DELETE https://platform.phishspot.com/api/v1/groups/grp_44Zx/relationships/contacts \ -H "Authorization: Bearer $TOKEN" -H "X-Account-Id: acct_0pAP5m3J" \ -H 'Content-Type: application/vnd.api+json' \ -d '{"data":[{"type":"contacts","id":"cont_9Qe1"}]}'Zapis do relacji tylko do odczytu (blocks kursu, campaigns autopilota, kategorie
szablonu) to 403 read_only_relationship. Powiązanie rekordu z innego konta to 404.
27.7 Przejścia stanów
Dział zatytułowany „27.7 Przejścia stanów”Zmiany stanu nie są własnymi czasownikami. Każde zdarzenie procesu, dla każdego typu, przechodzi przez jeden punkt końcowy:
curl -X POST https://platform.phishspot.com/api/v1/campaigns/camp_0u1kQ9/transitions \ -H "Authorization: Bearer $TOKEN" \ -H "X-Account-Id: acct_0pAP5m3J" \ -H 'Content-Type: application/json' \ -d '{"event":"start"}'W razie powodzenia otrzymujesz dokument JSON:API zaktualizowanego rekordu (HTTP 200), więc nie musisz go ponownie pobierać.
| Typ | Zdarzenia |
|---|---|
campaigns | start, stop, pause, cancel, cancel_schedule |
autopilots | start, pause, stop |
secured_domains | verify_dns |
deliverability_tests | confirm (argumenty {"code":"123456"}), mark_not_received, refresh_status |
platform_domains | check |
webhook_endpoints | toggle |
Tryby niepowodzenia:
- Zły stan dla zdarzenia →
422 invalid_transition, zmeta.current_state. - Nieznane zdarzenie →
404 unknown_transition, zmeta.allowedwymieniającym poprawne. - Niewystarczająca rola →
403 forbidden. - Start kampanii, która nie przechodzi kontroli wstępnych →
422, po jednym obiekciepreflight_failedna przyczynę (brak domeny, brak odbiorców itd.). - Błędny, wygasły lub wyczerpany kod testu dostarczalności →
422zsource.pointer/data/attributes/code(opis podaje liczbę pozostałych prób).
Forma zbiorcza POST /:type/transitions istnieje w tablicy tras, ale żadne zdarzenie nie
jest obecnie włączone zbiorczo — zwraca 400 not_bulk. Wykonaj pętlę po punkcie końcowym
dla pojedynczych rekordów.
27.8 Tworzenie masowe
Dział zatytułowany „27.8 Tworzenie masowe”Do importu wielu rekordów w jednym żądaniu:
curl -X POST https://platform.phishspot.com/api/v1/contacts/bulk \ -H "Authorization: Bearer $TOKEN" \ -H "X-Account-Id: acct_0pAP5m3J" \ -H 'Content-Type: application/json' \ -d '{ "records": [ { "attributes": { "email": "ada@example.com", "first_name": "Ada" }, "relationships": { "groups": ["grp_44Zx"] } }, { "attributes": { "email": "not-an-email" } } ] }'Odpowiedź to zwykły JSON z jednym wynikiem na rekord wejściowy — błędny wiersz nigdy nie psuje całej partii:
{ "type": "contacts", "results": [ { "index": 0, "status": "ok", "id": "cont_9Qe1" }, { "index": 1, "status": "invalid", "errors": { "email": ["is invalid"] } } ]}status | Znaczenie |
|---|---|
ok | Utworzono; id to nowy identyfikator z prefiksem. |
invalid | Walidacja nie przeszła; errors zawiera komunikaty. |
forbidden | Rola tokenu nie pozwala tworzyć tego typu. |
related_not_found | Wskazany identyfikator relacji nie istnieje lub należy do innego konta. |
Zasady:
- Maksymalnie 100 rekordów na żądanie; nadmiar jest ignorowany.
- Wartości relacji to identyfikatory z prefiksem — pojedynczy dla relacji „do jednego”, tablica dla „do wielu”.
- To tworzenie, nie upsert. Ponowne wysłanie istniejącego adresu e-mail daje wynik
invalid(duplikat), a nie aktualizację. Do zmiany istniejącego rekordu użyjPATCH /contacts/:id. - Dostępne dla każdego typu, którego akcje obejmują
create.
27.9 Raporty i polecenia
Dział zatytułowany „27.9 Raporty i polecenia”Kilka operacji naprawdę nie jest zasobowym CRUD-em. Znajdują się pod stałymi ścieżkami,
przyjmują zwykły application/json i nadal zwracają błędy w formacie JSON:API.
| Punkt końcowy | Zwraca |
|---|---|
POST /campaigns/:id/duplicate | 201 z dokumentem JSON:API nowej kampanii (grupy i odbiorcy skopiowani). |
POST /campaigns/:id/clone_to_account | Sklonuj do innej organizacji, którą administrujesz. Treść {"target_account_id":"acct_…"}. 201 z dokumentem nowej kampanii-szkicu; odbiorcy i grupy nie są kopiowani. meta.name_warning_terms zawiera nazwy organizacji źródłowej wciąż obecne w skopiowanym e-mailu. 422 invalid_target, gdy nie administrujesz organizacji docelowej. |
POST /campaigns/:id/schedule | 200 z dokumentem zaktualizowanej kampanii. Treść {"scheduled_at":"…"}, czytana w strefie czasowej konta. |
GET /campaigns/:id/results | Zbiorcze statystyki kampanii (zwykły JSON). |
GET /campaigns/:id/recipients | Stronicowana lista odbiorców — ?page=, 25 na stronę. Filtry: stage (sent/opened/clicked/submitted/trained/all), replied, group_id, department. |
GET /campaigns/:id/replies | Stronicowane odpowiedzi, od najnowszych (?page=). |
GET /campaigns/:id/timeline | Oś czasu zdarzeń jednego odbiorcy; wymaga ?contact_id=. |
POST /phishing_templates/:id/deploy | 201 z dokumentem utworzonej kampanii. ?quick_launch=true wysyła od razu. |
POST /platform_domains/provision_byod | 201 z dokumentem domeny oraz meta.nameservers i meta.next_step. Treść {"domain_name":"…"}. |
POST /deliverability_tests | Wysyła jeden prawdziwy e-mail. Treść {"secured_domain_id":"sdm_…","sending_domain_id":"pdm_…","recipient_email":"…"} — zabezpieczona domena musi być zweryfikowana, a adres musi należeć dokładnie do niej. 201 z dokumentem testu oraz meta.next_step. Błędy: 422 domain_not_verified / invalid_recipient / sending_domain_unavailable, 429 rate_limited (20 testów na konto na godzinę), 502 send_failed. Wymaga roli administratora lub edytora. |
POST /media | 201 z dokumentem nowego medium. multipart/form-data: medium[name], medium[attachment]. |
POST /blocks/files | 201 z dokumentem nowego bloku. multipart/form-data: block[course_id] oraz zwykłe atrybuty bloku i block[video_file] i/lub block[document_file]. |
POST /blocks/:id/files | 200 z dokumentem zaktualizowanego bloku. multipart/form-data: block[video_file] i/lub block[document_file]. |
POST /reported_messages | 201 z {"id":"rep_…","url":"…"}. multipart/form-data; używane przez dodatek do Outlooka. |
GET /reported_messages/:id/evidence | Zachowane nagłówki, status kompletności i potwierdzone powiązanie z symulacją. Wymaga dostępu do konta; lista i szczegóły zasobu nadal zwracają tylko metadane. |
GET /trends | Raport trendu podatności. ?start_date=&end_date= (ma pierwszeństwo) lub ?range=30d|90d|6m|all. |
Wgrywanie plików
Dział zatytułowany „Wgrywanie plików”Plik nie może jechać w dokumencie JSON:API, więc obie powierzchnie wgrywania to zwykłe
żądania multipart/form-data. Obie nadal zwracają dokument JSON:API przy powodzeniu
i błędy JSON:API przy niepowodzeniu — wysyłaj Accept: application/vnd.api+json
i pozwól swojemu klientowi HTTP ustawić nagłówek Content-Type z granicą multipart.
# Nowe medium w bibliotececurl -X POST "https://platform.phishspot.com/api/v1/media" \ -H "Authorization: Bearer $TOKEN" -H "X-Account-Id: $ACCT" \ -F 'medium[name]=Logo' -F 'medium[attachment]=@logo.png'
# Nowy blok wideo, tworzony razem ze swoim plikiemcurl -X POST "https://platform.phishspot.com/api/v1/blocks/files" \ -H "Authorization: Bearer $TOKEN" -H "X-Account-Id: $ACCT" \ -F 'block[course_id]=crs_7Hb1Xk' -F 'block[name]=Jak rozpoznać fałszywą fakturę' \ -F 'block[genre]=video' -F 'block[video_file]=@lekcja.mp4'
# Podmiana pliku w bloku, który już istniejecurl -X POST "https://platform.phishspot.com/api/v1/blocks/blk_9Kd2Vq/files" \ -H "Authorization: Bearer $TOKEN" -H "X-Account-Id: $ACCT" \ -F 'block[video_file]=@lekcja-v2.mp4'Bloki video i file_download twórz tutaj, nie przez POST /blocks. Te gatunki są
nieprawidłowe bez załącznika, więc tworzenie w JSON:API nie jest w stanie ich wytworzyć.
POST /blocks/files przyjmuje block[course_id] (identyfikator z prefiksem crs_…) oraz
te same atrybuty co powierzchnia JSON:API — name, body, order, genre, metadata,
html_data — i co najmniej jeden plik. POST /blocks/:id/files podmienia plik później,
samodzielnie. Oba przyjmują block[video_file], block[document_file] albo oba naraz
i egzekwują dokładnie te same limity co panel administracyjny:
- Wideo — MP4 (H.264 + AAC) lub WebM (VP8/VP9 + Opus), do 300 MB, maksymalnie 1920×1080 i 10 minut.
- Dokument — dowolny format, do 100 MB.
Odrzucony plik to 422, a złamana reguła trafia do pola detail błędu. Brak pliku — albo
brak block[course_id] przy tworzeniu — to 400 parameter_missing. Kurs lub blok należący
do innego konta to 404, a blok w kursie globalnym, którego nie jesteś właścicielem — 403.
Zapis do bloku, którego kampania już trwa, to 422 — bloki są zablokowane na czas trwania
kampanii.
Powierzchnie poza JSON:API
Dział zatytułowany „Powierzchnie poza JSON:API”Istnieją dla wdrożonych klientów i celowo nie są JSON:API — traktuj ich kształty jako osobne kontrakty:
/api/v1/outlook/*— handshake parowania dodatku do Outlooka i sonda wersji (Rozdział 21)./api/v1/integrations/spam/:token[/:format]— pobranie listy dozwolonych nadawców dla filtra antyspamowego, uwierzytelniane tokenem w ścieżce (Rozdział 22).- Callbacki Mailguna i OAuth, których kształt narzuca strona trzecia.
27.10 Szybki start
Dział zatytułowany „27.10 Szybki start”export TOKEN=… # Ustawienia konta → Tokeny API
# 1. Do jakich kont ma dostęp ten token?curl -s https://platform.phishspot.com/api/v1/accounts \ -H "Authorization: Bearer $TOKEN" -H 'Accept: application/vnd.api+json'
export ACCT=acct_0pAP5m3J
# 2. Lista trwających kampanii, od najnowszychcurl -s "https://platform.phishspot.com/api/v1/campaigns?filter[state]=in_progress&sort=-created_at" \ -H "Authorization: Bearer $TOKEN" -H "X-Account-Id: $ACCT" \ -H 'Accept: application/vnd.api+json'
# 3. Dodanie odbiorcy do grupycurl -s -X POST https://platform.phishspot.com/api/v1/contacts \ -H "Authorization: Bearer $TOKEN" -H "X-Account-Id: $ACCT" \ -H 'Content-Type: application/vnd.api+json' -H 'Accept: application/vnd.api+json' \ -d '{"data":{"type":"contacts","attributes":{"email":"ada@example.com"}, "relationships":{"groups":{"data":[{"type":"groups","id":"grp_44Zx"}]}}}}'
# 4. Start kampaniicurl -s -X POST https://platform.phishspot.com/api/v1/campaigns/camp_0u1kQ9/transitions \ -H "Authorization: Bearer $TOKEN" -H "X-Account-Id: $ACCT" \ -H 'Content-Type: application/json' -d '{"event":"start"}'27.11 Migracja ze starego API
Dział zatytułowany „27.11 Migracja ze starego API”| Stary punkt końcowy | Nowe miejsce |
|---|---|
/accounts/:account_id/<cokolwiek> | Płaska ścieżka + nagłówek X-Account-Id (lub ?account_id=). |
Identyfikatory całkowite (/campaigns/42) | Identyfikatory z prefiksem (/campaigns/camp_0u1kQ9) wszędzie, także w powiązaniach relacji. |
POST /campaigns/:id/{start,stop,pause,cancel,cancel_schedule} | POST /campaigns/:id/transitions z {"event":"start"} itd. |
POST /autopilots/:id/{start,pause,stop} | POST /autopilots/:id/transitions. |
POST /secured_domains/:id/verify_dns | POST /secured_domains/:id/transitions {"event":"verify_dns"}. |
POST /platform_domains/:id/check | POST /platform_domains/:id/transitions {"event":"check"}. |
POST /webhooks/endpoints/:id/toggle | POST /webhook_endpoints/:id/transitions {"event":"toggle"}. |
GET /webhooks/endpoints, GET /webhooks/events | GET /webhook_endpoints, GET /webhook_events. |
POST /groups/:id/add_contacts, DELETE /groups/:id/remove_contacts | POST / DELETE /groups/:id/relationships/contacts. |
POST /accounts/:id/contacts/import | POST /contacts/bulk — tylko tworzy, zob. niżej. |
POST /accounts/:id/media | POST /media (nadal multipart). |
POST /courses/:course_id/blocks z block[video_file] / block[document_file] | POST /blocks/files (multipart, block[course_id]); POST /blocks/:id/files podmienia plik później. |
POST /accounts/:id/reported_messages | POST /reported_messages + X-Account-Id. |
POST /accounts/:id/platform_domains/provision_byod | POST /platform_domains/provision_byod. |
POST /campaigns/:id/{duplicate,schedule} | Te same ścieżki; odpowiedzi to teraz dokumenty JSON:API kampanii. |
GET /campaigns/:id/{results,recipients,replies,timeline} | Te same ścieżki i te same treści. |
GET /accounts/:id/trends | GET /trends. |
POST /phishing_templates/:id/deploy | Ta sama ścieżka. Konto wdrażające pochodzi teraz z X-Account-Id (parametr account_id w treści nadal działa). |
Zmiany kontraktu obowiązujące wszędzie:
- Odpowiedzi to dokumenty JSON:API pod
data, a nie gołe obiekty czy tablice. - Typ MIME to
application/vnd.api+json, a nieapplication/json. - Kolekcje są stronicowane (
page[number]/page[size], domyślnie 25, maks. 200) i podają sumy wmeta.record_count. Stare punkty końcowe zwracały nieograniczone tablice. - Błędy to obiekty błędów JSON:API ze stabilnymi wartościami
code(§27.5). /contacts/bulknie robi upsertu. Stary import dopasowywał istniejących odbiorców po adresie e-mail, aktualizował ich i automatycznie tworzył brakujące grupy. Nowy punkt końcowy tworzy rekordy i zgłasza zduplikowany e-mail jako wynikinvaliddla danej pozycji; grupy muszą już istnieć i są wskazywane identyfikatorem z prefiksem. Jeśli polegałeś na semantyce upsertu, utrzymuj własne mapowanie e-mail → identyfikatorcont_…(albo przewertujGET /contacts) i użyjPATCHna istniejącym rekordzie, lub korzystaj dalej z importu CSV w panelu administracyjnym, który się nie zmienił.- Kilka kodów statusu przesunęło się wraz z modelem: akcja chroniona w złym stanie to teraz
422 invalid_transition(albo403, gdy stara akcja autoryzowała najpierw),checkna domenie, której nie jesteś właścicielem, to403(było404), a dodanie odbiorcy z innego konta do grupy to404(było cicho ignorowane).
Powiązane rozdziały
Dział zatytułowany „Powiązane rozdziały”- Tworzenie i zarządzanie tokenami API w panelu administracyjnym: Rozdział 14 Tokeny API.
- Te same możliwości z poziomu klienta AI w języku naturalnym: Rozdział 29 Serwer MCP.
- Odpowiednik tych punktów końcowych oparty na push: Rozdział 26 Webhooki.
- Punkt końcowy listy dozwolonych nadawców w kontekście: Rozdział 22 Lista dozwolonych filtra antyspamowego.
- Dodatek do Outlooka korzystający z
outlook/version: Rozdział 20 Dodatek do Outlooka.
Raporty filtrowane i kampanie zbiorcze
Dział zatytułowany „Raporty filtrowane i kampanie zbiorcze”GET /api/v1/reports/:id zwraca zasób JSON:API typu campaign_reports. Identyfikator może wskazywać kampanię (camp_…), kampanię zbiorczą (ucmp_…) lub wybrane konto (acct_…). Konto wskazujesz nagłówkiem X-Account-Id. Raport mogą odczytać i pobrać aktywni członkowie konta uprawnieni do obsługi kampanii.
Wielokrotny wybór obsługują filter[department][], filter[title][], filter[location][], filter[risk_level][], filter[domain_status][] i filter[group_id][]. Dopuszczalna jest też pojedyncza wartość tekstowa. Wartości jednej kategorii łączy OR, a poszczególne kategorie — AND. Poziomy ryzyka to low, medium, high, critical; statusy domen to verified, unverified. Grupy wskazujesz publicznymi identyfikatorami. filter[search] przeszukuje imiona, nazwiska i adresy e-mail. Filtry logiczne kontaktów to ever_clicked, ever_submitted, never_targeted oraz completed_training. Raport obsługuje również stage (all, sent, opened, clicked, submitted, trained) i replied.
Parametr group_by przyjmuje department, title, location lub group. Pominięcie go oznacza brak grupowania. page[number] wybiera stronę zawierającą 25 udziałów. Odpowiedź obejmuje summary, numerowane scenarios, podsumowania sekcji groups oraz wiersze recipients. W meta znajdują się liczba wszystkich wierszy i dostępne opcje filtrów. Przynależność do grup jest aktualna, więc sekcje mogą się nakładać. Ogólne statystyki liczą każdą parę kampania–kontakt tylko raz. Procenty odnoszą się do udanych wysyłek; niewysłane wiadomości i błędy doręczenia są liczone osobno.
GET /api/v1/reports/:id/download przyjmuje te same filtry i grupowanie oraz format_type (pdf, docx, markdown, rtf, csv), report_template_id i anonymous=true. Przy powodzeniu odpowiedzią jest plik z właściwym typem mediów, a przy błędzie — dokument błędów JSON:API. Wersja anonimowa zastępuje dane osobowe fikcyjnymi i pomija treści odpowiedzi, odpowiedzi z quizów oraz podglądy. Łącze links.related w odpowiedzi raportu zachowuje jego filtry przy pobieraniu.
Kampanie zbiorcze
Dział zatytułowany „Kampanie zbiorcze”/api/v1/umbrella_campaigns udostępnia standardowe operacje CRUD JSON:API oraz zapisywalną relację campaigns. Nazwę zapisujesz w atrybucie name, a scenariusze wskazujesz publicznymi identyfikatorami kampanii. Zastąpienie relacji ustawia całą listę; POST i DELETE relacji dodają lub odłączają wskazane scenariusze. Każdy scenariusz musi należeć do wybranego konta, mieć identyczną akcję po kliknięciu wraz z jej ustawieniami i należeć najwyżej do jednej kampanii zbiorczej. Przed zmianą jego akcji należy odłączyć go osobnym żądaniem. Usunięcie kampanii zbiorczej wymaga ?confirm=true lub meta.confirm: true na najwyższym poziomie dokumentu i zachowuje scenariusze.
{ "data": { "type": "umbrella_campaigns", "attributes": {"name": "Jesienna akcja edukacyjna"}, "relationships": { "campaigns": {"data": [{"type": "campaigns", "id": "camp_…"}]} } }}Szablony raportów
Dział zatytułowany „Szablony raportów”/api/v1/report_templates obsługuje standardowe CRUD. Klient odczytuje szablony wbudowane i przypisane do jego konta. Administrator platformy zarządza własnymi szablonami i widzi wszystkie przypisania. Wbudowane szablony są chronione przed edycją i usunięciem: clean pomija logotypy, a cyber zapewnia domyślne oznaczenia Cyberbezpiecznego Samorządu.
Zapisywalne atrybuty to name_translations, header_translations, footer_translations, introduction_translations (obiekty z tekstami en/pl), accent_color (#RRGGBB), account_ids (publiczne identyfikatory kont), logo_data i footer_logo_data (PNG/JPEG zakodowane base64, do 2 MB po odkodowaniu i 16 megapikseli). Obie wersje nazwy są wymagane i mają limit 120 znaków; pozostałe teksty — 4000 znaków na język. Obrazy są dopasowywane do raportu, a ich metadane usuwane. Pusta wartość obrazu usuwa go; pominięte pole zachowuje dotychczasowy obraz. Przypisania kont są widoczne tylko dla administratora platformy.
Aby skopiować szablon, utwórz nowy przez POST z copy_from: "rtpl_…" i wybranymi zmianami. Usunięcie wymaga potwierdzenia tak jak przy kampanii zbiorczej. Identyfikator dostępnego szablonu podajesz przy eksporcie; bez niego wybierany jest cyber. Tworzenie masowe obu nowych typów działa przez istniejący kontrakt /api/v1/:type/bulk, z tymi samymi uprawnieniami i walidacją.
Automatyczne programy edukacyjne
Dział zatytułowany „Automatyczne programy edukacyjne”POST /autopilots tworzy wersję roboczą; POST /autopilots/:id/transitions z {"event":"start"} włącza cykliczną wysyłkę. Minimalne żądanie utworzenia wymaga name; odbiorcy, intensywność i pola zaawansowane korzystają z wartości domyślnych. Dla wybranych grup ustaw all_groups=false i zapisywalną relację groups. Pole country przyjmuje kod kraju, a industry_naics kod NAICS — bez wewnętrznych identyfikatorów bazy danych.
Odczyt autopilota zawiera execution (najbliższą i ostatnią wysyłkę, liczby uczestników i wiadomości, stan i przyczynę blokady), recent_dispatches oraz recent_activity (po maksymalnie 20 wpisów). Są to pola tylko do odczytu. Wysyłka zawiera publiczne identyfikatory kontaktu i kampanii, temat, trudność, powód wyboru oraz status. accepted oznacza przyjęcie przez usługę pocztową; delivered wymaga potwierdzenia doręczenia.
Wstrzymanie: {"event":"pause"}. Trwałe zatrzymanie wymaga {"event":"stop","args":{"confirm":true}}. Usunięcie wymaga DELETE /autopilots/:id?confirm=true; działający lub wstrzymany program trzeba wcześniej zatrzymać. Wznowienie nie nadrabia zaległych wiadomości. Zadania w kolejce ponownie sprawdzają stan programu i kwalifikację odbiorcy. Wszystkie autopiloty konta mają wspólny limit dwóch wiadomości na kontakt dziennie i odstęp minimum czterech godzin. Kampanii zarządzanych przez autopilota nie można ręcznie wysłać ponownie ani resetować ich uczestników.
Obsługa załączników wiadomości
Dział zatytułowany „Obsługa załączników wiadomości”Każde żądanie zawiera parent_type=campaign|template i publiczny parent_id odpowiedniego zasobu. Obowiązują uwierzytelnianie konta i uprawnienia do treści; dodawanie i usuwanie wymaga roli administratora lub edytora oraz edytowalnej treści.
| Endpoint | Wynik |
|---|---|
GET /email_attachments | Lista JSON:API: identyfikatory eatt_…, nazwy, typy MIME, rozmiary w bajtach i adresy pobierania. |
POST /email_attachments | Wgrywanie multipart: file, parent_type, parent_id; zwraca listę i HTTP 201. |
DELETE /email_attachments/:id | Usuwa jeden załącznik; wymaga confirmed=true; zwraca pozostałą listę. |
Dozwolone: PDF, DOCX, PNG, JPEG i GIF; 5 MB na plik, 10 MB łącznie, 10 plików. Przesyłaj bajty pliku, nie token istniejącego pliku ani zewnętrzny URL. Błędy walidacji zwracają JSON:API z HTTP 422.
Historia szkoleń i certyfikaty
Dział zatytułowany „Historia szkoleń i certyfikaty”GET /training_records?campaign_id=camp_…&page=1 zwraca zasoby JSON:API training_records: 25 uczestników na stronę, po 50 ostatnich prób, łączną liczbę prób i metadane wystawionego certyfikatu. GET /training_records/:certificate_id/certificate?campaign_id=camp_… zwraca PDF (application/pdf). Obie operacje wymagają aktywnego członkostwa na koncie i prawa odczytu kampanii. Identyfikatory certyfikatów zaczynają się od tco_. Te endpointy nie wystawiają certyfikatów ani nie zmieniają zaliczenia.
Integracja katalogu Microsoft Entra
Dział zatytułowany „Integracja katalogu Microsoft Entra”Aktywny administrator konta może odczytać zgodę, wybrane grupy, harmonogram, stan kolejki i 50 ostatnich wyników synchronizacji przez GET /api/v1/directory_integration. Odpowiedź nie zawiera tokenów ani danych uwierzytelniających. PATCH przyjmuje JSON:API data.attributes: allowed_group_ids (maksymalnie 50 identyfikatorów GUID grup), sync_users_enabled, sync_groups_enabled i sync_schedule (off, hourly, daily, weekly).
POST /api/v1/directory_integration/consent przyjmuje tenant_id i opcjonalne ustawienia, zwracając meta.consent_url do uzyskania zgody administratora Microsoft. POST /api/v1/directory_integration/sync kolejkuje jeden oczekujący przebieg. DELETE wymaga wartości logicznej JSON confirmed: true; kontakty i grupy pozostają w platformie. Podczas oczekującej lub trwającej synchronizacji zmiana ustawień i odłączenie są zablokowane. Konto demo wymaga wybrania odizolowanej grupy przed włączeniem synchronizacji i importuje wyłącznie skrzynki dopuszczone do wysyłki demo.
Atrybuty JSON:API kampanii obejmują training_only (wartość logiczna, dostępna do odczytu i zmiany przed uruchomieniem). true wymaga end_action_type: "redirect_to_course", relacji course wskazującej dostępny kurs i landing_page_enabled: false. Sekwencja przyjmuje assign_training jako pierwszy krok oraz completed_training jako etykietę warunku. source_step_id musi wskazywać wcześniejszy krok wiadomości lub szkolenia na tej samej ścieżce. Warunek spełnia wyłącznie potwierdzone zaliczenie.
Treść edytora wizualnego
Dział zatytułowany „Treść edytora wizualnego”GET /api/v1/visual_content zwraca html, css i project. Podaj parent_type (campaign, template, sequence), publiczny parent_id i field (email_content, landing_html, end_action_html); dla sekwencji również step_id. PATCH przyjmuje ten sam wybór oraz obiekt JSON:API data z type: "visual_content" i atrybutami html, opcjonalnym css (tylko strona docelowa) i project. Projekt zawiera version: 1, zgodne html i css oraz obiekt GrapesJS data. Limit projektów to 2 MB na rekord. Przy bezpośredniej zmianie HTML pomiń projekt. Obowiązują te same uprawnienia i blokady stanu co w panelu. Operacja nie wysyła wiadomości.
Raporty okresowe i pakiet przekazania
Dział zatytułowany „Raporty okresowe i pakiet przekazania”GET /api/v1/period_report zwraca zasób JSON:API period_reports dla wybranego konta. Opcjonalne from i to to daty ISO (RRRR-MM-DD), obejmowane w całości, z zakresem najwyżej dziesięciu lat. Domyślnie jest to ostatni rok do dzisiaj. Atrybuty zawierają wyniki bieżącego i poprzedniego okresu, zmiany w punktach procentowych, kampanie z publicznymi identyfikatorami, historię kwartalną, aktualne działy, metodologię i rekomendacje. Odpowiedź nie zawiera rekordów odbiorców.
GET /api/v1/period_report/download?format_type=pdf stosuje te same daty i wybór konta. Formaty: pdf, csv, json, zip. Raporty są dostępne dla aktywnych członków konta; ZIP wymaga roli administratora lub redaktora i zawiera treści oraz konfiguracje do ponownego użycia (do 128 MB). Nieznany format lub błędne daty zwracają błędy JSON:API 422. Nieaktywne członkostwo i brak uprawnień blokują dostęp. ZIP jest pakietem administracyjnym, a nie automatycznym odtworzeniem bazy. Pomija odbiorców, odpowiedzi uczestników, kody aktywacji i dane dostępowe integracji.
Delegowane połączenia skrzynek Outlook
Dział zatytułowany „Delegowane połączenia skrzynek Outlook”Administrator konta pobiera GET /api/v1/outlook_mailbox_connections z X-Account-Id: acct_…. JSON:API zwraca ostatnie 500 zasobów outlook_mailbox_connections: publiczny contact_id, e-mail, nazwę urządzenia, stan i czas połączenia oraz bezpieczny kod błędu. Dane uwierzytelniające OAuth i treść poczty nie są zwracane. DELETE /api/v1/outlook_mailbox_connections/omc_… wymaga JSON {"confirmed":true}; usuwa zapisany dostęp delegowany, zachowując aktywację urządzenia i wcześniejsze zgłoszenia.
Aktywowany dodatek Outlook korzysta z osobnego kontraktu klienta (zwykły JSON, token bearer z reported_messages:create i przypisany X-Account-Id):
| Metoda i ścieżka | Wynik |
|---|---|
GET /api/v1/outlook/mailbox | Stan połączenia bez danych uwierzytelniających. |
POST /api/v1/outlook/mailbox | Krótkotrwały connect_url do otwarcia w przeglądarce użytkownika i logowania Microsoft. |
POST /api/v1/outlook/mailbox/report | Przesłanie item_id i/lub internet_message_id wybranej wiadomości; serwer pobiera MIME i zwraca tylko id oraz url zgłoszenia. Brak lub cofnięcie dostępu zwraca 409 authorization_required. |
DELETE /api/v1/outlook/mailbox | Odłączenie dostępu delegowanego z {"confirmed":true}. |
DELETE /api/v1/outlook/mailbox/revoke_device | Unieważnienie aktywacji i powiązanych danych dostępu z {"confirmed":true}. |
Polecenia odrzucają zwykłe tokeny konta i kontakty z obcego konta. UPN logowania Microsoft musi odpowiadać adresowi sparowanego kontaktu. Zgoda wymaga interaktywnego logowania i nie może być nadana przez API.