Bulk API — Guia do Integrador
Guia de referência para quem integra sistemas externos (ERPs, marketplaces, PIMs) com a Bulk API da plataforma. Descreve o modelo de execução, o formato dos payloads, os recursos suportados e as garantias operacionais.
Sumário
- Visão geral
- Autenticação
- Endpoints
- Modelo assíncrono
- Formato do payload
- Resposta de submissão
- Consulta de status
Visão geral
A Bulk API permite submeter até 500 operações por requisição, em um único POST, que serão processadas de forma assíncrona. O retorno imediato contém um batch_id que o integrador usa para consultar o resultado individual de cada operação.
Principais características:
- Assíncrono por padrão: a resposta de submissão é 202 Accepted com
batch_id. - Processamento unitário: cada ação é processada em sua própria transação. Falhas em uma ação não invalidam o lote inteiro.
- Escopo multi-tenant: todas as operações são automaticamente filtradas pela
accountassociada aotoken+channelCodeinformados. - Concorrência controlada: locks por recurso + identificador + conta evitam que duas operações concorrentes se sobreponham no mesmo objeto.
- Idempotência opcional: através do campo
date, é possível descartar mensagens fora de ordem. - Status persistido por 24 horas: o resultado de cada batch fica disponível por 24 horas após a submissão.
Autenticação
Todos os endpoints exigem dois cabeçalhos:
| Header | Descrição |
|---|---|
token | API key do usuário API cadastrado na conta |
channelCode | Código do canal cuja conta executará as operações |
A requisição deve ser feita no domínio api.uoou.com.br (produção). Requisições em outros hostnames são redirecionadas para a documentação pública.
A combinação token + channelCode resolve, no lado do servidor, a account e o channel usados como contexto durante o processamento das ações.
Endpoints
POST /api/v1/bulk/
Recebe um array de ações, enfileira cada uma em background e retorna um batch_id.
GET /api/v1/bulk/{batchId}
Retorna o status consolidado e os resultados individuais de cada ação do lote.
Modelo assíncrono
POST /api/v1/bulk/
↓
gera batch_id (UUID v4)
cria metadata no Redis (TTL 1h)
publica N mensagens no RabbitMQ, uma por ação
↓
Response 202 Accepted { batch_id, total, message }
(processamento em background)
↓
Consumer → adquire lock → executa operação → libera lock → salva resultado no Redis
GET /api/v1/bulk/{batch_id}
↓
lê metadata + resultados do Redis
retorna status (pending | processing | completed) + results[]
O cliente deve fazer polling no endpoint de status até que completed === total (ou o TTL de 1h expire).
Formato do payload
{
"actions": [
{
"action": "create",
"resource": "product",
"data": { "sku": "ABC-001", "price": 9990, "translations": { "pt_BR": { "name": "Camiseta" } } }
},
{
"action": "update_price",
"resource": "product",
"data": { "sku": "ABC-001", "price": 7990 },
"date": "2026-04-15T12:00:00Z"
}
]
}
Campos da ação
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
action | string | sim | Nome da operação (ex: create, update_price). Lista completa em Recursos |
resource | string | sim | Recurso alvo: product, attribute, option, taxon |
data | object | sim | Payload específico da operação (SKU, valores, etc.) |
date | string | não | ISO 8601- Usado para controle de versão |
Valores monetários
Todos os campos de dinheiro (price, cost_price, promotional_price, fixed_discount, etc.) devem ser enviados em centavos como inteiro. Ex: R$ 99,90 → 9990.
Traduções
Campos traduzíveis viajam dentro de translations.{locale}:
{
"translations": {
"pt_BR": { "name": "Camiseta Básica", "description": "..." }
}
}
Envios parciais são permitidos — se o locale já existia, campos ausentes mantêm seus valores anteriores:
{ "translations": { "pt_BR": { "description": "Nova descrição" } } }
→ preserva name, atualiza description.
Para limpar um campo, envie explicitamente null:
{ "translations": { "pt_BR": { "description": null } } }
Resposta de submissão
HTTP 202 Accepted
{
"success": true,
"batch_id": "fb8b746d-7ac2-4c74-be8d-00bd9b06c357",
"total": 30,
"message": "Batch queued for processing."
}
HTTP 400 Bad Request — payload inválido ou acima do limite:
{
"error": true,
"code": 400,
"message": "The \"actions\" field is required and must be an array."
}
Consulta de status
GET /api/v1/bulk/{batch_id}
{
"batch_id": "fb8b746d-7ac2-4c74-be8d-00bd9b06c357",
"total": 30,
"completed": 28,
"status": "processing",
"created_at": "2026-04-21 14:30:00",
"results": [
{ "index": 0, "success": true, "data": { "id": 1041638, "sku": "ABC-001", "name": "Camiseta" } },
{ "index": 1, "error": true, "code": 409, "message": "A product with SKU \"ABC-001\" already exists." },
{ "index": 2, "success": true, "skipped": true, "message": "Operation skipped: a newer version was already processed." }
]
}
Estados possíveis
pending— lote foi criado, mas nenhuma ação foi processada aindaprocessing— algumas ações já processadas, outras pendentescompleted— todas as ações concluídas (sucesso ou erro individual)
Shape dos resultados
Cada entrada em results[] tem sempre o campo index (posição original da ação no payload). Além disso:
Sucesso:
{ "index": 0, "success": true, "data": { ... } }
Sucesso com skip (versão antiga descartada):
{ "index": 0, "success": true, "skipped": true, "message": "..." }
Erro de validação/negócio (4xx):
{ "index": 1, "error": true, "code": 409, "message": "...", "tips": "..." }
Erro interno (5xx) — mensagem sanitizada + error_id para rastreio:
{ "index": 2, "error": true, "code": 500, "message": "An internal error occurred while processing this action. Use the error_id to track this issue.", "error_id": "80a15f4b2bca46fba44eb0c24cbb0951" }
O error_id é utilizado para a equipe da Uoou Solutions identificar internamente os registros de erros.