> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.partnerpage.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.partnerpage.io/_mcp/server.

# Track a sale

POST https://v1.api.partnerpage.io/api/deal-registration/private/v1alpha/tracking/sale/
Content-Type: application/json

Called by your server for every payment of a customer you reported as a lead. `amount` and `taxAmount` are in the currency's minor unit (cents for USD, yen for JPY; a multiple of 10 for three-decimal currencies such as KWD). Each `invoiceId` is one sale: reporting it again with the same values changes nothing and answers 200; a `paidAt` or `eventName` the retry leaves out is not compared, and one it sends that differs is refused as other values (409 `invoice_id_reused`), so a corrected date is never silently dropped. The first sale enrolls the lead at your commission rate; each sale inside the earning window earns on the amount less tax, maturing your payout delay after `paidAt`. When you import past invoices, send them oldest first: an earlier sale sent later moves the window's start back to it only while none of the lead's commissions is paid or on a payout, and the result matches an oldest-first import, so sales that fall after the moved window earn nothing and earlier sales it now covers earn; once one is paid or on a payout, the earlier sale is recorded and earns nothing. Pass the `clickId` from the customer's `pp_click` cookie whenever you have it: a sale with a `clickId` for a customer we do not know yet creates the lead, and the lead's own click confirms a lead the pasted script reported. Anyone can report a lead from the browser, so while no call of your server has named its click, the earnings of a lead only the script reported stay on hold until you release them, even once you approve the lead. A customer whose payments Stripe reports is refused. Every call is real: try it on a test directory.

```bash
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"}'
```

Reference: https://docs.partnerpage.io/api-reference/partner-tracking/track-a-sale

## Authentication

- `Authorization` header (required) (prefixed with ` Token   `) — Your API key, sent as `Authorization: Token <your API key>`

## Request

### Body (application/json)

This endpoint expects a TrackingSaleRequest.

- `trackingId` (string, required) — The directory's Tracking ID, from Settings › Partner links (pp_trk_...).
- `customerId` (string, required) — Your own ID for the customer, as in the lead call.
- `amount` (long, required) — What the customer paid, tax included, in the currency's minor unit (cents for USD).
- `currency` (string, required) — The three-letter ISO 4217 code, for example USD.
- `invoiceId` (string, required) — Your own ID for the invoice; one sale per invoice ID.
- `taxAmount` (long, optional, default: 0) — The tax part of the amount, in the same unit.
- `paidAt` (datetime, optional, nullable) — When the customer paid; now when left out. 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. A move that would void commissions already earned is refused (409 paid_at_voids_earnings) unless voidLaterEarnings is true. After that it is recorded and earns nothing.
- `voidLaterEarnings` (boolean, optional, default: false) — Set true to let an earlier paidAt move the earning window back even though it voids the commissions of later sales now outside it. Without it such a sale is refused and nothing is recorded.
- `eventName` (string, optional, default: ) — Your own name for the event, for example Sign up.
- `clickId` (string, optional, nullable) — The click ID from the customer's pp_click cookie. Send it whenever you have it: a customer PartnerPage does not know yet needs it, and it confirms a lead the pasted script reported. Without it, what the sales of a lead only the script reported earn stays on hold until you release it.

## Response

### 200

This invoice was already reported with these values (a `paidAt` or `eventName` left out is not compared)

- `id` (string, required)
- `leadId` (string, required)
- `invoiceId` (string, required)
- `amount` (integer, required)
- `currency` (string, required)
- `taxAmount` (integer, required)
- `commission` (TrackingSaleCommission, required, nullable)

### 201

Sale recorded, with its commission when it earns one

- `id` (string, required)
- `leadId` (string, required)
- `invoiceId` (string, required)
- `amount` (integer, required)
- `currency` (string, required)
- `taxAmount` (integer, required)
- `commission` (TrackingSaleCommission, required, nullable)

## Errors

### 400 Bad Request Error

A field is missing or malformed (an unknown currency, an amount too large or not a multiple of 10 for a three-decimal currency, `paidAt` in the future or before the customer's click and lead), the body is not a JSON object, or, for a customer we do not know yet, the click is unknown or expired or partner links are off

- `any`

### 401 Unauthorized Error

The API key is missing or invalid; a dashboard login is refused

- `any`

### 403 Forbidden Error

The Tracking ID is another organisation's, the key lacks the decide permission on Partner leads, Partner leads are off, or payouts are on and the key may not confirm payouts (a sale earns)

- `any`

### 404 Not Found Error

No directory has this Tracking ID (`{"code": "unknown_tracking_id"}`), or no lead carries this customer ID and no `clickId` was sent (`{"code": "unknown_customer"}`)

- `any`

### 409 Conflict Error

The invoice ID was reported with other values, a `paidAt` or `eventName` the call sends included (`invoice_id_reused`), Stripe reports this customer's payments (`lead_paid_through_stripe`), the lead is being sent to Salesforce (`salesforce_push_running`), an earlier sale would move the window while a payout to the partner is being sent (`payout_sending`), or an earlier `paidAt` would move the window back and void commissions of later sales (`paid_at_voids_earnings`: check the date, or send `voidLaterEarnings: true`); nothing was recorded: retry `salesforce_push_running` and `payout_sending` shortly

- `any`

### 429 Too Many Requests Error

Rate limit exceeded: up to 1,000 requests an hour per API key to each of the lead, sale and refund calls

- `any`

## Types

### TrackingSaleCommission

- `id` (string, required)
- `amount` (string, required)
- `currency` (string, required)
- `status` (enum, required) — * `pending` - Awaiting approval * `eligible` - Eligible * `committed` - Paid * `declined` - Declined * `void` - No longer eligible
  - Allowed values: `pending`, `eligible`, `committed`, `declined`, `void`

## Examples

**Request**

```json
{
  "trackingId": "pp_trk_4fQ9xZ2mK7pL1sV8nB3cR6tY",
  "customerId": "user_123",
  "amount": 5000,
  "currency": "usd",
  "invoiceId": "inv_123",
  "eventName": "Invoice paid",
  "clickId": "4c1e0a4e-2f8c-4a1b-9d9e-3b2a1c0d8e7f"
}
```

**SDK Code**

```python Example request (application/json)
import requests

url = "https://v1.api.partnerpage.io/api/deal-registration/private/v1alpha/tracking/sale/"

payload = {
    "trackingId": "pp_trk_4fQ9xZ2mK7pL1sV8nB3cR6tY",
    "customerId": "user_123",
    "amount": 5000,
    "currency": "usd",
    "invoiceId": "inv_123",
    "eventName": "Invoice paid",
    "clickId": "4c1e0a4e-2f8c-4a1b-9d9e-3b2a1c0d8e7f"
}
headers = {
    "Authorization": "Token  <apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Example request (application/json)
const url = 'https://v1.api.partnerpage.io/api/deal-registration/private/v1alpha/tracking/sale/';
const options = {
  method: 'POST',
  headers: {Authorization: 'Token  <apiKey>', 'Content-Type': 'application/json'},
  body: '{"trackingId":"pp_trk_4fQ9xZ2mK7pL1sV8nB3cR6tY","customerId":"user_123","amount":5000,"currency":"usd","invoiceId":"inv_123","eventName":"Invoice paid","clickId":"4c1e0a4e-2f8c-4a1b-9d9e-3b2a1c0d8e7f"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```