> ## Documentation Index
> Fetch the complete documentation index at: https://developers.introw.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Affiliate conversions

> Attribute affiliate conversions to partner clicks, from your backend or the browser.

Track when traffic from a partner's affiliate link converts. Introw attributes every conversion to a signed, server-side click record, so attribution cannot be forged or double-counted. You report conversions with the [Record an affiliate conversion](/api-reference/affiliate/record-an-affiliate-conversion) endpoint — either server-to-server or directly from the browser with the `affiliate.js` snippet.

## How attribution works

1. A partner shares an Introw affiliate link (`/r/{code}`). Introw logs a server-side click and redirects the visitor to your site with an opaque `irw_id` query param.
2. The `affiliate.js` snippet on your site reads `irw_id`, stores it in a first-party, last-click `_introw_aff` cookie (90-day window), and strips it from the URL.
3. When the visitor converts (signup, purchase, …), you call the conversions endpoint with the cookie value as `clickId`.
4. Introw verifies the signed click token, deduplicates, and creates a campaign **form submission** from the conversion payload.

Because every conversion is anchored to a real click and deduplicated, partners cannot inflate their numbers and you cannot accidentally double-count.

## Forgiving responses

The browser snippet must never surface errors on your page, so the endpoint is intentionally lenient:

| `status`    | HTTP  | Meaning                                                       |
| ----------- | ----- | ------------------------------------------------------------- |
| `recorded`  | `201` | A new conversion was attributed and a form submission created |
| `duplicate` | `200` | This click already converted — nothing new was recorded       |
| `ignored`   | `200` | The click token was missing, invalid, or expired              |

## Choose an integration

### Option A — Client-side (browser)

Add the snippet once in the `<head>` of every page. It stores the attribution cookie, auto-tracks HubSpot form submissions, and exposes `window.introw.affiliate.track(...)`. The **organisation publishable key** is safe to expose and the same for every campaign — the visitor's click cookie decides which campaign a conversion belongs to, so one snippet covers all your campaigns. Find your key on any campaign's **Install** tab.

```html theme={"theme":{"light":"github-light","dark":"github-dark"}}
<script async src="https://app.introw.io/affiliate.js" data-publishable-key="pk_aff_org_..."></script>
```

Call `track` when a visitor converts. You don't need to pass a key — the snippet's organisation key is used automatically, and the visitor's click cookie decides which campaign the conversion belongs to:

```js theme={"theme":{"light":"github-light","dark":"github-dark"}}
window.introw.affiliate.track({
  email: customer.email,
  properties: { plan: "pro" },
});
```

Each campaign also has its own publishable key (on its **Install** tab), but you rarely need it. Passing it scopes the call to that one campaign as a guard: if the visitor's click belongs to a different campaign, the conversion is **ignored** rather than recorded. It does not relabel the conversion — the click cookie is always the source of truth for attribution.

```js theme={"theme":{"light":"github-light","dark":"github-dark"}}
// Optional: only record if this visitor's click is for this specific campaign.
window.introw.affiliate.track({ publishableKey: "pk_aff_...", email: customer.email });
```

Browser conversions are accepted only from the campaign's allowed origins (configure them on the campaign's **Install** tab). Leave the allowlist empty to accept any origin.

#### HubSpot forms

Once the snippet is installed, HubSpot form submissions are tracked automatically — no embed-code changes and no `track` call. The snippet listens for both classic (`hsFormCallback` postMessage) and HubSpot Forms v4 (`hs-form-event:*`) submit events, reads the submitted email, and records the conversion against the visitor's click cookie.

### Option B — Server-to-server

Recommended when conversions happen on your backend. Use a secret API key with the `affiliate:write` scope and send the click id captured from the `_introw_aff` cookie:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "https://api.introw.io/api/v1/affiliate/conversions" \
  -H "x-api-key: $INTROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "clickId": "<value of the _introw_aff cookie>",
    "email": "customer@example.com",
    "properties": { "plan": "pro" }
  }'
```

## Conversion properties

Send arbitrary metadata in `properties` (for example `plan`, deal value, or order id). Each key is mapped onto a campaign form field via the campaign's field mapping; unmapped keys are stored alongside the submission.

## Authentication

The server-to-server path uses a secret API key with the `affiliate:write` scope — see [Authentication](/general/authentication). The browser path authenticates with the campaign publishable key plus the Origin allowlist instead of a secret key.

## API reference

<CardGroup cols={1}>
  <Card title="Record an affiliate conversion" icon="bullseye-arrow" href="/api-reference/affiliate/record-an-affiliate-conversion">
    POST /api/v1/affiliate/conversions
  </Card>
</CardGroup>
