> ## 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.

# Recherche partenaire

> Flow en 2 étapes — recherche anonymisée puis révélation du profil complet

## Vue d'ensemble

En plus de la [synchronisation complète](/integration-guide) (`GET /freelances`), les
partenaires peuvent interroger le pool Pylote **à la demande**, en 2 étapes :

1. **Stage 1 — Recherche anonymisée** (`POST /partners/search`) : votre requête est
   classée par notre moteur de matching. Vous recevez des profils **partiels et
   anonymisés** : de quoi évaluer la pertinence, rien pour identifier ou contacter.
2. **Stage 2 — Révélation** (`POST /partners/reveal`) : à partir de l'`id` d'un
   résultat, vous recevez le profil complet (format JSON Resume, identique à
   `GET /freelances`). C'est à ce moment que le freelance est informé de la
   consultation (event `profile_view` émis automatiquement côté Pylote).

Cette séparation vous permet d'implémenter votre propre logique de crédits :
la consommation n'a lieu qu'au Stage 2.

## Stage 1 — Rechercher

```javascript theme={null}
const response = await axios.post(
  'https://client-p.pylote.io/partners/search',
  {
    keywords: 'react "design system" -wordpress',
    skills: ['React', 'TypeScript'],                      // ET : toutes requises (name du référentiel Skills)
    workAreas: ['Île-de-France'],                          // OU (name du référentiel Regions)
    seniority: ['senior_6-10_ans', 'master_10_ans'],       // OU (slug du référentiel Seniority)
    available: true,
    page: 1,
    limit: 50,
  },
  { headers: { 'x-api-key': API_KEY } },
);
// response.data.freelances : profils anonymisés classés par pertinence
// response.data.infos      : { total, page, limit, totalPages }
```

### Sémantique de `keywords`

| Syntaxe           | Effet                                          |
| ----------------- | ---------------------------------------------- |
| `react node`      | ET implicite : les deux termes doivent matcher |
| `"design system"` | Expression exacte                              |
| `-wordpress`      | Exclusion                                      |
| OU                | Non supporté à ce stade                        |

### Filtres et référentiel

Les filtres sont **exacts** (pas de tolérance de frappe) : une valeur hors
référentiel ne provoque pas d'erreur, elle ne matche simplement aucun profil.
Les valeurs se récupèrent via le endpoint public `POST /referential` :

```javascript theme={null}
const { data } = await axios.post('https://client-p.pylote.io/referential', {
  action: 'list:all',
  field: 'Seniority',   // ou 'Skills', 'Regions'
});
// -> [{ name: 'Senior 6-10 ans', slug: 'senior_6-10_ans', ... }, ...]
```

Attention au champ à utiliser selon le filtre :

| Filtre      | Table référentiel | Champ à envoyer | Exemple           |
| ----------- | ----------------- | --------------- | ----------------- |
| `skills`    | `Skills`          | `name`          | `React`           |
| `workAreas` | `Regions`         | `name`          | `Île-de-France`   |
| `seniority` | `Seniority`       | **`slug`**      | `senior_6-10_ans` |

La géographie se filtre par zones de mobilité (régions, pays), pas par code
postal + rayon : mappez vos zones cibles vers nos régions.

### Ce que contient un résultat

Chaque profil du Stage 1 contient : `id` (la clé du Stage 2), `headline`,
`summary`, localisation (ville/région/pays/CP), compétences, langues, séniorité,
métiers, disponibilité, préférences de mission, historique de missions
(intitulés, durées et descriptions, **sans** champ employeur), et `matchRank`.

Tous les textes libres (`summary` du profil et des missions) sont **rédigés** :
emails, téléphones, URLs et nom du freelance retirés. Ce sont des textes écrits
par le freelance : un nom d'entreprise peut occasionnellement y apparaître.

`matchRank` est le **rang absolu** du profil dans le classement de pertinence
(1 = meilleur match). C'est un rang réel, pas un score en pourcentage.

Ce que le Stage 1 ne contient jamais : nom, email (même proxy), téléphone,
liens de profils (CV, LinkedIn), champ employeur structuré.

## Stage 2 — Révéler un profil

```javascript theme={null}
const response = await axios.post(
  'https://client-p.pylote.io/partners/reveal',
  {
    freelanceId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', // id du Stage 1
    recruiterEmail: 'marie.gilles@lutessa.com',           // doit être whitelisté
  },
  { headers: { 'x-api-key': API_KEY } },
);
// response.data : profil complet au format JSON Resume
```

À savoir :

* `recruiterEmail` doit être dans votre [whitelist](/tracking-obligations). Sans
  cela : 404. C'est ce qui alimente le "{company} via {partenaire}" que voit le
  freelance dans ses statistiques.
* L'event `profile_view` est émis automatiquement : n'appelez pas
  `POST /partners/events` pour la consultation elle-même. Gardez-le pour les
  actions granulaires (click\_cv, click\_linkedin, add\_favorite...).
* Le contact passe par l'**email proxy Pylote** et le **lien LinkedIn tracké**
  du profil révélé. Le téléphone n'est pas exposé via le canal partenaire.
* Un recruteur `opt_out` ne peut pas révéler de profil (403) : la révélation
  expose des coordonnées, elle exige le tracking.

## Rate limiting

120 requêtes/minute par clé API, toutes routes confondues. Au-delà : `429`.
Une recherche = une requête, quel que soit `limit`.
