Libraries and SDKs
The official Loyalino Node SDK is a single, dependency-free file that wraps the REST API and webhook signature verification. It runs on Node 18 and later.
Install
Download the SDK and drop it into your project, or copy the source from the bottom of this page.
curl -o loyalino.js https://help.loyalino.io/sdk.jsNo build step. It uses the built-in
fetch and node:crypto, so there is nothing to install.Quickstart
import Loyalino from "./loyalino.js";
const loyalino = new Loyalino(process.env.LOYALINO_API_KEY);
// Read your program settings
const program = await loyalino.getProgram();
// Find a customer and award points
const { data } = await loyalino.listCustomers({ email: "ada@example.com" });
const customer = data[0];
await loyalino.awardPoints(customer.id, 100, {
type: "earned",
description: "Birthday bonus",
});
// Redeem a reward
const redemption = await loyalino.redeem(customer.id, { reward_id: "rw_5" });
console.log(redemption.discount_code);Methods
| Method | Calls |
|---|---|
listCustomers(query) | GET /customers |
getCustomer(id) | GET /customers/:id |
createCustomer(body) | POST /customers |
updateCustomer(id, body) | PATCH /customers/:id |
awardPoints(id, points, extra) | POST /customers/:id/points_transactions |
listCustomerTransactions(id, query) | GET /customers/:id/points_transactions |
listTransactions(query) | GET /points_transactions |
getTransaction(id) | GET /points_transactions/:id |
createActivity(body) | POST /activities |
redeem(customerId, body) | POST /customers/:id/redemptions |
listRewards(query) | GET /rewards |
getReward(id) | GET /rewards/:id |
listEarnRules(query) | GET /earn_rules |
listVipTiers(query) | GET /vip_tiers |
getProgram() | GET /program |
listWebhookEndpoints(query) | GET /webhook_endpoints |
createWebhookEndpoint(body) | POST /webhook_endpoints |
deleteWebhookEndpoint(id) | DELETE /webhook_endpoints/:id |
Every method returns the parsed JSON response. On a non-2xx status the SDK throws an Error with .status and .code set from the error envelope.
Verify webhooks
Use the exported verifyWebhook helper with your endpoint secret, the raw request body, and the Loyalino-Signature header.
import { verifyWebhook } from "./loyalino.js";
// Express example (use the raw body, not the parsed JSON)
app.post("/loyalino-webhook", express.raw({ type: "application/json" }), (req, res) => {
const ok = verifyWebhook(
process.env.LOYALINO_WEBHOOK_SECRET,
req.headers["loyalino-signature"],
req.body.toString("utf8"),
);
if (!ok) return res.status(400).send("bad signature");
const event = JSON.parse(req.body.toString("utf8"));
console.log(event.type, event.data);
res.sendStatus(200);
});Full source
The entire SDK. Copy it into loyalino.js if you prefer not to download it.
// loyalino.js — official Node client for the Loyalino loyalty REST API.
// Requires Node 18+ (uses global fetch). ESM. No dependencies.
//
// import Loyalino, { verifyWebhook } from "./loyalino.js";
// const loyalino = new Loyalino(process.env.LOYALINO_API_KEY);
// const program = await loyalino.getProgram();
//
import crypto from "node:crypto";
export class Loyalino {
constructor(apiKey, options) {
if (!apiKey) throw new Error("Loyalino: apiKey is required");
options = options || {};
let base = options.baseUrl || "https://app.loyalino.io/api/v1";
while (base.length && base.charAt(base.length - 1) === "/") base = base.slice(0, -1);
this.apiKey = apiKey;
this.baseUrl = base;
}
async request(method, path, opts) {
opts = opts || {};
const url = new URL(this.baseUrl + path);
if (opts.query) {
for (const key of Object.keys(opts.query)) {
const value = opts.query[key];
if (value !== undefined && value !== null) url.searchParams.set(key, String(value));
}
}
const headers = { Authorization: "Bearer " + this.apiKey };
if (opts.body) headers["Content-Type"] = "application/json";
const res = await fetch(url, {
method: method,
headers: headers,
body: opts.body ? JSON.stringify(opts.body) : undefined,
});
const text = await res.text();
const data = text ? JSON.parse(text) : {};
if (!res.ok) {
const err = new Error((data.error && data.error.message) || ("Loyalino API error " + res.status));
err.status = res.status;
err.code = data.error && data.error.code;
throw err;
}
return data;
}
// Customers
listCustomers(query) { return this.request("GET", "/customers", { query: query }); }
getCustomer(id) { return this.request("GET", "/customers/" + encodeURIComponent(id)); }
createCustomer(body) { return this.request("POST", "/customers", { body: body }); }
updateCustomer(id, body) { return this.request("PATCH", "/customers/" + encodeURIComponent(id), { body: body }); }
// Points
awardPoints(id, points, extra) {
const body = Object.assign({ points: points }, extra || {});
return this.request("POST", "/customers/" + encodeURIComponent(id) + "/points_transactions", { body: body });
}
listCustomerTransactions(id, query) {
return this.request("GET", "/customers/" + encodeURIComponent(id) + "/points_transactions", { query: query });
}
listTransactions(query) { return this.request("GET", "/points_transactions", { query: query }); }
getTransaction(id) { return this.request("GET", "/points_transactions/" + id); }
// Activities + redemptions
createActivity(body) { return this.request("POST", "/activities", { body: body }); }
redeem(customerId, body) {
return this.request("POST", "/customers/" + encodeURIComponent(customerId) + "/redemptions", { body: body });
}
// Program (read-only)
listRewards(query) { return this.request("GET", "/rewards", { query: query }); }
getReward(id) { return this.request("GET", "/rewards/" + id); }
listEarnRules(query) { return this.request("GET", "/earn_rules", { query: query }); }
listVipTiers(query) { return this.request("GET", "/vip_tiers", { query: query }); }
getProgram() { return this.request("GET", "/program"); }
// Webhook endpoints
listWebhookEndpoints(query) { return this.request("GET", "/webhook_endpoints", { query: query }); }
createWebhookEndpoint(body) { return this.request("POST", "/webhook_endpoints", { body: body }); }
deleteWebhookEndpoint(id) { return this.request("DELETE", "/webhook_endpoints/" + id); }
}
// Verify a webhook signature header against the raw request body + your secret.
export function verifyWebhook(secret, signatureHeader, rawBody) {
const parts = {};
String(signatureHeader || "").split(",").forEach(function (p) {
const i = p.indexOf("=");
if (i > 0) parts[p.slice(0, i).trim()] = p.slice(i + 1).trim();
});
const expected = crypto.createHmac("sha256", secret).update(parts.t + "." + rawBody).digest("hex");
try {
return crypto.timingSafeEqual(Buffer.from(parts.v1 || "", "hex"), Buffer.from(expected, "hex"));
} catch (e) {
return false;
}
}
export default Loyalino;