Kreward External API
The Kreward API lets you embed your loyalty program into any website or backend. Available on the Growth (HK$450/month) and Enterprise plans.
Overview
The Kreward API lets you embed your loyalty program into any website or backend. Available on the Growth (HK$450/month) and Enterprise plans.
• Enroll a customer and get an instant wallet card QR code
• Add or deduct points / stamps in real time
• Query a customer current balance
• Trigger a redemption from your backend
Quick start (3 steps)
- Get your API keys — Go to Dashboard → API and generate a key pair. You get a public key and a secret key.
- Test the connection — Call GET /v1/ping with your public key. You should get {"ok":true}.
- Enroll your first customer — Call POST /v1/enroll with an email. The response includes QR codes for Apple Wallet and Google Wallet.
Authentication
Include your API key in the Authorization header using the Bearer scheme on every request.
PUBLIC
Safe in frontend JavaScript. Enrollment only. Can be restricted by domain whitelist.
SECRET
Server-side only. Full access. Never expose in frontend code.
Authorization: Bearer kw_pub_a3f... # Public key Authorization: Bearer kw_sk_9xB... # Secret key (server only)
Errors
All errors return JSON with an error field and an HTTP status code.
{ "error": "customer_not_found" }| Status | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request — missing or invalid parameter |
| 401 | Missing or invalid API key |
| 403 | Forbidden — plan not eligible, or origin blocked |
| 404 | Customer not found |
| 409 | Conflict — e.g. insufficient balance |
| 429 | Rate limit exceeded (100 req/min) |
| 500 | Internal server error |
Endpoints
Verify the API is reachable and your key is valid.
curl https://api.kreward.net/v1/ping \ -H "Authorization: Bearer kw_pub_YOUR_KEY"
{ "ok": true, "merchant": "My Shop" }Returns your merchant details, active card templates, and current plan.
curl https://api.kreward.net/v1/me \ -H "Authorization: Bearer kw_sk_YOUR_SECRET"
{ "merchant": "My Cafe", "plan": "growth", "templates": [
{ "id": "uuid...", "name": "Stamp Card", "type": "stamps", "goal": 8 }
] }Enroll a customer. Creates account if needed. Returns QR codes for Apple Wallet and Google Wallet.
| Field | Type | Description |
|---|---|---|
| string | If omitted, returns a QR linking to your enrollment page. | |
| name | string | Customer display name (optional) |
| template_id | uuid | Card template (defaults to first active) |
curl -X POST https://api.kreward.net/v1/enroll \
-H "Authorization: Bearer kw_pub_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","name":"Jane"}'const res = await fetch('https://api.kreward.net/v1/enroll', {
method: 'POST',
headers: { 'Authorization': 'Bearer kw_pub_YOUR_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({ email: '[email protected]', name: 'Jane' }),
});
const data = await res.json();
// data.qr_ios / data.qr_android -> base64 PNG$ch = curl_init('https://api.kreward.net/v1/enroll');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true,CURLOPT_POST=>true,
CURLOPT_HTTPHEADER=>['Authorization: Bearer kw_sk_SECRET','Content-Type: application/json'],
CURLOPT_POSTFIELDS=>json_encode(['email'=>'[email protected]','name'=>'Jane'])]);
$data = json_decode(curl_exec($ch),true);{ "enrolled": true, "card_id": "uuid...", "balance": 0,
"qr_ios": "data:image/png;base64,...",
"qr_android": "data:image/png;base64,...",
"wallet_url_ios": "https://...", "wallet_url_android": "https://..." }Display qr_ios / qr_android as <img src="..."> — no file storage needed. Or redirect to wallet_url_ios / wallet_url_android for one-tap install.
| Param | Type | Description |
|---|---|---|
| email required | string | Customer email address |
| template_id | uuid | Specific card template |
curl "https://api.kreward.net/v1/[email protected]" \ -H "Authorization: Bearer kw_sk_YOUR_SECRET"
{ "card_type": "stamps", "balance": 5, "goal": 8, "name": "Jane" }| Field | Type | Description |
|---|---|---|
| email required | string | Customer email |
| delta required | integer | Amount to add (positive) or deduct (negative). E.g. +1 stamp, -50 points. |
| note | string | Optional note (order ID, etc.) |
| template_id | uuid | Target a specific template |
curl -X POST https://api.kreward.net/v1/transaction \
-H "Authorization: Bearer kw_sk_YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","delta":1,"note":"Order #1234"}'await fetch('https://api.kreward.net/v1/transaction', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.KREWARD_SECRET_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ email: order.email, delta: 1, note: order.id }),
});wp_remote_post('https://api.kreward.net/v1/transaction', [
'headers' => ['Authorization'=>'Bearer '.$_ENV['KREWARD_SECRET'],'Content-Type'=>'application/json'],
'body' => json_encode(['email'=>$email,'delta'=>1,'note'=>'Order #'.$order_id]),
]);{ "ok": true, "new_balance": 6, "card_type": "stamps", "goal": 8 }Tiers are milestones identified by their at threshold (see GET /v1/customer). Claiming a tier marks it redeemed — it does not spend balance, except redeeming the final stamp milestone (at == stamps_total) on a stamps card, which loops the card (balance becomes the held overflow, milestones re-arm).
| Field | Type | Description |
|---|---|---|
| email required | string | Customer email |
| at required | integer | The tier threshold to claim (positive integer) |
curl -X POST https://api.kreward.net/v1/redeem \
-H "Authorization: Bearer kw_sk_YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","at":8}'{ "ok": true, "redeemed_at": 8, "looped": true, "redeemed_tiers": [],
"balance_before": 8, "balance_after": 0, "card_type": "stamps" }Plain HTML / JavaScript
Add an enrollment form to any static website in under 10 minutes using only the public key.
<form id="loyalty-form">
<input type="email" id="lf-email" placeholder="Your email" required>
<button type="submit">Get my loyalty card</button>
</form>
<div id="loyalty-result" style="display:none"></div>
<script>
document.getElementById('loyalty-form').addEventListener('submit', async e => {
e.preventDefault();
const res = await fetch('https://api.kreward.net/v1/enroll', {
method:'POST',
headers:{'Authorization':'Bearer kw_pub_YOUR_KEY','Content-Type':'application/json'},
body: JSON.stringify({ email: document.getElementById('lf-email').value }),
});
const d = await res.json();
if (d.qr_ios) {
document.getElementById('loyalty-result').innerHTML =
'<a href="'+d.wallet_url_ios+'"><img src="'+d.qr_ios+'" width="160"></a> '+
'<a href="'+d.wallet_url_android+'"><img src="'+d.qr_android+'" width="160"></a>';
document.getElementById('loyalty-result').style.display='block';
}
});
</script>WordPress / WooCommerce
Step 1 — Store your secret key in wp-config.php (never hardcode it in plugin files):
define('KREWARD_SECRET_KEY', 'kw_sk_YOUR_SECRET');Step 2 — Add to functions.php or a custom plugin:
add_action('woocommerce_order_status_completed', function($order_id) {
$order = wc_get_order($order_id);
if (!$order->get_billing_email()) return;
wp_remote_post('https://api.kreward.net/v1/transaction', [
'headers' => ['Authorization'=>'Bearer '.KREWARD_SECRET_KEY,'Content-Type'=>'application/json'],
'body' => json_encode(['email'=>$order->get_billing_email(),'delta'=>1,'note'=>'Order #'.$order_id]),
]);
});
add_action('woocommerce_created_customer', function($customer_id) {
$user = get_user_by('id', $customer_id);
wp_remote_post('https://api.kreward.net/v1/enroll', [
'headers' => ['Authorization'=>'Bearer '.KREWARD_SECRET_KEY,'Content-Type'=>'application/json'],
'body' => json_encode(['email'=>$user->user_email,'name'=>$user->display_name]),
]);
});Shopify Integration
Option A — Shopify Flow (no code)
- Install Shopify Flow from the App Store (free).
- Create a workflow triggered by "Order paid".
- Add a "Send HTTP request" action to POST /v1/transaction.
- Set the Authorization header and body with email from order.email and delta: 1.
Option B — Custom webhook (Node.js)
app.post('/webhooks/shopify/order-paid', async (req, res) => {
const order = req.body;
res.sendStatus(200);
if (!order.email) return;
await fetch('https://api.kreward.net/v1/transaction', {
method:'POST',
headers:{'Authorization':`Bearer ${process.env.KREWARD_SECRET_KEY}`,'Content-Type':'application/json'},
body:JSON.stringify({ email:order.email, delta:1, note:`Shopify ${order.name}` }),
});
});Security best practices
- Never expose your secret key in frontend code or public repositories. Use environment variables.
- Restrict allowed origins for your public key so it only works from your own domains.
- Rotate keys if you suspect a compromise — revoke and generate a new pair instantly.
- Use HTTPS only — the API enforces TLS.
- Store the secret key once — shown only at creation, never recoverable.
- Rate limit: 100 requests per minute per key.