Skip to navigation

Partner tracking

Report the customers your partners bring you, and what they pay.
View as Markdown

Add one script to your site and report signups, sales and refunds. Each partner then gets credit, and commission, for the customers they bring you. Set it up in the five steps below, choosing your stack in step 2.

How it works

  1. A visitor arrives through a partner’s link: the short link /r/{key}, or your own page with ?via={key}.
  2. The script keeps that click in the pp_click cookie on your domain.
  3. The visitor signs up, and you report a lead with the click.
  4. The customer pays, and your server reports a sale.
  5. Each sale earns the partner a commission.

Only clicks and leads come from the browser. Sales and refunds come from your server, with your API key.

Before you begin

  • Tracking ID: in the PartnerPage dashboard, open Settings › Partner program › Partner links and click Copy next to the Tracking ID. It starts with pp_trk_. Replace pp_trk_… in every sample below with it. The Tracking ID is safe to put on your site.
  • API key: create one under Settings › Developers. The server samples read it from $PARTNERPAGE_API_KEY. A read-only key cannot report. See Authentication.
  • Amounts are in the currency’s minor unit: cents for USD, yen for JPY. For three-decimal currencies (KWD, BHD, …), send a multiple of 10.

Set it up

2

Add the script to every page

Choose your stack to see where the script goes.

<script async src="https://sdk.partnerpage.io/tracking.js"
data-tracking-id="pp_trk_…"></script>

Put this in the <head> of every page. It keeps a visitor’s partner click for the days set in A click counts for (90 by default) and passes it to Stripe Payment Links and Pricing Tables by itself.

Every hostname the script runs on must be in Allowed hostnames. Old data-directory tags keep working.

Check the install: open https://<your hostname>/?via=test and look for the pp_click cookie, or use Verify installation in Settings › Partner program › Partner links. For a hostname written as *.yoursite.com, check www.yoursite.com.

3

Track leads

After a customer signs up, report the lead. Read clickId from the pp_click cookie. Report the lead only when the cookie holds a click UUID. A customer with no cookie, or with a value that is not a UUID (such as the test that ?via=test writes), has no partner to credit, so skip the call. It would answer 400 on clickId.

To report it from the browser, call trackLead once the script has loaded. It sends nothing when there is no click. Read Security first.

PartnerPage.trackLead({ customerId: "user_123", email: "jane@example.com", name: "Jane Doe" });

If this can run before the script has loaded, such as inline on a page that has just opened, use the queue instead:

(window.PartnerPageLinks = window.PartnerPageLinks || []).push(['signup', { customerId: "user_123", email: "jane@example.com", name: "Jane Doe" }]);

Or from your server, with your API key. This is more reliable. See Track a lead.

curl -X POST https://v1.api.partnerpage.io/api/deal-registration/private/v1alpha/tracking/lead/ \
-H "Authorization: Token $PARTNERPAGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"trackingId": "pp_trk_…",
"clickId": "<pp_click cookie>",
"customerId": "user_123",
"email": "jane@example.com",
"name": "Jane Doe",
"eventName": "Sign up"
}'
FieldTypeRequiredDescription
trackingIdstringYesYour directory’s Tracking ID.
clickIdUUID or nullFor a new customerThe value of the pp_click cookie.
customerIdstring, up to 255 charactersYesYour own ID for the customer. One lead per customer per directory.
emailemail, up to 80 charactersYesThe customer’s email address.
namestring, up to 101 characters, or nullNoThe customer’s name.
companystring, up to 255 characters, or nullNoThe customer’s company.
eventNamestring, up to 255 charactersNoYour own name for the event, for example Sign up.

The answer is 201 for a new lead and 200 for a customer you already reported.

Response fieldTypeDescription
idUUIDThe lead.
customerIdstringYour ID for the customer.
statestringThe lead’s state: submitted, approved, declined, won or lost.
partnerEntryIdUUIDThe partner the lead is credited to.
4

Track sales

With Stripe connected in Settings › Partner program › Stripe, there is nothing to do. Otherwise report each paid invoice of a customer who has a lead from your server. One sale per invoiceId. See Track a sale.

curl -X POST https://v1.api.partnerpage.io/api/deal-registration/private/v1alpha/tracking/sale/ \
-H "Authorization: Token $PARTNERPAGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"trackingId": "pp_trk_…",
"clickId": "<pp_click cookie>",
"customerId": "user_123",
"amount": 5000,
"currency": "usd",
"invoiceId": "inv_123",
"eventName": "Invoice paid"
}'

Send the clickId from the pp_click cookie whenever you have it. A sale without it, for a customer only the script on your site reported, is held until you release its commission. Approving the lead does not release it.

If you send all your invoices anyway, those of customers with no lead answer 404 unknown_customer. That answer is expected and safe to ignore, and it writes nothing. Those calls still count against the rate limit.

Every call is real: there is no test mode, and a sale you report can earn a commission that gets paid out. Try these calls on a test directory first.

FieldTypeRequiredDescription
trackingIdstringYesYour directory’s Tracking ID.
customerIdstring, up to 255 charactersYesYour ID for the customer, as in the lead.
amountinteger, 1 to 10^15YesWhat the customer paid, tax included, in the currency’s minor unit.
currencystringYesThe three-letter ISO 4217 code, for example usd. Codes whose minor unit cannot be read safely are refused: ISK, UGX, UYI, CLF, UYW, withdrawn currencies, and metal, fund and test codes such as XAU and XXX.
invoiceIdstring, up to 255 charactersYesYour own ID for the invoice. One sale per invoice.
taxAmountinteger, 0 up to amountNoThe tax part of amount, in the same unit. Defaults to 0.
paidAtISO 8601 datetime or nullNoWhen the customer paid; defaults to now. Send it when you report late or import past invoices, and send past invoices oldest first: the first sale reported starts the earning window. An earlier sale sent later moves the start back to it while none of the lead’s commissions is paid or on a payout, and the result matches an oldest-first import: sales after the moved window earn nothing, and earlier sales it now covers earn. If that would cancel commissions later sales already earned, the call is refused with 409 paid_at_voids_earnings, so a mistyped date can’t cancel them; send voidLaterEarnings: true to move it anyway. After that it is recorded and earns nothing.
voidLaterEarningsbooleanNoSet true to let an earlier paidAt move the earning window back even though it cancels the commissions of later sales. Defaults to false.
eventNamestring, up to 255 charactersNoYour own name for the event, for example Invoice paid.
clickIdUUID or nullFor a new customer; send it whenever you have itThe value of the pp_click cookie. With a clickId, a sale for a customer you have not reported creates the lead, and the lead’s own click confirms a lead the browser reported.

The answer is 201 for a new sale and 200 for an invoice you already reported with the same values. A retry may leave out paidAt and eventName; if it sends either with another value than the first call, it answers 409 invoice_id_reused and changes nothing.

Response fieldTypeDescription
idUUIDThe sale.
leadIdUUIDThe customer’s lead.
invoiceIdstringYour ID for the invoice.
amountintegerThe amount, in the currency’s minor unit.
currencystringThe currency code, in capitals.
taxAmountintegerThe tax, in the same unit.
commissionobject or nullThe commission this sale earned: id, amount (a decimal string, such as "5.00"), currency and status. Null when the sale earned none.

To report a refund, send the invoiceId of the sale. See Refund a sale. Because amount is a total, you can safely send a call again: the same total adds nothing, and a higher total adds the difference.

curl -X POST https://v1.api.partnerpage.io/api/deal-registration/private/v1alpha/tracking/refund/ \
-H "Authorization: Token $PARTNERPAGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"trackingId":"pp_trk_…","invoiceId":"inv_123","amount":5000}'
FieldTypeRequiredDescription
trackingIdstringYesYour directory’s Tracking ID.
invoiceIdstring, up to 255 charactersYesThe invoice ID of the sale.
amountinteger, 1 to 10^15, or nullNoThe total refunded on this invoice so far, in the sale’s minor unit; omit for the whole sale.
taxAmountinteger, 0 to 10^15, or nullFor a partial refund of a sale with taxThe total tax refunded on this invoice so far, in the same unit; required for a partial refund of a sale with tax. It is kept for your records only: the commission goes down by the refunded share of the sale (amount over the sale’s amount), whatever the tax split.

The answer is 200.

Response fieldTypeDescription
saleIdUUIDThe sale.
invoiceIdstringYour ID for the invoice.
refundedAmountintegerThe total refunded on this invoice, in the currency’s minor unit.
refundIdUUID or nullThe new refund. Null when this total was already recorded.
5

Check your figures

Clicks, leads, conversions, revenue and commissions per partner appear in the dashboard under Partner program › Partners.

These settings are in Settings › Partner program › Partner links.

  • Partner links: on or off. Partners earn on the leads and sales their link brings. To switch them on, set the signup page and save.
  • Link format: the short link yourdirectory.com/r/{partner-key} (recommended), or the query parameter yoursite.com/signup?via={partner-key}. The query parameter needs your signup page’s site in Allowed hostnames, and the script verified there with Verify installation.
  • Sends visitors to: your signup page, the destination of every partner link. It must be a public https:// address.
  • Partner key: what {partner-key} is for each partner: the partner’s name (such as blue-fern) or the partner’s ID.
  • A click counts for: 1 to 365 days, 90 by default. A customer who signs up within this window is credited to the partner.
  • Tracking ID: needed by the script and by the API calls. Click Copy to copy it. Regenerate… makes a new one: your script and API calls stop working until you replace the old ID everywhere.

Allowed hostnames

The script tracks clicks only on these hostnames.

  • Add each one as example.com or *.example.com, up to 20 hostnames.
  • *.example.com covers the subdomains of example.com, not example.com itself.
  • A wildcard over a shared suffix, such as *.co.uk or *.webflow.io, is refused because it covers other companies’ sites. Use your own domain, like *.yoursite.webflow.io.
  • IP addresses and local names (localhost, .local, .internal) are refused.

Verify installation

Choose a saved hostname and click Verify installation. PartnerPage reads that hostname’s home page and looks for the script with your Tracking ID. A *.yoursite.com hostname is checked at www.yoursite.com. The check tracks nothing.

ResultMeaning
InstalledThe script with your Tracking ID is on the page.
Old Tracking IDThe page has a script with another Tracking ID. Copy the current one.
Old scriptThe page has the old script. Replace it with the tag in step 2.
Not foundNo script with your Tracking ID on the home page. A script added through Google Tag Manager can’t be seen by this check.
No answerThe site did not answer.

Security

When leads come from the browser (trackLead), send clickId on the customer’s sales, or report the lead from your server with its clickId: a lead the browser reported is confirmed by its own click, and a lead planted from the browser is moved to the real click. The move happens only while the lead still awaits your approval, has no sale reported from your server and has not started earning, and only while partner links are on; after that the lead stays with its partner, and a call naming another click changes nothing. Until a call from your server names the lead’s click, the commissions on its sales stay on hold, even after you approve the lead; you release them on the commission. Use a customerId nobody can guess (not an email or a sequential number).

Keep your API key on your server, never in browser code. See Authentication.

Rules

  • Every sale inside the earning window earns at the rate locked when the lead was enrolled, at its first sale, except:
    • a declined lead earns nothing;
    • a lead that already earned on its won deal value keeps that commission and its sales earn nothing more;
    • a partner whose listing was removed earns nothing on later sales;
    • a lead is enrolled only by a sale reported while the commission program and commissions are on; sales reported before that earn nothing.
  • An invoiceId is counted once.
  • A refund before payout lowers the commission, and after payout becomes a recovery.
  • A won deal without a sale is a conversion but not revenue.
  • Read-only keys cannot report. See Full-access and read-only keys.
  • There is no test mode. Every call is real, so try these calls on a test directory first.

Errors

Errors have three shapes, so check the status first:

  • A problem with a field answers with that field, as {"amount": ["…"]}. A body that is not a JSON object answers {"nonFieldErrors": ["…"]}.
  • An error with a code in the table answers {"code": "…", "message": "…"}.
  • A 401, a 403, a 429 and a body that cannot be parsed answer {"detail": "…"}.
StatusCallField or codeMeaning
400All{"<field>": ["…"]}A field is missing or malformed.
400All{"nonFieldErrors": ["Send a JSON object."]}The body is not a JSON object.
400All{"detail": "…"}The body cannot be parsed as JSON.
400LeadclickIdSend clickId for a customer PartnerPage does not know yet. A customer with no click is not reported.
400Lead, and sale for a new customerclickIdThe click is unknown, belongs to another directory, or is older than the directory’s click window.
400Lead, and sale for a new customerpartner_links_offPartner links are off.
400Sale, refundamount, taxAmountThis amount is too large.
400SaletaxAmounttaxAmount can’t be more than amount.
400Sale, refundamount, taxAmountFor this currency, send a multiple of 10.
400SalecurrencySend a supported three-letter ISO 4217 currency code.
400SalepaidAtpaidAt can’t be in the future or before the customer’s click and lead.
400RefundamountRefunds can’t add up to more than the sale.
400RefundtaxAmountSend taxAmount for a partial refund of a sale with tax.
400RefundtaxAmountThe refunded tax can’t be less than already refunded, more than the sale’s tax or more than the refunded amount.
401All{"detail": "…"}The API key is missing or invalid.
403All{"detail": "…"}The Tracking ID belongs to another organization, the key cannot report, or Partner leads are off for this directory.
404Allunknown_tracking_idNo directory has this tracking ID.
404Saleunknown_customerNo lead carries this customer ID. Send clickId to create it. Expected for a customer no partner brought; nothing is written.
404Refundunknown_invoiceNo sale carries this invoiceId.
409Saleinvoice_id_reusedThis invoiceId was reported with other values.
409Salelead_paid_through_stripeStripe reports this customer’s payments.
409Salesalesforce_push_runningThis Partner lead is being sent to Salesforce. Report the payment again in a minute.
409Refundrefund_below_recordedMore than this was already refunded on this invoice.
409Salepayout_sendingAn earlier sale would move the earning window while a payout to the partner is being sent. Report the payment again in a minute.
409Salepaid_at_voids_earningsThis paidAt would move the earning window back and cancel commissions later sales earned. Check the date, or send it again with voidLaterEarnings: true. Nothing was recorded.
409Refundpayout_sendingA payout to this partner is being sent. Report the refund again in a minute.
429All{"detail": "…"}Over the limit of 1,000 requests per hour per API key per endpoint. Wait and try again. See Rate limits.