REST · JSON · HTTPS

API Reference

The Loyal Aura REST API lets your own systems read members and transactions, record visits and purchases, and redeem rewards — and webhooks let us notify your server when something happens.

Base URL

https://api.loyalaura.com/api

Auth

Bearer token (API key)

Key format

lk_live_… / lk_test_…

Authentication

Every request needs an Authorization header carrying an API key. Create and revoke keys in Settings → Developers → API keys. The full key is shown only once, when you create it — store it somewhere safe.

Authorization: Bearer lk_live_xxxxxxxxxxxx
  • A key acts on behalf of the team member who created it and can access the same businesses they can. If that user manages more than one business, add business_id as a query parameter (or an X-Business-Id header).
  • Keys can be labelled live or test (prefix lk_live_ or lk_test_) to help you tell them apart. Both work against your real account data — there is no separate sandbox environment.
  • API access requires an active subscription. Revoked or expired keys return 401.

Examples

List members

curl https://api.loyalaura.com/api/loyalty-members/ \
  -H "Authorization: Bearer lk_live_xxxxxxxxxxxx"

Record a purchase

curl -X POST https://api.loyalaura.com/api/scan/ \
  -H "Authorization: Bearer lk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "member_identifier": "<wallet pass serial, email or phone>",
    "value": 12.50
  }'

member_identifier accepts the wallet pass serial number (the value in the card's barcode), or the member's email or phone. Optional fields: value (purchase amount), reward_id (redeem a specific reward), redeem_reward (claim the reward a stamp card has earned) and preview_only (calculate the result without saving it).

Endpoints

Paths are relative to https://api.loyalaura.com/api. Note the trailing slash on every path.

Members

GET/loyalty-members/List the loyalty members of your business.
GET/loyalty-members/{id}/Retrieve a single member.
GET/loyalty-members/{id}/details/Member profile with program, tier and card details.
POST/loyalty-members/Create a member.
PATCH/loyalty-members/{id}/Update member fields.
DELETE/loyalty-members/{id}/Delete a member.

Scans (earn & redeem)

POST/scan/Record a visit or purchase for a member, or redeem a reward — the same action as the staff scanner.

Transactions

GET/loyalty-transactions/List loyalty transactions. Filter with ?member_id= or ?template_id=.

Program setup

GET/loyalty-programs/List your loyalty programs.
GET/rewards/List the rewards members can redeem.
GET/locations/List your business locations.

Webhooks

Add an endpoint URL in Settings → Developers → Webhooks and choose the events you want. We send a JSON POST with this envelope:

{
  "id": "<event id>",
  "type": "developer.test",
  "created": 1700000000,
  "business_id": "<business id>",
  "data": { ... }
}
  • Headers: X-Loyal-Aura-Signature, X-Loyal-Aura-Event, X-Loyal-Aura-Event-Id, X-Loyal-Aura-Delivery-Id, X-Loyal-Aura-Timestamp.
  • The signature header looks like t=<unix time>,v1=<hex>, where v1 is an HMAC-SHA256 of <t>.<raw request body> using your endpoint's signing secret (whsec_…).
  • Respond with a 2xx status within 10 seconds. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 8 hours and 24 hours. Use X-Loyal-Aura-Event-Id to ignore duplicates.
  • Use Send test event in the dashboard to deliver a developer.test event and check your handler.

Verify a signature

// Node.js — verify an incoming Loyal Aura webhook
import crypto from "node:crypto";

function verify(rawBody, header, secret, toleranceSeconds = 300) {
  // header: "t=1700000000,v1=<hex>"
  const parts = Object.fromEntries(
    header.split(",").map((kv) => kv.split("=", 2).map((s) => s.trim())),
  );
  const t = Number(parts.t);
  if (!t || !parts.v1) return false;
  if (Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}