Le SDK est le chemin recommandé pour les stacks JavaScript et TypeScript. Si vous êtes sur un autre runtime — PHP, Python, Ruby, Go, Elixir — ou si vous ne voulez pas de dépendance, appelez les endpoints REST publics directement. Ce guide couvre le pattern le plus utilisé : un postback serveur à serveur déclenché depuis votre backend de checkout après un paiement réussi.

## Quand utiliser le postback

Le postback est le bon pattern quand :

* La conversion a lieu côté serveur (webhook Stripe, IPN PayPal, processeur de paiement custom).
* Vous avez besoin de garanties de livraison plus strictes que ce qu'un pixel navigateur peut offrir.
* Vous opérez sur une stack non-JS.
* La session navigateur peut être perdue entre le clic et la conversion (Safari ITP, cross-device, app mobile).

Si la conversion est liée à une page que le client est en train de visiter, le [SDK](/fr/docs/api/integration/sdk) est plus simple.

## Endpoint

```http
POST /api/v1/conversions/postback
Authorization: Bearer <VOTRE_CLE_API>
Content-Type: application/json
```

## Payload

```json
{
  "externalId": "ord_42",
  "conversionType": "SALE",
  "amount": 4990,
  "currency": "EUR",
  "sessionId": "rcs_7fKj2...",
  "metadata": { "plan": "pro" }
}
```

| Champ                 | Type           | Requis   | Notes                                                                                                                |
| --------------------- | -------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `externalId`          | string (1–255) | oui      | Votre identifiant de commande / transaction. Sert de clé d'idempotence.                                              |
| `conversionType`      | string         | oui      | L'un de `SALE`, `LEAD`, `TRIAL`, `CUSTOM`.                                                                           |
| `amount`              | integer        | oui      | Valeur de la conversion en **unités mineures (centimes)**. Ex. 49,90 € → `4990`. Les décimaux sont rejetés avec 422. |
| `currency`            | string         | oui      | Code ISO 4217 à 3 lettres (mis en majuscules côté serveur).                                                          |
| `sessionId` / `rcsid` | string         | un parmi | Identifiant de session depuis le cookie de clic. Préféré quand intact.                                               |
| `clickId` / `cid`     | string         | un parmi | Identifiant de clic retourné par l'URL de tracking. Alternative à `sessionId`.                                       |
| `customerEmailHash`   | string         | un parmi | SHA-256 hex de l'email client en minuscules. Fallback si la session est perdue.                                      |
| `customerEmail`       | string         | un parmi | Email client en clair (hashé côté serveur). Fallback.                                                                |
| `customerId`          | string         | non      | Votre identifiant client interne.                                                                                    |
| `convertedAt`         | string         | non      | Datetime ISO 8601 de la conversion. Par défaut : maintenant.                                                         |
| `customParams`        | object         | non      | Jusqu'à 5 paramètres personnalisés : `{ s1, s2, s3, s4, s5 }`.                                                       |
| `metadata`            | object         | non      | Format libre. Persisté sur la conversion, exposé dans les exports.                                                   |

Au moins un identifiant d'attribution (`sessionId`/`rcsid`, `clickId`/`cid`, `customerEmailHash` ou `customerEmail`) doit être présent. Si plusieurs sont fournis, la priorité est : `clickId` → `sessionId` → `customerEmailHash` → `customerEmail`.

## Calculer le hash de l'email

<Callout type="warning" title="Hashez côté serveur, pas côté client">
  Le hash est un identifiant de fallback ; si vous le calculez dans le navigateur, un attaquant peut le rejouer pour s'attribuer des conversions. Hashez toujours côté serveur, après avoir vérifié que l'email appartient bien au client qui paie.
</Callout>

```python
import hashlib
email = customer_email.lower().strip()
email_hash = hashlib.sha256(email.encode()).hexdigest()
```

```php
$emailHash = hash('sha256', strtolower(trim($email)));
```

```ruby
require 'digest'
email_hash = Digest::SHA256.hexdigest(email.downcase.strip)
```

Le hash est ce que nous stockons. L'email en clair ne quitte jamais votre infrastructure.

## Exemple : handler webhook Stripe

Pattern typique : votre serveur reçoit déjà un webhook Stripe `checkout.session.completed`. Étendez le handler pour déclencher un postback RefCampaign.

```python
# Python / Flask
import os
import requests

REFCAMPAIGN_KEY = os.environ['REFCAMPAIGN_API_KEY']
REFCAMPAIGN_URL = 'https://app.refcampaign.com/api/v1/conversions/postback'

def on_stripe_completed(session):
    requests.post(
        REFCAMPAIGN_URL,
        headers={
            'Authorization': f'Bearer {REFCAMPAIGN_KEY}',
            'Content-Type': 'application/json',
        },
        json={
            'externalId': session.id,
            'conversionType': 'SALE',
            'amount': session.amount_total,  # déjà en centimes
            'currency': session.currency.upper(),
        },
        timeout=10,
    )
```

## Idempotence et retries

L'endpoint est **idempotent sur `externalId`** — un appel répété avec le même `externalId` pour le même marchand renvoie la conversion existante (`{ success: true, conversionId, message: "Conversion already tracked" }`) sans créer de doublon. Si le même `externalId` est soumis par un *autre* marchand, le serveur retourne `409`.

Si votre handler webhook retry sur erreur transitoire, c'est sûr — renvoyer le même `externalId` ne crée jamais de conversion en double.

Pour les retries au niveau réseau : respectez le header `Retry-After` sur `429`, traitez les `5xx` comme transitoires avec backoff, traitez les `4xx` comme terminaux (pas de retry, log + alerte).

## Vérifier la livraison

Le tableau de bord marchand affiche les conversions entrantes en temps réel. Pour les pipelines automatisés, requêtez la liste des conversions pour confirmer l'arrivée :

```bash
curl https://app.refcampaign.com/api/v1/conversions?campaignId=cmp_xxx&limit=10 \
  -H "Authorization: Bearer $REFCAMPAIGN_API_KEY"
```

## Sécurité

* Envoyez la clé API uniquement sur HTTPS.
* Stockez-la dans un gestionnaire de secrets — jamais dans le code source.
* Utilisez une clé dédiée par environnement (production / staging / local).
* Voir [authentification](/fr/docs/api/authentication) pour la checklist complète.
