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

# Whitelist and tracking

> Whitelist your recruiters (required to contact them) and track profile views (contractual obligation)

Two partner obligations, of a different nature:

|                | Recruiter whitelist                         | View tracking              |
| -------------- | ------------------------------------------- | -------------------------- |
| **Nature**     | Essential for things to work                | **Contractual commitment** |
| **Endpoint**   | `POST /partners/whitelist`                  | `POST /partners/events`    |
| **Frequency**  | Once per recruiter                          | On every profile view      |
| **If missing** | The recruiter cannot contact the freelancer | API access suspension      |

## 1. Whitelist: essential for recruiter-freelancer contact

### Why it is essential

Pylote protects freelancers' identity: their real email is **never** exposed. Every profile returned by `GET /freelances` has a proxy email at `@freelance.pylote.io`.

For the proxy to work both ways, **the recruiter must also have a proxy email**. Pylote generates one when you whitelist them: `lutessa.marieg@recruiter.pylote.io`.

This pair of proxy emails is what enables:

* The recruiter to **send an email to the freelancer** (relayed by Pylote)
* The freelancer to **reply** (relayed the other way)
* Pylote to **measure interactions** in the freelancer's dashboard

<Warning>
  Without whitelisting, the recruiter simply **cannot reach** Pylote freelancers. No message will get through, the proxy email does not exist.

  Whitelist **every recruiter on your platform** as soon as they access Pylote profiles.
</Warning>

### Initial setup: whitelist all your recruiters at once

<Tip>
  **At onboarding**, you probably already have dozens (or even hundreds) of recruiters on your platform. Rather than making N single calls, use `POST /partners/whitelist/batch` which accepts up to 500 per call.
</Tip>

```bash theme={null}
curl -X POST "https://client-p.pylote.io/partners/whitelist/batch" \
  -H "x-api-key: your-partner-key" \
  -H "Content-Type: application/json" \
  -d '{
    "recruiters": [
      { "recruiterEmail": "marie.gilles@lutessa.com",  "firstname": "Marie",  "lastname": "Gilles",  "company": "Lutessa" },
      { "recruiterEmail": "paul.durand@tmc-europe.com", "firstname": "Paul",  "lastname": "Durand",  "company": "TMC Europe" }
    ]
  }'
```

**Response** (partial success possible - one recruiter in error does not fail the others):

```json theme={null}
{
  "results": [
    { "recruiterEmail": "marie.gilles@lutessa.com",   "whitelistedEmail": "lutessa.marieg@recruiter.pylote.io" },
    { "recruiterEmail": "paul.durand@tmc-europe.com", "whitelistedEmail": "tmceurope.pauld@recruiter.pylote.io" }
  ]
}
```

### Ongoing: whitelist a new recruiter

For each new recruiter later added to your platform, the single endpoint:

```bash theme={null}
curl -X POST "https://client-p.pylote.io/partners/whitelist" \
  -H "x-api-key: your-partner-key" \
  -H "Content-Type: application/json" \
  -d '{
    "recruiterEmail": "marie.gilles@lutessa.com",
    "firstname": "Marie",
    "lastname": "Gilles",
    "company": "Lutessa"
  }'
```

**Response:**

```json theme={null}
{
  "whitelistedEmail": "lutessa.marieg@recruiter.pylote.io"
}
```

<Warning>
  The `company` field must be the **recruiter's** company (e.g. "Lutessa"), not yours (e.g. "Agrega"). This is the name shown in the freelancer's dashboard.
</Warning>

## 2. Tracking: contractual obligation

### Why it is mandatory

Pylote offers freelancers a **visibility** feature: they see in real time which companies view their profile, with which keywords and which actions.

It is a **premium feature** and a major conversion lever for Pylote. Without partner tracking, freelancers see nothing of the views going through your platform - which degrades the product experience and the value of Pylote.

<Warning>
  Tracking is a **contractual commitment**. Any partner using the Pylote API commits to reporting every recruiter interaction via `POST /partners/events`.

  Failure to comply may result in suspension of API access.
</Warning>

### What the freelancer sees

When a Lutessa recruiter views a profile via Agrega, the freelancer sees in their dashboard:

```
Lutessa via Agrega - viewed your profile - 2h ago
Lutessa via Agrega - downloaded your CV - 1h ago
```

### Events to track

Every recruiter-freelancer interaction must trigger a call to `POST /partners/events`.

| Action           | When to send it                                      | Mandatory |
| ---------------- | ---------------------------------------------------- | --------- |
| `profile_view`   | The recruiter opens the freelancer's profile         | **Yes**   |
| `click_cv`       | The recruiter clicks the CV link                     | **Yes**   |
| `click_linkedin` | The recruiter clicks the LinkedIn link               | **Yes**   |
| `click_phone`    | The recruiter clicks the phone number                | Yes       |
| `click_email`    | The recruiter clicks the email                       | Yes       |
| `add_favorite`   | The recruiter adds the freelancer to their favorites | Yes       |

<Note>
  At a minimum, `profile_view`, `click_cv` and `click_linkedin` must be tracked. The `click_phone`, `click_email` and `add_favorite` events are also expected if those actions are available on your platform.
</Note>

### Sending an event

```bash theme={null}
curl -X POST "https://client-p.pylote.io/partners/events" \
  -H "x-api-key: your-partner-key" \
  -H "Content-Type: application/json" \
  -d '{
    "recruiterEmail": "marie.gilles@lutessa.com",
    "freelanceId": "bc1d49cf-1cc7-4afe-92aa-0ebd545fcd12",
    "action": "profile_view"
  }'
```

**Response:**

```json theme={null}
{ "status": "ok" }
```

| Field            | Description                                                                                                   |
| ---------------- | ------------------------------------------------------------------------------------------------------------- |
| `recruiterEmail` | The recruiter's real email (must be whitelisted)                                                              |
| `freelanceId`    | The `meta.id` field of the JSON Resume returned by `GET /freelances`                                          |
| `action`         | Interaction type (`profile_view`, `click_cv`, `click_linkedin`, `click_phone`, `click_email`, `add_favorite`) |

### Implementation example (Node.js)

```javascript theme={null}
async function trackProfileView(recruiterEmail, freelanceId) {
  await axios.post(
    `https://client-p.pylote.io/partners/events`,
    {
      recruiterEmail,
      freelanceId,
      action: 'profile_view'
    },
    {
      headers: {
        'x-api-key': process.env.PYLOTE_PARTNER_KEY,
        'Content-Type': 'application/json'
      }
    }
  );
}

// In your profile display handler:
app.get('/freelance/:id', async (req, res) => {
  const freelance = await getFreelanceFromDB(req.params.id);
  const recruiter = req.user;

  // Track BEFORE displaying the profile
  await trackProfileView(recruiter.email, freelance.pyloteId);

  res.render('freelance-profile', { freelance });
});
```

## Full flow

```mermaid theme={null}
sequenceDiagram
    participant R as Recruiter
    participant P as Partner
    participant API as Pylote API
    participant F as Freelancer (extension)

    Note over P,API: 1. Whitelist (once per recruiter)
    P->>API: POST /partners/whitelist
    Note right of P: {recruiterEmail, firstname, lastname, company}
    API-->>P: {whitelistedEmail: "lutessa.marieg@recruiter.pylote.io"}

    Note over P,API: 2. Sync (periodic)
    P->>API: GET /freelances?modifiedTime=...
    API-->>P: [{meta: {id: "abc123"}, basics: {email: "...@freelance.pylote.io"}}]

    Note over R,F: 3. Daily usage
    R->>P: Opens a freelancer's profile
    P->>API: POST /partners/events {action: "profile_view"}
    API-->>P: {status: "ok"}
    API->>F: Dashboard: "Lutessa via Agrega"

    R->>P: Contacts the freelancer
    P->>API: Email via proxy recruiter.pylote.io <-> freelance.pylote.io
    API->>F: Email relayed to the freelancer
```

## Deletion obligations

In addition to tracking, you must **delete deleted profiles** from your database within **30 days** of receiving the deletion status.

When `GET /freelances` returns a profile with `meta.status: "deleted"`:

1. Delete the profile from your database
2. Delete all associated data (cached CV, notes, etc.)
3. Keep only the ID to avoid re-importing the profile

<Warning>
  The **30-day** deadline is contractual. Beyond it, Pylote reserves the right to suspend API access.
</Warning>

The deletion statuses are:

* `account_deleted` - the freelancer deleted their account
* `account_banned` - account banned by Pylote
* `account_excluded` - account excluded
* `account_invisible` - profile made invisible

<Info>
  Use the `GET /freelances/deleted-freelances` route to fetch the full list of profiles to delete if you missed some syncs.
</Info>

## Data destruction on termination

If the partnership agreement is terminated (for any reason whatsoever):

1. Your API key will be **revoked** on the effective date of termination
2. You must **stop all use** of the API immediately
3. You must **destroy all data** obtained from the API stored in your systems

<Note>
  Exception: data already integrated into the profiles of your recruiter clients and processed under their own responsibility is not subject to this destruction obligation.
</Note>
