Sale tracking
Record successful purchases — the event that creates a commission.
Sale tracking is what creates a commission. There are three ways to get a sale event into Partli, easiest first:
- Connect your Stripe account via OAuth — no code, we listen for paid checkouts and invoices automatically.
- Use the JS snippet + your own Stripe webhook (forward sales to our REST API).
- POST to the REST API directly from any backend.
This page documents option 3 — the underlying API call. Options 1 and 2 also end up calling this same endpoint internally.
POST /api/track/sale
await fetch("https://partli.app/api/track/sale", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PARTLI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
externalId: "inv_abc123", // your invoice id (used for dedupe)
customerExternalId: "cus_xyz", // your customer id
amountCents: 4900, // sale amount in cents ($49.00)
currency: "USD", // optional, defaults to USD
clickId: cookies.get("pref_cid"), // attribution cookie
eventName: "subscription_paid", // optional, for your reporting
metadata: { plan: "starter" }, // optional, free-form jsonb
}),
});What happens after the call
- We resolve
clickIdto a partner. No attribution → no-op. - We upsert a customer record keyed on
customerExternalId. - We create the sale event row.
- We resolve the right reward (group-level → program-level fallback).
- For one-time interval: skip if a prior commission exists for this customer.
- For recurring: skip if past the duration cap.
- Calculate
amountCentsand create apendingcommission withpayable_at = now + holdingPeriodDays. - Fire
sale.created+commission.createdwebhooks.
Idempotent by externalId
Re-posting the same
externalId is a no-op. Safe to call from a Stripe webhook handler that may retry on transient failures.Stripe webhook → track/sale
The simplest integration: in your Stripe webhook handler, listen forinvoice.paid (or checkout.session.completed) and forward to /api/track/sale:
// Inside your Stripe webhook handler
if (event.type === "invoice.paid") {
const invoice = event.data.object;
// Pull the click ID from invoice metadata, customer metadata, or
// your own DB lookup (you stored it on signup)
const clickId = invoice.metadata.partli_click_id;
await fetch("https://partli.app/api/track/sale", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PARTLI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
externalId: invoice.id,
customerExternalId: invoice.customer,
amountCents: invoice.amount_paid,
currency: invoice.currency.toUpperCase(),
clickId,
}),
});
}Refund handling
Automated clawback via webhook isn't live yet. If a sale is refunded, find the related commission in your dashboard and mark it as clawed back manually. As long as the commission is still in pending or approved (not yet paid out), no money has moved.