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_CLE2. 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-abcdef123456Stockez 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_refdoit être unique : un doublon renvoie409 duplicate_order_ref(idempotence — vous pouvez ré-essayer sans risque).type:SALEpour un achat,BUYBACKpour 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
| Code | Erreur | Explication |
|---|---|---|
| 400 | invalid_input | Corps de requête invalide (détails dans la réponse). |
| 401 | unauthorized | Clé API absente, révoquée ou invalide. |
| 404 | click_not_found / not_found | click_id ou annonce inconnue (ou pas la vôtre). |
| 409 | duplicate_order_ref | Commande déjà notifiée. |
| 422 | attribution_window_expired | Clic de plus de 45 jours. |
| 429 | rate_limited | Trop 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 minEn 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
SUBMITTED → ACCEPTED → SHIPPED → RECEIVED → PAID. 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).
| Statut | Signification |
|---|---|
| SUBMITTED | Demande créée par le particulier, en attente de votre décision. |
| ACCEPTED | Vous avez confirmé la reprise ; le client doit expédier l'appareil. |
| SHIPPED | Le particulier a renseigné son numéro de suivi. |
| RECEIVED | Vous avez réceptionné l'appareil ; inspection en cours. |
| COUNTER_OFFER | Après inspection, vous proposez un montant révisé (inférieur). |
| PAID | Paiement effectué au particulier — dossier clos. |
| REJECTED | Demande refusée avant envoi (non éligible, fraude…). |
| CANCELLED | Annulé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 — sinon400 invalid_status.limit: max 200 (défaut 50) ;offsetpour 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.shippingLabelUrlfacultatif (URL, ≤ 500 car.). - receive : depuis
SHIPPEDouACCEPTED(dépôt/envoi non déclaré par le client). - counter : depuis
RECEIVED.amountCentsdoit être strictement inférieur àoffer_cents, sinon400 counter_not_lower;reasonobligatoire (1–500 car.). C'est ensuite le particulier qui accepte ou refuse depuis sa page de suivi. - pay : depuis
RECEIVED.payoutRefobligatoire (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énement | Déclencheur |
|---|---|
| trade_in.created | Nouvelle demande soumise par un particulier (statut SUBMITTED). |
| trade_in.shipped | Le particulier a renseigné son suivi d'expédition. |
| trade_in.counter_accepted | Le particulier a accepté votre contre-offre. |
| trade_in.counter_declined | Le particulier a refusé votre contre-offre (retour appareil). |
| trade_in.cancelled | Reprise 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.storageeturlfacultatifs ; la capacité est normalisée automatiquement (ex. « 256go » → « 256 GB »).