Visão geral
São três endereços. O mesmo token serve para os três; o de produtos e o de estoque só funcionam depois de ligados no cartão da loja.
| Rota | Para quê | Precisa ligar? |
|---|---|---|
/pedidos.php | Mandar as vendas da loja (e mudar o status: pago, enviado, cancelado). O estoque baixa sozinho. | Não — já vem ligada |
/produtos.php | Criar e atualizar produtos, variações e fotos. | Sim: "Receber produtos pela API" |
/estoque.php | Dizer o saldo certo de um SKU, ou somar/tirar unidades. | Sim: "Receber estoque pela API" |
Endereço base: https://fluvhub.com/sistema/apps/lojas_virtuais/api
"ativo": false, variação que você não mandar fica como está, e todo saldo mudado aparece no histórico do produto com o nome da sua loja.Começar em 3 passos
- Crie a conexão. No Fluv Hub, vá em Lojas & Marketplaces › Conectar conta › Loja virtual e escolha API própria. Dê um nome (ex.: "Meu site") e clique em Criar.
- Copie o token. No cartão da loja, o botão
</>mostra os três endereços e o token (começa comfh_). O botão "Copiar tudo para o dev" junta tudo num texto só. - Ligue o que vai usar. Na engrenagem do cartão, ligue "Receber produtos pela API" e/ou "Receber estoque pela API". Os dois nascem desligados. Ligar o estoque pede um usuário que possa alterar estoque.
Teste a conexão (não grava nada):
curl https://fluvhub.com/sistema/apps/lojas_virtuais/api/produtos.php \
-H "Authorization: Bearer fh_SEU_TOKEN"{ "ok": true, "loja": "Meu site", "mensagem": "Conectado. Envie produtos por POST; consulte com ?codigo=<SKU>.", "estoque_pela_api": true }Token e segurança
- Mande o token no cabeçalho
Authorization: Bearer fh_…(ouX-Fluv-Token: fh_…). - Nunca no endereço. Produtos e estoque recusam
?token=com 401, porque endereço fica gravado em log. (Pedidos ainda aceita, para quem já usava, mas prefira o cabeçalho.) - Só HTTPS. Chamada sem HTTPS em produtos e estoque volta 403.
- O token diz qual é a loja e a empresa. Nenhum campo do corpo troca isso.
- Guarde o token no servidor, nunca no JavaScript do navegador do cliente.
- Vazou? Cartão da loja ›
</>› Gerar outro token. O antigo para na hora. - Toda chamada fica registrada (loja, rota, resultado). Saldo e cadastro mudados aparecem no histórico do produto como "API <nome da loja>".
Regras de toda chamada
- Corpo em JSON UTF-8, com
Content-Type: application/json. Máximo de 2 MB por chamada. - Um item ou um lote. Mande um objeto, ou uma lista:
{"pedidos": […]},{"produtos": […]},{"itens": […]}— até 100 por chamada. - Limite: 120 chamadas por minuto por loja. Passou, volta 429: espere e mande em lote.
- Números com ponto:
59.90."59,90"é recusado (é ambíguo). - Teste sem gravar: acrescente
?teste=1no endereço (ou"teste": trueno corpo). A resposta diz o que aconteceria. - Pode repetir. Mandar o mesmo pedido, produto ou saldo de novo não duplica nada.
A resposta
Cada item do lote volta em resultados, na mesma ordem (indice), com ok e o que foi feito (acao) ou o motivo (erro). Um item errado não impede os outros.
{
"ok": false,
"teste": false,
"resultados": [
{ "indice": 0, "ok": true, "codigo": "CAMISA-P", "acao": "criado" },
{ "indice": 1, "ok": false, "codigo": "CAMISA-M", "erro": "preco tem de ser número (use ponto: 59.90)" }
]
}| HTTP | Quer dizer |
|---|---|
200 | Tudo certo. |
207 | Parte do lote entrou, parte não — veja resultados. |
422 | Nada entrou — cada item diz o porquê. |
400 | JSON inválido. |
401 | Token ausente, errado, ou mandado no endereço. |
403 | Rota desligada no cartão, estoque comandado pelo ERP, conta de demonstração ou sem HTTPS. O erro diz qual. |
404 | Consulta (GET) de algo que não existe. |
405 | Método errado (use POST para mandar e GET para consultar). |
413 | Mais de 100 itens ou mais de 2 MB. |
423 | A loja está pausada no Fluv Hub. |
429 | Passou de 120 chamadas por minuto. |
Pedidos
POST https://fluvhub.com/sistema/apps/lojas_virtuais/api/pedidos.php
Cada venda da loja vira um pedido em Pedidos, pronto para nota fiscal e etiqueta. Com o sku igual ao do Fluv Hub, o estoque baixa sozinho.
{
"id": "1042",
"numero": "1042",
"data": "2026-11-03T10:30:00-03:00",
"status": "pago",
"cliente": { "nome": "Maria Silva", "documento": "123.456.789-09",
"email": "maria@email.com", "telefone": "11999998888" },
"entrega": { "cep": "01001-000", "logradouro": "Praça da Sé", "numero": "100",
"complemento": "", "bairro": "Sé", "cidade": "São Paulo", "uf": "SP" },
"itens": [ { "sku": "CAMISA-P", "nome": "Camisa P", "quantidade": 2, "preco": 59.90, "desconto": 0 } ],
"frete": 15.00,
"desconto": 0,
"total": 134.80,
"pagamento": "Pix",
"rastreio": "",
"observacoes": ""
}| Campo | Tipo | O que é |
|---|---|---|
id obrigatório | texto | O id do pedido na sua loja. É a chave: o mesmo id de novo atualiza, nunca duplica. |
itens[] obrigatório | lista | Ao menos 1 item com quantidade e sku ou nome. preco é o unitário; desconto, o do item. |
status | texto | pendente, pago (padrão), enviado, entregue ou cancelado. Aceita também em inglês (paid, shipped, cancelled…). |
numero | texto | O número que o cliente vê. |
data | data e hora | ISO 8601, ex.: 2026-11-03T10:30:00-03:00. |
cliente | objeto | nome, documento (CPF/CNPJ), email, telefone. |
entrega | objeto | cep, logradouro, numero, complemento, bairro, cidade, uf. |
frete, desconto, total | número | Valores do pedido em reais. |
pagamento | texto | Forma de pagamento (ex.: Pix, Cartão 3x). |
rastreio, observacoes | texto | Código de rastreio e observações. |
Mudar o status
Mande o mesmo id com o status novo. cancelado cancela no Fluv Hub e devolve o estoque; enviado e entregue andam a situação para frente (nunca para trás).
acao de cada resultado: criado, atualizado, igual (nada mudou) ou ignorado (pedido pendente com a loja configurada para receber só pagos, ou cancelamento de um pedido que nunca entrou).
Consultar
GET https://fluvhub.com/sistema/apps/lojas_virtuais/api/pedidos.php?id=1042
numero_fluvhub é o número do pedido dentro do Fluv Hub (o prefixo da loja + o seu número).
{ "ok": true, "pedido": { "id": "1042", "numero": "1042", "numero_fluvhub": "SITE-1042", "status": "pago", "total": 134.8, "recebido_em": "2026-11-03 10:30:12" } }Produtos
POST https://fluvhub.com/sistema/apps/lojas_virtuais/api/produtos.php
Cria ou atualiza pelo codigo (o SKU). Existe = muda só os campos que você mandou; o resto fica como está. Não existe = cria (aí nome é obrigatório).
{
"codigo": "CAMISA-P",
"nome": "Camisa P",
"descricao": "<p>Algodão 100%</p>",
"preco": 59.90,
"custo": 21.00,
"marca": "Minha Marca",
"categoria": "Camisetas",
"unidade": "UN",
"peso": 0.25,
"altura_cm": 3, "largura_cm": 20, "comprimento_cm": 30,
"ncm": "61091000",
"gtin": "7891234567895",
"estoque_minimo": 2,
"ativo": true,
"estoque": 10,
"fotos": ["https://cdn.minhaloja.com/camisa-p-1.jpg", "https://cdn.minhaloja.com/camisa-p-2.jpg"]
}| Campo | Tipo | O que é |
|---|---|---|
codigo obrigatório | texto (até 60) | O SKU. É a chave do produto. |
nome | texto (até 255) | Obrigatório só para criar. |
descricao | texto / HTML | Aceita HTML simples (negrito, listas, parágrafos). Script e afins são removidos. |
preco | número | Preço de venda. |
custo | número | Custo de compra (vai para o Preço Sugerido). 0 não apaga o custo de hoje. |
marca, categoria, unidade | texto | Categoria que não existe é criada com o nome que você mandou. Unidade padrão: UN. |
peso | número (kg) | Peso com embalagem. |
altura_cm, largura_cm, comprimento_cm | número (cm) | Medidas da embalagem. |
ncm, cest | texto | Para a nota fiscal. Pontos são tirados (6109.10.00 → 61091000). |
gtin | texto | Código de barras (EAN): 8, 12, 13 ou 14 dígitos. |
estoque_minimo | inteiro | Estoque mínimo do produto (o mesmo campo do cadastro). |
ativo | true / false | false tira de venda (é assim que se "apaga"). |
estoque | inteiro | Saldo certo. Só muda com "Receber estoque pela API" ligado — veja Estoque. Sem este campo, o saldo nunca é tocado. |
fotos | lista de endereços | Veja Fotos. |
variacoes | lista | Veja Variações. |
- Não mande
nullpara "limpar" um campo (é recusado). Para não mudar, simplesmente não mande o campo. - Campo que não existe volta como aviso em
avisos, não como erro. acao:criadoouatualizado(com?teste=1:criaria/atualizaria).- SKU de produto que vem do ERP integrado (Bling, Tiny…) é recusado: ele é cuidado lá.
- O limite de produtos do seu plano vale igual ao da tela. Cada variação conta como um SKU.
- Com
estoquejunto e a rota de estoque desligada, o produto salva eestoque.errodiz por que o saldo não mudou.
Consultar
GET https://fluvhub.com/sistema/apps/lojas_virtuais/api/produtos.php?codigo=CAMISA-P
Devolve o produto como está hoje: os mesmos campos, o saldo, as fotos (endereços públicos, a primeira é a capa) e, no produto com grade, as variacoes com saldo e fotos de cada uma.
Variações (cor, tamanho…)
Mande a grade no produto-mãe. Cada variação tem o próprio codigo (SKU) e até 2 atributos.
{
"codigo": "CAMISETA",
"nome": "Camiseta Básica",
"preco": 59.90,
"fotos": ["https://cdn.minhaloja.com/camiseta.jpg"],
"variacoes": [
{ "codigo": "CAM-AZ-M", "atributos": { "Cor": "Azul", "Tamanho": "M" }, "estoque": 3,
"fotos": ["https://cdn.minhaloja.com/camiseta-azul.jpg"] },
{ "codigo": "CAM-AZ-G", "atributos": { "Cor": "Azul", "Tamanho": "G" }, "estoque": 5 },
{ "codigo": "CAM-PR-M", "atributos": { "Cor": "Preta", "Tamanho": "M" }, "preco": 64.90, "estoque": 0 }
]
}- Em cada variação:
codigoeatributos(obrigatórios ao criar), e se quiserpreco,custo,gtin,peso, medidas,estoque_minimo,ativo,estoqueefotos. - Nome, descrição, NCM e categoria são do produto-mãe.
- Variação que já existe é encontrada pelo código. As que você não mandar ficam como estão — dá para mandar só a que mudou.
- Produto com variações não tem estoque próprio: mande
estoqueem cada variação. - Atalho: mandar o código de uma variação direto no topo (
{"codigo": "CAM-AZ-M", "preco": 69.9}) atualiza só ela. - A resposta traz
estoque_variacoesefotos_variacoescom o resultado de cada uma.
Fotos
Mande fotos como uma lista de endereços https://. O Fluv Hub baixa cada imagem, confere e guarda — a foto aparece no cadastro e vai para os anúncios como qualquer foto subida na tela.
"fotos": [
"https://cdn.minhaloja.com/camisa-p-frente.jpg",
"https://cdn.minhaloja.com/camisa-p-costas.jpg"
]- A galeria fica igual à lista, na ordem: a primeira é a capa. Foto que estava e não veio sai (inclusive a que foi subida à mão na tela).
- Não mandou
fotos? As fotos ficam como estão. Lista vazia[]é recusada (tirar todas, só pela tela). - O mesmo endereço não é baixado de novo. Pode mandar o catálogo inteiro toda hora. Trocou a imagem? Troque o endereço (ex.:
…/camisa.jpg?v=2). - Até 12 fotos por produto e 4 próprias por variação (as do produto-mãe valem para ela).
- Formatos JPG, PNG, WEBP ou GIF, até 15 MB, com pelo menos 300 px no menor lado (o ideal é 1200 px ou mais). A imagem é convertida para JPG de até 2000 px.
- O endereço tem de abrir a imagem direto, sem login, com o nome do site (não IP).
- Foto que falha não muda nada: se um endereço não abre ou a imagem não serve, o produto inteiro não é alterado e o
errodiz qual (fotos[1]) e por quê. - Até 100 fotos novas por chamada. Num lote grande com muita foto nova, os produtos que passarem disso voltam com erro: mande-os na chamada seguinte.
- Com
?teste=1o endereço é conferido, mas nada é baixado: a resposta diz quantas fotos seriam baixadas (fotos.baixaria).
{ "indice": 0, "ok": true, "codigo": "CAMISA-P", "acao": "atualizado",
"fotos": { "ok": true, "total": 2, "novas": 1, "removidas": 1 } }Estoque
POST https://fluvhub.com/sistema/apps/lojas_virtuais/api/estoque.php
É o mesmo ajuste da tela de Estoque: o número segue para os anúncios ligados ao produto (Mercado Livre, Shopee, Magalu, TikTok Shop…), kits recalculam e o histórico do produto registra a mudança.
{ "sku": "CAMISA-P", "estoque": 12 }{ "sku": "CAMISA-P", "somar": -2 }{ "itens": [
{ "sku": "CAMISA-P", "estoque": 5 },
{ "sku": "CAMISA-M", "somar": 3, "motivo": "contagem" }
] }| Campo | Tipo | O que é |
|---|---|---|
sku obrigatório | texto | O SKU no Fluv Hub. |
estoque | inteiro ≥ 0 | O saldo certo. Use este ou somar. |
somar | inteiro | Quanto entrou (positivo) ou saiu (negativo). |
motivo | texto | ajuste (padrão) ou contagem (balanço/inventário). |
entrada_ja_contada | texto | sim ou nao. Só quando a resposta trouxer uma pergunta (veja abaixo). |
- Só funciona com o estoque "Pelo Fluv Hub" ou "Misto" em Integrações › Modo de Operação. Em "Pelo sistema de gestão", quem manda é o ERP: volta 403.
- Saldo que ficaria negativo é recusado e nada muda.
- SKU que divide o saldo com outro (produto vinculado) ajusta o principal — a resposta diz qual em
ajustado_em. - Kit (combo) não recebe número: o saldo dele vem dos componentes.
- Se o produto acabou de receber entrada pelo anúncio, o item volta com
pergunta: mande de novo com"entrada_ja_contada": "sim"(já está no saldo) ou"nao"(somar).
{ "indice": 0, "sku": "CAMISA-P", "ok": true, "alterado": true,
"estoque_antes": 10, "estoque_agora": 12, "resumo": "…" }Com ?teste=1, cada item traz estoque_antes, estoque_depois, o_que_acontece e mudam_junto (SKUs que dividem o saldo), sem gravar.
Consultar
GET https://fluvhub.com/sistema/apps/lojas_virtuais/api/estoque.php?sku=CAMISA-P
{ "ok": true, "sku": "CAMISA-P", "estoque": 12, "anuncios": [ … ] }Traz também estoque_compartilhado_com quando o SKU divide o saldo e combo quando é kit.
Erros e o que fazer
| Mensagem | O que fazer |
|---|---|
| Token ausente ou inválido | Confira o cabeçalho Authorization: Bearer fh_… (sem aspas, com o token inteiro). |
| Envie o token no cabeçalho… | Tire o ?token= do endereço e gere outro token (o antigo pode ter ido para algum log). |
| Token não reconhecido | O token foi trocado. Pegue o novo no botão </> do cartão. |
| Desligado no Fluv Hub… | Ligue "Receber produtos/estoque pela API" na engrenagem do cartão da loja. |
| Estoque pelo sistema de gestão | Mude para "Pelo Fluv Hub" ou "Misto" em Integrações › Modo de Operação, ou mande o estoque para o ERP. |
| … tem de ser número (use ponto: 59.90) | Mande número JSON (59.9), não texto com vírgula. |
| nome é obrigatório para criar o produto | Produto novo precisa de nome. |
| Limite de produtos do plano | Cada variação conta como um SKU. Aumente o plano ou desative SKUs que não usa. |
| fotos[i]: … | Abra o endereço no navegador: tem de mostrar só a imagem, por https, sem login, com 300 px ou mais. |
| O saldo ficaria negativo | Confira o saldo com GET antes de tirar unidades. |
| Precisa de resposta: … | Mande o item de novo com "entrada_ja_contada": "sim" ou "nao". |
| Muitas chamadas / Limite de 120 por minuto | Junte em lotes de até 100 e espere 1 minuto. |
Exemplos de código
Atualizar preço, saldo e fotos de um produto. Troque fh_SEU_TOKEN pelo seu token.
curl -X POST https://fluvhub.com/sistema/apps/lojas_virtuais/api/produtos.php \
-H "Authorization: Bearer fh_SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"codigo":"CAMISA-P","preco":59.9,"estoque":12,"fotos":["https://cdn.minhaloja.com/camisa-p-1.jpg"]}'// Node 18+ (no servidor — nunca exponha o token no navegador)
const resp = await fetch('https://fluvhub.com/sistema/apps/lojas_virtuais/api/produtos.php', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.FLUVHUB_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify({
codigo: 'CAMISA-P',
preco: 59.9,
estoque: 12,
fotos: ['https://cdn.minhaloja.com/camisa-p-1.jpg'],
}),
});
const dados = await resp.json();
for (const r of dados.resultados) {
if (!r.ok) console.error(r.codigo, r.erro);
}<?php
$ch = curl_init('https://fluvhub.com/sistema/apps/lojas_virtuais/api/produtos.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('FLUVHUB_TOKEN'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'codigo' => 'CAMISA-P',
'preco' => 59.9,
'estoque' => 12,
'fotos' => ['https://cdn.minhaloja.com/camisa-p-1.jpg'],
]),
]);
$dados = json_decode(curl_exec($ch), true);
foreach ($dados['resultados'] as $r) {
if (!$r['ok']) error_log($r['codigo'] . ': ' . $r['erro']);
}import os, requests
resp = requests.post(
'https://fluvhub.com/sistema/apps/lojas_virtuais/api/produtos.php',
headers={'Authorization': 'Bearer ' + os.environ['FLUVHUB_TOKEN']},
json={
'codigo': 'CAMISA-P',
'preco': 59.9,
'estoque': 12,
'fotos': ['https://cdn.minhaloja.com/camisa-p-1.jpg'],
},
timeout=120,
)
for r in resp.json()['resultados']:
if not r['ok']:
print(r['codigo'], r['erro'])Mandar um pedido
curl -X POST https://fluvhub.com/sistema/apps/lojas_virtuais/api/pedidos.php \
-H "Authorization: Bearer fh_SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":"1042","status":"pago","cliente":{"nome":"Maria"},"itens":[{"sku":"CAMISA-P","quantidade":1,"preco":59.9}],"total":59.9}'Cancelar o mesmo pedido
curl -X POST https://fluvhub.com/sistema/apps/lojas_virtuais/api/pedidos.php \
-H "Authorization: Bearer fh_SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":"1042","status":"cancelado"}'Sincronizar o estoque de vários SKUs
curl -X POST https://fluvhub.com/sistema/apps/lojas_virtuais/api/estoque.php \
-H "Authorization: Bearer fh_SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"itens":[{"sku":"CAMISA-P","estoque":12},{"sku":"CAMISA-M","estoque":4}]}'Perguntas rápidas
Quanto custa usar a API?
Nada a mais: a API faz parte do Fluv Hub. Valem os limites do seu plano (por exemplo, o número de produtos).
A API apaga produto?
Não. Não existe rota de apagar. Para tirar um produto de venda, mande "ativo": false. Variação que você não mandar fica como está.
Posso mandar o catálogo inteiro toda hora?
Pode. Produto igual não muda nada, e foto com o mesmo endereço não é baixada de novo. Mande em lotes de até 100 e respeite 120 chamadas por minuto.
O estoque que mando vai para os marketplaces?
Vai. É o mesmo ajuste da tela de Estoque: o número segue para os anúncios ligados ao produto (menos os do Full).
Meu token vazou. E agora?
Em Lojas & Marketplaces, abra o cartão da loja, clique em </> e depois em "Gerar outro token". O antigo para de funcionar na hora.
Uso Bling ou Tiny. Posso mandar estoque pela API?
Só se o estoque estiver "Pelo Fluv Hub" ou "Misto" em Integrações › Modo de Operação. Em "Pelo sistema de gestão", quem manda no saldo é o ERP e a rota de estoque responde 403.