Konwencje stosowane w API

Wszystkie operacje API trzymają się zbioru określonych konwencji. Są one szczegółowo opisane w tym dokumencie.

Metody HTTP

API korzysta z czterech metod HTTP.

GET

Metoda GET służy do pobierania danych z API, zarówno pojedynczych obiektów, jak i większych ich zbiorów. O ile nie jest powiedziane inaczej, wywołania GET są bezpieczne i idempotentne - nie zmieniają stanu obiektów. Wszędzie tam, gdzie można użyć metody GET, można także użyć metody HEAD, aby pobrać jedynie nagłówki odpowiedzi.

POST

Wywołania POST służą dwóm celom:

Wywołania POST nie są bezpieczne ani idempotentne. W większości przypadków wymagają określenia treści zapytania w formacie JSON. Każdą z operacji zaimplementowanych jako POST należy traktować jako potencjalnie nie wpasowującą się w żadne schematy. Dotyczy to także operacji tworzenia obiektów - zazwyczaj przy tej operacji nie jest wysyłany cały docelowy obiekt (choćby dlatego, że z reguły nie ma on jeszcze identyfikatora), a czasami konieczne jest podanie dodatkowych danych (np. danych użytkownika przy tworzeniu konta).

PUT

Wywołania PUT służą najczęściej do modyfikacji istniejących obiektów. Wymagają przesłania zakodowanej w JSON nowej reprezentacji (lub fragmentu reprezentacji) obiektu. W niektórych przypadkach mogą służyć do utworzenia lub modyfikacji obiektów, jeśli identyfikator obiektu jest znany przed jego utworzeniem. Wywołania PUT są idempotentne (wielokrotne wywołania mają taki sam efekt, jak jedno), ale nie bezpieczne (mogą zmieniać stan obiektów).

DELETE

Wywołania DELETE służą do usuwania obiektów. Podobnie jak PUT, są idempotentne, ale nie bezpieczne.

Nagłówki HTTP

API wykorzystuje następujące standardowe nagłówki zapytania HTTP:

  • Content-Type - typ zawartości. W większości przypadków będzie to application/json. Nagłówek ten jest wymagany jeśli operacja wymaga przesłania treści w zapytaniu.
  • Accept-Language - preferowany język użytkownika (dostępne: "pl" - polski i "en" - angielski). Wykorzystywany do tłumaczenia komunikatów błędów API i niektórych ze zwracanych wartości.
  • Authorization - dane autentykacyjne. Dostępne schematy to Basic (standardowa autentykacja HTTP) i Bearer (token OAuth2).
  • If-None-Match - wartość ETag zapamiętanego wyniku zapytania GET (używane przy cache'owaniu pobranej zawartości).
  • Cache-Control, Vary - nagłówki odpowiedzialne za cache'owanie odpowiedzi API.

Niektóre ze standardowych nagłówków odpowiedzi API:

  • Content-Type - typ zawartości odpowiedzi.
  • WWW-Authenticate - sposób autentykacji, w przypadku błędu 401.
  • Allow - lista metod HTTP dostępnych dla obiektu.
  • Location - adres URL utworzonego obiektu.
  • X-Total-Count - całkowita liczba znalezionych obiektów (bez paginacji) w przypadku pobierania listy obiektów.

Niektóre z operacji mogą definiować własne nagłówki. W takim przypadku ich znaczenie zostanie wyjaśnione w dokumentacji konkretnej operacji.

Obsługa błędów

Wywołanie API może zakończyć się błędem - w takim przypadku kod odpowiedzi HTTP będzie jednym z następujących:

  • 500 Internal Server Error - wewnętrzny błąd API. Występowanie tego typu błędów prosimy zgłaszać do Biura Obsługi Klienta.
  • 400 Bad Request - treść zapytania zawiera niepoprawnie sformatowany JSON.
  • 401 Unauthorized - błąd autentykacji, np. niepoprawne dane logowania lub nieaktualny token sesji.
  • 403 Forbidden - błąd autoryzacji, np. próba usunięcia obiektu, którego nie możemy usunąć.
  • 404 Not Found - próba odwołania się do nieistniejącego obiektu.
  • 405 Method Not Allowed - wywołana metoda HTTP nie jest dostępna dla wybranego zasobu.
  • 409 Conflict - nie można wykonać operacji na zasobie ze względu na konflikt z aktualnym stanem zasobu; np. próba zmiany wartości pola obiektu, którego nie można zmieniać.
  • 415 Unsupported Media Type - nagłówek Content-Type zapytania jest inny niż application/json.
  • 422 Unprocessable Entity - błędy walidacji, np. podanie zbyt długiej wartości tekstowej.

Wszystkie inne kody błędów, błędy warstwy niższej niż HTTP oraz błędy bez ustawionego nagłówka Content-Type: application/json należy traktować jak 500 Internal Server Error.

Jeśli błędna odpowiedź zawiera nagłówek Content-Type: application/json, to treść odpowiedzi zawiera bardziej szczegółowe informacje o błędzie niż sam kod HTTP. Na przykład próba wykonania zapytania:

curl 'https://api.monit24.pl/v3/sensors?limit=foo&fields=id,bar'

zakończy się błędem walidacji (422) podobnym do poniższego:

[
   {
      "code" : 4222064,
      "description" : "Niepoprawna wartość.",
      "error" : "incorrect_value",
      "field" : "query.fields[1]",
      "message" : "Allowed values: \"city\", \"continent\", \"country\", \"id\", \"is_available\", \"links\", \"name\""
   },
   {
      "code" : 4220555,
      "description" : "Niepoprawny typ wartości.",
      "error" : "incorrect_type",
      "field" : "query.limit",
      "message" : "Value has to be of the type integer"
   }
]

Opis błędu jest JSON-ową tablicą obiektów, z których każdy opisuje jeden "problem" z przesłanym zapytaniem. Obiekt ten zawiera pola:

  • code - wewnętrzny 7-cyfrowy kod błędu, którego pierwsze 3 cyfry odpowiadają statusowi HTTP. Kod ten odpowiada jednoznacznie tekstowej wartości w polu error, bardziej przyjaznej użytkownikowi. Dokumentacja każdej z operacji API zawiera szczegółową listę możliwych kodów błędów.
  • description - opis błędu dla programisty, w języku polskim. Nie jest przeznaczony do wyświetlenia użytkownikowi końcowemu i nie jest obowiązkowy.
  • error - szczegółowy symbol błędu. Symbol ten odpowiada jednoznacznie liczbowemu kodowi błedu w polu code.
  • field - "wskaźnik" na fragment zapytania, który spowodował błąd. Składnia tego pola jest następująca: kropka oznacza atrybut obiektu, zaś liczbowy indeks w nawiasach kwadratowych - element tablicy. Na przykład query.fields[1] ozancza drugą wartość w polu tablicy fields przekazanej w parametrze GET (podobnie data oznacza JSON-ową treść zapytania POST lub PUT). To pole jest opcjonalne, może zostać wykorzystane np. do przypisania komunikatów błędów do odpowiednich pól formularza przedstawianego użytkownikowi końcowemu.
  • message - komunikat do wyświetlenia użytkownikowi, w języku polskim lub angielskim, w zależności od nagłówka Accept-Language zapytania.

Standardowe operacje

Większość operacji API operujących na obiektach trzyma się standardowych schematów interfejsu i reguł działania. Są one szczegółowo opisane w tym miejscu.

Niestandardowe operacje

Niektóre operacje API nie wpasowują się łatwo w standardowe schematy opisane powyżej lub wpasowują się jedynie częściowo. Takie niestandardowe operacje dzielą się na dwie kategorie:

  • operacje służące jedynie do pobierania pewnych danych,
  • operacje modyfikujące dane w systemie.

Pierwsza z tych kategorii zawiera na przykład operacje pobierania danych do wykresów (charts), operacja pobierania danych Service Level z wybranego przedziału czasowego, operacja pobierania pliku HAR, czy skompresowanej zawartości strony z momentu awarii. Wszystkie tego typu operacje wykorzystują metodę HTTP GET. Definiują własny format (i być może typ) odpowiedzi, mogą także określać niestandardowe parametry i nagłówki. Wyjątkiem są operacje "zaawansowanego pobierania", (POST */search), które są zaimplementowane jako POST ze względu na ograniczenia GET (operacje te działają podobnie do pobierania listy obiektów, ale zamiast w parametrach GET parametry są przekazywane jako JSON, w treści zapytania).

Operacje z drugiej kategorii to np. operacje modyfikujące wiele obiektów jednocześnie ("bulk"), operacje wymuszania analiz, wysyłania testowych SMS, zmiany hasła do konta, czy rejestracji konta. Wszystkie wykorzystują metodę HTTP POST i definiują własny format zapytania oraz odpowiedzi. Wyjątkiem jest operacja aktywacji konta, która wykorzystuje metodę GET ze względu na to, że jej adres URL służy jako link aktywacyjny w wiadomości potwierdzającej adres e-mail użytkownika.

Data i czas

Format zapisu wartości określających moment w czasie odpowiada tokenowi date-time gramatyki zdefiniowanej w punkcie 5.6 RFC 3339 ("Internet Date/Time Format"). Format ten jest podzbiorem standardu ISO 8601. Przykłady poprawnych określeń daty i czasu w tym formacie:

  • 2016-01-01T00:00:00Z
  • 2000-02-15T15:30:45+01:00
  • 1980-11-30T08:00:30-08:00

Ze względów technicznych API nie obsługuje dat wcześniejszych niż 1970-00-00T00:00:00Z oraz późniejszych niż 9999-12-31T23:59:59Z.

Przekazując daty w tym formacie do API można użyć dowolnego przesunięcia strefy czasowej, ale jest wymagane jego jawne określenie, aby uniknąć niejednoznaczności. Znak "T" można zastąpić pojedynczą spacją.

Można także alternatywnie użyć pojedynczej nieujemnej liczby całkowitej w miejsce czasu - zostanie ona zinterpretowana jako timestamp uniksowy. Liczby ujemne są interpretowane jako określenie czasu w przeszłości względem aktualnego czasu, np. "-7200" oznacza "2 godziny temu".

API zawsze zwraca czas w formacie RFC 3339, z przesunięciem Z (+00:00, UTC).

Autentykacja

API udostępnia 4 metody autentykacji: HTTP Basic Auth, OAuth2, operacje na zasobie auth_token oraz na zasobie sessions.

HTTP Basic Auth

Najprostsza metoda autentykacji. W tej metodzie autentykacja przebiega poprzez wysłanie odpowiednio zakodowanego loginu i hasła użytkownika w nagłówku Authorization zapytania HTTP do API. W programie curl służy do tego opcja -u, na przykład:

curl -u USERNAME:PASSWORD 'https://api.monit24.pl/v3/services'

W przypadku użycia tej metody autentykacji, zasięg (customer lub administrator) zostanie wykryty automatycznie.

sessions

Możliwa jest także autentykacja za pomocą operacji na obiektach sesji. Sesję można utworzyć wykonując zapytanie POST z loginem i hasłem na adres /sessions, na przykład:

curl -X POST -H 'Content-Type: application/json' -d '{"username":"USERNAME","password":"PASSWORD"}' \
   'https://api.monit24.pl/v3/sessions'
{
   "id" : "1c24bf6c90ecc2be5330ccef6dcd225846098197",
   "token" : "a42657354f9c90b9f4e25225e9c644a5"
}

W odpowiedzi otrzymujemy identyfikator utworzonego obiektu sesji oraz token dostępowy stowarzyszony z nią. Nie jest możliwe późniejsze pobranie tego tokena, dlatego należy go zapamiętać po stronie klienta API.

Token można wykorzystać do autentykacji przekazując go w nagłówku Authorization z typem "Bearer", na przykład:

curl -H 'Authorization: Bearer a42657354f9c90b9f4e25225e9c644a5' https://api.monit24.pl/v3/services

Informacje o aktualnie posiadanej sesji (lub innej, znając jej identyfikator) można pobrać za pomocą operacji GET /sessions/my_session:

curl -H 'Authorization: Bearer a42657354f9c90b9f4e25225e9c644a5' 'https://api.monit24.pl/v3/sessions/my_session'

Przykładowa odpowiedź:

{
   "account_id" : 1,
   "created_at" : "2020-02-21T08:46:12Z",
   "distribution_id" : null,
   "expiration_duration" : 3600,
   "expiration_time" : "2020-02-21T09:48:46Z",
   "id" : "1c24bf6c90ecc2be5330ccef6dcd225846098197",
   "manager_id" : null,
   "scope" : "customer",
   "user_agent" : "HTTPClient/1.0 (2.8.3, ruby 2.5.5 (2019-03-15))"
}

Odpowiedź zawiera między innymi pozostały czas ważności tokena (jest on przedłużany przy wykonywaniu operacji API, przy tworzeniu tokena można go zmienić za pomocą parametru expiration_duration) i zasięg autoryzacji ("customer" dla użytkowników lub "administrator" dla administratorów).

auth_token

UWAGA: zasób auth_token jest przestarzały i zostanie usunięty w jednej z nadchodzących wersji API. Zamiast niego należy używać operacji na zasobie sessions.

Możliwa jest także autentykacja za pomocą starszych operacji na tokenie aktualnej sesji. Token można uzyskać wykonując zapytanie POST z loginem i hasłem na adres /auth_token, na przykład:

curl -X POST -H 'Content-Type: application/json' -d '{"username":"USERNAME","password":"PASSWORD"}' \
   'https://api.monit24.pl/v3/auth_token'
{
   "id" : "29e35628844df0696f437c7a6e255a88"
}

Token można wykorzystać do autentykacji przekazując go w nagłówku Authorization z typem "Bearer", na przykład:

curl -H 'Authorization: Bearer 29e35628844df0696f437c7a6e255a88' https://api.monit24.pl/v3/services

Informacje o aktualnie posiadanej sesji można pobrać za pomocą operacji GET /auth_token:

curl -H 'Authorization: Bearer 29e35628844df0696f437c7a6e255a88' 'https://api.monit24.pl/v3/auth_token'

Przykładowa odpowiedź:

{
   "account_id" : 104,
   "expires_in" : 1800,
   "id" : "29e35628844df0696f437c7a6e255a88",
   "scope" : "customer"
}

Odpowiedź zawiera między innymi pozostały czas ważności tokena (jest on przedłużany przy wykonywaniu operacji API, przy tworzeniu tokena można go zmienić za pomocą parametru timeout) i zasięg autoryzacji ("customer" dla użytkowników lub "administrator" dla administratorów).

Sesję można zakończyć wykonując zapytanie DELETE /auth_token.

OAuth2

Ostatnią z dostępnych metod autentykacji jest wykorzystanie OAuth2 w trybie implicit. Od poprzedniej metody różni się jedynie sposobem pozyskania tokena sesji - polega na przekierowaniu użytkownika do formularza logowania na serwerze autoryzacji Monit24.pl wraz z adresem przekierowania po poprawnej autentykacji i innymi parametrami zdefiniowanymi w specyfikacji OAuth2.

Tokeny generowane przez operacje na zasobach /auth_token i sessions mogą być stosowane zamiennie z uzyskanymi przez OAuth2. Przykład działania można zobaczyć logując się do Monit24.pl za pomocą OAuth2 w interaktywnej dokumentacji API.

Autoryzacja

Aktualnie posiadane uprawnienia do poszczególnych obiektów można pobrać za pomocą operacji GET na odpowiednim zasobie permissions. Na przykład, aby pobrać nasze uprawnienia do grupy o identyfikatorze 123456, można wykonać następujące zapytanie:

curl -u USERNAME:PASSWORD 'https://api.monit24.pl/v3/groups/123456/permissions'

Przykładowa odpowiedź API:

{
   "create_periodic_report_addresses" : true,
   "create_notification_addresses" : true,
   "create_services" : true,
   "delete" : false,
   "own" : true,
   "read" : true,
   "send_test_sms_notifications" : true,
   "share" : true,
   "update" : true
}

Każdy obiekt permissions będzie zawierał standardowe pola:

  • own - określa, czy obiekt należy do nas,
  • read - określa, czy posiadamy uprawnienia do odczytu obiektu,
  • update - określa, czy możemy zmodyfikować obiekt (metodą PUT),
  • delete - określa, czy możemy usunąć obiekt (metodą DELETE).

Poza tymi standardowymi polami, obiekt permissions może zawierać inne pola, różne dla różnych typów obiektów. Dla grupy są to:

  • create_periodic_report_addresses - czy możemy tworzyć adresy raportów okresowych przypisane do grupy,
  • create_notification_addresses - czy możemy tworzyć adresy powiadomień przypisane do grupy,
  • create_services - czy możemy tworzyć usługi przypisane do grupy,
  • send_test_sms_notifications - czy możemy wysyłać testowe powiadomienia SMS na numery telefonu przypisane do grupy,
  • share - czy możemy udostępniać grupę innym użytkownikom.

W większości przypadków można użyć mechanizmu dowiązywania ("embed"), aby pobrać uprawnienia razem z obiektem lub obiektami. Można także filtrować i sortować względem uprawnień.

Zasady obliczania uprawnień do poszczególnych typów obiektów są szczegółowo opisane w tym miejscu.

Słowniki

Słowniki to proste zasoby mające następujące właściwości:

  • są publicznie dostępne do odczytu,
  • mają wartości wspólne dla wszystkich użytkowników (w tym sensie są ,,globalne'' w skali systemu),
  • posiadają jedynie tekstowy identyfikator, nazwę i opcjonalnie opis,
  • zmieniają się rzadko lub wcale.

Przykładami słowników są kanały powiadomień (notification_channels) lub języki, które można przypisać do konta (languages). Słowniki są dostępne jako podzasoby zasobu /dictionaries. Nie posiadają stowarzyszonych obiektów uprawnień, zaś nazwy i opisy podlegają tłumaczeniom w zależności od nagłówka Accept-Language.

Pamięć podręczna

API wspiera wykorzystanie pamięci podręcznej (cache) po stronie klienta na dwa sposoby:

  • ETag - odpowiedź na każde zapytanie GET zawiera nagłówek ETag ("sumę kontrolną" odpowiedzi). Wykonując ponownie zapytanie o ten sam zasób, można wysłać wartość otrzymanego nagłówka ETag w nagłówku If-None-Match. Jeśli nowa odpowiedź ma ten sam ETag, zostanie zwrócony status 304 Not Modified - pozwala to oszczędzić na transferze danych. Popularne przeglądarki WWW wykorzystują ten mechanizm automatycznie jeśli odpowiedź zawiera nagłówek ETag.
  • Cache-Control - pozwala na oznaczenie odpowiedzi API jako ważnej przez określny czas, co umożliwia automatyczne jej wykorzystanie przez klienta nawet bez późniejszej komunikacji z API. Ten mechanizm jest wykorzystywany w przypadku zasobów publicznych, które rzadko ulegają zmianom (stacje monitorujące, typy błędów, słowniki itp.) oraz danych, które nie zmieniają się ze swojej natury (historia analiz, sprawdzeń, historyczne dane do wykresów, zawartość pobranych stron, pliki HAR itp.).

Języki i strefy czasowe

Konta użytkowników posiadają dwa atrybuty odpowiedzialne za dostosowanie sposobu działania systemu monitorującego do lokalizacji i preferowanego języka użytkownika: language_id i time_zone_id.

Atrybut language_id określa preferowany język użytkownika. Jest to identyfikator jednego z elementów słownika languages. Jego zmiana wpływa na:

  • język raportów okresowych dotyczących usług należących do konta,
  • język powiadomień dotyczących usług należących do konta,
  • język niektórych raportów awarii generowanych przez stacje monitorujące, w zależności od typu usługi.

Atrybut time_zone_id określa strefę czasową konta. Jest to identyfikator jednego z elementów słownika time_zones. Jego zmiana wpływa na:

Uwaga: aktualnie raporty okresowe są zawsze wysyłane dla strefy czasowej Europe/Warsaw, która jest także domyślną strefą nowych kont. W przyszłości raporty będą wysyłane w strefie czasowej konta, którego dotyczą.

Wersjonowanie API

Schemat numeracji wersji API jest w dużej części zgodny z zasadami Semantic Versioning. Numer wersji składa się z trzech liczb oddzielonych kropkami, w kolejności: MAJOR, MINOR i PATCH, przy czym PATCH nie jest obowiązkowy i domyślnie ma wartość 0. Na przykład wersja "3.1.5" składa się z MAJOR równego 3, MINOR równego 1 i PATCH równego 5. Numery specjalnych wersji (np. beta) mogą na końcu zawierać dodatkowe znaki, oddzielone od podstawowego numeru myślnikiem. Zmiany wprowadzane w poszczególnych wersjach będą opisane w historii zmian API.

Zmiany poszczególnych części numeru wersji mają różne znaczenie.

MAJOR

Zmiana głównego numeru wersji (np. z 3.1.4 na 4.0) oznacza duże zmiany, które nie są kompatybilne wstecznie. W takim przypadku zmienia się adres URL operacji API (np. fragment "/v3/" zmienia się na "/v4/"), zaś operacje z poprzedniej wersji są dostępne przez odpowiednio długi czas równolegle z nową wersją, aby umożliwić dostosowanie aplikacji klienckich do nowej wersji. Każda zmiana głównej wersji będzie opatrzona dokumentem szczegółowo opisującym poszczególne niekompatybilne wstecznie zmiany.

MINOR

Wersje MINOR mogą zawierać zmiany interfejsu API, które są kompatybilne wstecznie. Przykładem takich zmian jest dodanie nowej operacji API lub nowego pola do obiektu. Takie wersje nie wiążą się ze zmianą adresu URL operacji API.

PATCH

Wersje PATCH nie zawierają zmian w interfejsie API. Służą do drobnych poprawek (np. literówek w dokumentacji) oraz naprawiania znalezionych błędów w działaniu operacji API.