Loyalty API (SoPoints)
Gridline integrates with SoPoints to provide a full loyalty rewards system. Earn points on purchases, redeem for rewards, and track customer loyalty history.
Note: The Loyalty API is a wrapper around the SoPoints external service. All loyalty operations are processed through the SoPoints platform and synced back to Gridline in real-time.
Earn Points
/v1/loyalty/earnAward points to a customer based on a transaction. Points are calculated using the merchant's earn rate configuration.
{
"customer_id": "cus_xyz789",
"order_id": "ord_a1b2c3d4",
"amount": 2499,
"currency": "CAD",
"metadata": {
"source": "pos_terminal",
"location_id": "loc_001"
}
}{
"id": "txn_pts_001",
"type": "earn",
"customer_id": "cus_xyz789",
"points_earned": 25,
"balance_after": 350,
"earn_rate": "1 point per $1",
"order_id": "ord_a1b2c3d4",
"created_at": "2024-01-15T10:31:00Z"
}Status Codes
200Points earned successfully400Invalid request or duplicate order404Customer not found
Redeem Points
/v1/loyalty/redeemRedeem points for a discount on an order. Validates that the customer has sufficient balance before processing.
{
"customer_id": "cus_xyz789",
"points": 100,
"order_id": "ord_e5f6g7h8",
"reward_id": "rwd_free_drink"
}{
"id": "txn_pts_002",
"type": "redeem",
"customer_id": "cus_xyz789",
"points_redeemed": 100,
"balance_after": 250,
"discount_amount": 500,
"discount_currency": "CAD",
"reward": {
"id": "rwd_free_drink",
"name": "Free Drink",
"points_cost": 100
},
"order_id": "ord_e5f6g7h8",
"created_at": "2024-01-15T12:00:00Z"
}Status Codes
200Points redeemed successfully400Insufficient points balance404Customer or reward not found
Get Balance
/v1/loyalty/balance/:customer_idRetrieve the current points balance and tier status for a customer.
{
"customer_id": "cus_xyz789",
"points_balance": 350,
"lifetime_points": 1250,
"tier": {
"name": "Gold",
"multiplier": 1.5,
"next_tier": "Platinum",
"points_to_next_tier": 750
},
"available_rewards": [
{
"id": "rwd_free_drink",
"name": "Free Drink",
"points_cost": 100
},
{
"id": "rwd_10_off",
"name": "$10 Off",
"points_cost": 200
}
]
}Transaction History
/v1/loyalty/history/:customer_idRetrieve the points transaction history for a customer, including all earn and redeem events.
{
"data": [
{
"id": "txn_pts_002",
"type": "redeem",
"points": -100,
"balance_after": 250,
"description": "Redeemed: Free Drink",
"created_at": "2024-01-15T12:00:00Z"
},
{
"id": "txn_pts_001",
"type": "earn",
"points": 25,
"balance_after": 350,
"description": "Order #ord_a1b2c3d4",
"created_at": "2024-01-15T10:31:00Z"
}
],
"pagination": {
"total": 48,
"page": 1,
"per_page": 20,
"has_more": true
}
}Loyalty Webhook Events
Subscribe to these events to receive real-time notifications about loyalty activity:
| Event | Description |
|---|---|
points.earned | Points awarded to a customer after purchase |
points.redeemed | Points redeemed for a reward or discount |
points.expired | Points expired due to inactivity |
tier.upgraded | Customer moved up to a higher loyalty tier |
SoPoints Integration Guide
Follow these steps to configure SoPoints loyalty in your Gridline integration:
- Enable Loyalty — Navigate to Dashboard > Settings > Loyalty and activate SoPoints integration
- Configure Earn Rate — Set your points-per-dollar ratio (default: 1 point per $1 spent)
- Define Rewards — Create redeemable rewards with point costs
- Set Up Webhooks — Subscribe to loyalty events for real-time sync
- Test in Sandbox — Use test API keys to verify the flow before going live
import { Gridline } from '@gridline/sdk';
const client = new Gridline({ apiKey: process.env.GRIDLINE_API_KEY });
// After a successful order, earn points
async function handleOrderPaid(order: Order) {
const result = await client.loyalty.earn({
customer_id: order.customer_id,
order_id: order.id,
amount: order.total,
currency: order.currency,
});
console.log(`Customer earned ${result.points_earned} points`);
console.log(`New balance: ${result.balance_after}`);
}
// At checkout, redeem points
async function redeemAtCheckout(customerId: string, rewardId: string, orderId: string) {
const balance = await client.loyalty.getBalance(customerId);
const reward = balance.available_rewards.find(r => r.id === rewardId);
if (!reward) throw new Error('Reward not available');
if (balance.points_balance < reward.points_cost) {
throw new Error('Insufficient points');
}
return client.loyalty.redeem({
customer_id: customerId,
points: reward.points_cost,
order_id: orderId,
reward_id: rewardId,
});
}