Guides
Express.js Integration
This guide shows how to integrate RecurCite with an Express.js application. You'll add evidence tracking to your authentication, billing, and feature usage routes.
Prerequisites
- Express.js 4.x or 5.x
- Node.js 18+
@recurcite/sdkinstalled- A RecurCite API key
1. Initialize the SDK
lib/recurcite.ts
import { init } from "@recurcite/sdk";
export const recurcite = init({
apiKey: process.env.RECURCITE_API_KEY!,
signingSecret: process.env.RECURCITE_SIGNING_SECRET,
});2. Track logins
Add login tracking to your authentication route. Express gives you direct access to the request IP and user agent:
routes/auth.ts
import { Router } from "express";
import { recurcite } from "../lib/recurcite";
const router = Router();
router.post("/login", async (req, res) => {
const user = await authenticate(req.body.email, req.body.password);
if (!user) {
return res.status(401).json({ error: "Invalid credentials" });
}
// Track login for RecurCite evidence
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,
},
});
const token = generateToken(user);
res.json({ token });
});
export default router;3. Track terms acceptance
routes/terms.ts
router.post("/accept-terms", requireAuth, async (req, res) => {
const { termsVersion } = req.body;
await recurcite.track({
type: "terms.accepted",
payload: {
version: termsVersion,
accepted_at: new Date().toISOString(),
},
stripe_refs: {
stripe_customer_id: req.user.stripeCustomerId,
},
});
res.json({ success: true });
});4. Usage tracking middleware
For automatic feature usage tracking, create an Express middleware that fires on specific routes:
middleware/track-usage.ts
import { recurcite } from "../lib/recurcite";
/**
* Express middleware to track feature usage.
* Use on routes that represent billable or trackable actions.
*/
export function trackUsage(featureKey: string) {
return async (req: any, _res: any, next: any) => {
if (req.user?.stripeCustomerId) {
// Fire and forget — don't block the request
recurcite.track({
type: "product.used",
payload: {
feature_key: featureKey,
count: 1,
occurred_at: new Date().toISOString(),
},
stripe_refs: {
stripe_customer_id: req.user.stripeCustomerId,
},
}).catch(() => {}); // Non-blocking
}
next();
};
}
// Usage:
// app.post("/api/exports", trackUsage("exports"), exportHandler);
// app.get("/api/reports", trackUsage("reports"), reportHandler);Tip
The fire-and-forget pattern (.catch(() => {})) ensures evidence tracking never blocks or breaks your request flow. The SDK handles retries internally.
5. Track cancellations
routes/billing.ts
router.post("/cancel", requireAuth, async (req, res) => {
const { subscriptionId } = req.body;
// 1. Track cancellation request
await recurcite.track({
type: "cancellation.requested",
payload: { occurred_at: new Date().toISOString() },
stripe_refs: {
stripe_customer_id: req.user.stripeCustomerId,
stripe_subscription_id: subscriptionId,
},
});
// 2. Cancel in Stripe
const sub = await stripe.subscriptions.cancel(subscriptionId);
// 3. Track confirmed
await recurcite.track({
type: "cancellation.confirmed",
payload: {
occurred_at: new Date().toISOString(),
receipt_id: sub.id,
},
stripe_refs: {
stripe_customer_id: req.user.stripeCustomerId,
stripe_subscription_id: subscriptionId,
},
});
res.json({ success: true, canceledAt: sub.canceled_at });
});6. Stripe webhook for CE3
Track transaction.completed events in your Stripe webhook to enable CE3:
routes/stripe-webhook.ts
import { Router } from "express";
import Stripe from "stripe";
import { recurcite } from "../lib/recurcite";
const router = Router();
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
router.post(
"/webhook",
express.raw({ type: "application/json" }),
async (req, res) => {
const event = stripe.webhooks.constructEvent(
req.body,
req.headers["stripe-signature"]!,
process.env.STRIPE_BILLING_WEBHOOK_SECRET!
);
if (event.type === "invoice.paid" || event.type === "invoice.payment_succeeded") {
const invoice = event.data.object as Stripe.Invoice;
await recurcite.track({
type: "transaction.completed",
payload: {
charge_id: invoice.charge as string,
amount: invoice.amount_paid,
currency: invoice.currency,
occurred_at: new Date(invoice.created * 1000).toISOString(),
},
stripe_refs: {
stripe_customer_id: invoice.customer as string,
stripe_subscription_id: invoice.subscription as string,
stripe_invoice_id: invoice.id,
},
});
}
res.json({ received: true });
}
);
export default router;Environment variables
.env
RECURCITE_API_KEY=rc_live_your_api_key_here
RECURCITE_SIGNING_SECRET=your_signing_secret_hereNext steps
- API Reference — raw HTTP endpoints if you prefer cURL
- Stripe Billing Setup — decision map for where to add each event
- Test Mode — verify your integration