API-Integration

Eine praktische Anleitung mit kopierbaren Beispielen zur Integration von Gibbonds GraphQL-API — aus einem Verwaltungssystem, einer Mobil- oder Web-App oder einem anderen Vertriebskanal. Das vollständige Schema findest du in der API-Referenz.

Auf dieser Seite
⚡

Pro-Tarif. Erstelle auf der Seite API-Schlüssel im Gibbond-Admin einen API-Schlüssel und speichere ihn in einer serverseitigen Umgebungsvariable. Die folgenden Beispiele setzen voraus, dass GIBBOND_API_KEY einen WRITE-Schlüssel enthält.

So greifen die Komponenten ineinander

Es gibt zwei Arten von Zugangsdaten. Entscheidend ist, die richtige zu wählen:

  • API-Schlüssel (glk_live_…) — identifiziert dein Backend. Er kann die Daten aller Mitglieder lesen und ändern und darf deshalb nur auf deinem Server liegen. Nutze ihn für die Kommunikation zwischen Servern und zum Erstellen von Kundentokens.
  • Kundentoken — kurzlebige Zugangsdaten, die auf einen Kunden beschränkt sind. Dein Backend erstellt das Token mit dem API-Schlüssel und übergibt es an einen Client (Browser oder App). Der Client kann damit nur die Daten dieses Kunden sehen und für ihn handeln. Deshalb kannst du das Token sicher an das Gerät senden.

Wähle dein Integrationsmuster

Du entwickelst…VerwendeWeiter zu
Einen Verwaltungsprozess, eine ERP-Synchronisierung oder ein Headless-BackendDen API-Schlüssel direktAus deinem Backend
Eine Mobil- oder Single-Page-App, die einem Mitglied seine Punkte zeigtAPI-Schlüssel auf deinem Server → Kundentoken auf dem GerätIn einer App
Ein Kassensystem, einen Marktplatz oder einen Headless-Checkout zum PunktesammelnAPI-Schlüssel, awardPointsForOrderAus einem anderen Kanal

Eine Hilfsfunktion für Anfragen

Alle Aufrufe haben dasselbe Format. Fasse sie deshalb in einer Hilfsfunktion zusammen. Dieses Node-Beispiel verwendet das integrierte fetch (Node 18+):

// gibbond.js
const ENDPOINT = "https://app.gibbond.com/api/v1/graphql";

export async function gibbond(query, variables, token) {
  const res = await fetch(ENDPOINT, {
    method: "POST",
    headers: {
      "Authorization": "Bearer " + token,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, variables }),
  });

  if (res.status === 429) {
    const retryAfter = Number(res.headers.get("Retry-After") || 1);
    const err = new Error("Rate limited");
    err.retryAfter = retryAfter;
    throw err;
  }

  const json = await res.json();
  if (json.errors && json.errors.length) {
    const code = json.errors[0].extensions && json.errors[0].extensions.code;
    throw new Error(code + ": " + json.errors[0].message);
  }
  return json.data;
}

Muster 1 — aus deinem Backend (Server zu Server)

Mit einem WRITE-Schlüssel kannst du für jedes Mitglied deines Shops handeln. Melde einen Kunden zum Treueprogramm an, lies seine Daten und vergib Punkte:

import { gibbond } from "./gibbond.js";
const KEY = process.env.GIBBOND_API_KEY;
const id = "gid://shopify/Customer/123456789";

// Enroll a customer (idempotent — safe to call again).
await gibbond(
  "mutation($id: ID!, $email: String) { enrollCustomer(customerId: $id, email: $email) { ok customer { enrolled } } }",
  { id, email: "ada@example.com" },
  KEY,
);

// Read their balance and tier.
const { customer } = await gibbond(
  "query($id: ID!) { customer(id: $id) { balance pending lifetime tier { name multiplier } } }",
  { id },
  KEY,
);

// Grant points. idempotencyKey makes retries safe — the same key never double-applies.
await gibbond(
  "mutation($id: ID!, $pts: Int!, $k: String) { awardPoints(customerId: $id, points: $pts, reason: \"Newsletter signup\", idempotencyKey: $k) { ok pointsApplied balance { balance } } }",
  { id, pts: 100, k: "newsletter-123456789" },
  KEY,
);
ℹ️

Ein negativer Betrag bei awardPoints sowie Aufrufe von clawbackPoints erfordern einen ADMIN-Schlüssel — verwende für diese Abläufe separate, besonders sorgfältig geschützte Zugangsdaten.

Muster 2 — in einer Mobil- oder Web-App

Lege den API-Schlüssel niemals in einer App ab. Stelle stattdessen auf deinem Server einen kleinen Endpunkt bereit, der für den angemeldeten Nutzer ein Kundentoken erstellt. Lass die App mit diesem Token direkt mit Gibbond kommunizieren.

Woher kommt die Kunden-GID?

Ein Kundentoken ist an die GID gebunden, die du an mintCustomerToken übergibst. Du bist also dafür verantwortlich, es für den richtigen Kunden zu erstellen. Gibbond authentifiziert dein Backend über den API-Schlüssel, aber nicht den Endkunden. Vor dem Erstellen muss dein Server bereits zuverlässig wissen, welcher Shopify-Kunde angemeldet ist. Das lässt sich auf drei Arten feststellen, in dieser Reihenfolge empfohlen:

So meldet deine App den Kunden anWoher die GID kommt
Shopify Customer Account API (empfohlen)Melde den Kunden über Shopifys OAuth-basierte Customer Account API an. Dein Backend nutzt das erhaltene Token, um den verifizierten Kunden abzurufen und seine GID zu lesen. Die Identität ist entlang der gesamten Kette von Shopify signiert.
Eine native Shopify-OberflächeWenn die Anwendung innerhalb von Shopify läuft (eine App-Einbettung im Theme oder eine UI-Erweiterung für Kundenkonten), liefert Shopify bereits die signierte Identität — das Feld logged_in_customer_id des App-Proxys oder sub eines Kundenkonto-Sitzungstokens. Lies den Wert serverseitig und verwende diese GID.
Deine eigene AnmeldungEin Kanal mit eigenen Konten (eine individuelle App oder ein Kassensystem) ordnet den Nutzer anhand einer bei der Kundenerstellung gespeicherten Zuordnung einer Shopify-Kunden-GID zu. Du stehst für diese Verknüpfung ein. Schütze diesen Weg deshalb sorgfältig.
✅

Empfohlen: die Customer Account API. Authentifiziere Kunden in einem Headless-Shop oder einer Mobil-App über Shopifys Customer Account API und entnimm die GID dem von Shopify verifizierten Token. So ist die gesamte Kette — Kunde → Shopify-verifizierte Identität → dein Backend → erstelltes Kundentoken — von Grund auf vertrauenswürdig, ohne dass du eine GID-Zuordnung absichern musst. Diese Prüfung gehört in die unten gezeigte Middleware requireAuth.

ℹ️

Wenn deine Oberfläche bereits innerhalb der Shopify-Sitzung läuft (ein Theme-, Checkout- oder Kundenkonto-Block), benötigst du diese API normalerweise nicht — die App-Blöcke des Shops und die Kundenkonto-Integration verifizieren die Shopify-Identität für dich. Nutze Kundentokens, wenn du außerhalb dieses Kontexts arbeitest.

Auf deinem Server

Deine eigene Authentifizierung sagt dir, welcher Shopify-Kunde angemeldet ist (seine GID). Erstelle für ihn ein Token:

import express from "express";
import { gibbond } from "./gibbond.js";

const app = express();

// requireAuth is YOUR session middleware; it sets req.customerGid.
app.post("/loyalty/session", requireAuth, async (req, res) => {
  const data = await gibbond(
    "mutation($id: ID!) { mintCustomerToken(customerId: $id, ttlSeconds: 900) { token expiresAt } }",
    { id: req.customerGid },
    process.env.GIBBOND_API_KEY, // WRITE key, server-side only
  );
  // Hand the app the token, when it expires, and the id it is bound to.
  res.json({
    token: data.mintCustomerToken.token,
    expiresAt: data.mintCustomerToken.expiresAt,
    customerId: req.customerGid,
  });
});

In der App

Hole ein Token von deinem Server und rufe Gibbond anschließend direkt auf. Die Abfrage me bezieht sich auf den Kunden des Tokens — eine id ist nicht nötig:

// 1. Get a short-lived token from YOUR backend.
const session = await fetch("/loyalty/session", { method: "POST" }).then(r => r.json());

// 2. Call Gibbond with the customer token.
async function loyalty(query, variables) {
  const res = await fetch("https://app.gibbond.com/api/v1/graphql", {
    method: "POST",
    headers: {
      "Authorization": "Bearer " + session.token,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, variables }),
  });
  return (await res.json()).data;
}

// Show the member their points, tier, and reward codes.
const { me } = await loyalty("{ me { name balance lifetime tier { name multiplier } } }");
const { activeCodes } = await loyalty(
  "query($id: ID!) { activeCodes(customerId: $id) { code rewardName status expiresAt } }",
  { id: session.customerId },
);

renderRewardsScreen(me, activeCodes);

Prämien aus der App einlösen

Ein Kundentoken kann eine Prämie für seinen eigenen Kunden einlösen — auf dem Gerät ist kein Händlerschlüssel nötig. Die Mutation liefert einen einmal verwendbaren Rabattcode für den Checkout zurück:

const { redeemReward } = await loyalty(
  "mutation($id: ID!, $reward: ID!) { redeemReward(customerId: $id, rewardId: $reward) { ok code { code rewardName pointsSpent expiresAt } } }",
  { id: session.customerId, reward: "gid_or_reward_id_from_rewards_query" },
);
if (redeemReward.ok) applyDiscountCode(redeemReward.code.code);
ℹ️

Tokens sind standardmäßig 15 Minuten gültig (setze ttlSeconds auf bis zu 24 Stunden). Wenn ein Aufruf UNAUTHENTICATED zurückgibt, rufe /loyalty/session für ein neues Token auf und versuche es erneut.

Muster 3 — Punkte aus einem anderen Vertriebskanal sammeln

Wenn eine Bestellung außerhalb deines Shopify-Checkouts erfolgt — über ein Kassensystem, einen Marktplatz oder einen Headless-Shop — melde sie mit awardPointsForOrder. Gibbond wendet den Stufenmultiplikator des Mitglieds, aktive Boosts und Meilensteine genau wie bei einer Shopify-Bestellung an. Der Aufruf ist bezüglich orderId idempotent, sodass Wiederholungen sicher sind:

await gibbond(
  `mutation($input: OrderInput!) {
    awardPointsForOrder(input: $input) {
      ok
      pointsApplied
      balance { balance lifetime }
    }
  }`,
  {
    input: {
      orderId: "pos-2026-000123",
      customerId: "gid://shopify/Customer/123456789",
      customerEmail: "ada@example.com",
      orderName: "POS #000123",
      lineItems: [
        { productId: "gid://shopify/Product/55", collectionIds: ["gid://shopify/Collection/9"], lineTotal: 42.0 },
        { lineTotal: 8.5 },
      ],
    },
  },
  process.env.GIBBOND_API_KEY,
);

Jede Bestellposition enthält eine optionale productId, optionale collectionIds und einen erforderlichen lineTotal — produkt- und kollektionsbezogene Boosts und Ausschlüsse gelten wie bei Shopify-Bestellungen. Um Punkte später zu bestätigen (z. B. nach der Abwicklung), rufe confirmPoints(customerId, orderId) auf; um sie bei einer Erstattung zurückzunehmen, rufe clawbackPoints mit einem ADMIN-Schlüssel auf.

Fehler, Wiederholungen und Idempotenz

  • Codes. Fehler werden im GraphQL-Array errors zurückgegeben. Lies errors[0].extensions.code — UNAUTHENTICATED (401), FORBIDDEN (403, falscher Berechtigungsumfang), RATE_LIMITED (429), BAD_USER_INPUT (400), NOT_FOUND (404).
  • Anfragelimits. 600 Anfragen pro Minute und Zugangsdaten. Bei 429 wartest du die im Header Retry-After angegebenen Sekunden und versuchst es dann erneut.
  • Idempotenz. Übergib einen idempotencyKey an awardPoints und eine stabile orderId an awardPointsForOrder. Derselbe Schlüssel oder dieselbe Bestellung wird nie doppelt angewendet — du kannst Aufrufe nach Netzwerkfehlern daher erneut versuchen.
  • Tokenablauf. Wenn ein Kundentoken UNAUTHENTICATED liefert, erstelle ein neues und versuche es erneut.

Sicherheitscheckliste

  • Bewahre API-Schlüssel nur in serverseitigen Geheimnissen auf — niemals in App-Paketen, Browsercode oder der Versionsverwaltung.
  • Verwende für alles, was auf einem Gerät läuft, ein Kundentoken; niemals einen API-Schlüssel.
  • Gib jeder Integration den kleinsten erforderlichen Berechtigungsumfang (READ-Schlüssel für Dashboards, WRITE fürs Sammeln und Einlösen, ADMIN nur für Erstattungen und Korrekturen).
  • Tausche einen Schlüssel im Admin aus, sobald er möglicherweise offengelegt wurde — der Widerruf wirkt sofort.
  • Erstelle Kundentokens mit dem kürzesten praktikablen ttlSeconds-Wert.

Die vollständige Liste der Typen, Abfragen, Mutationen und Felder findest du in der API-Referenz.