---
id: "api-reference/track-click"
type: "doc"
url: "https://affitor.com/docs/api-reference/track-click"
kind: "reference"
updated: "2026-10-18"
---
# Track Click

> Record an affiliate click event when a visitor lands via a referral link

`POST /api/v1/track/click` records an affiliate click event from the Affitor tracker script. It resolves the referral link from the `?aff=` parameter (or its alias `?via=`; `aff` wins when both are present), creates or reuses the customer record, and returns the `click_id` that downstream lead and sale calls will use for attribution.

This endpoint is called automatically by `affitor-tracker.js`. You only need to call it directly if you are building a custom tracker integration.

---

## Auth

No API key is required. This endpoint is public — the referral link embedded in `affiliate_url` is the only credential needed.

---

## Request

`POST https://api.affitor.com/api/v1/track/click`

<ParamList>
  <ParamField name="affiliate_url" type="string" required>
    Full URL including the `?aff=` or `?via=` query parameter, e.g. `https://yoursite.com?aff=PARTNER123`
  </ParamField>
  <ParamField name="page_url" type="string">
    Canonical page URL where the click occurred.
  </ParamField>
  <ParamField name="page_title" type="string">
    `document.title` at time of click.
  </ParamField>
  <ParamField name="referrer_url" type="string">
    `document.referrer` — where the visitor came from.
  </ParamField>
  <ParamField name="session_id" type="string">
    Browser session identifier.
  </ParamField>
  <ParamField name="user_agent" type="string">
    User agent string; falls back to the HTTP `User-Agent` header.
  </ParamField>
  <ParamField name="screen_resolution" type="string">
    Screen dimensions, e.g. `1920x1080`.
  </ParamField>
  <ParamField name="viewport_size" type="string">
    Browser viewport dimensions, e.g. `1280x800`.
  </ParamField>
  <ParamField name="language" type="string">
    Browser language code, e.g. `en-US`.
  </ParamField>
  <ParamField name="timezone" type="string">
    IANA timezone string, e.g. `America/New_York`.
  </ParamField>
  <ParamField name="existing_click_id" type="string">
    The `click_id` stored for this visitor (the tracker sends it when the visitor comes back through another partner's link). A customer that already has this click ID is reused and keeps its first partner; the click is still recorded for the new partner. With `program_id`, a click ID whose customer belongs to another program is not reused, and the click gets a new click ID and customer. When no customer has the click ID, a new customer is created with it.
  </ParamField>
  <ParamField name="program_id" type="string | integer">
    The program of the page (the tracker sends it). A numeric string such as `"42"` is accepted. When sent, the `aff` code is looked up only inside this program, and an id that is not a program returns `400` `Unknown program_id`. Without it, the lookup covers every program.
  </ParamField>
  <ParamField name="visitor_ip" type="string">
    Your visitor's IP address, when your server records the click for that visitor. Used for the click's location only when the request carries the Bearer API key of the program the link belongs to (not a key past its rotation deadline) and the value is an IP address; otherwise the request's own IP is used. It never causes the click to be refused.
  </ParamField>
  <ParamField name="additional_data" type="object">
    Pass `{"test_mode": true, "program_id": 1}` to create a test event that does not affect production attribution.
  </ParamField>
</ParamList>

### Example request body

<CodeGroup items={[
  {
    label: "cURL",
    lang: "bash",
    code: `curl -X POST https://api.affitor.com/api/v1/track/click \\
  -H "Content-Type: application/json" \\
  -d '{
    "affiliate_url": "https://yoursite.com/pricing?aff=PARTNER123",
    "page_url": "https://yoursite.com/pricing",
    "page_title": "Pricing — YourSite",
    "referrer_url": "https://blog.example.com/post",
    "session_id": "sess_abc123",
    "screen_resolution": "1920x1080",
    "viewport_size": "1280x800",
    "language": "en-US",
    "timezone": "America/New_York"
  }'`
  },
  {
    label: "JSON body",
    lang: "json",
    code: `{
  "affiliate_url": "https://yoursite.com/pricing?aff=PARTNER123",
  "page_url": "https://yoursite.com/pricing",
  "page_title": "Pricing — YourSite",
  "referrer_url": "https://blog.example.com/post",
  "session_id": "sess_abc123",
  "screen_resolution": "1920x1080",
  "viewport_size": "1280x800",
  "language": "en-US",
  "timezone": "America/New_York"
}`
  },
]} />

---

## Response

### Success

```json
{
  "success": true,
  "click_id": "cust_42_1715000000000",
  "partner_code": "PARTNER123",
  "cookie_window_days": 60
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | `boolean` | `true` on success |
| `click_id` | `string` | Unique identifier for this click/customer relationship — store in a first-party cookie and pass to lead/sale calls |
| `partner_code` | `string` | The short partner code resolved from the referral link |
| `cookie_window_days` | `number` | Attribution window in days set by the program; `click_id` remains valid for this many days |

### Test mode response

When `additional_data.test_mode` is `true`:

```json
{
  "success": true,
  "message": "Test click event tracked successfully",
  "data": {
    "eventId": 101,
    "programId": 1,
    "test_mode": true
  }
}
```

---

## Errors

| Status | Condition |
|--------|-----------|
| `400 Bad Request` | `affiliate_url` is missing |
| `400 Bad Request` | `affiliate_url` cannot be parsed as a URL |
| `400 Bad Request` | No `?aff=` or `?via=` parameter found in `affiliate_url` |
| `400 Bad Request` | `program_id` was sent but is not a program (`Unknown program_id`) |
| `400 Bad Request` | No referral link found matching the `aff` value |
| `400 Bad Request` | Referral link configuration is invalid (missing partner or program) |
| `403 Forbidden` | Test mode only: the Bearer key and `additional_data.program_id` name different programs (`program_id does not match the API key`) |
| `410 Gone` | Referral link exists but is inactive. Body has `error.code: link_disabled`; no click is recorded |
| `500 Internal Server Error` | Unexpected server error |

---

## What happens on success

1. The `aff` value is extracted from `affiliate_url` and matched to a referral link.
2. If `existing_click_id` is provided and a customer already has that click ID, that customer is reused and keeps its first partner. The exception: when `program_id` is sent and that customer belongs to another program, it is not reused and a new click ID is generated. When no customer is reused, a new customer record is created with `customer_status: click` under the returned `click_id`.
3. A click event is created in `affiliate_click_events` with geo, device, and browser data derived server-side.
4. The referral link's `clicks` counter and `last_clicked` timestamp are updated.
5. The partner-program click metrics are incremented.
6. `click_id`, `partner_code`, and `cookie_window_days` are returned for the caller to store in a cookie.

---

## Next steps

After a click is tracked:

- [Lead Tracking](/docs/brand/tracking/lead-tracking-signup/) — identify the customer when they sign up
- [Payment Tracking](/docs/brand/tracking/payment-tracking-stripe/) — record a sale against the same customer
- [Testing Integration](/docs/brand/tracking/testing-integration/) — verify the full flow end-to-end
