Przejdź do treści
TruckMaestro
Zaloguj sięZacznij za darmo

Dla firm

API i webhooki dla TMS

Odczytuj dane floty i wysyłaj zlecenia do kierowców z własnego systemu, a o zdarzeniach dowiaduj się natychmiast przez podpisane webhooki. Dostępne w planie Firma.

Uwierzytelnianie

Klucz API tworzy właściciel firmy w panelu (Integracje). Każde zapytanie wysyłaj z nagłówkiem Authorization: Bearer. Klucz widać tylko raz — w bazie trzymamy jego skrót. Klucz ma zakresy uprawnień i działa wyłącznie na danych Twojej firmy.

curl https://truckmaestro.com/api/v1/integrations/drivers \
  -H "Authorization: Bearer tt_live_XXXXXXXXXXXXXXXXXXXXXXXX"

Limit: 120 zapytań na minutę na klucz (nagłówki X-RateLimit-*, po przekroczeniu 429 i Retry-After).

Endpointy

Wszystkie pod adresem /api/v1/integrations, odpowiedzi w JSON, daty w ISO 8601.

MetodaŚcieżkaZakres
GET/api/v1/integrations/ping—
GET/api/v1/integrations/driversread:drivers
GET/api/v1/integrations/drivers/{id}read:drivers
GET/api/v1/integrations/workdaysread:days
GET/api/v1/integrations/ordersread:orders
GET/api/v1/integrations/orders/{id}read:orders
POST/api/v1/integrations/orderswrite:orders
GET/api/v1/integrations/violationsread:violations
  • Paginacja kursorowa: ?limit=1–100 (domyślnie 50) i ?cursor=…; odpowiedź: items, nextCursor, hasMore.
  • Filtry: driverId, from/to (dni pracy, naruszenia), status (zlecenia), updatedSince (dni i zlecenia — lista rośnie po dacie zmiany, więc nextCursor nadaje się do synchronizacji przyrostowej).
  • Pozycja kierowcy jest zwracana tylko, gdy kierowca udostępnia lokalizację firmie — tak jak w panelu.
  • Błędy mają format error.code and error.message: 401 zły lub odwołany klucz, 402 brak planu Firma, 403 brak zakresu, 429 limit.

Wysyłanie zleceń

POST /orders jest idempotentny po id (UUID nadany przez Twój system): ponowienie zwraca 200 i zapisane zlecenie bez zmian, nowe zlecenie to 201. Kierowca dostaje powiadomienie i widzi zlecenie w aplikacji.

curl -X POST https://truckmaestro.com/api/v1/integrations/orders \
  -H "Authorization: Bearer tt_live_XXXXXXXXXXXXXXXXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "0192f3c4-6a5e-7b21-9c3a-1f6d2a7b9e10",
    "driverId": "<driver id from /drivers>",
    "number": "ZL/11/2026/14", "client": "Raben",
    "stops": [
      { "kind": "loading",   "place": "Poznań, ul. Głogowska 1", "country": "PL", "plannedAt": "2026-11-10T08:00:00+01:00" },
      { "kind": "unloading", "place": "Berlin, Industriestr. 5", "country": "DE" }
    ]
  }'

Webhooki

Wybierz adres HTTPS i zdarzenia w panelu. Każda dostawa to POST z JSON-em i poniższymi nagłówkami.

  • workday.finished
  • violation.detected
  • order.stop.arrived
  • order.stop.departed
  • permit.expiring
Content-Type: application/json
User-Agent: TruckMaestro-Webhooks/1
X-TruckMaestro-Event: workday.finished
X-TruckMaestro-Event-Id: 0192f3c4-9d4b-7a10-8e55-3b1c0f6a2d77   # the same on every retry — deduplicate on it
X-TruckMaestro-Delivery-Id: …
X-TruckMaestro-Attempt: 1
X-TruckMaestro-Signature: t=1762191730,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
{
  "id": "0192f3c4-9d4b-7a10-8e55-3b1c0f6a2d77",
  "type": "workday.finished",
  "createdAt": "2026-11-03T17:42:10Z",
  "companyId": "0192…",
  "data": { "driver": { "id": "…", "name": "Marek Kowalski", "email": "…" }, "workDay": { "id": "…", "date": "2026-11-03", "workMinutes": 610, "…": "…" } }
}
  • Odpowiedz kodem 2xx w ciągu 8 sekund. Przekierowania nie są śledzone.
  • Przy błędzie ponawiamy po 1 min, 5 min, 30 min, 2 h i 12 h (6 prób łącznie). Potem dostawę można wysłać ponownie z panelu.
  • To samo zdarzenie może przyjść więcej niż raz — deduplikuj po polu id (nagłówek X-TruckMaestro-Event-Id).
  • Po 10 nieudanych próbach z rzędu wyłączamy webhook i informujemy właściciela.
  • Adresy muszą być publiczne i zaczynać się od https://; adresy lokalne i prywatne są blokowane.

Weryfikacja podpisu

Podpis to HMAC-SHA256 z tekstu „t.surowe_ciało” (znacznik czasu, kropka, surowe ciało) kluczem będącym sekretem webhooka (whsec_…). Licz go z surowych bajtów żądania, porównuj w stałym czasie i odrzucaj zbyt stare znaczniki t (np. starsze niż 5 minut).

Node.js

import crypto from 'node:crypto'

// rawBody: the request body exactly as received (Buffer/string) — NOT re-serialised JSON
export function verify(rawBody, signatureHeader, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(signatureHeader.split(',').map(p => p.trim().split('=')))
  const t = Number(parts.t)
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false          // replay protection
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  const a = Buffer.from(expected), b = Buffer.from(parts.v1 ?? '')
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

PHP

function verify(string $rawBody, string $header, string $secret, int $tolerance = 300): bool
{
    parse_str(str_replace(',', '&', $header), $p);                  // t=…&v1=…
    if (!isset($p['t'], $p['v1']) || abs(time() - (int) $p['t']) > $tolerance) {
        return false;
    }
    return hash_equals(hash_hmac('sha256', $p['t'].'.'.$rawBody, $secret), $p['v1']);
}

Python

import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.strip().split("=", 1) for p in header.split(","))
    t = int(parts.get("t", 0))
    if not t or abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Wróć do oferty dla firm