Skip to main content

SDK & API

SDK Installation

The @recurcite/sdk is a server-first Node.js package for sending evidence events to RecurCite. It handles authentication, payload validation, HMAC signing, and batch operations.

Installation

bash
npm install @recurcite/sdk

Or with your preferred package manager:

bash
yarn add @recurcite/sdk
# or
pnpm add @recurcite/sdk

Initialization

Import and initialize the SDK with your API key. The client is stateless — you can create it once and reuse across your application.

lib/recurcite.ts
import { init } from "@recurcite/sdk";

export const recurcite = init({
  apiKey: process.env.RECURCITE_API_KEY!,
  // Optional: HMAC signing for tamper-proof events
  signingSecret: process.env.RECURCITE_SIGNING_SECRET,
});

Server-side only

Never expose your API key or signing secret in client-side code. The SDK is designed for Node.js server environments only.

Sending events

track()

Send a single evidence event. Every event must include a type, payload, and ideally stripe_refs to link it to a Stripe customer.

typescript
await recurcite.track({
  type: "terms.accepted",
  payload: {
    version: "2.0",
    accepted_at: new Date().toISOString(),
  },
  stripe_refs: {
    stripe_customer_id: "cus_abc123",
  },
});

batchTrack()

Send multiple events in a single request. Useful for bulk imports or high-throughput scenarios.

typescript
await recurcite.batchTrack([
  {
    type: "product.used",
    payload: {
      feature_key: "api_calls",
      count: 150,
      occurred_at: new Date().toISOString(),
    },
    stripe_refs: { stripe_customer_id: "cus_abc123" },
  },
  {
    type: "user.login",
    payload: {
      occurred_at: new Date().toISOString(),
      ip: "203.0.113.42",
    },
    stripe_refs: { stripe_customer_id: "cus_abc123" },
  },
]);

Idempotency

By default, the SDK generates a unique event_id for each event. To ensure retries don't create duplicates, provide your own deterministic ID:

typescript
import { generateEventId } from "@recurcite/sdk";

await recurcite.track({
  event_id: generateEventId("terms.accepted", userId),
  type: "terms.accepted",
  payload: {
    version: "2.0",
    accepted_at: new Date().toISOString(),
  },
});

Tip

Use generateEventId(type, uniqueKey) to create deterministic IDs based on the event type and a unique identifier like the user ID or transaction ID.

HMAC signing

When you provide a signingSecret during initialization, the SDK automatically signs every request with an X-Recurcite-Signature header. The server verifies the signature and stores the verification status for audit and export flows.

typescript
const recurcite = init({
  apiKey: process.env.RECURCITE_API_KEY!,
  signingSecret: process.env.RECURCITE_SIGNING_SECRET,
});

// All subsequent track() calls are automatically signed

Error handling

The SDK never throws on network or server errors — it returns a structured result object. This ensures evidence tracking never breaks your application flow:

typescript
const result = await recurcite.track({
  type: "terms.accepted",
  payload: { version: "2.0", accepted_at: new Date().toISOString() },
  stripe_refs: { stripe_customer_id: "cus_abc123" },
});

if (result.status === "error") {
  console.error("RecurCite error:", result.error);
}

// result.status: "accepted" | "deduplicated" | "error"
// result.event_id: the generated or provided event ID
// result.error?: human-readable error message (only on error)
StatusMeaningAction
400Invalid payload — missing required fields or unknown event typeFix the payload and retry
401Invalid or missing API keyCheck your RECURCITE_API_KEY
409Duplicate event_id (idempotency conflict)Event already recorded — safe to ignore
429Rate limitedBack off and retry with exponential delay
500Server errorRetry with backoff; contact support if persistent

TypeScript types

The SDK is fully typed. Key types you'll work with:

typescript
import type {
  TrackEvent,
  StripeRefs,
  EventType,
} from "@recurcite/sdk";

// EventType: "terms.accepted" | "user.login" | "product.used" | "transaction.completed" | ...
// StripeRefs: { stripe_customer_id?, stripe_subscription_id?, ... }
// TrackEvent: { type, payload, stripe_refs?, event_id? }

CE3 integration (Visa Compelling Evidence 3.0)

To enable CE3 eligibility, send transaction.completed events with session signals (IP address, email, device fingerprint) after each successful charge. Use email_sha256 for email matching. Raw emails are rejected at ingestion.

lib/recurcite-ce3.ts
await recurcite.track({
  type: "transaction.completed",
  payload: {
    charge_id: "ch_abc123",
    amount: 4999,
    currency: "usd",
    ip: req.ip,
    email_sha256: createHash("sha256").update(user.email.toLowerCase().trim()).digest("hex"),
    device_fingerprint: req.headers["x-device-id"],
    product_description: "Pro Plan — Monthly",
    merchandise_or_services: "services",
    occurred_at: new Date().toISOString(),
  },
  stripe_refs: {
    stripe_customer_id: user.stripeCustomerId,
  },
});

Tip

Send transaction.completed on every successful charge. CE3 requires at least 2 prior undisputed transactions within 120–365 days with matching IP or email signals.

Integration examples

Express middleware

middleware/recurcite.ts
import { recurcite } from "../lib/recurcite";

// Track logins on authentication
app.post("/auth/login", async (req, res) => {
  const user = await authenticate(req.body);

  await recurcite.track({
    type: "user.login",
    payload: {
      occurred_at: new Date().toISOString(),
      ip: req.ip,
      user_agent: req.headers["user-agent"],
    },
    stripe_refs: {
      stripe_customer_id: user.stripeCustomerId,
    },
  });

  res.json({ token: generateToken(user) });
});

Next.js API route

app/api/accept-terms/route.ts
import { recurcite } from "@/lib/recurcite";

export async function POST(req: Request) {
  const { userId, stripeCustomerId, termsVersion } = await req.json();

  await recurcite.track({
    type: "terms.accepted",
    payload: {
      version: termsVersion,
      accepted_at: new Date().toISOString(),
    },
    stripe_refs: {
      stripe_customer_id: stripeCustomerId,
    },
  });

  return Response.json({ success: true });
}

Get your API key

  1. Go to Dashboard → Developers
  2. Click Create key
  3. Copy the key (shown only once)
  4. Add to your environment: RECURCITE_API_KEY=rc_live_…

Next steps