Partnerdocumentatie

Alles wat u nodig heeft om uw verkoop- en inkoopadvertenties op QuizzBuy te publiceren en ons de bestellingen te melden die aan ons verkeer worden toegeschreven.

1. Een API-sleutel verkrijgen

Neem contact op met uw QuizzBuy-contactpersoon (of het contactformulier). Wij genereren voor u een sleutel in het formaat zzb_live_… — deze wordt u slechts één keer meegedeeld, bewaar hem op een veilige plek. Geef hem mee bij elke aanvraag in de HTTP-header:

Authorization: Bearer zzb_live_VOTRE_CLE

2. Uw sleutel controleren

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. Uw advertenties publiceren

Een advertentie is ofwel een inkoopaanbod (BUYBACK — u neemt een toestel terug tegen een bepaalde prijs), ofwel een verkoopaanbod (SALE — een gereviseerd product dat u verkoopt). Het verzenden is een upsert: hetzelfde externalId opnieuw verzenden werkt de advertentie bij.

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 of GAMING.
  • condition: LIKE_NEW, EXCELLENT, GOOD, FAIR of BROKEN.
  • priceCents: prijs in centen (52000 = 520 €).
  • Elke nieuwe advertentie (of wijziging) doorloopt moderatie vóór publicatie.

Bijwerken, weergeven of deactiveren:

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

Wanneer een QuizzBuy-bezoeker naar uw site doorklikt, bevat de landings-URL een click-ID:

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

Bewaar deze waarde (cookie of sessie aan de kant van uw site, aanbevolen duur: 45 dagen). Hiermee kan de bestelling worden toegeschreven. De naam van de parameter (zzb_click standaard) is op aanvraag configureerbaar.

5. Een bestelling melden (postback S2S)

Zodra een klant een bestelling plaatst (aankoop of gevalideerde inkoopaanvraag) en u over een zzb_click beschikt, roept u onze postback aan vanaf uw 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 }
  • Toeschrijvingsvenster: 45 dagen na de klik; daarna, antwoord 422 attribution_window_expired.
  • order_ref moet uniek zijn: een duplicaat geeft 409 duplicate_order_ref terug (idempotentie — u kunt zonder risico opnieuw proberen).
  • type: SALE voor een aankoop, BUYBACK voor een inkoop.
  • currency: alleen EUR — elke andere valuta wordt geweigerd (422 unsupported_currency).

6. Testen in sandbox

Voeg "test": true toe aan de postback: de aanvraag wordt volledig gevalideerd (sleutel, click_id, venster van 45 dagen) maar er wordt geen conversie geregistreerd of gefactureerd.

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

7. Foutcodes

CodeFoutUitleg
400invalid_inputOngeldige aanvraagbody (details in het antwoord).
401unauthorizedAPI-sleutel ontbreekt, ingetrokken of ongeldig.
404click_not_found / not_foundclick_id of advertentie onbekend (of niet van u).
409duplicate_order_refBestelling al gemeld.
422attribution_window_expiredKlik ouder dan 45 dagen.
429rate_limitedTe veel aanvragen — probeer het over een minuut opnieuw.

8. Facturatie

Elke toegeschreven conversie genereert een commissie (contractueel tarief, zichtbaar via /api/v1/me). QuizzBuy stuurt u periodiek een overzichtsfactuur van de gevalideerde conversies. Voor alle vragen: neem contact met ons op.

9. Marketplace — verkopen op QuizzBuy

Advertenties van het type SALE met een quantity > 0 worden rechtstreeks op QuizzBuy verkocht (klant betaalt bij ons, uitkering van het nettobedrag na commissie). Extra velden: quantity (voorraad), color, grade (A/B/C), batteryHealth (%), warrantyMonths. De advertentie wordt automatisch gekoppeld aan de bijbehorende productfiche (merk + model + capaciteit + kleur).

# 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 — op de hoogte blijven van verkopen

Registreer een HTTPS-endpoint; wij melden u elke stap van een bestelling met uw producten (order.created, order.paid, order.cancelled). Het geheim wordt alleen bij aanmaak teruggegeven — bewaar het.

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"

Elke levering is ondertekend. Controleer de header 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

Bij mislukking (≠ 2xx), proberen wij opnieuw met backoff: 1 min, 5 min, 30 min, 2 u, 12 u. U ontvangt ook een e-mailmelding van de verkoop.

11. Uw bestellingen verwerken

Bekijk uw bestelregels, bevestig ontvangst en verzend vervolgens met een trackingnummer (de klant wordt automatisch op de hoogte gebracht). Het afleveradres is pas zichtbaar na betaling.

# 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. Beheerde inkoop (trade-in)

De beheerde inkoop gaat verder dan de eenvoudige inkoop via doorverwijzing: de particulier dient zijn toestel in via QuizzBuy, en u beheert het volledige dossier via de API (acceptatie, ontvangst, tegenbod, betaling). De prijs die aan de klant wordt getoond, is vergrendeld bij indiening op basis van uw inkoopprijslijst (zie §14); u kunt deze pas na ontvangst naar beneden bijstellen via een gemotiveerd tegenbod.

Alle endpoints authenticeren met uw API-sleutel (Authorization: Bearer zzb_live_…) en geven alleen uw dossiers terug.

Levenscyclus

SUBMITTEDACCEPTEDSHIPPEDRECEIVEDPAID. Twee vertakkingen: REJECTED (weigering vóór verzending) en COUNTER_OFFER (tegenbod na inspectie, dat de particulier accepteert — vervolgens PAID — of weigert — toestel wordt teruggestuurd, CANCELLED).

StatusBetekenis
SUBMITTEDAanvraag aangemaakt door de particulier, in afwachting van uw beslissing.
ACCEPTEDU heeft de inkoop bevestigd; de klant moet het toestel verzenden.
SHIPPEDDe particulier heeft zijn trackingnummer ingevoerd.
RECEIVEDU heeft het toestel ontvangen; inspectie loopt.
COUNTER_OFFERNa inspectie stelt u een herzien (lager) bedrag voor.
PAIDBetaling aan de particulier uitgevoerd — dossier afgesloten.
REJECTEDAanvraag geweigerd vóór verzending (niet in aanmerking komend, fraude…).
CANCELLEDGeannuleerd (weigering van een tegenbod, toestel teruggestuurd).

Weergeven & raadplegen

# 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 (optioneel filter): een waarde uit de bovenstaande tabel — anders 400 invalid_status.
  • limit: max 200 (standaard 50); offset voor paginering.
  • De customer (contactgegevens van de particulier) is alleen zichtbaar via deze partner-API, nooit in de webhooks.

Overgangen

Elke actie is een POST en geeft { "ok": true, "trade_in": { … } } terug. Een overgang vanuit een incompatibele status geeft 409 invalid_status terug (met de werkelijke current). De particulier wordt bij elke stap per e-mail op de hoogte gebracht.

# 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: alleen vanuit SUBMITTED. shippingLabelUrl optioneel (URL, ≤ 500 tekens).
  • receive: vanuit SHIPPED of ACCEPTED (verzending niet door de klant gemeld).
  • counter: vanuit RECEIVED. amountCents moet strikt lager zijn dan offer_cents, anders 400 counter_not_lower; reason verplicht (1–500 tekens). Vervolgens accepteert of weigert de particulier vanaf zijn volgpagina.
  • pay: vanuit RECEIVED. payoutRef verplicht (overschrijvingsreferentie, 1–120 tekens).
  • reject: alleen vanuit SUBMITTED — geen body vereist.

13. Webhooks inkoop (trade_in.*)

Dezelfde webhooks (§10, identieke X-ZZbuy-Signature-ondertekening) dekken de beheerde inkoop. Abonneer u bij het aanmaken van een endpoint op alle of een deel van de trade_in.*-gebeurtenissen:

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"]
  }'
GebeurtenisTrigger
trade_in.createdNieuwe aanvraag ingediend door een particulier (status SUBMITTED).
trade_in.shippedDe particulier heeft zijn verzendtracking ingevoerd.
trade_in.counter_acceptedDe particulier heeft uw tegenbod geaccepteerd.
trade_in.counter_declinedDe particulier heeft uw tegenbod geweigerd (toestel teruggestuurd).
trade_in.cancelledInkoop geannuleerd.

Inhoud van de levering (de contactgegevens van de particulier staan er niet in — haal ze op 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. Uw inkoopprijslijst pushen

Een "push"-alternatief voor de door ons getrokken CSV-flow: stuur uw inkoopprijslijst rechtstreeks door. Elke regel bevat de 4 bedragen naargelang de staat van het toestel. De prijzen voeden dezelfde tabel als de automatische synchronisatie — ze verschijnen dus meteen in de vergelijker en dienen als vergrendelde prijs voor de beheerde inkoop (§12). De bewerking is een upsert per (merk, categorie, model, capaciteit).

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 tot 2000 regels per aanvraag.
  • category: SMARTPHONE, TABLET, LAPTOP, SMARTWATCH, AUDIO of GAMING.
  • priceNewCents (nieuw), priceGoodCents (goed), priceFairCents (gebruikssporen), priceBrokenCents (kapot) — in centen, ≥ 0.
  • storage en url optioneel; de capaciteit wordt automatisch genormaliseerd (bijv. « 256go » → « 256 GB »).