Partner documentation
Everything you need to publish your sale and buyback listings on QuizzBuy and notify us of orders attributed to our traffic.
1. Get an API key
Contact your QuizzBuy representative (or the contact form). We generate a key for you in the format zzb_live_… — it is given to you only once, so keep it safe. Pass it on every request in the HTTP header:
Authorization: Bearer zzb_live_VOTRE_CLE2. Verify your key
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. Publish your listings
A listing is either a buyback offer (BUYBACK — you take back a device at a given price), or a sale offer (SALE — a refurbished product you're selling). The submission is an upsert: resending the same externalId updates the listing.
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 or GAMING.condition: LIKE_NEW, EXCELLENT, GOOD, FAIR or BROKEN.priceCents: price in cents (52000 = €520).- Each new listing (or edit) goes through moderation before publication.
Update, list or deactivate:
# 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. Click tracking
When a QuizzBuy visitor clicks through to your site, the landing URL contains a click identifier:
https://votre-site.fr/vendre?zzb_click=6f1e0c9a-3b2d-4e8f-9a10-abcdef123456Store this value (cookie or session on your side, recommended duration: 45 days). It is what allows the order to be attributed. The parameter name (zzb_click by default) is configurable on request.
5. Notify an order (S2S postback)
As soon as a customer places an order (a purchase or a confirmed buyback request) and you have a zzb_click, call our postback from your server:
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 }- Attribution window: 45 days after the click; beyond that, the response is
422 attribution_window_expired. order_refmust be unique: a duplicate returns409 duplicate_order_ref(idempotency — you can safely retry).type:SALEfor a purchase,BUYBACKfor a buyback.currency: EUR only — any other currency is rejected (422 unsupported_currency).
6. Test in sandbox
Add "test": true to the postback: the request is fully validated (key, click_id, 45-day window) but no conversion is recorded or billed.
{ "click_id": "…", "type": "SALE", "amount_cents": 10000, "order_ref": "TEST-1", "test": true }
# → 200 { "test": true, "valid": true, "commission_cents": 500 }7. Error codes
| Code | Error | Explanation |
|---|---|---|
| 400 | invalid_input | Invalid request body (details in the response). |
| 401 | unauthorized | API key missing, revoked, or invalid. |
| 404 | click_not_found / not_found | Unknown click_id or listing (or not yours). |
| 409 | duplicate_order_ref | Order already notified. |
| 422 | attribution_window_expired | Click older than 45 days. |
| 429 | rate_limited | Too many requests — retry in a minute. |
8. Billing
Each attributed conversion generates a commission (contractual rate, visible via /api/v1/me). QuizzBuy sends you a periodic summary invoice of validated conversions. For any question: contact us.
9. Marketplace — selling on QuizzBuy
SALE listings with a quantity > 0 are sold directly on QuizzBuy (customer payment handled by us, net of commission paid out to you). Additional fields: quantity (stock), color, grade (A/B/C), batteryHealth (%), warrantyMonths. The listing is automatically linked to the matching product page (brand + model + capacity + color).
# 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 — get notified of sales
Register an HTTPS endpoint; we notify you at every stage of an order containing your products (order.created, order.paid, order.cancelled). The secret is only returned at creation — store it.
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"Each delivery is signed. Check the X-ZZbuy-Signature header (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 minOn failure (≠ 2xx), we retry with backoff: 1 min, 5 min, 30 min, 2 h, 12 h. You also receive a sale notification email.
11. Process your orders
List your order lines, acknowledge receipt, then ship with a tracking number (the customer is notified automatically). The delivery address is only visible after payment.
# 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. Managed buyback (trade-in)
Managed buyback goes further than a simple redirect-based buyback: the individual submits their device from QuizzBuy, and you manage the entire case via the API (acceptance, receipt, counter-offer, payment). The price shown to the customer is locked at submission from your buyback price grid (see §14); you can only revise it downward after receipt, via a justified counter-offer.
All endpoints authenticate with your API key (Authorization: Bearer zzb_live_…) and only return your cases.
Lifecycle
SUBMITTED → ACCEPTED → SHIPPED → RECEIVED → PAID. Two branches: REJECTED (declined before shipping) and COUNTER_OFFER (counter-offer after inspection, which the individual accepts — then PAID — or declines — device returned, CANCELLED).
| Status | Meaning |
|---|---|
| SUBMITTED | Request created by the individual, awaiting your decision. |
| ACCEPTED | You have confirmed the buyback; the customer must ship the device. |
| SHIPPED | The individual has provided their tracking number. |
| RECEIVED | You have received the device; inspection in progress. |
| COUNTER_OFFER | After inspection, you propose a revised (lower) amount. |
| PAID | Payment made to the individual — case closed. |
| REJECTED | Request declined before shipping (not eligible, fraud…). |
| CANCELLED | Cancelled (counter-offer declined, device returned). |
List & retrieve
# 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(optional filter): a value from the table above — otherwise400 invalid_status.limit: max 200 (default 50);offsetfor pagination.- The
customer(individual's contact info) is only visible on this partner API, never in webhooks.
Transitions
Each action is a POST and returns { "ok": true, "trade_in": { … } }. A transition from an incompatible status returns 409 invalid_status (with the actual current). The individual is notified by email at each step.
# 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: only from
SUBMITTED.shippingLabelUrloptional (URL, ≤ 500 chars). - receive: from
SHIPPEDorACCEPTED(drop-off/shipping not declared by the customer). - counter: from
RECEIVED.amountCentsmust be strictly lower thanoffer_cents, otherwise400 counter_not_lower;reasonrequired (1–500 chars). The individual then accepts or declines from their tracking page. - pay: from
RECEIVED.payoutRefrequired (transfer reference, 1–120 chars). - reject: only from
SUBMITTED— no body required.
13. Buyback webhooks (trade_in.*)
The same webhooks (§10, identical X-ZZbuy-Signature signature) cover managed buyback. Subscribe to all or some of the trade_in.* events when creating an 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"]
}'| Event | Trigger |
|---|---|
| trade_in.created | New request submitted by an individual (SUBMITTED status). |
| trade_in.shipped | The individual has provided their shipping tracking number. |
| trade_in.counter_accepted | The individual has accepted your counter-offer. |
| trade_in.counter_declined | The individual has declined your counter-offer (device returned). |
| trade_in.cancelled | Buyback cancelled. |
Delivery body (the individual's contact info is not included — retrieve it 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. Push your buyback price grid
A "push" alternative to our CSV pull flow: send your buyback price grid directly. Each row carries the 4 amounts according to device condition. Prices feed the same table as the automatic sync — so they appear immediately in the comparator and serve as the locked price for managed buyback (§12). The operation is an upsert by (brand, category, model, capacity).
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 to 2000 rows per request.category: SMARTPHONE, TABLET, LAPTOP, SMARTWATCH, AUDIO or GAMING.priceNewCents(new),priceGoodCents(good),priceFairCents(fair),priceBrokenCents(broken) — in cents, ≥ 0.storageandurloptional; capacity is normalized automatically (e.g. « 256go » → « 256 GB »).