@repitoo/sdk
v0.1.2
Published
Repitoo inside your system: turn loyalty on per business, add stamps from the till (with an offline queue), verify webhooks and open <repitoo-modal>.
Maintainers
Readme
@repitoo/sdk
Repitoo inside your system: turn loyalty on per business, add stamps from the till (even offline), verify webhooks and open the Repitoo window in your POS.
npm install @repitoo/sdkWorks in Node 20+ and in the browser (only @repitoo/sdk/modal), with no AWS or framework
dependency.
Your system isn't JavaScript? You don't need the SDK: Repitoo is an HTTP API with JSON and
works from any language (PHP, Python, Java, C#, Go, Ruby, Delphi…) and any till, web or desktop.
AGENTS.md describes every call, the webhook signature and how to open the window; the full
reference is https://api.repitoo.com/openapi.json. See the examples at the end.
The two keys
| Key | Who uses it | For |
| ------------------------------ | ------------------------------- | ------------------------------------------------------------------------------- |
| rpt_partner_… / rpt_test_… | Your server | Turning businesses on and off, getting new keys and opening the Repitoo window. |
| rpt_live_… | The server of a business's till | Adding stamps, finding cards and handing out rewards in that business. |
No key ever goes to the browser. Where you store them is up to you (environment variables,
Secrets Manager, Parameter Store): Repitoo never touches your AWS. keyFromEnv,
secretsManagerKeys and parameterStoreKeys read them using your own client.
With an rpt_test_ key everything works the same, but the businesses are test ones: they don't
count toward your invoice and don't notify anyone.
1. Turn loyalty on for a business
Only when the business signs up for it, never when the POS is installed.
import { RepitooPartner } from "@repitoo/sdk";
const partner = new RepitooPartner(process.env.REPITOO_PARTNER_KEY!);
const { business, apiKey, created } = await partner.activateBusiness({
externalId: "store-123", // your id: repeating it returns the same business
name: "Corner Coffee",
ownerEmail: "[email protected]",
program: { rewardDescription: "A free coffee", stampsRequired: 8 },
});
// apiKey (rpt_live_…) only comes back when created is true: store it right away.deactivateBusiness(business.id) when it stops paying for it (its customers keep their stamps
and it no longer counts as an active location) and reactivateBusiness(business.id) to bring it
back.
2. Add stamps from the till
import { RepitooTill } from "@repitoo/sdk";
const till = new RepitooTill(businessKey);
const { card, stampsAdded, rewardEarned } = await till.recordSale(
{ customer: { phone: "+584141234567" }, amount: 25.5, reference: "F-1001" },
"F-1001", // the idempotency key: the invoice number works
);- Send phone numbers with their country code (
+58…): an 8-digit number without one is read as Costa Rican. amountfor by-amount programs;stampsfor by-visit programs (1 by default).- A customer who doesn't exist yet is enrolled with that sale.
findCard,redeemRewardandreverseStamps(a voided sale) round out the till.
Offline
import { OfflineQueue } from "@repitoo/sdk";
const queue = new OfflineQueue(till, yourStore); // in memory if you don't pass one
await queue.record(sale, invoice); // answers now, or { queued: true }
await queue.flush(); // when the connection is backEach sale keeps the time it happened (occurredAt), so the stamps count on that day, and it is
always resent with the same key: it never adds stamps twice. Repitoo accepts sales up to 7 days
old. What Repitoo refuses for good (a reused invoice, a customer with no details) ends up in
rejected for a person to review.
3. The Repitoo window in your POS
Your server asks for the address (valid for 30 minutes) and the page opens it:
const { url } = await partner.openWindow(business.id); // on your server<repitoo-modal label="Stamps"></repitoo-modal>
<script type="module">
import "@repitoo/sdk/modal";
const modal = document.querySelector("repitoo-modal");
modal.addEventListener("repitoo-stamped", (event) => console.log(event.detail.card));
modal.open(urlFromYourServer);
</script>There the cashier finds the customer, gives them their card, adds stamps and hands out the
reward. Events: repitoo-stamped, repitoo-redeemed and repitoo-close. Your domain must be
among your partner account's allowed origins (Repitoo sets them up for you); otherwise the window
won't show.
<repitoo-modal> is a standard HTML element: it works the same with or without a framework (in
Angular, with CUSTOM_ELEMENTS_SCHEMA; in React, with a ref). Without the SDK, an <iframe>
and a postMessage listener are enough (examples/html/till.html). In a desktop till or an app
(WPF, Electron, Android, iOS), open the address in a WebView: it works the same, and webhooks
tell you what changed.
4. Webhooks
Create them in your partner portal (Keys → Notifications to your system). Each delivery carries
Repitoo-Signature: t=…,v1=… (HMAC-SHA256 of the body with your whsec_…):
import { verifyWebhook } from "@repitoo/sdk";
const event = verifyWebhook(secret, request.headers.get("repitoo-signature"), rawBody);In another language: v1 is the hex HMAC-SHA256 of "<t>.<body>" with your whsec_… as the
key; compare it in constant time and reject a t more than 5 minutes old.
Answer 2xx within 10 s. Otherwise Repitoo retries after 1 min, 5 min, 30 min, 2 h, 12 h and
24 h. A retry carries the same event.id: ignore what you already processed.
Errors
Every non-2xx response is a RepitooError with status, error (the code) and retryable
(no connection, 408, 429 or 5xx: worth retrying). 402 payment_required: your partner account or
the business is paused.
Examples
| Language or stack | Example |
| -------------------- | --------------------------------------------------------------------------------- |
| Any (HTTP) | examples/http/curl.sh: activate, sell and open the window |
| Node, no framework | examples/node/server.mjs (with the SDK) and examples/node/fetch.mjs (without) |
| Node with the queue | examples/offline-till.ts: offline sales |
| React | examples/react/LoyaltyButton.tsx: the till's button |
| React Router / Remix | examples/react-router/loyalty.tsx: server action and useFetcher |
| Next.js | examples/nextjs: server and webhook |
| Angular | examples/angular: the till's button |
| Plain HTML | examples/html/till.html: the window in an <iframe> |
| Python | examples/python/webhook.py (Flask) |
| PHP | examples/php/webhook.php |
| C# / .NET | examples/csharp/RepitooWebhook.cs: webhook and sale |
| Java | examples/java/RepitooTill.java: sale with HttpClient |
| Go | examples/go/webhook.go |
For your coding assistant: AGENTS.md, llms.txt and the skills/repitoo-partner skill.
