Moderação em lote
Aprova ou recusa vários produtos de uma vez, por lista de ids ou por filtro com simulação e confirmação.
Quando a fila tem dezenas de produtos parecidos, decida todos de uma vez. Existem três rotas com o mesmo efeito de aprovar e recusar um produto, repetido para cada item: uma genérica (/bulk, com a ação no corpo) e duas dedicadas (/bulk-approve, /bulk-reject). Cada produto passa pelos mesmos efeitos da operação individual (aviso ao seller, reindexação, ligação ao catálogo mestre).
Há dois jeitos de dizer quais produtos:
- Lista de ids, resposta imediata.
- Filtro, em duas chamadas: primeiro você simula e recebe o total, uma amostra e um
confirm_token; depois repete o mesmo corpo com o token para executar.
Aprovar ou recusar em lote (ação no corpo)
/api/v1/admin/moderation/bulkAdminAprova ou recusa vários produtos
Corpo da requisição
product_idsarrayobrigatóriofilter.actionstringobrigatórioapproverejectreasonstringaction é reject. Aceita também rejected_reason e notes.filterobjectsimulatebooleantrue só calcula e devolve o total, a amostra e o confirm_token, sem alterar nada.confirm_tokenstring{ "message": "bulk moderation complete", "action": "approve", "requested": 3, "updated": 2 }Aprovar em lote
/api/v1/admin/moderation/bulk-approveAdminAprova vários produtos, com resultado por id
Corpo da requisição
idsarrayobrigatórioproduct_ids como apelido antigo. Obrigatório quando não há filter.filterobjectsimulatebooleanconfirm_tokenstringRecusar em lote
/api/v1/admin/moderation/bulk-rejectAdminRecusa vários produtos, com resultado por id
Corpo da requisição
idsarrayobrigatórioproduct_ids. Obrigatório quando não há filter.rejected_reasonstringobrigatórioreason e notes. A checagem é feita antes de qualquer alteração.filterobjectsimulatebooleanconfirm_tokenstringExemplo de requisição
curl -X POST "$BASE_URL/api/v1/admin/moderation/bulk-reject" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ids": ["d8a6c5e7-8e44-4055-a0e2-49014f5d17f6", "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d"], "rejected_reason": "Fotos fora do padrão."}'Resposta
{
"action": "reject",
"requested": 2,
"updated": 1,
"results": [
{ "id": "d8a6c5e7-8e44-4055-a0e2-49014f5d17f6", "status": "ok" },
{ "id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d", "status": "error", "error": "product not found: 0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d" }
]
}Campos da resposta
actionstringapproverejectrequestedintegerupdatedintegerresultsarraybulk-approve e bulk-reject.results[].statusstringokerrorresults[].errorstringstatus é error.O HTTP é 200 mesmo quando alguns itens falham. Confira updated e results.
Lote por filtro (simular e confirmar)
Para decidir "tudo o que casa com um critério" sem listar ids, mande filter no lugar da lista. O lote por filtro só alcança produtos que aguardam moderação (R) e nunca executa sem uma simulação antes.
- Simule
Mande o corpo com
filtere"simulate": true. A resposta traztotal, umasample, oconfirm_token(válido por cerca de uma hora, vejaexpires_at) easync. - Mostre ao usuário
Confirme o total e a amostra com quem vai decidir.
- Execute
Repita exatamente o mesmo corpo, sem
simulate, comconfirm_token. Se o recorte cresceu ou o corpo mudou, o token é recusado.
O filtro aceita chaves como name_contains, sku, sku_contains, brand, category_id, category_tree, vendor_id, price_min, price_max (preços em centavos), created_after, created_before, ids, exclude_ids e all (true para a tabela inteira). Uma chave desconhecida volta 400 invalid_filter listando as chaves aceitas, e um filtro vazio também é recusado.
{
"simulate": true,
"action": "approve",
"description": "Aprovar produtos em moderação",
"total": 42,
"sample": [],
"confirm_token": "eyJ0IjoiZGZlMjU1NWUtYmZkMC00MWE4...",
"expires_at": "2026-10-11T22:40:35Z",
"async": false,
"by": "filter"
}Na execução por filtro o corpo da resposta tem action, by: "filter", total_requested, status, affected e failed (lista dos itens que falharam). Lotes grandes viram uma tarefa em segundo plano: a resposta é 202 com async: true e o objeto job.
Erros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 400 | Lista vazia (product_ids/ids ausentes). | {"error":"product_ids is required"} em /bulk, {"error":"ids is required"} nas rotas dedicadas. |
| 400 | action inválida em /bulk. | {"error":"action must be 'approve' or 'reject'"} |
| 400 | Recusa sem motivo. | {"error":"rejected_reason is required"} |
| 400 | Filtro inválido ou vazio. | {"error":"invalid_filter","detail":"..."} |
| 409 | confirm_token de outro corpo, ou o recorte cresceu desde a simulação. | {"error":"confirm_token_invalid","detail":"..."} |
| 428 | Filtro executado sem simulação prévia. | {"error":"simulate_required","detail":"..."} |
| 401 / 403 | Sem token, módulo desligado ou sem privilégio. | {"error":"module_disabled","module":"product-moderation"} |