Dokumentacja integracji
Jak uwierzytelniać żądania do API Tracera i odbierać zdarzenia webhookiem, w imieniu Twojej organizacji.
Uwierzytelnianie kluczem API
Dołącz swój klucz API w nagłówku X-API-Key do każdego żądania. Utwórz klucz w zakładce Klucze API w ustawieniach organizacji.
curl "https://api.tracera.app/api/v1/external/events?limit=20&eventType=report_completed" \ -H "X-API-Key: ftk_your_api_key"
Pobieranie zdarzeń (pull)
GET /api/v1/external/events zwraca zdarzenia Twojej organizacji, stronicowane i opcjonalnie filtrowane po eventType. Limit żądań: 30/min na adres IP (burst 10). Parametr limit domyślnie 20, maksymalnie 100.
curl "https://api.tracera.app/api/v1/external/events?limit=20&eventType=report_completed" \ -H "X-API-Key: ftk_your_api_key"
{
"content": [
{
"id": "3f2a9c34-1e9a-4c3b-9e2b-9a5d9a6b6b12",
"organizationUUID": "8f6e2a3d-...-...-...",
"eventType": "report_completed",
"payload": { "reportId": "..." },
"occurredAt": "2026-07-23T09:12:04.512Z"
}
],
"total": 128,
"page": 1,
"limit": 20
}Typy zdarzeń
Te same wartości eventType pojawiają się zarówno w odpowiedzi API, jak i w treści webhooka.
| eventType | Opis |
|---|---|
user_registered | Nowy użytkownik zarejestrował się w organizacji. |
report_completed | Raport inspekcji został ukończony. |
alert_violation | Reguła alertu RCP została naruszona. |
fault_reported | Zgłoszono nową usterkę. |
fault_resolved | Usterka została rozwiązana. |
Wpisy RCP (pull)
GET /api/v1/external/rcp/entries zwraca odbicia RCP (check-in/check-out) Twojej organizacji w zadanym zakresie dat, stronicowane. Ze względu na wysoką częstotliwość te dane nie są wysyłane webhookiem — pobieraj je okresowo. Zakres domyślny to ostatnie 30 dni, limit domyślnie 20, maksymalnie 100.
curl "https://api.tracera.app/api/v1/external/rcp/entries?from=2026-07-01T00:00:00Z&to=2026-07-31T23:59:59Z&limit=50" \ -H "X-API-Key: ftk_your_api_key"
{
"content": [
{
"id": "6a1e2b3c-4d5e-...-...-...",
"userId": 42,
"entryType": "check_in",
"scannedAt": "2026-07-23T07:58:11Z",
"status": "valid",
"nfcTagId": 7,
"nfcTagUuid": "9c8b7a6d-...-...-..."
}
],
"total": 964,
"page": 1,
"limit": 50
}Usterki (pull)
GET /api/v1/external/faults zwraca usterki Twojej organizacji, stronicowane i opcjonalnie filtrowane po status oraz severity. Limit domyślnie 20, maksymalnie 100.
curl "https://api.tracera.app/api/v1/external/faults?status=open&limit=20" \ -H "X-API-Key: ftk_your_api_key"
{
"content": [
{
"id": "b2f1a3c4-...-...-...",
"tagId": 7,
"reporterId": 42,
"assigneeId": 15,
"title": "Leaking valve",
"description": "Valve on line 3 is leaking coolant.",
"severity": "high",
"status": "open",
"createdAt": "2026-07-20T08:15:00Z",
"updatedAt": "2026-07-20T08:15:00Z"
}
],
"total": 12,
"page": 1,
"limit": 20
}Inspekcje (pull)
GET /api/v1/external/inspections zwraca inspekcje Twojej organizacji, stronicowane i filtrowane po status, type oraz nfcTagId. Limit domyślnie 20, maksymalnie 100.
curl "https://api.tracera.app/api/v1/external/inspections?status=completed&limit=20" \ -H "X-API-Key: ftk_your_api_key"
{
"content": [
{
"id": "c3d2e1f0-...-...-...",
"inspectionType": "checklist",
"status": "completed",
"scannedAt": "2026-07-22T11:00:00Z",
"completedAt": "2026-07-22T11:20:00Z",
"progress": 100,
"nfcTag": { "id": "9c8b7a6d-...-...-...", "name": "Pump Station A" },
"user": { "id": "1a2b3c4d-...-...-...", "firstName": "Anna", "lastName": "Kowalska" },
"createdAt": "2026-07-22T11:00:00Z"
}
],
"total": 340,
"page": 1,
"limit": 20
}Webhooki (push)
Każde zdarzenie jest wysyłane jako POST na Twój targetUrl (musi być https://). Zweryfikuj nagłówek X-FieldTag-Signature sekretem pokazanym przy tworzeniu webhooka, zanim zaufasz treści żądania.
{
"id": "3f2a9c34-1e9a-4c3b-9e2b-9a5d9a6b6b12",
"organizationUUID": "8f6e2a3d-...-...-...",
"eventType": "report_completed",
"payload": { "reportId": "..." },
"occurredAt": "2026-07-23T09:12:04.512Z"
}Dostarczenie ponawiane jest do 3 razy przy błędzie lub statusie spoza zakresu 2xx, z limitem czasu 5 s na próbę.
Weryfikacja podpisu
Bezpieczeństwoconst crypto = require("crypto")
function isValidSignature(secret, rawBody, signatureHeader) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex")
return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected))
}import hashlib
import hmac
def is_valid_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature_header, expected)