npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@raba7ni/raba7ni

v1.0.8

Published

Official SDK for the Raba7ni Platform Developer API

Downloads

927

Readme

@raba7ni/raba7ni

Official TypeScript SDK for the Raba7ni Loyalty Platform Developer API.

npm version TypeScript Node.js

Installation

npm install @raba7ni/raba7ni

Quick Start

import { Raba7niSDK } from "@raba7ni/raba7ni";

const sdk = new Raba7niSDK({
    appId: "app_your_app_id",
    apiKey: "dev_your_api_key",
});

// Test connection
const scopes = await sdk.testConnection();
console.log("Connected! Scopes:", scopes.scopes);

// Validate a member
const result = await sdk.validateMember(5, "+1234567890");
console.log("Is member:", result.is_member);

Configuration

| Option | Type | Default | Description | | ------------ | -------- | ------------------------- | ----------------------------------- | | appId | string | required | Application ID (starts with app_) | | apiKey | string | required | API Key (starts with dev_) | | baseUrl | string | https://app.raba7ni.com | API base URL | | locale | string | en-us | Locale for responses | | timeout | number | 30000 | Request timeout (ms) | | maxRetries | number | 3 | Max retry attempts | | retryDelay | number | 1000 | Initial retry delay (ms) |

const sdk = new Raba7niSDK({
    appId: "app_...",
    apiKey: "dev_...",
    timeout: 60000, // 60 seconds
    maxRetries: 5,
});

API Reference

Connection & Scopes

testConnection()

Test API connection and get available scopes.

const result = await sdk.testConnection();
// { scopes: ['members:read', 'members:write', ...], application: { id, name } }

getWebhookEvents()

Get list of available webhook events.

const events = await sdk.getWebhookEvents();

Member APIs

validateMember(cardId, phoneNumber, options?)

Check if a phone number is a registered member.

const result = await sdk.validateMember(5, "+1234567890", {
    include_member_data: true,
});

if (result.is_member) {
    console.log("Member:", result.member);
}

getMemberDetails(cardId, phoneNumber)

Get detailed member information.

[!CAUTION] > Backend/Staff Use Only - This endpoint is designed for store owner/staff backend systems (POS, admin dashboards). It should NOT be exposed to customer-facing applications as a "login" method. Anyone knowing a phone number could access member data without verification.

For customer-facing apps, use requestMemberOTP() + OTP verification before showing member data. See Member Authentication below.

// Staff/Admin lookup (backend only)
const details = await sdk.getMemberDetails(5, "+1234567890");
console.log("Points:", details.card_relationship.points);

requestMemberOTP(email, cardId)

Request OTP for new member registration.

const otp = await sdk.requestMemberOTP("[email protected]", 5);

if (otp.member_exists) {
    console.log("Member already registered");
} else {
    console.log("OTP sent, expires at:", otp.expires_at);
}

findOrCreateMember(request)

Find existing member or create new one. Optionally create order and award points.

[!NOTE] Phone behavior:

  • New members: phone_number is required along with verification_code
  • Existing members: phone_number is optional - if provided and member has no phone, it will be updated
  • Response includes phone_updated: true when an existing member's phone was added
// Basic usage - find or create member
const result = await sdk.findOrCreateMember({
    card_id: 5,
    name: "John Doe",
    email: "[email protected]",
    phone_number: "+1234567890", // Required for NEW members only
    verification_code: "123456", // Required for NEW members only
});

// With order and points
const result = await sdk.findOrCreateMember({
    card_id: 5,
    name: "John Doe",
    email: "[email protected]",
    phone_number: "+1234567890",
    create_order: true,
    order: {
        award_points: true,
        total_amount: 50.0,
        items: [
            { name: "Coffee", amount: 15.0 },
            { name: "Sandwich", amount: 35.0 },
        ],
    },
});

console.log("Is new member:", result.is_new);
console.log("Phone updated:", result.phone_updated); // true if missing phone was added
console.log("Points awarded:", result.transaction?.points);

Member Authentication for Customer Apps

When building customer-facing apps (e-commerce, mobile apps, customer portals), never use getMemberDetails() as a login method. Instead, implement proper OTP-based authentication:

// Step 1: Check if member exists and send OTP
async function initiateLogin(email: string, cardId: number) {
    const otp = await sdk.requestMemberOTP(email, cardId);

    if (otp.member_exists) {
        // OTP sent to existing member's email
        return { needsOTP: true, email };
    } else {
        // New member - OTP sent for registration
        return { needsOTP: true, isNew: true, email };
    }
}

// Step 2: Verify OTP and get/create member
async function verifyLoginOTP(
    email: string,
    otp: string,
    cardId: number,
    memberData?: { name: string; phone: string }
) {
    // For existing members: findOrCreateMember finds by email
    // For new members: provide name + phone for registration
    const result = await sdk.findOrCreateMember({
        card_id: cardId,
        email,
        verification_code: otp,
        name: memberData?.name, // Required for new members
        phone_number: memberData?.phone, // Required for new, optional for existing
    });

    // If member existed without phone and we provided one
    if (result.phone_updated) {
        console.log("Member phone was updated");
    }

    // Now safe to show member data - they verified ownership
    return result.member;
}

[!IMPORTANT] The verification_code parameter ensures the person requesting data actually owns the email address. Without this, anyone could query member data just by knowing a phone number.


Card APIs

listCards()

Get all loyalty cards accessible by your API key.

const { cards, total } = await sdk.listCards();
cards.forEach((card) => console.log(card.name));

getCardInfo(cardId)

Get details about a specific card.

const card = await sdk.getCardInfo(5);
console.log("Points per currency:", card.points_per_currency);

listCardRewards(cardId)

Get available rewards for a card.

const { rewards } = await sdk.listCardRewards(5);
rewards.forEach((reward) => {
    console.log(`${reward.name}: ${reward.points} points`);
});

Claim Request APIs

listClaimRequests(cardId, status?)

Get claim requests for a card.

// All claims
const { claim_requests } = await sdk.listClaimRequests(5);

// Filtered by status
const pending = await sdk.listClaimRequests(5, "pending");

requestClaimOTP(memberEmail, cardId, rewardId?)

Request OTP for claiming a reward.

const otp = await sdk.requestClaimOTP("[email protected]", 5, 10);
console.log("Claim OTP expires:", otp.expires_at);

createClaimRequest(request)

Create a new reward claim request.

const { claim_request } = await sdk.createClaimRequest({
    member_email: "[email protected]",
    card_id: 5,
    reward_id: 10,
    verification_code: "123456",
    member_note: "Please deliver to office",
});

console.log("Claim ID:", claim_request.id);
console.log("Status:", claim_request.status); // 'pending'

getClaimRequest(requestId)

Get claim request details.

const { claim_request } = await sdk.getClaimRequest(123);

approveClaimRequest(requestId, staffNote?)

Approve a pending claim request.

const { claim_request } = await sdk.approveClaimRequest(123, "Approved!");

rejectClaimRequest(requestId, staffNote?)

Reject a pending claim request.

const { claim_request } = await sdk.rejectClaimRequest(123, "Out of stock");

Referral APIs

getReferralStats(cardId)

Get referral statistics for a card.

const stats = await sdk.getReferralStats(5);
console.log("Total referrals:", stats.total_referrals);
console.log("Successful:", stats.successful_referrals);

listReferrers(cardId)

Get top referrers for a card.

const { referrers } = await sdk.listReferrers(5);
referrers.forEach((r) => {
    console.log(`${r.member_name}: ${r.referral_count} referrals`);
});

listReferrerReferrals(cardId, memberId)

Get all referrals made by a specific member.

const { referrals } = await sdk.listReferrerReferrals(5, 100);

validateReferralCode(referralCode, cardId)

Check if a referral code is valid.

const result = await sdk.validateReferralCode("REF123ABC", 5);
if (result.is_valid) {
    console.log("Referred by:", result.referrer?.name);
}

applyReferral(referralCode, cardId, refereeEmail)

Apply a referral code to a new member.

const { referral } = await sdk.applyReferral(
    "REF123ABC",
    5,
    "[email protected]"
);

Webhook Handling

Verifying Webhooks

import { WebhookHandler } from "@raba7ni/raba7ni";

const handler = new WebhookHandler("your_webhook_secret");

// Express example
app.post("/webhooks", (req, res) => {
    const signature = req.headers["x-webhook-signature"];
    const payload = JSON.stringify(req.body);

    handler.handleWebhook(payload, signature, {
        onMemberJoined: async (data) => {
            console.log("New member:", data.member.name);
        },
        onMemberLeft: async (data) => {
            console.log("Member left:", data.member.name);
        },
        onClaimRequestCreated: async (data) => {
            console.log("New claim:", data.claim_request.id);
        },
        onClaimRequestProcessed: async (data) => {
            console.log("Claim processed:", data.status);
        },
        onUnknownEvent: async (event, data) => {
            console.log("Unknown event:", event);
        },
    });

    res.status(200).send("OK");
});

Manual Verification

import { verifyWebhookSignature, parseWebhookPayload } from "@raba7ni/raba7ni";

const isValid = verifyWebhookSignature(payload, signature, secret);
if (isValid) {
    const webhook = parseWebhookPayload(payload);
    console.log("Event:", webhook.event);
}

Error Handling

import {
    Raba7niError,
    AuthenticationError,
    RateLimitError,
    ValidationError,
} from "@raba7ni/raba7ni";

try {
    await sdk.validateMember(5, "+123");
} catch (error) {
    if (error instanceof AuthenticationError) {
        console.error("Invalid credentials");
    } else if (error instanceof RateLimitError) {
        console.error("Rate limited, retry after:", error.retryAfter);
    } else if (error instanceof ValidationError) {
        console.error("Validation errors:", error.validationErrors);
    } else if (error instanceof Raba7niError) {
        console.error("API error:", error.message, error.code);
    }
}

Rate Limiting

The SDK tracks rate limits automatically:

const info = sdk.getRateLimitInfo();
console.log("Hourly remaining:", info.hourlyRemaining);
console.log("Daily remaining:", info.dailyRemaining);

Utilities

import {
    normalizePhoneNumber,
    validateEmail,
    validateCardId,
} from "@raba7ni/raba7ni";

// Normalize phone numbers
normalizePhoneNumber("+1 (555) 123-4567"); // '+15551234567'

// Validate email format
validateEmail("[email protected]"); // true

// Validate card ID
validateCardId("5"); // 5 (number)

TypeScript Types

All types are exported for full TypeScript support:

import type {
    Member,
    Card,
    Reward,
    ClaimRequest,
    Order,
    Transaction,
    WebhookPayload,
    ClaimRequestCreatedWebhookData,
    ClaimRequestProcessedWebhookData,
} from "@raba7ni/raba7ni";

Support

Requirements

  • Node.js 18+
  • TypeScript 5.0+ (for TypeScript users)

License

MIT © Raba7ni