API pública

Documentação de integração

Referência para desenvolvedores, ERPs e ferramentas de IA integrarem sistemas ao TotalSeller HUB com mais clareza e menos tentativa e erro.

Contrato observado em 27 de junho de 2026
Visão geral

Introdução

A API pública permite que ERPs autorizados enviem dados cadastrais e comerciais, consultem pedidos importados de marketplaces e confirmem seu processamento. Todos os endpoints listados nesta página foram identificados como disponíveis no contrato atual.

A documentação descreve o comportamento confirmado no contrato em 27/06/2026. Não há versionamento no caminho atual, como /api/v1. Mudanças futuras serão refletidas nesta página.

Fluxo ERP → HUB → Marketplaces

  1. O ERP autentica cada requisição com as credenciais autorizadas.
  2. Dados cadastrais de produto são enviados para /api/products.
  3. Preço e estoque são enviados separadamente para /api/products/price-stock.
  4. O TotalSeller HUB controla o processamento entre a origem e os marketplaces.
  5. Pedidos disponíveis são consultados pelo ERP e confirmados após a importação.

O HUB é a camada de controle do fluxo. O integrador deve manter idempotência, tratar warnings e registrar as respostas relevantes para suporte operacional.

Base URL e formato

As rotas públicas usam o prefixo /api. A URL do ambiente autorizado é fornecida no processo de integração.

https://YOUR_BASE_URL/api/products

Envie Accept: application/json. Requisições com body JSON também devem enviar Content-Type: application/json. Filtros de GET são enviados por query string; POST, PUT e DELETE recebem JSON conforme cada contrato.

Autenticação

Todos os endpoints, exceto a renovação de credencial, exigem estes headers:

HeaderObrigatórioDescrição
Seller-IdSimIdentificador da credencial da API.
Access-TokenSimToken de acesso ativo e não expirado.
Accept: application/json
Content-Type: application/json
Seller-Id: YOUR_SELLER_ID
Access-Token: YOUR_ACCESS_TOKEN

Credenciais ausentes, inválidas, revogadas, inativas ou expiradas retornam HTTP 401.

{
  "status": "error",
  "code": 401,
  "message": "Credenciais inválidas",
  "data": null,
  "errors": {
    "credentials": "invalid"
  }
}
Nunca inclua tokens em repositórios, logs, tickets ou aplicações públicas. O API Console desta página mantém os valores somente na memória da aba e não usa localStorage.

Padrão de resposta JSON

A maioria dos endpoints responde com um envelope comum. O HTTP status acompanha o campo code.

{
  "status": "success",
  "code": 200,
  "message": "Request processed successfully.",
  "data": {},
  "errors": [],
  "warnings": []
}

Listagens paginadas também podem retornar:

{
  "pagination": {
    "current_page": 1,
    "per_page": 10,
    "total": 100,
    "total_pages": 10
  }
}

warnings sinaliza processamento parcial, dado ignorado ou uma situação que exige atenção. Uma resposta success com warnings não representa necessariamente sucesso integral.

Exceção confirmada: PUT /api/refresh_token usa success: true|false, sem o envelope padrão.

Boas práticas de integração

  • Use HTTPS e armazene credenciais em um cofre de segredos ou variável protegida do servidor.
  • Defina timeouts e trate separadamente falhas de rede, HTTP e validação.
  • Leia warnings mesmo quando status for success.
  • Implemente retries com backoff apenas em operações seguras e após avaliar a idempotência.
  • Use custom_code como vínculo estável do SKU controlado pelo ERP.
  • Não envie preço ou estoque no endpoint cadastral de produtos.
  • Não registre tokens nem respostas que contenham dados pessoais além do necessário.
  • Valide em ambiente autorizado antes de ativar rotinas em produção.
Catálogo

Produtos

GET/api/products
POST/api/products
PUT/api/products/{product_id}

Campos cadastrais

CampoTipoPOSTObservação
custom_codestringObrigatórioSKU controlado pelo ERP; define o upsert no POST.
parent_codestringObrigatórioAgrupa variações de um produto.
eanstringOpcionalCódigo EAN.
titlestringObrigatórioTítulo do SKU/produto.
descriptionstringOpcionalDescrição do produto.
category_codestringObrigatórioCategoria personalizada existente e final.
brand_codestringObrigatórioMarca personalizada existente.
cost_pricedecimalOpcionalCusto interno; não é preço de venda.
weight, height, width, lengthnumberOpcionalPeso e dimensões.
is_activeboolean/0/1OpcionalProduto é criado ativo por padrão.
imagesstring[]OpcionalHashes retornados pelo upload de imagens.
category, brandobjectOpcionalObjetos auxiliares para upsert; os códigos continuam obrigatórios.
price, base_price, campos de estoque, channels e price_tables são rejeitados com HTTP 422. Envie dados comerciais somente para /api/products/price-stock.

Buscar produtos

Filtros opcionais: custom_code, parent_code e integration_id. c003_id, C003_Id e account_id são aliases legados de integration_id. channel_id não seleciona uma integração com segurança; nesse caso o preço base é usado e a resposta inclui warning. Este endpoint não possui paginação.

curl -X GET 'https://YOUR_BASE_URL/api/products?custom_code=SKU-001&integration_id=12' \
  -H 'Accept: application/json' \
  -H 'Seller-Id: YOUR_SELLER_ID' \
  -H 'Access-Token: YOUR_ACCESS_TOKEN'

Na resposta, base_price é sempre o preço base. price usa a tabela ativa vinculada à integração quando aplicável; sem tabela válida, usa o preço base; tabela ativa sem preço para o produto retorna 0. O bloco channels é somente leitura.

Criar ou atualizar por custom_code

O POST é um upsert: cria quando custom_code não existe e atualiza quando já existe.

{
  "parent_code": "PRODUCT-001",
  "custom_code": "SKU-001",
  "ean": "7890000000001",
  "title": "Example Product",
  "description": "Example description",
  "category_code": "CATEGORY-001",
  "brand_code": "BRAND-001",
  "cost_price": 60,
  "weight": 0.5,
  "height": 10,
  "width": 20,
  "length": 30,
  "is_active": 1,
  "images": ["IMAGE_HASH_1"]
}

Atualizar por ID

product_id deve ser inteiro positivo. O payload é parcial: somente campos enviados são alterados.

{
  "title": "Updated Example Product",
  "cost_price": 65
}

Preço e estoque

POST/api/products/price-stock

Atualiza preço base, estoque de origem e tabelas de preço sem criar produtos. products e price_tables são opcionais individualmente, mas pelo menos um bloco não vazio deve ser enviado.

{
  "products": [
    {
      "custom_code": "SKU-001",
      "base_price": 110.94,
      "source_stock": 10
    }
  ],
  "price_tables": [
    {
      "table_code": "RETAIL",
      "table_name": "Retail price",
      "items": [
        {
          "custom_code": "SKU-001",
          "price": 110.94
        }
      ]
    }
  ]
}

Campos

CampoRegra
products[].custom_codeObrigatório; deve identificar produto existente.
products[].base_priceDecimal ≥ 0; preço base de venda.
products[].source_stockDecimal ≥ 0; recalcula estoque disponível.
price_tables[].table_codeObrigatório; string de até 80 caracteres.
price_tables[].table_nameObrigatório; string de até 120 caracteres.
price_tables[].itemsLista não vazia.
items[].custom_codeObrigatório; produto existente.
items[].priceObrigatório; decimal ≥ 0.

Cada item de products precisa conter base_price e/ou source_stock. Aliases como price, available_quantity, stock e quantity não são aceitos nesse bloco.

Regras comerciais

  • Preço base é o fallback quando não há tabela válida vinculada.
  • Tabela ativa sem preço para o produto resolve o preço como 0.
  • Atualizações são parciais; itens de tabela não enviados permanecem intactos.
  • Produto inexistente gera warning e não é criado.
  • O preço enviado ao marketplace pode receber ajustes configurados e proteção de markup mínimo.
  • Estoque disponível é max(source_stock - reserved_stock, 0).
O GET /api/products retorna a resolução base/tabela. Ajustes adicionais e markup mínimo pertencem ao fluxo de envio ao marketplace e não aparecem nesse campo.

Imagens

POST/api/images/upload

Importa uma ou várias imagens por URL. Cada item pode ser uma string ou um objeto {"url":"..."}.

{
  "images": [
    "https://cdn.example.com/product-1.jpg",
    { "url": "https://cdn.example.com/product-2.png" }
  ]
}

Regras confirmadas: apenas HTTP/HTTPS; hosts locais, privados ou reservados são bloqueados; credenciais embutidas na URL são bloqueadas; redirects não são seguidos; a resposta deve ser HTTP 200 com Content-Type: image/*; máximo de 10 MiB; largura mínima de 650 px. O retorno inclui hashes que podem ser usados em products[].images.

O processamento pode ser parcial e retornar warnings. Se nenhuma imagem puder ser processada, a API retorna HTTP 422.

Categorias personalizadas

GET/api/custom_categories?page=1&limit=10
POST/api/custom_categories
DELETE/api/custom_categories

A listagem aceita page (padrão 1) e limit (padrão 10). O POST aceita objeto ou lista e faz upsert por custom_code.

[
  {
    "custom_code": "CATEGORY-PARENT",
    "name": "Parent Category",
    "parent_code": ""
  },
  {
    "custom_code": "CATEGORY-CHILD",
    "name": "Child Category",
    "parent_code": "CATEGORY-PARENT"
  }
]

custom_code e name são obrigatórios. parent_code é opcional, mas o pai precisa existir antes do filho. Para excluir, envie {"custom_code":"CATEGORY-CHILD"}. A exclusão é bloqueada se a categoria não existir, tiver filhos ou estiver associada a produtos.

Categorias de canal

GET/api/channel_categories?channel_id=1
GET/api/channel_categories/children?channel_id=1&parent_code=CHANNEL-CATEGORY-001
POST/api/associate_categories

A raiz exige channel_id inteiro positivo. A busca de filhos também exige parent_code; não use o ID interno como pai.

[
  {
    "channel_id": 1,
    "category_id": "CHANNEL-CATEGORY-001",
    "custom_categories": [
      "CATEGORY-CHILD",
      "CATEGORY-OTHER"
    ]
  }
]
category_id, apesar do nome, recebe o código da categoria no marketplace. custom_categories recebe os códigos das categorias personalizadas.

Marcas personalizadas

GET/api/custom_brands?page=1&limit=10
POST/api/custom_brands
DELETE/api/custom_brands

A listagem é paginada. O POST aceita objeto ou lista e faz upsert por custom_code.

[
  {
    "custom_code": "BRAND-001",
    "name": "Example Brand"
  }
]

Para excluir, envie {"custom_code":"BRAND-001"}. A exclusão é bloqueada se a marca não existir ou estiver associada a produtos.

Marcas de canal

GET/api/channel_brands?channel_id=1
POST/api/associate_brands

A listagem exige channel_id inteiro positivo. O contrato da associação espera uma lista:

[
  {
    "channel_id": 1,
    "brand_id": 25,
    "custom_brands": ["BRAND-001"]
  }
]

brand_id é o ID retornado por GET /api/channel_brands. Duplicidades e referências inexistentes podem ser ignoradas com warnings.

Operação

Pedidos

GET/api/orders
GET/api/orders/{order_id}
POST/api/orders/{order_id}/acknowledge

Os endpoints permitem consultar pedidos já importados de marketplaces e confirmar o resultado da importação no ERP. Pedidos são limitados à integração resolvida para a credencial. Se a credencial não puder ser resolvida com segurança para uma única integração ativa, a API retorna HTTP 403 sem expor pedidos.

Listar pedidos disponíveis

Por padrão, retorna apenas pedidos com status PENDING_ERP. Quando não há registros, retorna HTTP 200 com data.orders: [].

QueryRegra
pagePadrão 1.
limitPadrão 50; máximo 100.
include_allQuando verdadeiro, libera filtros de outros status.
sync_statusAplicado somente com include_all.
hub_status, marketplace_statusFiltros de status.
marketplace_order_number, erp_order_numberFiltros por número.
date_from, date_toData do marketplace; aceita data ou datetime.
last_interaction_from, last_interaction_toJanela da última interação.
skuBusca por SKU do seller ou código do ERP.
curl -X GET 'https://YOUR_BASE_URL/api/orders?limit=50&sku=SKU-001' \
  -H 'Accept: application/json' \
  -H 'Seller-Id: YOUR_SELLER_ID' \
  -H 'Access-Token: YOUR_ACCESS_TOKEN'

Detalhe do pedido

order_id é o identificador retornado na listagem. A resposta pode incluir dados do pedido, marketplace, integração, empresa, comprador, status, valores, datas, itens, pacotes de envio e estado de sincronização. Um pedido fora do escopo da integração retorna 404.

Confirmar importação no ERP

Sucesso exige erp_order_number:

{
  "result": "success",
  "erp_order_number": "ERP-123456",
  "message": "Order imported successfully."
}

Erro exige message; erp_response é opcional e aceita string, objeto ou lista:

{
  "result": "error",
  "message": "Could not create the order in the ERP.",
  "erp_response": {
    "code": "CUSTOMER_VALIDATION_FAILED"
  }
}

O sucesso é idempotente quando repetido com o mesmo número ERP. Se o pedido já estiver confirmado com outro número, retorna HTTP 409. Pedidos ainda aguardando marketplace também retornam 409. Status aceitos: PENDING_ERP, ERROR_ERP e CONFIRMED_BY_ERP apenas para idempotência com o mesmo número.

Renovação de credencial

PUT/api/refresh_token

Não envia body e não usa Access-Token. Envie:

Seller-Id: YOUR_SELLER_ID
Refresh-Token: YOUR_REFRESH_TOKEN

A renovação substitui os tokens anteriores. No comportamento padrão atual, access tokens valem 7 dias e refresh tokens 90 dias; esses prazos podem variar por configuração.

{
  "success": true,
  "seller_id": "YOUR_SELLER_ID",
  "access_token": "NEW_ACCESS_TOKEN",
  "refresh_token": "NEW_REFRESH_TOKEN",
  "access_token_expires_at": "2026-07-07 12:00:00",
  "refresh_token_expires_at": "2026-09-28 12:00:00",
  "expires_in": 604800
}

Após mais de 10 tentativas em 5 minutos para a combinação de IP e Seller-Id, a API retorna HTTP 429. Tokens em texto puro são entregues somente na resposta de renovação; guarde-os com segurança.

Erros comuns

HTTPCausa comumAção
401Credencial ausente, inválida, revogada ou expirada.Confirme headers e renove a credencial quando aplicável.
403Credencial sem escopo seguro para a operação.Contate o suporte para revisar a autorização.
404Recurso não encontrado ou fora do escopo.Valide o identificador e o contexto da integração.
409Conflito de estado ou idempotência.Consulte o estado atual antes de repetir.
422Payload inválido ou campos no endpoint incorreto.Leia errors, corrija os campos e reenvie.
429Limite de tentativas de renovação atingido.Aguarde a janela e use backoff.
500Falha interna ou um comportamento conhecido do contrato atual.Registre horário e contexto sem tokens e acione o suporte.
{
  "status": "error",
  "code": 422,
  "message": "Campos obrigatórios não informados.",
  "data": null,
  "errors": {
    "custom_code": [
      "Campo obrigatório não informado: custom_code"
    ]
  }
}

Comportamentos conhecidos

Estes pontos estão confirmados no contrato atual e podem ser normalizados em versões futuras:

  1. Listagens vazias de categorias e marcas personalizadas podem retornar 404 em vez de 200 com lista vazia.
  2. O refresh token não usa o envelope padrão.
  3. Atualização de produto inexistente pode retornar 500 em vez de 404 ou 422.
  4. Exclusão de marca/categoria inexistente ou vinculada pode retornar 500.
  5. Associação de categoria usa código de marketplace em category_id; associação de marca usa ID em brand_id.
  6. Não existe contrato público confirmado de estoque independente por canal.
  7. A listagem de produtos não é paginada.
  8. O preço da consulta de produtos não inclui ajustes adicionais nem proteção de markup mínimo.

API Console

Use o console ao lado (ou abaixo, em telas menores) para montar comandos cURL e, quando o ambiente permitir CORS, enviar uma requisição diretamente do navegador.

Privacidade do console: Base URL, Seller-Id, tokens e payload permanecem somente nesta aba. O site não envia esses dados para um servidor próprio, não usa cookies para armazená-los e não usa localStorage.