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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/ping", {
headers: { "x-api-key": process.env.LOYALO_API_KEY },
});
const body = await res.json();
console.log(body); // { ok: true, shop: "...", scopes: [...] } 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.
/subscriptionsrecht: subscriptionsLijst 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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions", {
headers: { "x-api-key": process.env.LOYALO_API_KEY },
});
const { data, next_offset } = await res.json(); /subscriptions/{id}recht: subscriptionsEé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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID", {
headers: { "x-api-key": process.env.LOYALO_API_KEY },
});
const { data } = await res.json(); /subscriptions/{id}/pauserecht: subscriptions:writePauzeert 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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/pause", {
method: "POST",
headers: {
"x-api-key": process.env.LOYALO_API_KEY,
"Content-Type": "application/json",
},
});
const { data } = await res.json(); /subscriptions/{id}/resumerecht: subscriptions:writeHervat 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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/resume", {
method: "POST",
headers: {
"x-api-key": process.env.LOYALO_API_KEY,
"Content-Type": "application/json",
},
});
const { data } = await res.json(); /subscriptions/{id}/skiprecht: subscriptions:writeSlaat 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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/skip", {
method: "POST",
headers: {
"x-api-key": process.env.LOYALO_API_KEY,
"Content-Type": "application/json",
},
});
const { data } = await res.json(); /subscriptions/{id}/reschedulerecht: subscriptions:writeVerzet 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" }' const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/reschedule", {
method: "POST",
headers: {
"x-api-key": process.env.LOYALO_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ "date": "2026-11-15" }),
});
const { data } = await res.json(); /subscriptions/{id}/cancelrecht: subscriptions:writeZegt 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" }' const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/subscriptions/SUBSCRIPTION_ID/cancel", {
method: "POST",
headers: {
"x-api-key": process.env.LOYALO_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ "reason": "customer_request" }),
});
const { data } = await res.json(); /customersrecht: customersKlanten 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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/customers", {
headers: { "x-api-key": process.env.LOYALO_API_KEY },
});
const { data, next_offset } = await res.json(); /pointsrecht: pointsPuntentransacties, 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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/points", {
headers: { "x-api-key": process.env.LOYALO_API_KEY },
});
const { data, next_offset } = await res.json(); /pointsrecht: points:writePunten 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" }' const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/points", {
method: "POST",
headers: {
"x-api-key": process.env.LOYALO_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ "customer_id": "…", "points": 100, "note": "Goodwill", "idempotency_key": "ticket-4711" }),
});
const { data } = await res.json(); /redemptionsrecht: redemptionsVerzilverde 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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/redemptions", {
headers: { "x-api-key": process.env.LOYALO_API_KEY },
});
const { data, next_offset } = await res.json(); /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" const res = await fetch("https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/ping", {
headers: { "x-api-key": process.env.LOYALO_API_KEY },
});
const { data } = await res.json(); Foutcodes
| Status | Melding | Betekenis |
|---|---|---|
| 401 | Missing API key / Invalid API key | Sleutel ontbreekt, is fout of is ingetrokken. |
| 403 | Sleutel mist recht '…' | De sleutel heeft dit recht niet. Pas de rechten aan in Loyalo. |
| 404 | Not found | Onbekend pad of het object bestaat niet in deze winkel. |
| 429 | Rate limit exceeded | Meer verzoeken dan de limiet per minuut. Wacht en probeer opnieuw. |
| 500 | Foutmelding in het veld error | Er 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" {
"mcpServers": {
"loyalo": {
"type": "http",
"url": "https://mwugkfkidoakjoelhtir.supabase.co/functions/v1/v2-api/mcp",
"headers": { "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.
| Event | Wanneer |
|---|---|
subscription.created | Nieuw abonnement aangemaakt |
subscription.updated | Abonnement gewijzigd (datum, regels, frequentie) |
subscription.paused | Abonnement gepauzeerd |
subscription.resumed | Abonnement hervat |
subscription.cancelled | Abonnement opgezegd |
subscription.payment_failed | Incasso mislukt |
points.changed | Puntensaldo van een klant gewijzigd |
ping | Testbericht (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;
}