Pular para o conteúdo
Rockbase

API

Para quem prefere configurar pelo próprio programa: a agência do lojista, o ERP dele, um script que roda toda noite. A chave é da loja, e é o lojista quem a cria e quem a cancela — nós nunca guardamos a chave, só um resumo dela que não dá para desfazer.

Começando

  1. Entre no painel, vá em Conta e clique em "Criar chave".
  2. Dê um nome e escolha o que ela pode fazer. Uma chave que só lê um aplicativo não escreve em outro.
  3. Copie a chave. Ela aparece uma vez só — depois nem nós conseguimos vê-la.
  4. Mande a chave em todo pedido, no cabeçalho Authorization.
curl -H "Authorization: Bearer rbk_..." \
  https://api.rockbase.com.br/api/v1/produtos

É uma API de servidor. Não mandamos cabeçalho de CORS de propósito: uma chave de loja dentro do JavaScript de uma página é uma chave vazada.

As rotas

Exemplos em

GET/api/v1/produtos

O catálogo como o app o vê: fotos, atributos, variações e a sugestão pronta.

Precisa do escopo qualquer um de leitura

  • Só produtos publicados. Produto escondido não está na vitrine, então configurá-lo não mudaria nada lá.
  • Cada produto vem com sugestao: o mapa que nós montaríamos a partir da foto principal de cada variação — { "atributo": "Cor", "fotos": { "Azul": ["987"] } }. Vem null quando não há de onde tirar.
  • A resposta traz tem_mais; enquanto ele for verdadeiro, peça a página seguinte.
q texto
Busca por nome. Vazio traz a loja inteira.
pagina número
A partir de 1.
por_pagina número
Até 200. O padrão é 50.
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.rockbase.com.br/api/v1/produtos?por_pagina=200"

GET/api/v1/galeria/config

Tudo que está configurado, no mesmo formato que o PUT aceita de volta.

Precisa do escopo galeria:ler

  • Um item por foto, com as mesmas colunas da planilha. O que sai daqui volta para cá sem tradução.
  • Loja sem nada configurado devolve { "ok": true, "itens": [] }.
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.rockbase.com.br/api/v1/galeria/config"

PUT/api/v1/galeria/config

Substitui a configuração inteira. O produto que não vier no corpo perde o mapa.

Precisa do escopo galeria:escrever

  • Tudo ou nada: se qualquer item for inválido, nenhum é aplicado e a resposta traz a lista de erros com o índice de cada um.
  • A cota do plano vale igual. Se o lote não couber, a resposta é 402 e nada é gravado.
  • A resposta traz importacao: o id para acompanhar o lote, e estado, que num lote pequeno já vem concluida.
  • A loja é publicada uma vez, no fim.
curl -X PUT "https://api.rockbase.com.br/api/v1/galeria/config" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "itens": [
      {
        "produto_id": "123",
        "foto_id": "987",
        "atributo": "Cor",
        "valor": "Azul"
      },
      {
        "produto_id": "123",
        "foto_id": "988",
        "atributo": "Cor",
        "valor": "Preto"
      }
    ]
  }'

PATCH/api/v1/galeria/config

Muda só o que veio, sem tocar no resto. É a promessa da planilha.

Precisa do escopo galeria:escrever

  • Produto ausente do corpo continua exatamente como está. Para apagar, mande o item com "apagar": "sim".
  • Fora isso, o comportamento é o mesmo do PUT: tudo ou nada, cota antes de gravar, uma publicação no fim.
curl -X PATCH "https://api.rockbase.com.br/api/v1/galeria/config" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "itens": [
      {
        "produto_id": "123",
        "foto_id": "987",
        "atributo": "Cor",
        "valor": "Vermelho"
      }
    ]
  }'

GET/api/v1/agrupador/config

Os grupos de produtos irmãos, no mesmo formato que o PUT aceita de volta.

Precisa do escopo agrupador:ler

  • Um item por produto agrupado, com as mesmas colunas da planilha: grupo, atributo, valor e ordem.
  • Linhas com o mesmo grupo são o mesmo grupo — é o rótulo que une os irmãos.
  • Loja sem nada configurado devolve { "ok": true, "itens": [] }.
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.rockbase.com.br/api/v1/agrupador/config"

PUT/api/v1/agrupador/config

Substitui a configuração inteira. O produto que não vier no corpo sai do grupo.

Precisa do escopo agrupador:escrever

  • Itens que repetem o mesmo grupo formam um grupo, todos com a mesma atributo. Cada produto entra em um grupo só.
  • Tudo ou nada: se qualquer item for inválido, nenhum é aplicado e a resposta traz cada erro com o índice do item.
  • A cota do plano vale igual. Se o lote não couber, a resposta é 402 e nada é gravado.
  • A loja é publicada uma vez, no fim.
curl -X PUT "https://api.rockbase.com.br/api/v1/agrupador/config" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "itens": [
      {
        "produto_id": "101",
        "grupo": "tapete-kodi",
        "atributo": "Medida",
        "valor": "2,00x3,00m",
        "ordem": 0
      },
      {
        "produto_id": "102",
        "grupo": "tapete-kodi",
        "atributo": "Medida",
        "valor": "2,50x3,00m",
        "ordem": 1
      },
      {
        "produto_id": "103",
        "grupo": "tapete-kodi",
        "atributo": "Medida",
        "valor": "2,00x2,50m",
        "ordem": 2
      }
    ]
  }'

PATCH/api/v1/agrupador/config

Muda só o que veio, sem tocar no resto. É a promessa da planilha.

Precisa do escopo agrupador:escrever

  • Produto ausente do corpo continua como está — inclusive os outros produtos do mesmo grupo.
  • Para tirar um produto do grupo, mande o item com "apagar": "sim".
  • Produto que não existe mais na loja vira aviso no relatório, nunca recusa o lote.
curl -X PATCH "https://api.rockbase.com.br/api/v1/agrupador/config" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "itens": [
      {
        "produto_id": "102",
        "grupo": "tapete-kodi",
        "atributo": "Medida",
        "valor": "2,50x3,00m"
      }
    ]
  }'

GET/api/v1/banners/config

As seções da loja, inteiras, no mesmo formato que o PUT aceita de volta.

Precisa do escopo banners:ler

  • Cada seção sai completa: modelo, ordem, ligada, alcance e conteudo — inclusive os ids das imagens.
  • O alcance é a condição do motor de regras, o mesmo JSON das etiquetas: { "kind": "inCategory", "categoryId": "123" }, { "kind": "hasTag", "tag": "promo" }, { "kind": "all", "conditions": [] } para todos.
  • Loja sem nada configurado devolve { "ok": true, "secoes": [] }.
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.rockbase.com.br/api/v1/banners/config"

PUT/api/v1/banners/config

Substitui o conjunto. A seção que não vier no corpo sai, e as imagens dela vão para o expurgo de 30 dias.

Precisa do escopo banners:escrever

  • O id de cada seção é seu: é ele que o PATCH e o GET usam depois. Letras, números, hífen e sublinhado, até 64.
  • As imagens sobem ANTES, por /api/v1/banners/imagens — o corpo daqui só referencia os ids.
  • Tudo ou nada: um item inválido recusa o corpo inteiro, com o índice e a frase de cada erro.
  • O teto de seções publicadas do plano vale no estado final. Se não couber, 402 e nada é gravado.
  • A loja é publicada uma vez, no fim.
curl -X PUT "https://api.rockbase.com.br/api/v1/banners/config" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "secoes": [
      {
        "id": "colecao-verao",
        "modelo": "faixa",
        "ordem": 0,
        "ligada": true,
        "alcance": {
          "kind": "inCategory",
          "categoryId": "123456"
        },
        "conteudo": {
          "imagens": {
            "computador": {
              "imagemId": "a1b2c3",
              "proporcao": {
                "tipo": "lista",
                "valor": "3/1"
              }
            }
          },
          "textoAlternativo": "Coleção de verão"
        }
      }
    ]
  }'

PATCH/api/v1/banners/config

Cria ou atualiza só as seções que vieram, sem tocar no resto.

Precisa do escopo banners:escrever

  • Seção ausente do corpo continua exatamente como está — é a promessa da planilha, cumprida na API.
  • Para trocar só o alcance de uma seção, mande a seção inteira com o alcance novo: o GET devolve o formato pronto para editar.
curl -X PATCH "https://api.rockbase.com.br/api/v1/banners/config" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "secoes": [
      {
        "id": "colecao-verao",
        "modelo": "faixa",
        "ordem": 0,
        "ligada": true,
        "alcance": {
          "kind": "inCategory",
          "categoryId": "123456"
        },
        "conteudo": {
          "imagens": {
            "computador": {
              "imagemId": "a1b2c3",
              "proporcao": {
                "tipo": "lista",
                "valor": "3/1"
              }
            }
          },
          "textoAlternativo": "Coleção de verão"
        }
      }
    ]
  }'

POST/api/v1/banners/imagens

Reserva o espaço e devolve uma URL assinada por largura. O arquivo não passa por aqui.

Precisa do escopo banners:escrever

  • São dois passos: este POST reserva a cota e assina as URLs; depois, um PUT do arquivo em cada URL, direto na nossa CDN, com o Content-Type igual ao declarado.
  • Reduza antes de enviar: papel "computador" espera as larguras 1600, 1100 e 750; "celular", 1100 e 750. Mande o tamanho em bytes de cada uma.
  • O teto é 5 MB por arquivo, e os formatos são JPEG, PNG e WebP — GIF e SVG são recusados com o porquê, como no painel.
  • Sem espaço na conta, 402 — e nada do que já está no ar sai do ar.
  • A resposta traz o id da imagem, que é o que a seção referencia em imagemId.
curl -X POST "https://api.rockbase.com.br/api/v1/banners/imagens" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "papel": "computador",
    "tipo": "image/webp",
    "arquivos": [
      {
        "largura": 1600,
        "bytes": 220000
      },
      {
        "largura": 1100,
        "bytes": 70000
      },
      {
        "largura": 750,
        "bytes": 17000
      }
    ]
  }'

GET/api/v1/importacoes/{id}

O estado e o relatório de um lote.

Precisa do escopo qualquer um de leitura

  • O id vem no campo importacao da resposta do PUT ou do PATCH.
  • estado é concluida, processando ou falhou. Um lote muito grande continua sozinho e vira concluida sem você fazer nada.
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.rockbase.com.br/api/v1/importacoes/8f3c..."

Casos de uso

O roteiro completo de três problemas reais, com as rotas acima encadeadas. Os trechos são Node.js 18 ou mais novo, sem instalar nada — as mesmas chamadas estão em cada rota, nas quatro linguagens.

Configurar a loja inteira a partir da sugestão

A loja acabou de ligar a Galeria e não quer configurar produto por produto. Cada produto já vem com a sugestão que nós montaríamos; o script só a transforma em itens e grava tudo de uma vez. Numa loja em que as fotos seguem as variações, isso configura o catálogo inteiro sem ninguém abrir o painel.

const BASE = 'https://api.rockbase.com.br';
const cabecalho = { Authorization: `Bearer ${process.env.ROCKBASE_TOKEN}` };

const itens = [];
for (let pagina = 1; ; pagina += 1) {
  const resposta = await fetch(
    `${BASE}/api/v1/produtos?pagina=${pagina}&por_pagina=200`,
    { headers: cabecalho },
  );
  const { produtos, tem_mais } = await resposta.json();

  for (const produto of produtos) {
    if (produto.sugestao === null) continue;
    for (const [valor, fotos] of Object.entries(produto.sugestao.fotos)) {
      for (const foto_id of fotos) {
        itens.push({
          produto_id: produto.id,
          foto_id,
          atributo: produto.sugestao.atributo,
          valor,
        });
      }
    }
  }

  if (!tem_mais) break;
}

const gravacao = await fetch(`${BASE}/api/v1/galeria/config`, {
  method: 'PUT',
  headers: { ...cabecalho, 'Content-Type': 'application/json' },
  body: JSON.stringify({ itens }),
});

console.log(await gravacao.json());

Depois disso, o lojista confere no painel e corrige as poucas fotos que a sugestão errou — de preferência com um PATCH, que não mexe no resto.

Sincronizar com o ERP toda noite

O catálogo muda no ERP, não no painel: produto novo entra, foto muda de variação. Um script agendado manda toda noite só o que mudou, com PATCH — o que não veio no corpo fica como está. Se o lote for grande, a resposta volta antes de terminar, e o script acompanha pela rota de importações.

const BASE = 'https://api.rockbase.com.br';
const cabecalho = { Authorization: `Bearer ${process.env.ROCKBASE_TOKEN}` };

// O que mudou no ERP desde ontem, já nas colunas da planilha:
// { produto_id, foto_id, atributo, valor } — ou { ..., apagar: 'sim' }.
const itens = await mudancasDoDia();

const resposta = await fetch(`${BASE}/api/v1/galeria/config`, {
  method: 'PATCH',
  headers: { ...cabecalho, 'Content-Type': 'application/json' },
  body: JSON.stringify({ itens }),
});

let { importacao, estado } = await resposta.json();

// Lote grande continua sozinho no servidor; só acompanhe até o fim.
while (estado === 'processando') {
  await new Promise((espera) => setTimeout(espera, 2000));
  const consulta = await fetch(`${BASE}/api/v1/importacoes/${importacao}`, {
    headers: cabecalho,
  });
  ({ estado } = (await consulta.json()).importacao);
}

console.log(estado); // 'concluida'

O mesmo desenho vale para o Agrupador: troque o caminho por /api/v1/agrupador/config e as colunas pelas dele.

Agrupar os irmãos que o nome já entrega

"Tapete Kodi 2,00x3,00" e "Tapete Kodi 2,50x3,00" são o mesmo modelo em produtos separados. Quando a regra da loja é essa — a última palavra do nome é o que muda —, um script agrupa o catálogo inteiro de uma vez. O painel faz sugestão parecida; a API serve quando a regra é sua e você quer rodá-la sempre que o catálogo mudar.

const BASE = 'https://api.rockbase.com.br';
const cabecalho = { Authorization: `Bearer ${process.env.ROCKBASE_TOKEN}` };

// A última palavra é o valor; o resto do nome é o grupo.
const grupos = new Map();
for (let pagina = 1; ; pagina += 1) {
  const resposta = await fetch(
    `${BASE}/api/v1/produtos?pagina=${pagina}&por_pagina=200`,
    { headers: cabecalho },
  );
  const { produtos, tem_mais } = await resposta.json();

  for (const produto of produtos) {
    const palavras = produto.nome.trim().split(/\s+/);
    if (palavras.length < 2) continue;

    const valor = palavras.pop();
    const grupo = palavras.join(' ').toLowerCase().replace(/\s+/g, '-');

    const membros = grupos.get(grupo) ?? [];
    membros.push({
      produto_id: produto.id,
      grupo,
      atributo: 'Medida',
      valor,
      ordem: membros.length,
    });
    grupos.set(grupo, membros);
  }

  if (!tem_mais) break;
}

// Grupo de um produto só não é grupo.
const itens = [...grupos.values()].filter((g) => g.length > 1).flat();

await fetch(`${BASE}/api/v1/agrupador/config`, {
  method: 'PUT',
  headers: { ...cabecalho, 'Content-Type': 'application/json' },
  body: JSON.stringify({ itens }),
});

Antes de gravar de verdade, imprima os grupos e confira: o PUT substitui a configuração inteira. Para ajustes pontuais depois, use o PATCH.

Limites e erros

São 60 requisições por minuto por chave. Passar disso é 429, com o cabeçalho Retry-After dizendo quantos segundos esperar. Toda resposta traz X-RateLimit-Remaining.

CódigoQuando acontece
401Token ausente, inválido ou revogado. Os três dão a mesma resposta.
402O plano da loja não comporta o que você pediu. Nada foi gravado.
403O token existe, mas não tem o escopo daquela operação.
404App desconhecido, ou importação que não é desta loja.
422Corpo ou itens inválidos. A resposta traz cada erro com o índice do item.
429Passou de 60 requisições por minuto. O cabeçalho Retry-After diz quantos segundos esperar.
502A Nuvemshop não respondeu. Tente de novo.

A mensagem de erro de validação é a mesma que o lojista lê no Excel, porque é a mesma função que a escreve. Muda só o que numera: aqui é o índice do item, lá é o número da linha.

Se você não programa, o mesmo trabalho se faz pela planilha, sem escrever uma linha de código: veja como.