L Loyalino Developers
Get API key

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.

bash
curl -o loyalino.js https://help.loyalino.io/sdk.js
No build step. It uses the built-in fetch and node:crypto, so there is nothing to install.

Quickstart

javascript
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

MethodCalls
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.

javascript
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.

javascript
// 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;