Flavoso APIGrundlagenFehler

Fehler

Jeder Fehler hat dieselbe Form. Hier stehen alle Codes mit HTTP-Status und was dein Programm tun soll.

Die Form

Ein Fehler ist immer ein Objekt error mit code, message und request_id, bei invalid_request oft dazu param:

{
  "error": {
    "code": "invalid_request",
    "message": "price must be an integer amount in cents.",
    "request_id": "req_4f0c2a9d1e7b3c5a8f6d2e10",
    "param": "products[3].price"
  }
}
  • code ist der Vertrag: darauf darf sich dein Programm verlassen, er wird nicht umformuliert.
  • message ist ein englischer Hinweis für Entwickler und kann sich ändern. Zeig ihn nicht dem Wirt.
  • request_id steht auch im Kopf X-Request-Id; damit findest du die Anfrage im Protokoll.
  • param nennt das fehlerhafte Feld, auch tief im Rumpf: products[3].price.

Alle Codes

CodeHTTPBedeutungWas dein Programm tut
invalid_request400Ein Parameter oder der Rumpf stimmt nicht: Pflichtfeld fehlt, falscher Typ, ungültiges JSON, oder die Anfrage enthält ein Feld oder einen Abfrageparameter, den die Operation nicht kennt. param nennt das Feld, wenn es eins ist.Nicht unverändert wiederholen. Anfrage anhand von param und message korrigieren.
unauthorized401Kein Token, ein abgelaufenes oder widerrufenes Token, die Verbindung besteht nicht mehr, oder die App ist gesperrt.Mit dem Refresh-Token ein neues Access-Token holen und einmal wiederholen. Scheitert das mit invalid_grant, besteht die Verbindung nicht mehr; mit unauthorized_client ist die App gesperrt: Tokens behalten und später erneut versuchen.
account_inactive403Das Abo des Betriebs läuft nicht. GET /me und DELETE /connection funktionieren trotzdem.Nicht wiederholen. Später erneut versuchen oder den Betrieb informieren.
insufficient_scope403Der Verbindung fehlt das Recht für diese Operation. WWW-Authenticate nennt es.Den Inhaber bitten, deine App mit diesem Recht neu zu verbinden. Bis dahin die Funktion abschalten.
plan_required403Der Tarif des Betriebs enthält die Funktion nicht, etwa Reservierungen im Tarif Starter.Die Funktion für diesen Betrieb abschalten und dem Nutzer den Grund zeigen.
not_found404Das Objekt gibt es nicht, oder nicht in diesem Betrieb. Gilt auch für einen unbekannten Pfad.Kennung und Pfad prüfen. Ein gelöschtes Objekt aus deinem Speicher entfernen.
method_not_allowed405Diese Methode gibt es an diesem Pfad nicht.Methode und Pfad mit der Referenz vergleichen.
conflict409Dieselben Daten wurden im selben Moment woanders geändert, etwa vom Betrieb im Dashboard, dreimal hintereinander. Die Anfrage selbst ist richtig.Retry-After Sekunden warten und dieselbe Anfrage unverändert noch einmal schicken, mit demselben Idempotency-Key.
external_id_taken409Diese external_id hängt bei deiner App in diesem Betrieb schon an einem anderen Objekt derselben Art.Mit ?external_id= nachsehen, welches Objekt sie trägt, und die Zuordnung in deinem System korrigieren.
idempotency_in_progress409Eine erste Anfrage mit demselben Idempotency-Key läuft noch.Kurz warten und mit demselben Schlüssel wiederholen.
invalid_transition409Dieser Statuswechsel geht nicht, etwa rückwärts oder aus einem abgeschlossenen Stand. Bei Reservierungen nennt message die Stände, die gerade gehen.Das Objekt neu lesen: es wurde meist woanders schon geändert. Dann entscheiden.
slot_unavailable409Die Zeit ist keine buchbare Zeit des Tages oder liegt in der Vergangenheit, oder zu dieser Zeit ist für diese Personenzahl oder diese Tische kein Platz frei.Mit GET /reservations/availability freie Zeiten holen und eine davon anbieten.
stage_not_used409Der Betrieb benutzt diesen Stand nicht, etwa preparing. GET /store nennt die benutzten unter order_statuses.Den Schritt überspringen und zum nächsten Stand schalten.
payload_too_large413Der Rumpf ist größer, als die Operation annimmt, gezählt in Bytes: 64 KB, bei PUT /menu 2 MB.Die Anfrage aufteilen, etwa die Karte in mehrere Abgleiche mit merge.
idempotency_key_reused422Derselbe Idempotency-Key kam mit einer anderen Anfrage (Methode, Pfad oder Rumpf).Für jede neue Operation einen neuen Schlüssel nehmen. Den Fehler im Programm suchen.
limit_exceeded422Eine Grenze ist überschritten, etwa zu viele Gerichte in einem Abgleich.Die Anfrage aufteilen oder kürzen. Die Grenzen stehen beim jeweiligen Leitfaden.
rate_limited429Mehr als 120 Anfragen in der Minute für diese Verbindung.Retry-After Sekunden warten, dann weiter. Abfragen über die Zeit verteilen.
server_error500Bei Flavoso ist etwas schiefgegangen.Mit wachsendem Abstand wiederholen, bei POST mit demselben Idempotency-Key. Hält es an, request_id an den Support.

Fehler behandeln

Ein Fehler sagt dir, ob Wiederholen hilft. 401 heißt: Token erneuern. 403 heißt: dem Inhaber Bescheid geben, nicht wiederholen. 409 heißt meistens: das Objekt hat sich woanders geändert, also neu lesen. Jeder andere 4xx heißt: die Anfrage stimmt nicht, also nicht unverändert wiederholen.

export class FlavosoError extends Error {
  constructor(status, error) {
    super(`${status} ${error.code}: ${error.message}`);
    Object.assign(this, { status, code: error.code, param: error.param, requestId: error.request_id });
  }
}

export async function flavoso(conn, method, path, { body, idempotencyKey } = {}) {
  const res = await fetch(`https://api.flavoso.com/v1${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${await accessTokenFor(conn)}`,
      ...(body ? { "Content-Type": "application/json" } : {}),
      ...(idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const data = await res.json();
  if (res.ok) return data;
  const err = new FlavosoError(res.status, data.error);
  switch (err.code) {
    case "unauthorized": // expired or revoked: refresh once, then give up
      conn.expiresAt = 0;
      break;
    case "insufficient_scope": // ask the owner to connect again with this scope
    case "plan_required": // the restaurant's plan does not include this
    case "account_inactive": // the restaurant's subscription is not active
      await flagConnection(conn, err.code);
      break;
  }
  throw err; // log err.requestId: support can find the request with it
}

401 und eine verlorene Verbindung

Bei 401 holst du mit dem Refresh-Token ein neues Access-Token und wiederholst einmal. Scheitert auch das mit invalid_grant, besteht die Verbindung nicht mehr: der Inhaber oder deine App hat getrennt, das Refresh-Token ist abgelaufen oder widerrufen, oder der Betrieb ist nicht mehr aktiv. Zeig das in deiner Software an und biete „Neu verbinden“ an; Wiederholen hilft hier nicht.

Antwortet der Token-Endpunkt dagegen mit unauthorized_client, ist deine App gesperrt, und die Verbindungen bestehen weiter. Behalte die Tokens, zeig „vorübergehend gesperrt“ an und versuch es später wieder.

Wann wiederholen

FallWiederholen?
Netzfehler, ZeitüberschreitungJa, mit demselben Idempotency-Key.
429 rate_limitedJa, nach Retry-After Sekunden.
500 server_error, 502, 503, 504Ja, mit wachsendem Abstand.
409 idempotency_in_progress, 409 conflictJa, nach einer kurzen Pause (Retry-After), unverändert.
401 unauthorizedEinmal, nach dem Erneuern des Tokens.
400, 403, 404, 413, 422, übrige 409Nein, nicht unverändert.
const RETRYABLE = new Set([429, 500, 502, 503, 504]);

// send() makes the SAME request every time, with the same Idempotency-Key.
export async function withRetries(send, attempts = 5) {
  for (let attempt = 1; ; attempt++) {
    let res = null;
    try {
      res = await send();
    } catch (err) {
      if (attempt >= attempts) throw err; // network error: outcome unknown, so retry
    }
    if (res && (!RETRYABLE.has(res.status) || attempt >= attempts)) return res;
    const retryAfter = Number(res?.headers.get("retry-after"));
    const backoff = Math.min(30, 2 ** attempt) * (0.5 + Math.random() / 2);
    await new Promise((r) => setTimeout(r, (retryAfter > 0 ? retryAfter : backoff) * 1000));
  }
}

Mit dem Support

Schreib an support@flavoso.com und nenne die request_id (oder X-Request-Id), dazu Zeit und Operation. Damit finden wir die Anfrage. Dieselbe Kennung steht in deinem Anfrage-Protokoll.

Etwas unklar geblieben? Schreib an support@flavoso.com und nenne die Kennung aus X-Request-Id. Damit finden wir deine Anfrage im Protokoll.