Voor ontwikkelaars

Loyalo API & webhooks

Koppel je eigen systemen aan Loyalo: abonnementen, klanten en punten via de REST-API, plus ondertekende webhooks met voorbeelden in curl en JavaScript.

Authenticatie

Maak een sleutel aan in Loyalo onder Instellingen → API & webhooks. Stuur die mee in de header x-api-key (of als Bearer-token). Een sleutel hoort altijd bij één winkel en werkt alleen voor de rechten die je aanvinkt. Sleutels zijn na aanmaken niet meer op te vragen: bewaar ze veilig aan je eigen kant.

curl "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/ping" \
  -H "x-api-key: lo_live_xxxxxxxxxxxxxxxx"

Endpoints

Alle antwoorden zijn JSON met { ok: true, data: … }. Lijsten geven ook next_offset (null op de laatste pagina). Fouten geven { error: "…" } met een passende statuscode.

GET/subscriptionsrecht: subscriptions

Lijst met abonnementen van je winkel, nieuwste eerst.

Queryparameters: status (active | paused | cancelled | expired | failed), customer_id, email, limit (max 200), offset

curl "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions" \
  -H "x-api-key: $LOYALO_API_KEY"
GET/subscriptions/{id}recht: subscriptions

Eén abonnement met regels, bedrag en volgende incassodatum.

curl "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID" \
  -H "x-api-key: $LOYALO_API_KEY"
POST/subscriptions/{id}/pauserecht: subscriptions:write

Pauzeert het abonnement (Shopify én Mollie).

curl -X POST "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/pause" \
  -H "x-api-key: $LOYALO_API_KEY" \
  -H "Content-Type: application/json"
POST/subscriptions/{id}/resumerecht: subscriptions:write

Hervat een gepauzeerd abonnement.

curl -X POST "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/resume" \
  -H "x-api-key: $LOYALO_API_KEY" \
  -H "Content-Type: application/json"
POST/subscriptions/{id}/skiprecht: subscriptions:write

Slaat de eerstvolgende levering over; de datum schuift één interval op.

curl -X POST "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/skip" \
  -H "x-api-key: $LOYALO_API_KEY" \
  -H "Content-Type: application/json"
POST/subscriptions/{id}/reschedulerecht: subscriptions:write

Verzet de eerstvolgende levering naar een andere datum (binnen een jaar).

curl -X POST "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/reschedule" \
  -H "x-api-key: $LOYALO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "date": "2026-11-15" }'
POST/subscriptions/{id}/cancelrecht: subscriptions:write

Zegt het abonnement definitief op.

curl -X POST "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/cancel" \
  -H "x-api-key: $LOYALO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "customer_request" }'
GET/customersrecht: customers

Klanten met puntensaldo, bestedingen en taalvoorkeur.

Queryparameters: email (exact match), limit (max 200), offset

curl "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/customers" \
  -H "x-api-key: $LOYALO_API_KEY"
GET/pointsrecht: points

Puntentransacties, eventueel gefilterd op klant.

Queryparameters: customer_id, limit (max 500), offset

curl "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/points" \
  -H "x-api-key: $LOYALO_API_KEY"
POST/pointsrecht: points:write

Punten bijschrijven (positief) of afboeken (negatief). De klant geef je met customer_id (van Loyalo), shopify_customer_id of email: precies één. Dezelfde idempotency_key (of Idempotency-Key-header) boekt nooit twee keer.

curl -X POST "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/points" \
  -H "x-api-key: $LOYALO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "customer_id": "…", "points": 100, "note": "Goodwill", "idempotency_key": "ticket-4711" }'
GET/redemptionsrecht: redemptions

Verzilverde kortingen met code, status en vervaldatum.

Queryparameters: customer_id, status (pending | issued | failed | used | expired), limit (max 200), offset

curl "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/redemptions" \
  -H "x-api-key: $LOYALO_API_KEY"
GET/pingrecht: —

Controleert je sleutel en geeft de winkel en rechten terug.

curl "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/ping" \
  -H "x-api-key: $LOYALO_API_KEY"

Foutcodes

StatusMeldingBetekenis
401Missing API key / Invalid API keySleutel ontbreekt, is fout of is ingetrokken.
403Sleutel mist recht '…'De sleutel heeft dit recht niet. Pas de rechten aan in Loyalo.
404Not foundOnbekend pad of het object bestaat niet in deze winkel.
429Rate limit exceededMeer verzoeken dan de limiet per minuut. Wacht en probeer opnieuw.
500Foutmelding in het veld errorEr ging iets mis aan onze kant of bij Shopify/Mollie.

AI-assistenten (MCP)

Loyalo is ook een MCP-server: een AI-assistent zoals Claude of ChatGPT gebruikt dezelfde functies als tools. Koppel met het adres hieronder en een API-sleutel als Bearer-token. De assistent ziet alleen de tools die de rechten van die sleutel toestaan; geef hem liefst een eigen sleutel, alleen met schrijfrechten als hij abonnementen of punten mag wijzigen.

claude mcp add --transport http loyalo https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/mcp \
  --header "Authorization: Bearer lo_live_xxxxxxxxxxxxxxxx"

Webhooks

Stel in Loyalo een endpoint in met de gebeurtenissen die je wilt ontvangen. Elke bezorging is ondertekend en wordt bij een fout opnieuw geprobeerd met oplopende tussenpozen (1, 5, 20, 60 en 240 minuten). Antwoord met een 2xx-status binnen 10 seconden.

EventWanneer
subscription.createdNieuw abonnement aangemaakt
subscription.updatedAbonnement gewijzigd (datum, regels, frequentie)
subscription.pausedAbonnement gepauzeerd
subscription.resumedAbonnement hervat
subscription.cancelledAbonnement opgezegd
subscription.payment_failedIncasso mislukt
points.changedPuntensaldo van een klant gewijzigd
pingTestbericht (knop in Loyalo)

Voorbeeld van een bezorging

POST https://jouw-server.nl/loyalo-webhook
x-loyalo-event: subscription.paused
x-loyalo-timestamp: 1755763200
x-loyalo-signature: 4f3a…  (HMAC-SHA256 van "timestamp.body")

{
  "id": "7b1c…",
  "event": "subscription.paused",
  "created_at": "2026-08-21T08:00:00.000Z",
  "data": {
    "id": "9f2e…",
    "external_id": "gid://shopify/SubscriptionContract/123",
    "status": "paused",
    "source": "shopify",
    "customer_email": "klant@voorbeeld.nl",
    "amount": 24.95,
    "currency": "EUR",
    "next_billing_date": "2026-09-01T00:00:00.000Z",
    "billing_interval": "MONTH",
    "billing_interval_count": 1,
    "lines": [{ "title": "Koffie 1kg", "quantity": 1 }]
  }
}

Handtekening controleren

De handtekening is een HMAC-SHA256 over "timestamp.body" met het geheim van je endpoint (begint met whsec_). Vergelijk in constante tijd en weiger oude verzoeken.

import crypto from "node:crypto";

// Express-voorbeeld met de ruwe body (Buffer)
function verifyLoyaloWebhook(req, secret) {
  const timestamp = req.header("x-loyalo-timestamp");
  const signature = req.header("x-loyalo-signature");
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${req.rawBody}`)
    .digest("hex");

  const ok = crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature ?? ""),
  );
  // Weiger verzoeken ouder dan 5 minuten
  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
  return ok && fresh;
}