ANLEITUNG

In sechs Schritten angebunden

Diese Anleitung führt von der Key-Erstellung bis zur produktionsreifen Synchronisation des Dienstplans. Alle Codebeispiele lassen sich direkt kopieren.

1. API-Key erzeugen

Der API-Key wird im VetTime-Konto des Mandanten erzeugt und ist ausschließlich für diesen Mandanten gültig. Keys beginnen mit vt_live_. Falls ihr noch keinen Zugang habt, meldet euch über das Kontaktformular.

Legt den Key als Umgebungsvariable in eurem Backend ab, niemals im Frontend-Bundle oder in der Versionskontrolle.

2. Ersten Request senden

Ein minimaler Aufruf braucht nur from und to. Beide Daten sind inklusiv und beziehen sich auf Europe/Berlin.

curl
export VETTIME_API_KEY='vt_live_...'

curl --fail-with-body \
  --get 'https://app.vettime.de/api/public/v1/shifts' \
  --header "Authorization: Bearer ${VETTIME_API_KEY}" \
  --data-urlencode 'from=2026-08-17' \
  --data-urlencode 'to=2026-08-31'

3. Paginierung über den Cursor

Solange meta.has_more den Wert true hat, ruft ihr denselben Endpunkt mit meta.next_cursor auf. Alle übrigen Parameter bleiben unverändert, der Cursor ist undurchsichtig.

JavaScript
async function* fetchAllShifts(from, to, apiKey) {
  let cursor = null;

  do {
    const url = new URL('https://app.vettime.de/api/public/v1/shifts');
    url.searchParams.set('from', from);
    url.searchParams.set('to', to);
    url.searchParams.set('limit', '250');
    if (cursor) url.searchParams.set('cursor', cursor);

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${apiKey}` },
    });
    if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);

    const body = await res.json();
    yield* body.data;
    cursor = body.meta.has_more ? body.meta.next_cursor : null;
  } while (cursor);
}

4. Synchronisation als Snapshot

Die API ist kein Event-Stream. Sammelt den vollständigen Zeitraum ein und schreibt erst danach atomar. Da Zuweisungs-IDs nach erneutem Veröffentlichen wechseln können, braucht es regelmäßig einen vollständigen Abgleich statt reiner Deltas.

Sync-Strategie
// 1. Vollständigen Snapshot einsammeln
const snapshot = [];
for await (const shift of fetchAllShifts(from, to, apiKey)) {
  snapshot.push(shift);
}

// 2. Erst nach dem letzten Seitenabruf schreiben
await db.transaction(async (tx) => {
  const ids = snapshot.map((s) => s.id);

  await tx.upsertShifts(snapshot);

  // 3. Zuvor importierte Zuweisungen im Zeitraum entfernen,
  //    die im aktuellen Snapshot fehlen
  await tx.deleteShiftsInRangeExcept(from, to, ids);
});

5. Fehler robust behandeln

Bei 429 gilt Retry-After. Bei 5xx wird mit exponentiellem Backoff plus Jitter wiederholt. 400 und 401 sind nicht wiederholbar und gehören ins Log samt X-Request-Id.

Retry-Logik
async function requestWithRetry(url, apiKey, attempt = 0) {
  const res = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });

  if (res.status === 429) {
    const retryAfter = Number(res.headers.get('Retry-After') ?? 1);
    await sleep(retryAfter * 1000);
    return requestWithRetry(url, apiKey, attempt);
  }

  if (res.status >= 500 && attempt < 5) {
    const backoff = 2 ** attempt * 500 + Math.random() * 250; // Jitter
    await sleep(backoff);
    return requestWithRetry(url, apiKey, attempt + 1);
  }

  if (!res.ok) {
    const requestId = res.headers.get('X-Request-Id');
    const body = await res.text();
    throw new Error(`VetTime API ${res.status} (request ${requestId}): ${body}`);
  }

  return res.json();
}

6. Keys sicher betreiben

Keys niemals in URLs, Browser- oder Mobile-Code, Logs, E-Mails oder Chats übergeben. Rotiert Keys bei Personalwechsel und bei Verdacht auf Kompromittierung, ein ersetzter Key ist sofort ungültig. Beim Support-Kontakt nur X-Request-Id und UTC-Zeitpunkt nennen, nie den Key selbst.

Alle Details im Referenzteil

Vollständiges Antwortschema, Feldsemantik, Fehlercodes und Limits.

Zur API-Referenz