Documentation partenaires

Tout ce qu'il faut pour publier vos annonces de vente et de rachat sur QuizzBuy et nous notifier les commandes attribuées à notre trafic.

1. Obtenir une clé API

Contactez votre interlocuteur QuizzBuy (ou le formulaire de contact). Nous générons pour vous une clé au format zzb_live_… — elle ne vous est communiquée qu'une seule fois, conservez-la en lieu sûr. Passez-la sur chaque requête dans l'en-tête HTTP :

Authorization: Bearer zzb_live_VOTRE_CLE

2. Vérifier votre clé

curl https://quizzbuy.com/api/v1/me \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Réponse
{
  "company": { "id": "…", "name": "Votre société", "slug": "votre-societe" },
  "commission_rate": 0.05,
  "click_param": "zzb_click"
}

3. Publier vos annonces

Une annonce est soit une offre de rachat (BUYBACK — vous reprenez un appareil à un prix donné), soit une offre de vente (SALE — un produit reconditionné que vous vendez). L'envoi est un upsert : ré-envoyer le même externalId met l'annonce à jour.

curl -X POST https://quizzbuy.com/api/v1/listings \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "ref-interne-123",
    "type": "BUYBACK",
    "title": "Reprise iPhone 15 Pro 256 Go",
    "brand": "Apple",
    "model": "iPhone 15 Pro",
    "category": "SMARTPHONE",
    "storage": "256 GB",
    "condition": "GOOD",
    "priceCents": 52000,
    "currency": "EUR",
    "url": "https://votre-site.fr/reprise/iphone-15-pro",
    "country": "FR"
  }'
  • category : SMARTPHONE, LAPTOP, SMARTWATCH, AUDIO ou GAMING.
  • condition : LIKE_NEW, EXCELLENT, GOOD, FAIR ou BROKEN.
  • priceCents : prix en centimes (52000 = 520 €).
  • Chaque nouvelle annonce (ou modification) passe en modération avant publication.

Mettre à jour, lister ou désactiver :

# Lister vos annonces
curl "https://quizzbuy.com/api/v1/listings?type=BUYBACK&status=APPROVED" \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Mise à jour partielle
curl -X PATCH https://quizzbuy.com/api/v1/listings/ID_ANNONCE \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "priceCents": 49000 }'

# Désactiver (soft delete)
curl -X DELETE https://quizzbuy.com/api/v1/listings/ID_ANNONCE \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

4. Tracking des clics

Quand un visiteur QuizzBuy clique vers votre site, l'URL d'arrivée contient un identifiant de clic :

https://votre-site.fr/vendre?zzb_click=6f1e0c9a-3b2d-4e8f-9a10-abcdef123456

Stockez cette valeur (cookie ou session côté votre site, durée recommandée : 45 jours). C'est elle qui permettra d'attribuer la commande. Le nom du paramètre (zzb_click par défaut) est configurable sur demande.

5. Notifier une commande (postback S2S)

Dès qu'un client passe commande (achat ou demande de rachat validée) et que vous disposez d'un zzb_click, appelez notre postback depuis votre serveur :

curl -X POST https://quizzbuy.com/api/v1/postback \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "click_id": "6f1e0c9a-3b2d-4e8f-9a10-abcdef123456",
    "type": "BUYBACK",
    "amount_cents": 52000,
    "currency": "EUR",
    "order_ref": "CMD-2026-000123"
  }'

# Réponse 201
{ "conversion_id": "…", "status": "TO_INVOICE", "commission_cents": 2600 }
  • Fenêtre d'attribution : 45 jours après le clic ; au-delà, réponse 422 attribution_window_expired.
  • order_ref doit être unique : un doublon renvoie 409 duplicate_order_ref (idempotence — vous pouvez ré-essayer sans risque).
  • type : SALE pour un achat, BUYBACK pour un rachat.
  • currency : EUR uniquement — toute autre devise est refusée (422 unsupported_currency).

6. Tester en sandbox

Ajoutez "test": true au postback : la requête est intégralement validée (clé, click_id, fenêtre 45 j) mais aucune conversion n'est enregistrée ni facturée.

{ "click_id": "…", "type": "SALE", "amount_cents": 10000, "order_ref": "TEST-1", "test": true }
# → 200 { "test": true, "valid": true, "commission_cents": 500 }

7. Codes d'erreur

CodeErreurExplication
400invalid_inputCorps de requête invalide (détails dans la réponse).
401unauthorizedClé API absente, révoquée ou invalide.
404click_not_found / not_foundclick_id ou annonce inconnue (ou pas la vôtre).
409duplicate_order_refCommande déjà notifiée.
422attribution_window_expiredClic de plus de 45 jours.
429rate_limitedTrop de requêtes — ré-essayez dans une minute.

8. Facturation

Chaque conversion attribuée génère une commission (taux contractuel, visible via /api/v1/me). QuizzBuy vous adresse une facture récapitulative périodique des conversions validées. Pour toute question : contactez-nous.

9. Marketplace — vendre sur QuizzBuy

Les annonces SALE avec un quantity > 0 sont vendues directement sur QuizzBuy (paiement client chez nous, reversement du net de commission). Champs supplémentaires : quantity (stock), color, grade (A/B/C), batteryHealth (%), warrantyMonths. L'annonce est rattachée automatiquement à la fiche produit correspondante (marque + modèle + capacité + couleur).

# Mettre à jour le stock (sans re-modération)
curl -X PATCH https://quizzbuy.com/api/v1/listings/ID_ANNONCE/stock \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "quantity": 12 }'

# Ajouter une photo (multipart, max 5 × 5 Mo, jpeg/png/webp)
curl -X POST https://quizzbuy.com/api/v1/listings/ID_ANNONCE/images \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -F "file=@photo.jpg"

10. Webhooks — être averti des ventes

Enregistrez un endpoint HTTPS ; nous vous notifions à chaque étape d'une commande contenant vos produits (order.created, order.paid, order.cancelled). Le secret n'est retourné qu'à la création — stockez-le.

curl -X POST https://quizzbuy.com/api/v1/webhooks \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://votre-site.fr/webhooks/quizzbuy", "events": ["order.paid"] }'

# Réponse (secret affiché une seule fois)
{ "webhook": { "id": "…", "secret": "whsec_…", "events": ["order.paid"] } }

# Tester
curl -X POST https://quizzbuy.com/api/v1/webhooks/ID_WEBHOOK/test \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

Chaque livraison est signée. Vérifiez l'en-tête X-ZZbuy-Signature (t=timestamp,v1=hex) :

// Node.js
const [t, v1] = signature.split(",").map((p) => p.split("=")[1]);
const expected = crypto
  .createHmac("sha256", WEBHOOK_SECRET)
  .update(`${t}.${rawBody}`)
  .digest("hex");
const valid =
  crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) &&
  Math.abs(Date.now() / 1000 - Number(t)) < 300; // anti-replay 5 min

En cas d'échec (≠ 2xx), nous retentons avec backoff : 1 min, 5 min, 30 min, 2 h, 12 h. Vous recevez aussi un email de notification de vente.

11. Traiter vos commandes

Listez vos lignes de commande, accusez réception puis expédiez avec un numéro de suivi (le client est notifié automatiquement). L'adresse de livraison n'est visible qu'après paiement.

# Lister les commandes à traiter
curl "https://quizzbuy.com/api/v1/orders?status=PENDING" \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Accuser réception
curl -X POST https://quizzbuy.com/api/v1/orders/ID_LIGNE/ack \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Expédier
curl -X POST https://quizzbuy.com/api/v1/orders/ID_LIGNE/ship \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "carrier": "Colissimo", "trackingNumber": "6A123456789FR" }'

12. Reprise gérée (trade-in)

La reprise gérée va plus loin que le simple rachat par redirection : le particulier soumet son appareil depuis QuizzBuy, et vous pilotez tout le dossier via l'API (acceptation, réception, contre-offre, paiement). Le prix affiché au client est verrouillé à la soumission depuis votre grille de rachat (voir §14) ; vous ne pouvez le réviser à la baisse qu'après réception via une contre-offre motivée.

Tous les endpoints s'authentifient avec votre clé API (Authorization: Bearer zzb_live_…) et ne renvoient que vos dossiers.

Cycle de vie

SUBMITTEDACCEPTEDSHIPPEDRECEIVEDPAID. Deux embranchements : REJECTED (refus avant envoi) et COUNTER_OFFER (contre-offre après inspection, que le particulier accepte — puis PAID — ou refuse — retour appareil, CANCELLED).

StatutSignification
SUBMITTEDDemande créée par le particulier, en attente de votre décision.
ACCEPTEDVous avez confirmé la reprise ; le client doit expédier l'appareil.
SHIPPEDLe particulier a renseigné son numéro de suivi.
RECEIVEDVous avez réceptionné l'appareil ; inspection en cours.
COUNTER_OFFERAprès inspection, vous proposez un montant révisé (inférieur).
PAIDPaiement effectué au particulier — dossier clos.
REJECTEDDemande refusée avant envoi (non éligible, fraude…).
CANCELLEDAnnulée (refus d'une contre-offre, retour appareil).

Lister & consulter

# Lister vos reprises (plus récentes d'abord)
curl "https://quizzbuy.com/api/v1/trade-ins?status=SUBMITTED&limit=50&offset=0" \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# Réponse
{
  "total": 3,
  "trade_ins": [
    {
      "trade_in_id": "…",
      "reference": "TI-2026-000042",
      "status": "SUBMITTED",
      "device": {
        "brand": "Apple", "category": "SMARTPHONE", "model": "iPhone 15 Pro",
        "storage": "256 GB", "condition": "GOOD",
        "functional_status": "FULLY_WORKING", "battery_health": 92, "imei": "…"
      },
      "offer_cents": 52000,
      "currency": "EUR",
      "price_locked_until": "2026-08-05T12:00:00.000Z",
      "country": "FR",
      "customer": { "email": "…", "first_name": "…", "last_name": "…", "locale": "fr" },
      "comment": null,
      "counter_offer_cents": null, "counter_reason": null,
      "tracking_carrier": null, "tracking_number": null,
      "shipping_label_url": null, "payout_ref": null,
      "accepted_at": null, "shipped_at": null, "received_at": null,
      "paid_at": null, "cancelled_at": null,
      "created_at": "2026-07-21T12:00:00.000Z"
    }
  ]
}

# Détail d'une reprise
curl https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"
# → { "trade_in": { … } }
  • status (filtre facultatif) : une valeur du tableau ci-dessus — sinon 400 invalid_status.
  • limit : max 200 (défaut 50) ; offset pour la pagination.
  • Le customer (contact du particulier) n'est visible que sur cette API partenaire, jamais dans les webhooks.

Transitions

Chaque action est un POST et renvoie { "ok": true, "trade_in": { … } }. Une transition depuis un statut incompatible renvoie 409 invalid_status (avec le current réel). Le particulier est notifié par email à chaque étape.

# 1. Accepter (depuis SUBMITTED) — shippingLabelUrl facultatif (étiquette prépayée)
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/accept \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "shippingLabelUrl": "https://votre-site.fr/labels/ti-42.pdf" }'

# 2. Réceptionner l'appareil (depuis SHIPPED ou ACCEPTED)
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/receive \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"

# 3a. Payer le montant garanti (depuis RECEIVED) — clôt la reprise
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/pay \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "payoutRef": "VIR-2026-000123" }'

# 3b. …ou contre-offrer après inspection (depuis RECEIVED) — montant < offre garantie
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/counter \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "amountCents": 42000, "reason": "Rayures écran non déclarées" }'

# Refuser avant envoi (depuis SUBMITTED uniquement)
curl -X POST https://quizzbuy.com/api/v1/trade-ins/ID_REPRISE/reject \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE"
  • accept : seulement depuis SUBMITTED. shippingLabelUrl facultatif (URL, ≤ 500 car.).
  • receive : depuis SHIPPED ou ACCEPTED (dépôt/envoi non déclaré par le client).
  • counter : depuis RECEIVED. amountCents doit être strictement inférieur à offer_cents, sinon 400 counter_not_lower ; reason obligatoire (1–500 car.). C'est ensuite le particulier qui accepte ou refuse depuis sa page de suivi.
  • pay : depuis RECEIVED. payoutRef obligatoire (référence de virement, 1–120 car.).
  • reject : seulement depuis SUBMITTED — aucun corps requis.

13. Webhooks reprise (trade_in.*)

Les mêmes webhooks (§10, signature X-ZZbuy-Signature identique) couvrent la reprise gérée. Abonnez-vous à tout ou partie des événements trade_in.* à la création d'un endpoint :

curl -X POST https://quizzbuy.com/api/v1/webhooks \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://votre-site.fr/webhooks/quizzbuy",
    "events": ["trade_in.created", "trade_in.shipped", "trade_in.counter_accepted"]
  }'
ÉvénementDéclencheur
trade_in.createdNouvelle demande soumise par un particulier (statut SUBMITTED).
trade_in.shippedLe particulier a renseigné son suivi d'expédition.
trade_in.counter_acceptedLe particulier a accepté votre contre-offre.
trade_in.counter_declinedLe particulier a refusé votre contre-offre (retour appareil).
trade_in.cancelledReprise annulée.

Corps de la livraison (le contact du particulier n'y figure pas — récupérez-le via GET /api/v1/trade-ins/:id) :

{
  "trade_in_id": "…",
  "reference": "TI-2026-000042",
  "status": "SHIPPED",
  "device": {
    "brand": "Apple", "category": "SMARTPHONE", "model": "iPhone 15 Pro",
    "storage": "256 GB", "condition": "GOOD",
    "functional_status": "FULLY_WORKING", "battery_health": 92, "imei": "…"
  },
  "offer_cents": 52000,
  "currency": "EUR",
  "price_locked_until": "2026-08-05T12:00:00.000Z",
  "country": "FR"
}

14. Pousser votre grille de rachat

Alternative « push » au flux CSV tiré par nos soins : envoyez directement votre grille de prix de rachat. Chaque ligne porte les 4 montants selon l'état de l'appareil. Les prix alimentent la même table que la synchro automatique — ils apparaissent donc immédiatement dans le comparateur et servent de prix verrouillé pour la reprise gérée (§12). L'opération est un upsert par (marque, catégorie, modèle, capacité).

curl -X PUT https://quizzbuy.com/api/v1/buyback/prices \
  -H "Authorization: Bearer zzb_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "prices": [
      {
        "brand": "Apple",
        "category": "SMARTPHONE",
        "model": "iPhone 15 Pro",
        "storage": "256 GB",
        "priceNewCents": 60000,
        "priceGoodCents": 52000,
        "priceFairCents": 40000,
        "priceBrokenCents": 15000,
        "currency": "EUR",
        "url": "https://votre-site.fr/reprise/iphone-15-pro"
      }
    ]
  }'

# Réponse
{ "ok": true, "upserted": 1 }
  • prices : 1 à 2000 lignes par requête.
  • category : SMARTPHONE, TABLET, LAPTOP, SMARTWATCH, AUDIO ou GAMING.
  • priceNewCents (neuf), priceGoodCents (bon), priceFairCents (marqué), priceBrokenCents (cassé) — en centimes, ≥ 0.
  • storage et url facultatifs ; la capacité est normalisée automatiquement (ex. « 256go » → « 256 GB »).