Przejdź do treści
Nextriv

Dla deweloperów

External API

Stabilne, tylko do odczytu API server-to-server do pobierania urządzeń i pomiarów Nextriv. Poniżej znajdziesz kompletny kontrakt v1, przykłady curl i bezpieczny wzorzec synchronizacji snapshot → changes.

Kontrakt v1 w skrócie

External API jest przeznaczone dla integracji backendowych. Wszystkie siedem endpointów używa metody GET, nie przyjmuje body i nie zmienia danych w Nextriv.

Bazowy URL API
https://api.nextriv.app/api/external/v1

Tylko odczyt

Tożsamość, urządzenia, publiczny katalog metryk oraz pomiary — bez operacji zapisu.

Server-to-server

Klucz przechowuj po stronie serwera lub w menedżerze sekretów, nigdy w kodzie przeglądarki.

Stabilne v1

Zmiany addytywne są możliwe; klient powinien tolerować nowe pola bez przerywania integracji.

Dostęp dla planu PRO

Dostępne dla każdej organizacji na planie PRO — bez zgłoszeń i list oczekujących.

Uwierzytelnianie i dostęp

Klucz ma postać nxk_<public_id>.<secret>. Jest związany z jedną organizacją i zestawem scope’ów.

Wyłącznie nagłówek Authorization

Wysyłaj klucz tylko jako Authorization: Bearer <klucz API>. Nigdy nie umieszczaj go w URL, query stringu ani logach.

Zmienna środowiskowa

export NEXTRIV_API_KEY='nxk_<public_id>.<secret>'

Wszystkie przykłady curl na tej stronie zakładają, że zmienna NEXTRIV_API_KEY jest już ustawiona.

  1. 1

    Włącz dostęp

    Wystarczy plan PRO — klucz utworzysz od razu w panelu, bez czekania na zatwierdzenie.

  2. 2

    Utwórz klucz API

    W panelu Nextriv przejdź do Ustawienia → Klucze API, wybierz scope’y i utwórz klucz.

  3. 3

    Zapisz sekret

    Pełna wartość klucza jest pokazywana tylko raz. Zapisz ją od razu w bezpiecznym magazynie sekretów.

Scope’y

Nadaj kluczowi wyłącznie uprawnienia potrzebne danej integracji. Endpoint /identity pokazuje aktywne scope’y.

devices:read

Odczyt listy urządzeń, szczegółów urządzenia i publicznego katalogu jego metryk.

measurements:read

Odczyt najnowszych pomiarów, historycznego snapshotu raw oraz feedu zmian.

Endpointy

Ścieżki są względne wobec bazowego URL. API nie udostępnia parametru sortowania — kolejność stron jest sterowana przez serwer i nieprzezroczysty kursor.

GET/identityWymagany scope: dowolny aktywny klucz API

Tożsamość i aktywne scope’y

Najprostszy test klucza. Zwraca nazwę organizacji, wersję API i scope’y przypisane do klucza.

Parametry

Brak parametrów.

Kolejność

Endpoint nie jest stronicowany i nie przyjmuje parametru sortowania.

Ważne

  • Użyj go po utworzeniu lub rotacji klucza, aby potwierdzić organizację i uprawnienia.
  • api_version ma wartość v1.

Żądanie curl

curl --silent --show-error \
  --header "Authorization: Bearer $NEXTRIV_API_KEY" \
  --header 'Accept: application/json' \
  'https://api.nextriv.app/api/external/v1/identity'

Odpowiedź JSON

{
  "api_version": "v1",
  "organization_name": "ACME",
  "scopes": [
    "devices:read",
    "measurements:read"
  ]
}
GET/devicesWymagany scope: devices:read

Lista urządzeń pomiarowych

Zwraca widoczne urządzenia z publicznym ID, nazwą Nextriv, statusem, lokalizacją, czasem ostatniego pomiaru i kluczami metryk.

Parametry

ParametrMiejsceWymaganyOpis
limitqueryNieLiczba żądanych kompletnych rekordów: domyślnie 1000, minimum 1, maksimum 2000.
cursorqueryNieNieprzezroczysty next_cursor z poprzedniej strony.

Kolejność

Brak parametru sortowania. Przetwarzaj rekordy w kolejności odpowiedzi i kontynuuj wyłącznie zwróconym kursorem.

Ważne

  • status przyjmuje active albo inactive.
  • Gdy has_more=true, next_cursor prowadzi do kolejnej strony. Po końcowej stronie listy ma wartość null.

Żądanie curl

curl --silent --show-error --get \
  --header "Authorization: Bearer $NEXTRIV_API_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode 'limit=1000' \
  'https://api.nextriv.app/api/external/v1/devices'

Odpowiedź JSON

{
  "has_more": false,
  "items": [
    {
      "id": "dev_0123456789abcdef0123456789abcdef",
      "last_measurement_at": "2026-07-10T10:00:00Z",
      "location": "A1",
      "metrics": [
        "temperature",
        "humidity"
      ],
      "name": "NX-01",
      "product_name": "Nextriv Sense Industrial",
      "status": "active"
    }
  ],
  "next_cursor": null
}
GET/devices/{device_id}Wymagany scope: devices:read

Szczegóły urządzenia

Zwraca ten sam publiczny model urządzenia co element listy, ale dla jednego device_id.

Parametry

ParametrMiejsceWymaganyOpis
device_idpathTakPubliczne ID w formacie dev_ i 32 małe znaki szesnastkowe.

Kolejność

Endpoint zwraca jeden obiekt; sortowanie nie dotyczy.

Ważne

  • Nieznane ID oraz ID spoza organizacji zwraca ten sam błąd 404 resource_not_found.
  • product_name zawiera wyłącznie publiczną nazwę produktu Nextriv.

Żądanie curl

curl --silent --show-error \
  --header "Authorization: Bearer $NEXTRIV_API_KEY" \
  --header 'Accept: application/json' \
  'https://api.nextriv.app/api/external/v1/devices/dev_0123456789abcdef0123456789abcdef'

Odpowiedź JSON

{
  "id": "dev_0123456789abcdef0123456789abcdef",
  "last_measurement_at": "2026-07-10T10:00:00Z",
  "location": "A1",
  "metrics": [
    "temperature",
    "humidity"
  ],
  "name": "NX-01",
  "product_name": "Nextriv Sense Industrial",
  "status": "active"
}
GET/devices/{device_id}/metricsWymagany scope: devices:read

Katalog metryk urządzenia

Zwraca metryki, które dane urządzenie może publikować w External API, razem z typem, jednostką i nazwami PL/EN.

Parametry

ParametrMiejsceWymaganyOpis
device_idpathTakPubliczne ID w formacie dev_ i 32 małe znaki szesnastkowe.

Kolejność

Brak parametru sortowania. Traktuj key jako stabilny identyfikator, a nie pozycję w tablicy.

Ważne

  • type przyjmuje number, boolean, state albo string.
  • unit i decimals mogą mieć wartość null; aggregatable opisuje charakter metryki.

Żądanie curl

curl --silent --show-error \
  --header "Authorization: Bearer $NEXTRIV_API_KEY" \
  --header 'Accept: application/json' \
  'https://api.nextriv.app/api/external/v1/devices/dev_0123456789abcdef0123456789abcdef/metrics'

Odpowiedź JSON

{
  "device_id": "dev_0123456789abcdef0123456789abcdef",
  "metrics": [
    {
      "aggregatable": true,
      "decimals": 1,
      "key": "temperature",
      "name": {
        "en": "Temperature",
        "pl": "Temperatura"
      },
      "type": "number",
      "unit": "°C"
    },
    {
      "aggregatable": true,
      "decimals": 0,
      "key": "humidity",
      "name": {
        "en": "Humidity",
        "pl": "Wilgotność"
      },
      "type": "number",
      "unit": "%"
    }
  ]
}
GET/measurements/latestWymagany scope: measurements:read

Najnowsze widoczne pomiary

Zwraca najnowszy widoczny rekord pomiarowy dla każdego pasującego urządzenia albo dla jednego wskazanego device_id.

Parametry

ParametrMiejsceWymaganyOpis
device_idqueryNieOpcjonalne ograniczenie do jednego publicznego ID urządzenia.
limitqueryNieLiczba żądanych kompletnych rekordów: domyślnie 1000, minimum 1, maksimum 2000.
cursorqueryNieNieprzezroczysty next_cursor z poprzedniej strony.

Kolejność

Brak parametru sortowania. Kolejność strony ustala serwer; zachowaj ją podczas paginacji.

Ważne

  • Przy kontynuacji kursorem nie powtarzaj filtra device_id.
  • values jest mapą key metryki → obiekt z value oraz opcjonalną unit.

Żądanie curl

curl --silent --show-error --get \
  --header "Authorization: Bearer $NEXTRIV_API_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode 'device_id=dev_0123456789abcdef0123456789abcdef' \
  --data-urlencode 'limit=1000' \
  'https://api.nextriv.app/api/external/v1/measurements/latest'

Odpowiedź JSON

{
  "has_more": false,
  "items": [
    {
      "device_id": "dev_0123456789abcdef0123456789abcdef",
      "id": "measurement_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
      "measured_at": "2026-07-10T10:00:00Z",
      "received_at": "2026-07-10T10:00:02Z",
      "values": {
        "humidity": {
          "unit": "%",
          "value": 71.2
        },
        "temperature": {
          "unit": "°C",
          "value": 3.4
        }
      }
    }
  ],
  "next_cursor": null
}
GET/measurementsWymagany scope: measurements:read

Historyczny snapshot pomiarów raw

Czyta historyczny, bezstratny snapshot. Dane nie są agregowane, próbkowane ani pomijane.

Parametry

ParametrMiejsceWymaganyOpis
device_idqueryNieOpcjonalne ograniczenie do jednego publicznego ID urządzenia.
fromqueryNiePoczątek zakresu w formacie date-time ze strefą czasową.
toqueryNieKoniec historycznego snapshotu w formacie date-time ze strefą czasową.
limitqueryNieLiczba żądanych kompletnych rekordów: domyślnie 1000, minimum 1, maksimum 2000.
cursorqueryNieNieprzezroczysty next_cursor z poprzedniej strony.

Kolejność

Brak parametru sortowania. Kursor utrwala stabilną pozycję (measured_at, id); przetwarzaj strony w kolejności odpowiedzi.

Ważne

  • from nie może być późniejsze niż to. Oba znaczniki czasu muszą zawierać strefę czasową.
  • Przy cursor nie powtarzaj device_id, from ani to — filtry są zamrożone w tokenie.
  • Na końcowej stronie has_more=false, ale next_cursor pozostaje niepusty: rozpoczyna feed changes.

Żądanie curl

curl --silent --show-error --get \
  --header "Authorization: Bearer $NEXTRIV_API_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode 'device_id=dev_0123456789abcdef0123456789abcdef' \
  --data-urlencode 'from=2026-07-10T00:00:00Z' \
  --data-urlencode 'to=2026-07-11T00:00:00Z' \
  --data-urlencode 'limit=1000' \
  'https://api.nextriv.app/api/external/v1/measurements'

Odpowiedź JSON

{
  "has_more": false,
  "items": [
    {
      "device_id": "dev_0123456789abcdef0123456789abcdef",
      "id": "measurement_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
      "measured_at": "2026-07-10T10:00:00Z",
      "received_at": "2026-07-10T10:00:02Z",
      "values": {
        "humidity": {
          "unit": "%",
          "value": 71.2
        },
        "temperature": {
          "unit": "°C",
          "value": 3.4
        }
      }
    }
  ],
  "next_cursor": "cursor_snapshot_complete_example"
}
GET/measurements/changesWymagany scope: measurements:read

Feed zmian pomiarów

Kontynuuje synchronizację po ukończonym snapshotcie i obejmuje również pomiary dostarczone z opóźnieniem.

Parametry

ParametrMiejsceWymaganyOpis
cursorqueryTakKursor z końcowej strony /measurements albo z poprzedniej odpowiedzi changes.
limitqueryNieLiczba żądanych kompletnych rekordów: domyślnie 1000, minimum 1, maksimum 2000.

Kolejność

Brak parametru sortowania. Przetwarzaj feed w kolejności odpowiedzi i przesuwaj po change_seq.

Ważne

  • cursor jest obowiązkowy. Jego brak zwraca 400 invalid_request z issues.
  • Feed ma semantykę at-least-once; deduplikuj po publicznym id pomiaru.
  • source przyjmuje live, device_history, replay albo virtual.
  • Zachowuj next_cursor również wtedy, gdy has_more=false.

Żądanie curl

curl --silent --show-error --get \
  --header "Authorization: Bearer $NEXTRIV_API_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode "cursor=$CURSOR" \
  --data-urlencode 'limit=1000' \
  'https://api.nextriv.app/api/external/v1/measurements/changes'

Odpowiedź JSON

{
  "has_more": false,
  "items": [
    {
      "change_seq": 12001,
      "device_id": "dev_0123456789abcdef0123456789abcdef",
      "id": "measurement_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
      "measured_at": "2026-07-10T10:00:00Z",
      "received_at": "2026-07-10T10:00:02Z",
      "source": "live",
      "values": {
        "temperature": {
          "unit": "°C",
          "value": 3.4
        }
      }
    }
  ],
  "next_cursor": "cursor_changes_12001_example"
}

Paginacja i limity strony

Wszystkie endpointy stronicowane korzystają z nieprzezroczystego kursora. Nie dekoduj go, nie modyfikuj i nie buduj własnej pozycji strony.

  1. 1

    1000 domyślnie, 2000 maksymalnie

    limit przyjmuje wartości od 1 do 2000. Brak parametru oznacza 1000 rekordów.

  2. 2

    Maksymalnie 8 MiB

    Serwer może zwrócić mniej rekordów niż limit, aby strona nie przekroczyła 8 MiB. Rekord nigdy nie jest ucinany.

  3. 3

    Kursor zamraża kontekst

    Token wiąże organizację, typ zasobu, filtry i pozycję. Nie przenoś go między organizacjami ani endpointami.

  4. 4

    Nie powtarzaj filtrów

    Kolejną stronę snapshotu pobieraj przez zwrócony next_cursor, bez ponownego wysyłania device_id, from lub to.

  5. 5

    Sprawdzaj has_more

    has_more steruje paginacją. W raw i changes końcowy next_cursor nadal ma znaczenie dla dalszej synchronizacji.

Błędy i wygaśnięcie kursora

400 invalid_cursor

Uszkodzony lub sfałszowany

Nieprawidłowy podpis, typ, organizacja, faza albo zestaw filtrów. Użyj wyłącznie tokenu zwróconego przez API.

410

Wygasły kursor snapshotu

Kursory snapshotów z /devices, /measurements/latest i /measurements mogą zwrócić 410 po wygaśnięciu. Historyczny snapshot raw trzeba ukończyć w ciągu 24 godzin.

410

Wygasły feed zmian

Rolling cursor changes ma 30-dniowe okno. Po 410 rozpocznij nowy pełny snapshot.

Model snapshot → changes

Ten wzorzec daje bezstratny punkt startu, a potem pozwala utrzymywać lokalną kopię pomiarów bez ponownego pobierania całej historii.

  1. Rozpocznij snapshot

    Wywołaj GET /measurements z opcjonalnymi device_id, from i to. Bez to górna granica zostanie ustalona przy starcie.

  2. Pobierz wszystkie strony

    Dopóki has_more=true, przekazuj next_cursor do kolejnego GET /measurements i nie powtarzaj filtrów.

  3. Przejdź do changes

    Gdy has_more=false, przekaż końcowy next_cursor do GET /measurements/changes.

  4. Kontynuuj feed

    Przetwarzaj kolejne strony, trwale zapisuj nowy kursor i regularnie odpytuj ponownie, nawet gdy lista items jest pusta.

  • Zapisz nowy kursor dopiero po trwałym przetworzeniu całej strony.
  • Deduplikuj rekordy po publicznym id — feed gwarantuje at-least-once, nie exactly-once.
  • Feed obejmuje pomiary dostarczone z opóźnieniem oraz rekordy poza historyczną górną granicą snapshotu.
  • Retencja zmian wynosi 30 dni. Po 410 wykonaj nowy snapshot zamiast próbować odtwarzać stary kursor.

Nagłówki odpowiedzi

Każda odpowiedź zawiera identyfikator żądania oraz bieżący stan limitów. Limity są liczone niezależnie per klucz API i per organizacja.

NagłówekZnaczenie
RateLimit-LimitMaksymalna liczba żądań w aktualnie wiążącej polityce.
RateLimit-RemainingLiczba żądań pozostałych w wiążącym oknie.
RateLimit-ResetLiczba sekund do zwolnienia najstarszego slotu; to nie jest timestamp.
RateLimit-PolicyAktywne polityki per klucz API i per organizacja.
X-Request-IDIdentyfikator req_ i 32 małe znaki szesnastkowe, obecny w każdej odpowiedzi.
Retry-AfterLiczba sekund do ponowienia; zwracana przy 429 i 503.

Przykładowe nagłówki

RateLimit-Limit: <effective-limit>
RateLimit-Remaining: <remaining>
RateLimit-Reset: <seconds>
RateLimit-Policy: <key-policy>, <organization-policy>
X-Request-ID: req_0123456789abcdef0123456789abcdef

Nie koduj liczbowych limitów na stałe. Odczytuj politykę i pozostały budżet z każdej odpowiedzi; przy 429 respektuj Retry-After.

Kontrakt błędów

Błędy mają jeden stabilny envelope. Pole request_id przekaż pomocy technicznej, jeśli potrzebujesz analizy konkretnego żądania.

Kształt odpowiedzi

{
  "error": {
    "code": "string",
    "message": "string",
    "request_id": "req_..."
  },
  "issues": [
    {
      "path": "/query/limit",
      "code": "string",
      "message": "string"
    }
  ]
}

issues jest opcjonalne i pojawia się przy błędach walidacji. path używa JSON Pointer, na przykład /query/limit.

Statusy HTTP

StatusZnaczenie
400Nieprawidłowe parametry albo kursor.
401Brak lub nieprawidłowy klucz API.
402External API jest niedostępne w bieżącym planie.
403Nieaktywna organizacja albo niewystarczający scope.
404Urządzenie nie istnieje albo nie należy do organizacji.
410Prawidłowy kursor wygasł; rozpocznij nowy snapshot.
429Przekroczony limit żądań; użyj Retry-After.
500Nieoczekiwany błąd serwera.
503Usługa chwilowo niedostępna; użyj Retry-After.

Przykładowe błędy

Nieprawidłowy klucz API

401 invalid_api_key

{
  "error": {
    "code": "invalid_api_key",
    "message": "The API key is invalid.",
    "request_id": "req_0123456789abcdef0123456789abcdef"
  }
}

Brak zasobu

404 resource_not_found

{
  "error": {
    "code": "resource_not_found",
    "message": "Resource was not found.",
    "request_id": "req_0123456789abcdef0123456789abcdef"
  }
}

Nieprawidłowy kursor

400 invalid_cursor

{
  "error": {
    "code": "invalid_cursor",
    "message": "The cursor is invalid.",
    "request_id": "req_0123456789abcdef0123456789abcdef"
  }
}

Nieprawidłowe żądanie

400 invalid_request

{
  "error": {
    "code": "invalid_request",
    "message": "Request validation failed.",
    "request_id": "req_0123456789abcdef0123456789abcdef"
  },
  "issues": [
    {
      "code": "less_than_equal",
      "message": "Input should be less than or equal to 2000",
      "path": "/query/limit"
    }
  ]
}

Potrzebujesz pomocy z integracją External API?

Opisz planowaną integrację i wymagane scope’y. Pomożemy dobrać bezpieczny model synchronizacji.