Przejdź do głównej zawartości

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.

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:

Okno terminala
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…" }
}
}
PoleTypOpis
emailstringWymagane. Adres e-mail użytkownika.
passwordstringWymagane. Hasło użytkownika.
otp_attemptstringWymagane 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.

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_0pAP5m3JOwak9HY7DbjLBKYg

albo — 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:

Okno terminala
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.

  • Adres bazowy: https://platform.phishspot.com/api/v1. Wszystkie ścieżki poniżej są względem niego.
  • Typ MIME: application/vnd.api+json w obu nagłówkach Content-Type i Accept dla punktów końcowych zasobów. Błędny Content-Type to 415, błędny Accept to 406. Zwykłe punkty końcowe poleceń (§27.9) akceptują także application/json.
  • Koperta: odpowiedzi to dokumenty JSON:API — element data (obiekt lub tablica dla kolekcji), opcjonalne included oraz meta.
  • 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ściowe scheduled_at kampanii jest interpretowane w strefie czasowej konta.
  • links.self nigdy nie jest zwracane. Buduj adresy URL z type i id dokumentu.

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.

TypPrefiksTypPrefiks
campaignscamp_…platform_domainspdm_…
contactscont_…secured_domainssdm_…
groupsgrp_…eventsevt_…
deliverablesdelv_…resultsres_…
phishing_templatestmpl_…mediamed_…
phishing_template_categoriestcat_…reported_messagesrep_…
coursescourse_…webhook_endpointswhep_…
blocksblk_…webhook_eventswhev_…
autopilotsauto_…accounts / usersacct_… / user_…
deliverability_testsdtst_…

Identyfikator, którego nie da się zdekodować — zniekształcony prefiks albo prefiks innego typu — zwraca 404, nigdy 400.

Okno terminala
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" }
}
}

Każdy punkt końcowy zwracający kolekcję przyjmuje te same cztery rodziny parametrów.

?page[number]=2&page[size]=50

Domyś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ą.

?sort=-created_at # malejąco
?sort=name,-created_at # wiele kluczy

Sortować 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.

?filter[state]=in_progress # dowolna kolumna enum
?filter[campaign]=camp_0u1kQ9 # dowolne belongs_to, po identyfikatorze z PREFIKSEM
?filter[state]=draft,paused # przecinek = LUB

Każ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.

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.

?include=groups,course # dołącz powiązane zasoby do `included`
?fields[campaigns]=name,state # zwróć tylko te atrybuty

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>.

codeHTTPZnaczenie
(pusta treść)401Brakujący, nieznany lub wygasły token.
invalid_credentials401Błędny e-mail/hasło w POST /auth.
invalid_otp_attempt401Błędny kod 2FA.
account_required400Nie wybrano konta albo wybrano konto, do którego użytkownik nie należy.
parameter_missing400Brakuje wymaganego parametru najwyższego poziomu.
bad_request400Zniekształcona wartość — najczęściej enum spoza dozwolonego zbioru.
missing_records400POST /:type/bulk bez tablicy records.
not_supported400Tworzenie masowe dla typu, który nie wspiera tworzenia.
not_bulk400Zdarzenie przejścia nie ma formy zbiorczej.
forbidden403Uwierzytelniono, ale rola użytkownika nie pozwala na tę akcję.
read_only_relationship403Próba zapisu do relacji tylko do odczytu.
not_found404Brak takiego rekordu w tym koncie — także odpowiedź dla identyfikatora, którego nie da się zdekodować.
unknown_type404Segment :resource_type nie jest typem API.
unknown_transition404Brak takiego zdarzenia dla tego typu; meta.allowed wymienia poprawne.
otp_attempt_required4222FA jest włączone, a kodu nie przesłano.
unprocessable_content422Walidacja nie przeszła; zob. source.pointer.
invalid_transition422Zdarzenie nie ma zastosowania w bieżącym stanie rekordu.
missing_args422Przejście wymaga argumentów, których nie przesłano.
preflight_failed422Kampania nie przeszła kontroli przed startem — po jednym obiekcie błędu na przyczynę.
invalid_schedule422POST /campaigns/:id/schedule otrzymał nieużyteczny czas.
quick_launch_failed422Szybkie uruchomienie szablonu nie znalazło zweryfikowanej domeny lub odbiorców.
provision_failed422Provisioning BYOD odrzucony (pusta, zajęta lub nieprawidłowa domena).
invalid_date422Data 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ą.

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.

TypAkcjePrzejściaUwagi
campaignsindex show create update destroystart stop pause cancel cancel_schedulegroups to relacja zapisywalna; platform_domain i course to powiązania. statistics jest null, dopóki kampania nie wystartuje.
contactsindex show create update destroy—groups to relacja zapisywalna. Tworzenie masowe wspierane.
groupsindex show create update destroy—Członkostwo przez …/relationships/contacts.
deliverablesindex 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_templatesindex show—Tylko do odczytu. index pomija treści; pobierz pojedynczy szablon, aby je uzyskać.
phishing_template_categoriesindex—Płaska lista tylko do odczytu; hierarchię odtworzysz przez relację parent.
coursesindex show create update destroy—blocks to relacja tylko do odczytu.
blocksindex 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).
autopilotsindex show create update destroystart pause stopgroups zapisywalne; campaigns tylko do odczytu.
secured_domainsindex show create destroyverify_dnsBrak update.
deliverability_testsindex showconfirm mark_not_received refresh_statusHistoria 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_domainsindex show create update destroycheckZapis wymaga administratora platformy, nie administratora konta.
eventsindex 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.
resultsindex show create update destroy—Powiązania do block i contact.
mediaindex show update destroy—Brak tworzenia w JSON:API — wgrywaj przez POST /media (§27.9).
reported_messagesindex show—Tylko do odczytu. Przyjmowanie przez POST /reported_messages (§27.9).
webhook_endpointsindex show create update destroytogglesigning_secret jest ukryty w index, zwracany w show.
webhook_eventsindex show—Dziennik dostarczeń, tylko do odczytu.
accountsindex 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.

Okno terminala
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.

Powiązane rekordy są łączone identyfikatorami z prefiksem, a relacje „do wielu” mają własne punkty końcowe:

Okno terminala
# Dodanie odbiorców do grupy
curl -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ęcie
curl -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.

Zmiany stanu nie są własnymi czasownikami. Każde zdarzenie procesu, dla każdego typu, przechodzi przez jeden punkt końcowy:

Okno terminala
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ć.

TypZdarzenia
campaignsstart, stop, pause, cancel, cancel_schedule
autopilotsstart, pause, stop
secured_domainsverify_dns
deliverability_testsconfirm (argumenty {"code":"123456"}), mark_not_received, refresh_status
platform_domainscheck
webhook_endpointstoggle

Tryby niepowodzenia:

  • Zły stan dla zdarzenia → 422 invalid_transition, z meta.current_state.
  • Nieznane zdarzenie → 404 unknown_transition, z meta.allowed wymieniającym poprawne.
  • Niewystarczająca rola → 403 forbidden.
  • Start kampanii, która nie przechodzi kontroli wstępnych → 422, po jednym obiekcie preflight_failed na przyczynę (brak domeny, brak odbiorców itd.).
  • Błędny, wygasły lub wyczerpany kod testu dostarczalności → 422 z source.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.

Do importu wielu rekordów w jednym żądaniu:

Okno terminala
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"] } }
]
}
statusZnaczenie
okUtworzono; id to nowy identyfikator z prefiksem.
invalidWalidacja nie przeszła; errors zawiera komunikaty.
forbiddenRola tokenu nie pozwala tworzyć tego typu.
related_not_foundWskazany 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żyj PATCH /contacts/:id.
  • Dostępne dla każdego typu, którego akcje obejmują create.

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ńcowyZwraca
POST /campaigns/:id/duplicate201 z dokumentem JSON:API nowej kampanii (grupy i odbiorcy skopiowani).
POST /campaigns/:id/clone_to_accountSklonuj 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/schedule200 z dokumentem zaktualizowanej kampanii. Treść {"scheduled_at":"…"}, czytana w strefie czasowej konta.
GET /campaigns/:id/resultsZbiorcze statystyki kampanii (zwykły JSON).
GET /campaigns/:id/recipientsStronicowana lista odbiorców — ?page=, 25 na stronę. Filtry: stage (sent/opened/clicked/submitted/trained/all), replied, group_id, department.
GET /campaigns/:id/repliesStronicowane odpowiedzi, od najnowszych (?page=).
GET /campaigns/:id/timelineOś czasu zdarzeń jednego odbiorcy; wymaga ?contact_id=.
POST /phishing_templates/:id/deploy201 z dokumentem utworzonej kampanii. ?quick_launch=true wysyła od razu.
POST /platform_domains/provision_byod201 z dokumentem domeny oraz meta.nameservers i meta.next_step. Treść {"domain_name":"…"}.
POST /deliverability_testsWysył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 /media201 z dokumentem nowego medium. multipart/form-data: medium[name], medium[attachment].
POST /blocks/files201 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/files200 z dokumentem zaktualizowanego bloku. multipart/form-data: block[video_file] i/lub block[document_file].
POST /reported_messages201 z {"id":"rep_…","url":"…"}. multipart/form-data; używane przez dodatek do Outlooka.
GET /reported_messages/:id/evidenceZachowane 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 /trendsRaport trendu podatności. ?start_date=&end_date= (ma pierwszeństwo) lub ?range=30d|90d|6m|all.

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.

Okno terminala
# Nowe medium w bibliotece
curl -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 plikiem
curl -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ż istnieje
curl -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.

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.
Okno terminala
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 najnowszych
curl -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 grupy
curl -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 kampanii
curl -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"}'
Stary punkt końcowyNowe 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_dnsPOST /secured_domains/:id/transitions {"event":"verify_dns"}.
POST /platform_domains/:id/checkPOST /platform_domains/:id/transitions {"event":"check"}.
POST /webhooks/endpoints/:id/togglePOST /webhook_endpoints/:id/transitions {"event":"toggle"}.
GET /webhooks/endpoints, GET /webhooks/eventsGET /webhook_endpoints, GET /webhook_events.
POST /groups/:id/add_contacts, DELETE /groups/:id/remove_contactsPOST / DELETE /groups/:id/relationships/contacts.
POST /accounts/:id/contacts/importPOST /contacts/bulk — tylko tworzy, zob. niżej.
POST /accounts/:id/mediaPOST /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_messagesPOST /reported_messages + X-Account-Id.
POST /accounts/:id/platform_domains/provision_byodPOST /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/trendsGET /trends.
POST /phishing_templates/:id/deployTa 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 nie application/json.
  • Kolekcje są stronicowane (page[number]/page[size], domyślnie 25, maks. 200) i podają sumy w meta.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/bulk nie 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 wynik invalid dla 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 → identyfikator cont_… (albo przewertuj GET /contacts) i użyj PATCH na 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 (albo 403, gdy stara akcja autoryzowała najpierw), check na domenie, której nie jesteś właścicielem, to 403 (było 404), a dodanie odbiorcy z innego konta do grupy to 404 (było cicho ignorowane).

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.

/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_…"}]}
}
}
}

/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ą.

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.

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.

EndpointWynik
GET /email_attachmentsLista JSON:API: identyfikatory eatt_…, nazwy, typy MIME, rozmiary w bajtach i adresy pobierania.
POST /email_attachmentsWgrywanie multipart: file, parent_type, parent_id; zwraca listę i HTTP 201.
DELETE /email_attachments/:idUsuwa 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.

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.

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.

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.

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.

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żkaWynik
GET /api/v1/outlook/mailboxStan połączenia bez danych uwierzytelniających.
POST /api/v1/outlook/mailboxKrótkotrwały connect_url do otwarcia w przeglądarce użytkownika i logowania Microsoft.
POST /api/v1/outlook/mailbox/reportPrzesł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/mailboxOdłączenie dostępu delegowanego z {"confirmed":true}.
DELETE /api/v1/outlook/mailbox/revoke_deviceUnieważ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.