The plain HTTP API is the supported way to integrate: every example in these docs is a curl call you can make from any language. On top of it there is a browser balance widget you can use today, and three beta add-ons you can ask us for.
Node.js / TypeScript client (beta)
A thin, dependency-free client over the same REST API, for server-side code only — an API key must never reach a browser. Writes that carry an Idempotency-Key — the client adds one to most writes, and asks you for one when you reserve points or issue a voucher — are retried on 429, 5xx and network failures with the same key, so a retry can never apply twice. A write without a key is never retried. events.send uses the event's id as its key, or — when you give none — the same key the API would derive: the type plus order_ref (plus refund_ref for a refund, action_ref for an action, the email for a customer event). So a resent event is always the same event, and batches from events.sendBatch are retried safely too.
Once you have the package, usage looks like this:
import { StickyTier, verifyWebhook } from "@stickytier/sdk";
const st = new StickyTier({ apiKey: process.env.STICKYTIER_KEY });
// Give each version of an order its own id: without one, the key is
// type + order_ref, and a changed order would collide with the first (409).
await st.events.send({ id: "ORD-5001:v2", type: "order.paid", order_ref: "ORD-5001",
customer_ref: "CUST-1001", gross_cents: 249900, aggregate_version: 2 });
const { data: member } = await st.members.get("ref:CUST-1001");
// Checkout: hold points while the customer pays
const hold = await st.checkout.reserve(
{ member_id: "ref:CUST-1001", amount_cents: 50000, eligible_total_cents: 249900 }, "cart-88431");
await st.checkout.confirm(hold.data.id, "ORD-5001"); // or st.checkout.release(hold.data.id)Errors are StickyTierError with status, code (the error slug) and requestId.
PHP client (beta)
The same idea for PHP 7.4+, with the same retry rules — sendEvent and sendEvents derive the same event keys as the Node.js client. Once you have it:
$st = new \StickyTier\Client(getenv('STICKYTIER_KEY'));
$st->sendEvent(['type' => 'order.refunded', 'order_ref' => 'ORD-5001',
'refund_ref' => 'RF-220', 'refunded_cents' => 249900]);
$voucher = $st->findRedemptionByCode('RWD-7K2M-Q9XA'); // what a till doesErrors are \StickyTier\ApiException with the HTTP status, the error slug and the request id.
Balance widget (browser)
Your server mints a member session token for the signed-in shopper — read-only, one member, 15 minutes — and renders it into the page for the widget. The token opens only GET /v1/public/member, the one endpoint that allows cross-origin calls; it returns points, tier and referral code, never contact details.
curl -X POST https://stickytier.com/v1/members/ref:CUST-1001/session-tokens \
-H "Authorization: Bearer $KEY"
# → 201 { "data": { "token": "smt_…", "expires_at": "…", "member_id": "M-…" } }<div data-stickytier-widget data-token="smt_…"
data-portal-url="https://rewards.yourstore.com" data-accent="#0f766e"></div>
<script src="https://stickytier.com/widget/stickytier-widget.js" async></script>data-portal-url (a "View rewards" link), data-accent (a CSS colour) and data-locale (number formatting) are optional. After you swap in a fresh token, call window.StickyTierWidget.refresh(). The Balance widget page has every attribute, the events it fires and how to handle expiry.
Building your own UI or a mobile app? Call the endpoint directly with Authorization: Bearer smt_…. For a “My rewards” button that opens your hosted rewards page signed in, use POST /v1/members/{id}/portal-links (scope act, live keys only, single use, 5 minutes). Opening the link shows one “Continue signing in” button; the sign-in happens when the shopper presses it, so link scanners and previews cannot use it up.
WooCommerce plugin (beta)
StickyTier for WooCommerce is a beta reference plugin, available on request like the clients above. It has not yet been certified on a live store, so try it on a staging site with a sticky_test_… key first. It turns orders, cancellations and refunds into events (queued through Action Scheduler, retried without ever double-crediting), sends WordPress user ids as external_ref, and shows the balance widget in My Account and via the [stickytier_balance] shortcode. It does not redeem points at checkout yet. Paste a key with the ingest and read scopes under WooCommerce → StickyTier in WordPress.
Not using the plugin? Everything it does is in Integrate a custom store.
Status
Live API, event-processing and webhook status: stickytier.com/status.