Documentação de parceiros

Tudo o que precisa para publicar os seus anúncios de venda e retoma na QuizzBuy e para nos notificar das encomendas atribuídas ao nosso tráfego.

1. Obter uma chave API

Contacte o seu interlocutor QuizzBuy (ou o formulário de contacto). Geramos para si uma chave no formato zzb_live_… — é-lhe comunicada apenas uma vez, guarde-a em local seguro. Envie-a em cada pedido no cabeçalho HTTP:

Authorization: Bearer zzb_live_VOTRE_CLE

2. Verificar a sua chave

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. Publicar os seus anúncios

Um anúncio é uma oferta de retoma (BUYBACK — recompra um aparelho a um determinado preço) ou uma oferta de venda (SALE — um produto recondicionado que vende). O envio é um upsert: reenviar o mesmo externalId atualiza o anúncio.

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: preço em cêntimos (52000 = 520 €).
  • Cada novo anúncio (ou alteração) passa por moderação antes da publicação.

Atualizar, listar ou desativar:

# 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. Acompanhamento de cliques

Quando um visitante QuizzBuy clica para o seu site, o URL de destino contém um identificador de clique:

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

Guarde este valor (cookie ou sessão do seu lado, duração recomendada: 45 dias). É este valor que permitirá atribuir a encomenda. O nome do parâmetro (zzb_click por defeito) é configurável a pedido.

5. Notificar uma encomenda (postback S2S)

Assim que um cliente faz uma encomenda (compra ou pedido de retoma validado) e dispõe de um zzb_click, chame o nosso postback a partir do seu servidor:

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 }
  • Janela de atribuição: 45 dias após o clique; depois disso, resposta 422 attribution_window_expired.
  • order_ref deve ser único: um duplicado devolve 409 duplicate_order_ref (idempotência — pode tentar novamente sem risco).
  • type: SALE para uma compra, BUYBACK para uma retoma.
  • currency: apenas EUR — qualquer outra moeda é recusada (422 unsupported_currency).

6. Testar em sandbox

Adicione "test": true ao postback: o pedido é totalmente validado (chave, click_id, janela de 45 dias) mas nenhuma conversão é registada nem faturada.

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

7. Códigos de erro

CódigoErroExplicação
400invalid_inputCorpo do pedido inválido (detalhes na resposta).
401unauthorizedChave API ausente, revogada ou inválida.
404click_not_found / not_foundclick_id ou anúncio desconhecido (ou não é seu).
409duplicate_order_refEncomenda já notificada.
422attribution_window_expiredClique com mais de 45 dias.
429rate_limitedDemasiados pedidos — tente novamente dentro de um minuto.

8. Faturação

Cada conversão atribuída gera uma comissão (taxa contratual, visível em /api/v1/me). A QuizzBuy envia-lhe uma fatura recapitulativa periódica das conversões validadas. Para qualquer questão: contacte-nos.

9. Marketplace — vender na QuizzBuy

Os anúncios SALE com quantity > 0 são vendidos diretamente na QuizzBuy (pagamento do cliente connosco, repasse do líquido de comissão). Campos adicionais: quantity (stock), color, grade (A/B/C), batteryHealth (%), warrantyMonths. O anúncio é associado automaticamente à ficha de produto correspondente (marca + modelo + capacidade + cor).

# 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 — ser avisado das vendas

Registe um endpoint HTTPS; notificamo-lo a cada etapa de uma encomenda que contenha os seus produtos (order.created, order.paid, order.cancelled). O segredo só é devolvido na criação — guarde-o.

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"

Cada entrega é assinada. Verifique o cabeçalho 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

Em caso de falha (≠ 2xx), tentamos novamente com backoff: 1 min, 5 min, 30 min, 2 h, 12 h. Também recebe um email de notificação de venda.

11. Processar as suas encomendas

Liste as suas linhas de encomenda, confirme a receção e depois envie com um número de seguimento (o cliente é notificado automaticamente). O endereço de entrega só é visível após o pagamento.

# 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. Retoma gerida (trade-in)

A retoma gerida vai mais além do que a simples retoma por redirecionamento: o particular submete o seu aparelho a partir da QuizzBuy, e o parceiro gere todo o processo através da API (aceitação, receção, contraproposta, pagamento). O preço apresentado ao cliente fica bloqueado no momento da submissão de acordo com a sua grelha de retoma (ver §14); só pode revê-lo em baixa após a receção, através de uma contraproposta fundamentada.

Todos os endpoints autenticam-se com a sua chave API (Authorization: Bearer zzb_live_…) e devolvem apenas os seus processos.

Ciclo de vida

SUBMITTEDACCEPTEDSHIPPEDRECEIVEDPAID. Duas ramificações: REJECTED (recusa antes do envio) e COUNTER_OFFER (contraproposta após inspeção, que o particular aceita — depois PAID — ou recusa — devolução do aparelho, CANCELLED).

EstadoSignificado
SUBMITTEDPedido criado pelo particular, aguardando a sua decisão.
ACCEPTEDConfirmou a retoma; o cliente deve enviar o aparelho.
SHIPPEDO particular indicou o seu número de seguimento.
RECEIVEDRecebeu o aparelho; inspeção em curso.
COUNTER_OFFERApós inspeção, propõe um montante revisto (inferior).
PAIDPagamento efetuado ao particular — processo encerrado.
REJECTEDPedido recusado antes do envio (não elegível, fraude…).
CANCELLEDCancelada (recusa de uma contraproposta, devolução do aparelho).

Listar & consultar

# 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 (filtro opcional): um valor da tabela acima — caso contrário 400 invalid_status.
  • limit: máx. 200 (padrão 50); offset para a paginação.
  • O customer (contacto do particular) só é visível nesta API de parceiro, nunca nos webhooks.

Transições

Cada ação é um POST e devolve { "ok": true, "trade_in": { … } }. Uma transição a partir de um estado incompatível devolve 409 invalid_status (com o current real). O particular é notificado por email em cada etapa.

# 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: apenas a partir de SUBMITTED. shippingLabelUrl opcional (URL, ≤ 500 carateres).
  • receive: a partir de SHIPPED ou ACCEPTED (depósito/envio não declarado pelo cliente).
  • counter: a partir de RECEIVED. amountCents deve ser estritamente inferior a offer_cents, caso contrário 400 counter_not_lower; reason obrigatório (1–500 carateres). É depois o particular que aceita ou recusa a partir da sua página de acompanhamento.
  • pay: a partir de RECEIVED. payoutRef obrigatório (referência da transferência, 1–120 carateres).
  • reject: apenas a partir de SUBMITTED — nenhum corpo necessário.

13. Webhooks de retoma (trade_in.*)

Os mesmos webhooks (§10, assinatura X-ZZbuy-Signature idêntica) cobrem a retoma gerida. Subscreva a totalidade ou parte dos eventos trade_in.* na criação de um 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"]
  }'
EventoGatilho
trade_in.createdNovo pedido submetido por um particular (estado SUBMITTED).
trade_in.shippedO particular indicou o seu seguimento de expedição.
trade_in.counter_acceptedO particular aceitou a sua contraproposta.
trade_in.counter_declinedO particular recusou a sua contraproposta (devolução do aparelho).
trade_in.cancelledRetoma cancelada.

Corpo da entrega (o contacto do particular não consta aqui — obtenha-o 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. Enviar a sua grelha de retoma

Alternativa «push» ao fluxo CSV puxado por nós: envie diretamente a sua grelha de preços de retoma. Cada linha tem os 4 montantes consoante o estado do aparelho. Os preços alimentam a mesma tabela que a sincronização automática — aparecem assim imediatamente no comparador e servem de preço bloqueado para a retoma gerida (§12). A operação é um upsert por (marca, categoria, modelo, capacidade).

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 a 2000 linhas por pedido.
  • category: SMARTPHONE, TABLET, LAPTOP, SMARTWATCH, AUDIO ou GAMING.
  • priceNewCents (novo), priceGoodCents (bom), priceFairCents (marcado), priceBrokenCents (partido) — em cêntimos, ≥ 0.
  • storage e url opcionais; a capacidade é normalizada automaticamente (ex.: «256go» → «256 GB»).