Loyalty API Reference

Complete REST API documentation for customer loyalty, points, vouchers, and rewards management. Integrate loyalty functionality into your POS, mobile app, or e-commerce platform.

Introduction

The KiriMel Loyalty API allows you to programmatically manage customer loyalty programs, including registration, points earning/redemption, voucher management, and rewards. Built for seamless integration with POS systems like VectorPOS (VectorDine and VectorRetail).

🚀 Quick Start

Get started in 3 steps: 1) Create a loyalty group, 2) Add your sites, 3) Register customers and start awarding points. See the endpoints below for details.

⚠️ API Key Required

All Loyalty API endpoints require an API key with HMAC authentication. Generate your API keys in the Loyalty Dashboard after creating your loyalty group.

📖 Workflow Guides Available

Need step-by-step integration examples? Check out our Loyalty API Workflows page for complete POS integration scenarios including member checkout, voucher redemption, and points payment.

Authentication

The Loyalty API uses HMAC-SHA256 signature authentication for security. Each request must include three headers:

X-API-Key: your_api_key_here
X-Timestamp: 2024-01-01T12:00:00Z
X-Signature: computed_hmac_signature_here

Signature Calculation

The signature is computed as:

signature = HMAC-SHA256(timestamp + requestBody, apiSecret)

PHP Example

$timestamp = gmdate('Y-m-d\TH:i:s\Z');
$signature = hash_hmac('sha256', $timestamp . $requestBody, $apiSecret);

$headers = [
    'X-API-Key: ' . $apiKey,
    'X-Timestamp: ' . $timestamp,
    'X-Signature: ' . $signature
];

Node.js Example

const crypto = require('crypto');
const timestamp = new Date().toISOString();
const signature = crypto.createHmac('sha256', apiSecret)
    .update(timestamp + requestBody)
    .digest('hex');

Python Example

import hmac
import hashlib
import time

timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
signature = hmac.new(apiSecret.encode(), (timestamp + body).encode(), hashlib.sha256).hexdigest()

Rate Limits

API requests are rate-limited per API key to ensure fair usage:

  • Default: 100 requests per minute
  • Burst: 200 requests per minute (for short spikes)
  • Enterprise: Unlimited (contact sales for details)

Rate limit headers are included in every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067200

Create Loyalty Group

Creates a new loyalty group (company) with an associated tenant database. Each merchant can have one loyalty group containing multiple sites.

POST /group/create

Request Body

Parameter Type Required Description
company_slug string Required URL-safe company identifier (lowercase letters and numbers only, min 3 chars)
company_name string Required Legal company name
business_type string Required RESTAURANT or RETAIL

Example Request

curl -X POST http://localhost/api/loyalty/group/create \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here" \
  -d "company_slug=myrestaurant&company_name=My+Restaurant+Sdn+Bhd&business_type=RESTAURANT"

Response

{
  "success": true,
  "group_id": 123,
  "group_code": "myrestaurant_a1b2c3d4",
  "tenant_db": "kiriloyalty_myrestaurant_a1b2c3d4"
}

Add Site to Group

Add a new location/site to an existing loyalty group. A loyalty group can have multiple sites (e.g., main restaurant, branch outlet).

POST /group/sites/add

Request Body

Parameter Type Required Description
site_code string Required Unique site code (uppercase letters and numbers only, min 3 chars)
site_name string Required Display name for the location
site_type string Required RESTAURANT or RETAIL
address string Optional Street address
city string Optional City
state string Optional State/province
postal_code string Optional Postal code
phone string Optional Contact phone (include country code)

Example Request

curl -X POST http://localhost/api/loyalty/group/sites/add \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here" \
  -d "site_code=MAIN01&site_name=Main+Restaurant&site_type=RESTAURANT&address=123+Main+St&city=Kuala+Lumpur&phone=%2B60123456789"

Response

{
  "success": true,
  "site_id": 456,
  "site_code": "MAIN01"
}

Register Customer

Register a new customer in the loyalty program. Customers can be identified by phone number or QR code.

POST /customers/register

Request Body

Parameter Type Required Description
phone string Conditional* Phone number with country code (e.g., +60123456789)
qr_code string Conditional* QR code string
name string Optional Customer name
email string Optional Email address
birth_date string Optional Birthday for birthday rewards (YYYY-MM-DD format)

*Either phone or qr_code must be provided

Example Request

curl -X POST http://localhost/api/loyalty/customers/register \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here" \
  -d "phone=%2B60123456789&name=John+Doe&birth_date=1990-05-15"

Response

{
  "success": true,
  "customer_id": "CUST_123456",
  "wallet_id": "WALLET_789",
  "points_balance": 0,
  "tier": "BRONZE"
}

Lookup Customer

Find an existing customer by phone number, QR code, or promo code. Returns customer profile with current points balance.

GET /customers/lookup

Query Parameters

Parameter Type Required Description
phone string Conditional* Phone number with country code
identifier string Conditional* QR code or promo code

*At least one parameter must be provided

Example Request

curl -X GET "http://localhost/api/loyalty/customers/lookup?phone=%2B60123456789" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here"

Response

{
  "success": true,
  "data": {
    "id": 123,
    "site_id": 456,
    "phone": "+60123456789",
    "name": "John Doe",
    "email": "[email protected]",
    "status": "ACTIVE",
    "balance_points": 1500,
    "joined_at": "2026-05-01T10:00:00Z",
    "last_earn_at": "2026-05-10T12:00:00Z",
    "last_redeem_at": "2026-05-09T15:30:00Z",
    "last_seen_at": "2026-05-10T14:00:00Z"
  }
}

Lookup Customer by Email

Find an existing customer by email address. Returns customer profile with current points balance.

GET /customers/lookup-by-email

Query Parameters

Parameter Type Required Description
email string Required Email address to lookup

Example Request

curl -X GET "http://localhost/api/loyalty/customers/lookup-by-email?email=john%40example.com" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here"

Response

{
  "success": true,
  "data": {
    "id": 123,
    "site_id": 456,
    "phone": "+60123456789",
    "name": "John Doe",
    "email": "[email protected]",
    "status": "ACTIVE",
    "balance_points": 1500,
    "joined_at": "2026-05-01T10:00:00Z",
    "last_earn_at": "2026-05-10T12:00:00Z",
    "last_redeem_at": "2026-05-09T15:30:00Z",
    "last_seen_at": "2026-05-10T14:00:00Z"
  }
}

Earn Points

Award points to a customer's wallet after a purchase or action. Points are calculated based on your configured conversion rate.

POST /points/earn

Request Body

Parameter Type Required Description
customer_id string Required Customer ID from registration
points integer Required Points to award (must be positive integer)
amount decimal Required Purchase amount (for record-keeping, e.g., 25.50)
reference_id string Optional External reference ID for idempotency (e.g., order ID, transaction ID)
description string Optional Description of the points transaction

Example Request

curl -X POST http://localhost/api/loyalty/points/earn \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here" \
  -d "customer_id=CUST_123456&points=50&amount=25.50&reference_id=ORDER_123&description=Dining+reward"

Response

{
  "success": true,
  "transaction_id": "TXN_456",
  "points_awarded": 50,
  "new_balance": 150,
  "tier_progress": {
    "current_tier": "BRONZE",
    "points_to_next": 850,
    "next_tier": "SILVER"
  }
}

Redeem Points

Redeem points from a customer's wallet for rewards or discounts. Validates sufficient balance before processing.

POST /points/redeem

Request Body

Parameter Type Required Description
customer_id string Required Customer ID
points integer Required Points to redeem (must be positive integer)
reference_id string Optional External reference ID for idempotency
description string Optional Redemption description

Example Request

curl -X POST http://localhost/api/loyalty/points/redeem \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here" \
  -d "customer_id=CUST_123456&points=100&reference_id=BILL_456&description=Discount+redemption"

Response

{
  "success": true,
  "transaction_id": "TXN_789",
  "points_redeemed": 100,
  "new_balance": 50
}

Get Customer Balance

Retrieve a customer's current points balance, tier status, and expiry information.

GET /wallet/balance

Query Parameters

Parameter Type Required Description
customer_id string Required Customer ID from registration

Example Request

curl -X GET "http://localhost/api/loyalty/wallet/balance?customer_id=CUST_123456" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here"

Response

{
  "success": true,
  "customer_id": "CUST_123456",
  "balance": 150,
  "tier": {
    "name": "SILVER",
    "benefits": ["5% discount on birthdays", "Priority seating"]
  },
  "points_expiring_soon": 20,
  "expiry_date": "2024-06-30"
}

Create Voucher Batch

Generate a batch of unique vouchers for distribution. Each voucher has a unique 12-character code.

POST /vouchers/batches/create

Request Body

Parameter Type Required Description
name string Required Batch name (e.g., "Grand Opening Promo")
type string Required Voucher type: PERCENT, FIXED, or BOGO
value decimal Required Voucher value (percentage or amount)
quantity integer Required Number of vouchers to generate (max 1000 per batch)
valid_from string Optional Valid from date (YYYY-MM-DD format)
valid_until string Optional Valid until date (YYYY-MM-DD format)
min_purchase decimal Optional Minimum purchase amount for voucher use

Example Request

curl -X POST http://localhost/api/loyalty/vouchers/batches/create \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here" \
  -d "name=Grand+Opening+Promo&type=PERCENT&value=10&quantity=100&valid_from=2024-06-01&valid_until=2024-12-31&min_purchase=50"

Response

{
  "success": true,
  "batch_id": "BATCH_001",
  "vouchers_created": 100,
  "voucher_codes": ["VOUCHER_A1B2C3D4E5F6", "VOUCHER_G7H8I9J0K1L2", ...]
}

Redeem Voucher

Redeem a voucher code, applying the discount or reward to the customer's transaction. Validates voucher expiry and usage status.

POST /vouchers/redeem

Request Body

Parameter Type Required Description
code string Required Voucher code (12-character base62 string)
customer_id string Required Customer ID redeeming the voucher
purchase_amount decimal Optional Purchase amount for validation (for vouchers with min_purchase)
reference_id string Optional External reference ID (e.g., order ID, transaction ID)

Example Request

curl -X POST http://localhost/api/loyalty/vouchers/redeem \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "X-API-Key: your_api_key" \
  -H "X-Timestamp: 2024-01-01T12:00:00Z" \
  -H "X-Signature: signature_here" \
  -d "code=VOUCHER_A1B2C3D4E5F6&customer_id=CUST_123456&purchase_amount=75.00&reference_id=ORDER_456"

Response

{
  "success": true,
  "redemption_id": "REDEEM_123",
  "voucher_type": "PERCENT",
  "voucher_value": 10,
  "discount_applied": 7.50,
  "new_purchase_amount": 67.50
}

Error Handling

The API uses standard HTTP response codes and returns detailed error information in JSON format.

Error Response Format

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Customer does not have enough points to complete this redemption",
    "details": {
      "requested": 100,
      "available": 50,
      "shortfall": 50
    }
  }
}

HTTP Status Codes

Code Description
200 Request successful
400 Bad Request - Invalid parameters
401 Unauthorized - Invalid API key or signature
404 Not Found - Resource doesn't exist
429 Too Many Requests - Rate limit exceeded
500 Internal Server Error

Common Error Codes

Error Code Description
INVALID_API_KEY API key is invalid or expired
SIGNATURE_INVALID HMAC signature verification failed
CUSTOMER_NOT_FOUND Customer ID does not exist
INSUFFICIENT_BALANCE Not enough points in wallet
VOUCHER_EXPIRED Voucher has passed expiry date
VOUCHER_ALREADY_REDEEMED Voucher has already been used
VOUCHER_NOT_FOUND Voucher code does not exist
SITE_LIMIT_REACHED Maximum number of sites reached for your plan