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

# Server-to-server (S2S) tracking

> Track affiliate sign-ups and sales directly from your backend with the Partnero REST API. No JavaScript, no PartneroJS cookies, no tracking blockers.

Server-to-server (S2S) tracking sends sign-up and sale events directly from your backend to Partnero. Attribution is carried by a key that **you** capture from the referral URL and persist—no PartneroJS snippet, no `partnero_partner` / `partnero_referral` cookies, and no client-side calls to the Partnero API.

## Why use S2S

* **Resilient to tracking blockers** — ad-blockers, cookie-blocking browsers (Safari ITP, Brave, in-app browsers), and snippet load failures don't affect attribution.
* **Backend-only stacks** — works without any frontend JavaScript on your site.
* **Mobile and machine-to-machine** — required for native apps and headless integrations.
* **Strict privacy regimes** — no third-party-style storage, no extra consent banner row.
* **Full control** — you decide where the referral key lives between landing and sign-up.

<Warning>
  Always call the Partnero API from your backend. Keep `Authorization: Bearer YOUR_API_KEY` on the server—never embed it in browser or mobile code.
</Warning>

## Endpoints used

| Endpoint                                 | Purpose                                                   |
| ---------------------------------------- | --------------------------------------------------------- |
| `POST /v1/customers`                     | Track a sign-up and attribute it to a partner or referrer |
| `POST /v1/transactions`                  | Track a sale and trigger commission calculation           |
| `POST /v1/customers/{id}/balance/credit` | Manually credit a referrer (refer-a-friend)               |

All requests use `https://api.partnero.com` with `Authorization: Bearer YOUR_API_KEY`.

## How it works

1. **Visitor lands via a referral link** — for example `yoursite.com?aff=REFERRAL_KEY`. The exact query parameter depends on your program; confirm it under **Programs → Settings → Referral links** in the Partnero dashboard.
2. **You capture the key** — read it from the URL and persist it your way (server session, database row keyed by anonymous ID, your own first-party storage, etc.).
3. **You call `POST /v1/customers`** at sign-up with `partner.key` (affiliate) or `referring_customer.key` (refer-a-friend).
4. **You call `POST /v1/transactions`** at purchase—or let a connected billing integration do it for you.

Partnero ignores invalid keys, so you can call the API for **every** sign-up without first checking whether the visitor was referred.

## Two valid paths

Both options below are server-side; they differ only in *where* the referral key comes from.

| Path                        | Where the key is stored                                               | When to use                                                                          |
| --------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| **Pure S2S** *(this guide)* | Your storage (session, DB, mobile keystore)                           | No PartneroJS on the site, mobile apps, strict privacy                               |
| **Hybrid**                  | The `partnero_partner` / `partnero_referral` cookie set by PartneroJS | You already use PartneroJS and want to read the cookie server-side instead of via JS |

For the hybrid path, see [Affiliate API integration](/guides/tracking/affiliate-api) and [Refer-a-friend API integration](/guides/tracking/refer-a-friend-api), which both show reading the PartneroJS cookie from the request.

## Step 1: Capture the referral key

Read the parameter from the landing URL and persist it.

<Tabs>
  <Tab title="Server (read query)">
    ```javascript theme={null}
    app.get('/', (req, res) => {
      const aff = req.query.aff;
      if (aff) {
        req.session.affiliateKey = aff;
      }
      res.render('home');
    });
    ```
  </Tab>

  <Tab title="Browser (forward to backend)">
    ```javascript theme={null}
    const params = new URLSearchParams(window.location.search);
    const aff = params.get('aff');

    if (aff) {
      fetch('/api/track-landing', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ aff })
      });
    }
    ```

    Your `/api/track-landing` route stores the key against the visitor's session or anonymous ID. Repeat the same value on every sign-up request.
  </Tab>

  <Tab title="SPA (sessionStorage)">
    ```javascript theme={null}
    const aff = new URLSearchParams(window.location.search).get('aff');
    if (aff) {
      sessionStorage.setItem('affiliate_key', aff);
    }

    const aff = sessionStorage.getItem('affiliate_key');
    fetch('/api/signup', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, name, aff })
    });
    ```

    `sessionStorage` is per-tab and not sent in cross-site requests, so it isn't subject to cross-site cookie restrictions. If you need cross-tab persistence, store the key on your backend instead.
  </Tab>

  <Tab title="Mobile">
    See [mobile app integration](/guides/tracking/mobile-app) for iOS, Android, React Native, and Flutter examples that extract the key from a deep link and persist it in native storage.
  </Tab>
</Tabs>

## Step 2: Track sign-ups

When the user creates an account, call `POST /v1/customers` with the key from Step 1.

### Affiliate programs

Use `partner.key` for the affiliate program key.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl --location 'https://api.partnero.com/v1/customers' \
      --header 'Authorization: Bearer YOUR_API_KEY' \
      --header 'Content-Type: application/json' \
      --data '{
        "partner": { "key": "PARTNER_KEY_FROM_YOUR_STORAGE" },
        "key": "customer_123",
        "email": "customer@example.com",
        "name": "John",
        "surname": "Doe",
        "options": {
          "referral_url": "https://yoursite.com/pricing?aff=PARTNER_KEY_FROM_YOUR_STORAGE"
        }
      }'
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript theme={null}
    app.post('/signup', async (req, res) => {
      const user = await createUser(req.body);
      const partnerKey = req.session.affiliateKey;

      await fetch('https://api.partnero.com/v1/customers', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.PARTNERO_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          partner: partnerKey ? { key: partnerKey } : undefined,
          key: user.id,
          email: user.email,
          name: user.firstName,
          surname: user.lastName,
          options: { referral_url: req.session.landingUrl }
        })
      });

      res.json({ ok: true });
    });
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://api.partnero.com/v1/customers');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . getenv('PARTNERO_API_KEY'),
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'partner' => ['key' => $_SESSION['affiliate_key'] ?? null],
            'key'     => $user->id,
            'email'   => $user->email,
            'name'    => $user->first_name,
            'surname' => $user->last_name,
        ]),
    ]);
    curl_exec($ch);
    curl_close($ch);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import requests

    requests.post(
        "https://api.partnero.com/v1/customers",
        headers={
            "Authorization": f"Bearer {os.environ['PARTNERO_API_KEY']}",
            "Content-Type": "application/json",
        },
        json={
            "partner": {"key": session.get("affiliate_key")},
            "key": user.id,
            "email": user.email,
            "name": user.first_name,
            "surname": user.last_name,
        },
    )
    ```
  </Tab>
</Tabs>

See [Affiliate API integration](/guides/tracking/affiliate-api) for the full request body and additional partner identifier options.

### Refer-a-friend programs

Use `referring_customer.key` for the referrer. Omit the field entirely if the visitor was not referred.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl --location 'https://api.partnero.com/v1/customers' \
      --header 'Authorization: Bearer YOUR_API_KEY' \
      --header 'Content-Type: application/json' \
      --data '{
        "referring_customer": { "key": "REFERRAL_KEY_FROM_YOUR_STORAGE" },
        "key": "customer_456",
        "email": "friend@example.com",
        "name": "Alex"
      }'
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript theme={null}
    app.post('/signup', async (req, res) => {
      const user = await createUser(req.body);
      const referralKey = req.session.referralKey;

      const body = {
        key: user.id,
        email: user.email,
        name: user.firstName
      };

      if (referralKey) {
        body.referring_customer = { key: referralKey };
      }

      await fetch('https://api.partnero.com/v1/customers', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.PARTNERO_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(body)
      });

      res.json({ ok: true });
    });
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $body = [
        'key'     => $user->id,
        'email'   => $user->email,
        'name'    => $user->first_name,
    ];

    if (!empty($_SESSION['referral_key'])) {
        $body['referring_customer'] = ['key' => $_SESSION['referral_key']];
    }

    $ch = curl_init('https://api.partnero.com/v1/customers');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . getenv('PARTNERO_API_KEY'),
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode($body),
    ]);
    curl_exec($ch);
    curl_close($ch);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import requests

    body = {
        "key": user.id,
        "email": user.email,
        "name": user.first_name,
    }

    referral_key = session.get("referral_key")
    if referral_key:
        body["referring_customer"] = {"key": referral_key}

    requests.post(
        "https://api.partnero.com/v1/customers",
        headers={
            "Authorization": f"Bearer {os.environ['PARTNERO_API_KEY']}",
            "Content-Type": "application/json",
        },
        json=body,
    )
    ```
  </Tab>
</Tabs>

The response includes `referral_link` so you can show the new customer their share URL. See [Refer-a-friend API integration](/guides/tracking/refer-a-friend-api) for stats, balance, and reward endpoints.

## Step 3: Track sales

Record purchases against the same `customer.key` you sent at sign-up. Partnero calculates the commission automatically.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl --location 'https://api.partnero.com/v1/transactions' \
      --header 'Authorization: Bearer YOUR_API_KEY' \
      --header 'Content-Type: application/json' \
      --data '{
        "customer": { "key": "customer_123" },
        "key": "invoice_123",
        "amount": 99.99,
        "action": "sale"
      }'
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript theme={null}
    await fetch('https://api.partnero.com/v1/transactions', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.PARTNERO_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        customer: { key: user.id },
        key: order.id,
        amount: order.amount,
        action: 'sale'
      })
    });
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $ch = curl_init('https://api.partnero.com/v1/transactions');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . getenv('PARTNERO_API_KEY'),
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'customer' => ['key' => $user->id],
            'key'      => $order->id,
            'amount'   => $order->amount,
            'action'   => 'sale',
        ]),
    ]);
    curl_exec($ch);
    curl_close($ch);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import requests

    requests.post(
        "https://api.partnero.com/v1/transactions",
        headers={
            "Authorization": f"Bearer {os.environ['PARTNERO_API_KEY']}",
            "Content-Type": "application/json",
        },
        json={
            "customer": {"key": user.id},
            "key": order.id,
            "amount": order.amount,
            "action": "sale",
        },
    )
    ```
  </Tab>
</Tabs>

<Tip>
  Using Stripe, Paddle, or Chargebee? Connect them in **Program settings → Integrations** and Partnero will create transactions automatically—you can skip Step 3.
</Tip>

## When you cannot persist the landing click

If storing the referral key between landing and sign-up is impractical (e.g., podcast or video referrals), use attribution that doesn't depend on a remembered visit:

* **[Coupon-based tracking](/guides/tracking/coupon-tracking)** — partners share unique promo codes; checkout integrations attribute the sale.
* **[Transaction-based attribution](/guides/tracking/transaction-based-attribution)** — set the program to transaction-based and pass `partner_key` in payment metadata so each charge selects the partner.

## Comparison

| Method                                                              | Cookies                                       | Affected by ad-blockers / ITP | Best for                                |
| ------------------------------------------------------------------- | --------------------------------------------- | ----------------------------- | --------------------------------------- |
| **Server-to-server** *(this guide)*                                 | None                                          | No                            | Backends, mobile, strict privacy        |
| [PartneroJS](/guides/tracking/javascript-tracking)                  | Yes (`partnero_partner`, `partnero_referral`) | Yes                           | Standard web sites with frontend JS     |
| [Coupon-based](/guides/tracking/coupon-tracking)                    | None                                          | No                            | Influencers, podcasts, offline channels |
| [Transaction-based](/guides/tracking/transaction-based-attribution) | None                                          | No                            | Per-payment attribution via metadata    |

## Checklist

1. Confirm your program's **referral link URL format** and query parameter in the Partnero dashboard.
2. Persist the referral key with **your** session or storage between landing and sign-up.
3. Call **`POST /v1/customers`** from your server with `partner` or `referring_customer`.
4. Call **`POST /v1/transactions`** (or let a billing integration do it) using the same customer `key`.

## Next steps

<CardGroup cols={2}>
  <Card title="Affiliate API" icon="link" href="/guides/tracking/affiliate-api">
    Cookie-based variant and full customer / transaction fields
  </Card>

  <Card title="Refer-a-friend API" icon="user-group" href="/guides/tracking/refer-a-friend-api">
    Referrer fields, rewards, and dashboard API calls
  </Card>

  <Card title="Mobile apps" icon="mobile" href="/guides/tracking/mobile-app">
    Deep links and native storage patterns
  </Card>

  <Card title="API reference" icon="code" href="/api-reference/introduction">
    Full REST API documentation
  </Card>
</CardGroup>
