API GreenGo Checkout

Conecte seu ERP, site ou automação à sua loja: sincronize produtos, acompanhe pedidos e receba eventos em tempo real. Todos os valores são em centavos inteiros (13500 = R$ 135,00).

Autenticação

Gere uma chave em Painel → Integrações → Chaves de API. Envie em todas as chamadas, somente a partir do seu servidor:

curl https://SEU-DOMINIO/api/v1/store \
  -H "X-API-Key: gg_live_..."

Também aceitamos Authorization: Bearer gg_live_.... A chave dá acesso somente à loja em que foi criada. Revogue a qualquer momento no painel.

Erros e limites

Erros sempre têm o mesmo formato. details aparece quando há campos inválidos.

{ "error": "INVALID_PRODUCT", "message": "Revise os dados do produto.", "details": { "price": "Preco em centavos inteiros e nao negativo." } }
HTTPQuando
401Chave ausente, inválida ou revogada
404Recurso não existe nesta loja
409Transição de status inválida ou Idempotency-Key reutilizada com outro corpo
422Dados inválidos (veja details)
207Lote processado com alguns itens com erro (veja results)
429Mais de 120 requisições por minuto por chave. Respeite o cabeçalho Retry-After

Idempotência

Em POST /store/orders e POST /store/products, envie Idempotency-Key com um valor único por operação. Se a rede cair e você repetir a chamada, devolvemos a mesma resposta sem criar duplicado (cabeçalho Idempotent-Replayed: true). A chave vale por 24 horas.

Produtos

GET/api/v1/store/products?limit=50&offset=0&sku=&updated_since=
GET/api/v1/store/products/{id}
POST/api/v1/store/products
PATCH/api/v1/store/products/{id}
DELETE/api/v1/store/products/{id}

Produto com variações (cor, tamanho). Até 3 opções e 250 variações. Cada variação tem preço, estoque, SKU e foto próprios.

{
  "name": "Camiseta Linho",
  "price": 8990,
  "compareAt": 11990,
  "weightGrams": 350, "heightCm": 4, "widthCm": 25, "lengthCm": 30,
  "images": ["https://cdn.sualoja.com/camiseta.jpg"],
  "options": [{ "name": "Cor", "values": ["Areia", "Preto"] }, { "name": "Tamanho", "values": ["P", "M"] }],
  "variants": [
    { "sku": "CL-AR-P", "options": { "Cor": "Areia", "Tamanho": "P" }, "price": 8990, "stock": 4 },
    { "sku": "CL-PR-M", "options": { "Cor": "Preto", "Tamanho": "M" }, "price": 9490, "stock": 0, "image": "https://cdn.sualoja.com/preto.jpg", "weightGrams": 420 }
  ]
}

No PATCH, enviar variants substitui a lista inteira. Para mudar só estoque, use /store/inventory.

Peso e medidas para o frete (opcionais, do produto já embalado): weightGrams em gramas inteiros (1 a 100.000) e heightCm, widthCm, lengthCm em centímetros inteiros (1 a 300). Na variação, cada campo é opcional: vazio herda o valor do produto. Envie null para apagar um valor; campo ausente mantém o que já estava gravado (também nas variações ao reenviar variants). As cotações do Melhor Envio e da Frenet usam esses valores por item do carrinho; o que faltar vem do pacote padrão da conexão. A resposta traz shippingReady: true quando o produto (ou todas as variações) tem peso e as 3 medidas.

Sincronizar pelo ID do seu sistema

PUT/api/v1/store/products/external/{seuId}

Cria o produto se o ID ainda não existe na GreenGo, ou atualiza se já existe. Resposta 201 (criado) ou 200 (atualizado). É o jeito mais simples de manter o catálogo espelhado.

Lote de produtos

POST/api/v1/store/products/batch

Até 100 produtos por chamada, cada um com externalId. Um item inválido não impede os outros.

{ "products": [ { "externalId": "erp-10", "name": "Garrafa", "price": 5000, "stock": 2 }, { "externalId": "erp-11", "stock": 9 } ] }
→ { "created": 1, "updated": 1, "failed": 0, "results": [ { "index": 0, "ok": true, "created": true, "id": "prd_..." }, ... ] }

Estoque em massa

POST/api/v1/store/inventory

Até 500 itens, identificados por sku, variantId ou productId.

{ "items": [ { "sku": "CL-AR-P", "stock": 12 }, { "variantId": "var_...", "stock": 0 } ] }

Pedidos

GET/api/v1/store/orders?status=paid&from=2026-09-01&to=2026-09-30&q=&limit=50&offset=0
GET/api/v1/store/orders/{id}
POST/api/v1/store/orders

O total é sempre calculado pela GreenGo a partir do catálogo. CPF/CNPJ é validado e o telefone é normalizado para o formato internacional (5511999998888).

{
  "customer": { "name": "Ana Silva", "email": "ana@exemplo.com", "phone": "(11) 99999-8888", "document": "529.982.247-25" },
  "items": [ { "productId": "prd_...", "variantId": "var_...", "quantity": 2 } ],
  "paymentMethod": "pix",
  "address": { "cep": "01310-100", "street": "Avenida Paulista", "number": "1000", "complement": "", "district": "Bela Vista", "city": "São Paulo", "uf": "SP" },
  "shippingOptionId": "pac",
  "couponCode": "VOLTA10",
  "orderBump": false
}

Lojas com entrega (padrão) exigem address e shippingOptionId; lojas de produto digital desligam isso no painel. O pedido devolve a composição do total: subtotal, discount (cupom), pixDiscount, shipping, total. Cupom inválido responde 422 com o motivo (expirado, limite de usos, compra mínima). Para mostrar o total antes de criar o pedido, use POST /api/v1/checkout/quote com os mesmos campos (uf no lugar de address).

Mudar status (gateway, ERP)

POST/api/v1/store/orders/{id}/status
{ "status": "paid", "reference": "id-da-transacao-no-gateway" }
DePara
pendingpaid, failed, canceled
failedpaid, canceled
paidrefunded

Ao virar paid, o estoque é baixado. Em refunded, devolvido. O cliente é avisado por WhatsApp e e-mail quando esses canais estão configurados.

Checkout pelo seu site (itens com preço)

Para quem monta o carrinho no próprio site: kit com desconto, segunda peça mais barata, gravação, personalização. Seu servidor manda os itens já com preço e recebe o link do checkout transparente, aberto direto no pagamento, no seu domínio próprio quando ele está no ar. Os itens ficam travados: o navegador do comprador não muda preço nem quantidade.

POST/api/v1/store/checkout-sessions
curl -X POST https://SEU-DOMINIO/api/v1/store/checkout-sessions   -H "X-API-Key: gg_live_..." -H "Content-Type: application/json"   -d '{
  "items": [
    { "name": "Faca Sentinela", "unitPrice": 13500, "quantity": 1, "note": "Gravar: João", "image": "https://seusite.com/sentinela.jpg", "sku": "sentinela" },
    { "name": "Faca Raiz", "unitPrice": 6075, "quantity": 1, "note": "Peça adicional, 55% off", "sku": "raiz" }
  ],
  "returnUrl": "https://seusite.com/faca/sentinela",
  "utm": { "utm_source": "meta", "fbclid": "..." }
}'
201 { "session": { "id": "cart_...", "url": "https://checkout.seusite.com/ui_kits/checkout/?store=...&cart=...", "subtotal": 19575, "expiresAt": "..." } }
CampoRegra
unitPriceCentavos inteiros (R$ 135,00 = 13500). Calcule no seu servidor, nunca no navegador.
noteObservação da linha (gravação, variação, "peça adicional"). Aparece no pedido, no e-mail e no painel. Até 200 caracteres.
imageOpcional, só https.
returnUrlOpcional, https. Para onde o botão "Voltar para a loja" leva.
expiresInHoursOpcional, de 1 a 168. Padrão 48. Link vencido responde 410 SESSION_EXPIRED e não cobra.

Redirecione o comprador para session.url. Frete, cupom, desconto no Pix e forma de pagamento seguem as regras da loja no painel. Até 50 itens por sessão.

Webhooks de saída

Cadastre em Painel → Integrações ou pela API. Enviamos um POST JSON para sua URL a cada evento escolhido:

POST https://seusistema.com/webhooks/greengo
GreenGo-Event-Id: evt_...
GreenGo-Event-Type: order.paid
GreenGo-Signature: t=1790000000,v1=5f2c...

{ "id": "evt_...", "type": "order.paid", "createdAt": "2026-09-23T14:02:11.000Z", "storeCode": "MINHA-LOJA-7K2Q", "data": { "order": { ... } } }
GET/api/v1/store/webhooks
POST/api/v1/store/webhooks { "url", "events": ["order.paid"], "description" }
PATCH/api/v1/store/webhooks/{id}
DELETE/api/v1/store/webhooks/{id}
GET/api/v1/store/webhooks/{id}/deliveries

Validar a assinatura

Calcule HMAC-SHA256(segredo, t + "." + corpo_bruto) e compare com v1. Recuse eventos com t mais antigo que 5 minutos.

// Node.js (Express com express.raw())
const crypto = require("crypto");
function valido(req, segredo) {
  const partes = Object.fromEntries(req.get("GreenGo-Signature").split(",").map((p) => p.split("=")));
  const esperado = crypto.createHmac("sha256", segredo).update(partes.t + "." + req.body.toString("utf8")).digest("hex");
  const recente = Math.abs(Date.now() / 1000 - Number(partes.t)) < 300;
  return recente && crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(partes.v1));
}
// PHP
$corpo = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_GREENGO_SIGNATURE']), $p);
$esperado = hash_hmac('sha256', $p['t'] . '.' . $corpo, $segredo);
$valido = hash_equals($esperado, $p['v1']) && abs(time() - (int)$p['t']) < 300;

Tipos de evento

EventoQuandodata
order.createdPedido criado no checkout ou pela APIorder
order.paidPagamento confirmadoorder
order.failedPagamento recusadoorder
order.canceledPedido canceladoorder
order.refundedPedido estornado por inteiroorder, refund (id, amount, reason, totalRefunded, full)
order.partially_refundedEstorno parcial (um evento por estorno)order, refund
order.chargebackChargeback aberto: pagamento em disputaorder, dispute (status, amount)
order.chargeback_wonChargeback ganho: a venda volta a valerorder, dispute
order.chargeback_lostChargeback perdido: valor devolvido ao compradororder, dispute
order.invoice_issuedNota fiscal do pedido autorizada no emissor (eNotas ou Notazz), com número e links do PDF/XMLorder, invoice
product.created / updated / deletedMudança no catálogoproduct
cart.abandonedComprador deixou dados e não finalizoucart
cart.recoveredCarrinho abandonado virou pedidocart, order
import.finishedImportação de catálogo terminouimport
pingBotão "Enviar teste" do painelmessage

Webhooks de entrada

Para sua plataforma ou gateway avisar a GreenGo, crie uma conexão em Painel → Integrações → Receber eventos. Você recebe uma URL e um segredo. Envie:

POST /api/v1/webhooks/{idDaConexao}
X-Event-Id: id-unico-do-evento
X-Webhook-Signature: sha256=HMAC_SHA256(segredo, corpo_bruto)

{ "type": "order.updated", "order": { "id": "GG-AB12CD34", "status": "paid", "reference": "pay_123" } }
{ "type": "product.updated", "product": { "externalId": "erp-10", "price": 5500, "stock": 3 } }

Eventos repetidos com o mesmo X-Event-Id são ignorados. Um evento que falhou (por exemplo, transição inválida) não é marcado como processado e pode ser reenviado.