TypeScript SDK and helpers
The openpromoter npm package is a typed client for the OpenPromoter REST API. It adds helpers for signups, Stripe Checkout and webhook checks.
npm install openpromoter
The SDK runs on Node 18 or newer (it uses fetch and node:crypto) and has no dependencies.
Client
import { OpenPromoter } from "openpromoter";
const op = new OpenPromoter({
apiKey: process.env.OPENPROMOTER_API_KEY!,
baseUrl: "https://app.openpromoter.com", // or your APP_URL when self-hosting
});
baseUrl defaults to Cloud. You can also pass your own fetch.
Resources
| Resource | Methods |
|---|---|
op.programs |
list(), stats(programId, { days }) |
op.events |
list({ after?, limit? }) |
op.partners |
list(programId, { status, limit, before }), create(programId, { email, name, website?, country? }), get(partnerId), update(partnerId, { status?, groupId? }), createLink(partnerId, { destination, subId?, key? }), embedToken(programId, { email, name }) |
op.referrals |
list(programId, { status, limit, before }) |
op.clicks |
create({ programId, code, url?, visitorId?, ip?, userAgent? }) |
op.leads |
create({ programId, email, name?, clickId?, visitorId?, partnerCode?, customerId? }) |
op.conversions |
create({ programId, paymentId, customerId, amount, currency, interval?, email?, clickId?, partnerCode?, promoCode?, paidAt? }) |
op.refunds |
create({ programId, paymentId, refundId, refundedTotal, chargedTotal }) |
op.commissions |
list(programId, { status, partnerId, limit, before }), create(programId, { partnerId, amount, description, idempotencyKey? }) |
op.payouts |
list(programId, { status, limit, before }), prepare(programId, { partnerIds? }) |
op.campaigns |
list(programId), create(programId, { prompt, launch? }) |
op.autopilot |
list(programId), apply(programId, actionId) |
op.ask(programId, question) answers a plain-language question from your data.
op.payouts.prepare() creates payouts that wait for a person to approve them in the dashboard. The SDK cannot send money.
Signups
trackSignup reads the op_ref cookie from the request and records a lead. Because it never throws, a tracking problem can never break your signup flow. Call it from your auth provider's signup hook (Better Auth, NextAuth, Clerk, Supabase):
import { trackSignup } from "openpromoter";
await trackSignup(op, {
programId: "prg_123",
email: user.email,
cookieHeader: req.headers.get("cookie"),
});
Stripe Checkout
import { readReferralCookie, stripeCheckoutParams } from "openpromoter";
const session = await stripe.checkout.sessions.create({
...yourOptions,
...stripeCheckoutParams(readReferralCookie(req.headers.get("cookie"))),
});
This sets client_reference_id, metadata.op_click and subscription_data.metadata when the visitor came from a partner. It sets nothing otherwise.
Embedded dashboard
const { token } = await op.partners.embedToken("prg_123", { email: user.email, name: user.name });
// render <openpromoter-dashboard program="acme" token={token}> in your app
See Embedded dashboard.
Other billing systems
await op.conversions.create({
programId: "prg_123",
paymentId: "inv_1",
customerId: "cus_1",
amount: 4900,
currency: "usd",
interval: "month",
});
Webhooks
import { verifyWebhook } from "openpromoter";
const event = verifyWebhook(rawBody, req.headers.get("openpromoter-signature"), process.env.OPENPROMOTER_WEBHOOK_SECRET!);
verifyWebhook(body, header, secret, toleranceSeconds = 300) throws an OpenPromoterError if the signature is missing, wrong or too old. Otherwise it returns the parsed event. See Webhooks.
Errors
Failed requests throw OpenPromoterError with status, code and message. Validation errors also carry issues.
import { OpenPromoterError } from "openpromoter";
try {
await op.partners.create("prg_123", { email: "maya@example.com", name: "Maya" });
} catch (err) {
if (err instanceof OpenPromoterError && err.status === 429) {
// back off and retry
}
}
License
The SDK is MIT licensed. You can use it in any codebase. The OpenPromoter app itself is AGPL-3.0.