Sandbox: ambiente de testes com lojas e produtos de exemplo. Pedidos não chegam a lojas de verdade e as chaves daqui (abd_test_…) não valem na produção. Ir para a produção

Visão geral

O BridgesAgents reúne produtos, preços e estoque de lojas de qualquer segmento do varejo (supermercado, eletrônicos, farmácia, moda...) num formato pronto para agentes de IA de compra.

Há três formas de consultar o catálogo, todas com a mesma chave de agente:

  • MCP, para agentes de IA (Claude, ChatGPT, agentes próprios): o agente descobre e chama as tools sozinho.
  • API REST, para sistemas e integrações tradicionais.
  • UCP (Universal Commerce Protocol), para agentes que já falam esse padrão.

Comece por aqui: crie uma conta de desenvolvedor, gere a chave de agente em Chaves de agente no painel e envie em toda consulta no header Authorization: Bearer abd_… (detalhes em Autenticação).

RecursoURL
Servidor MCP (Streamable HTTP)https://api.sandbox.bridgesagents.com/mcp
API RESThttps://api.sandbox.bridgesagents.com/api/v1
Referência interativa (Swagger)https://api.sandbox.bridgesagents.com/swagger-ui.html

Conceitos

ConceitoO que é
LojistaA empresa (CNPJ) que vende.
LojaUnidade física de um lojista, com endereço, coordenada, como atende (retirada, entrega por raio ou faixas de CEP) e horário. Preço e estoque variam por loja. O cadastro é feito pelo lojista.
ProdutoO item em si, igual em qualquer loja: nome, marca, categoria, GTIN/EAN e unidade. Não tem preço.
OfertaProduto × loja: preço, preço promocional e estoque. É o que o agente compara para decidir onde comprar.

Detalhes que o agente deve levar em conta:

  • effectivePrice é o preço a pagar: o promocional quando existe, senão o normal.
  • updatedAt diz quando preço e estoque foram atualizados. stale: true marca oferta sem atualização há mais de 7 dias (o preço pode ter mudado); com mais de 30 dias ela não aparece para agentes.
  • soldByWeight indica venda por peso (ex.: hortifruti): o preço é por kg e o valor final só é conhecido na separação.
  • Unidades (unit): UN, KG, G, L, ML.
  • requiresPrescription: medicamento que exige receita. O agente não deve concluir a compra; oriente o consumidor a apresentar a receita na farmácia.
  • adult: true: produto para maiores de 18 (bebida alcoólica, tabaco). A BridgesAgents só marca; quem confirma a idade é o agente.
  • inStoreOnly: true: vendido só no local (combustível). Não entra em carrinho e não tem retirada nem entrega: mostre o preço, a distância e mapsUrl (rota até a loja). Para acompanhar o preço da gasolina, busque com radiusKm.
  • storeVerified: a empresa da loja tem CNPJ ativo na Receita Federal. storeRating e storeRatingCount: nota média (1 a 5) e quantas avaliações de quem comprou pelo BridgesAgents.
  • kind: GOOD (produto físico), FUEL (combustível), SERVICE (serviço), BOOKABLE (viagem, reserva) ou DIGITAL (curso, gift card). Só GOOD vai para carrinho; os outros se compram pelo buyUrl da loja.
  • currency é a moeda dos preços (ISO 4217, ex.: BRL). priceType diz como ler o preço: FIXED (fechado), FROM (a partir de), PER_NIGHT (por noite), PER_PERSON (por pessoa).
  • fulfillment diz como chega: DELIVERY, PICKUP, IN_STORE (no local), AT_CUSTOMER (a domicílio), BOOKING (reserva) ou DIGITAL. Serviço e reserva não têm estoque (stock nulo); digital pode ser ilimitado. Reserva e digital valem de qualquer lugar.
  • No produto: taxonomyId (categoria da Google Product Taxonomy), mpn (código do fabricante) e attributes (dados do tipo: color, size, duration_minutes, max_guests...).

Localização do consumidor

Busca e ofertas aceitam a localização do consumidor. Com ela, só aparecem lojas que entregam ali ou que ficam perto para retirada (até 30 km), e cada oferta diz como a loja atende.

  • Prefira latitude e longitude: são precisas. Só com o postalCode (CEP), a coordenada vem de um serviço de CEP e muitas vezes é apenas o centro da cidade; nesse caso a distância vem com distanceApproximate: true.
  • A entrega vale se o consumidor estiver no raio da loja ou numa das faixas de CEP que ela atende. Faixas de CEP funcionam mesmo sem coordenada, então mande o CEP sempre que tiver.

Conectar via MCP

O servidor usa o transporte Streamable HTTP em https://api.sandbox.bridgesagents.com/mcp.

Claude Code

claude mcp add --transport http bridgesagents https://api.sandbox.bridgesagents.com/mcp \
  --header "Authorization: Bearer abd_SUA_CHAVE"

Clientes que só aceitam servidores locais (stdio)

Use o mcp-remote como ponte. Exemplo de configuração (Claude Desktop, Cursor e similares):

{
  "mcpServers": {
    "bridgesagents": {
      "command": "npx",
      "args": ["mcp-remote", "https://api.sandbox.bridgesagents.com/mcp", "--header", "Authorization:Bearer ${BRIDGESAGENTS_KEY}"],
      "env": { "BRIDGESAGENTS_KEY": "abd_SUA_CHAVE" }
    }
  }
}

Testar sem agente

O MCP Inspector lista e executa as tools pelo navegador. Escolha Streamable HTTP, informe a URL acima e adicione o header Authorization com Bearer abd_….

Clientes com suporte a OAuth (como o Claude e o MCP Inspector) não precisam de header: no primeiro acesso recebem 401 com WWW-Authenticate, descobrem o servidor de autorização, se registram sozinhos e abrem a página em que o consumidor entra (ou cria a conta) e permite o acesso. Veja Autenticação.

npx @modelcontextprotocol/inspector

Tools MCP

search_products

Busca produtos pelo nome ou marca, em qualquer segmento. Ignora acentos e maiúsculas, aceita começo de palavra (arro → arroz) e sinônimos, inclusive regionais (bateria → pilha, bolacha → biscoito, macaxeira → mandioca). Tolera erro de digitação (arros → arroz) e entende termos em inglês e espanhol (rice, frijoles → feijão, haircut → corte).

ParâmetroTipoObrigatórioDescrição
querystringsimTexto da busca, em português. Ex.: "fone bluetooth".
limitnúmeronãoMáximo de resultados, de 1 a 50. Padrão: 10.
postalCodestringnãoCódigo postal do consumidor (no Brasil, o CEP; cep também é aceito). Com localização, só produtos que alguma loja atende ali. Fora do Brasil, mande latitude e longitude.
countrystringnãoPaís do consumidor, 2 letras (BR, US, PT). Padrão: BR. Fora do Brasil, o código postal vira coordenada aproximada (OpenStreetMap).
latitude, longitudenúmeronãoCoordenada do consumidor (mais precisa que o CEP).
radiusKmnúmeronãoSó lojas até esta distância do consumidor, em km (até 100). Exige localização. Ex.: postos num raio de 5 km.
[
  {
    "id": "00000000-0000-0000-0000-0000000000b8",
    "gtin": "7890000000103",
    "name": "Amoxicilina 500mg 21 cápsulas",
    "brand": "Marca F",
    "category": "Medicamentos",
    "unit": "UN",
    "requiresPrescription": true,
    "imageUrl": "https://img.example.com/amoxicilina.png"
  }
]

get_offers

Lista as ofertas de um produto por loja, da mais barata para a mais cara (empate: a mais perto).

ParâmetroTipoObrigatórioDescrição
productIdUUIDsimid vindo de search_products.
postalCodestringnãoCódigo postal do consumidor (no Brasil, o CEP; cep também é aceito).
countrystringnãoPaís do consumidor, 2 letras (BR, US, PT). Padrão: BR. Fora do Brasil, o código postal vira coordenada aproximada (OpenStreetMap).
latitude, longitudenúmeronãoCoordenada do consumidor.
radiusKmnúmeronãoSó lojas até esta distância do consumidor, em km (até 100). Exige localização. Ex.: postos num raio de 5 km.
[
  {
    "storeId": "00000000-0000-0000-0000-0000000000a4",
    "storeName": "Farmácia Exemplo Savassi",
    "city": "Belo Horizonte",
    "region": "MG",
    "price": 32.90,
    "promoPrice": null,
    "effectivePrice": 32.90,
    "stock": 20.000,
    "inStock": true,
    "soldByWeight": false,
    "requiresPrescription": true,
    "updatedAt": "2026-10-25T21:51:47Z",
    "stale": false,
    "distanceKm": 0.2,
    "distanceApproximate": false,
    "pickup": true,
    "delivery": { "fee": 0.00, "etaMinutes": 40, "minOrder": null, "coveredBy": "radius" },
    "openNow": true,
    "buyUrl": "https://api.sandbox.bridgesagents.com/r/00000000-0000-0000-0000-0000000000cb"
  }
]
CampoO que significa
distanceKmDistância em linha reta até a loja. Nula sem coordenada de um dos lados.
pickupDá para retirar na loja (retirada habilitada e loja perto do consumidor).
deliveryCondições de entrega (taxa, prazo em minutos, pedido mínimo) se a loja entrega ali; null se não entrega. coveredBy: cep_range (certo), radius (coordenadas exatas) ou radius_approximate (calculado com a coordenada do CEP: pode não valer, confirme com a loja). Sem localização, mostra as condições com coveredBy null.
openNowSe a loja está aberta agora, no fuso dela. Nulo se a loja não informou horário.
buyUrlLink para o consumidor comprar direto na loja: leva ao WhatsApp da loja (com a mensagem pronta, com produto e preço) ou ao site dela. Nulo se a loja não informou canal de compra. Mostre ao consumidor quando ele escolher a oferta.

create_cart

Monta um carrinho numa loja e devolve confirmUrl: o link que o consumidor abre para revisar, informar nome e WhatsApp (e endereço, na entrega) e confirmar. O agente não fecha a compra sozinho e não recebe dados pessoais. O pedido vai para o painel e para o e-mail da loja; o pagamento é com a loja. Se a loja cadastrou chave Pix, o link mostra QR Code e copia e cola com o valor do pedido.

ParâmetroTipoObrigatórioDescrição
storeIdUUIDsimLoja (de get_offers). Um carrinho é de uma loja só.
itemslistasimDe 1 a 50 itens { productId, quantity }. Quantidade fracionada só em produto vendido por peso.
fulfillmentstringsimProduto: pickup (retirar) ou delivery (entrega). Serviço: in_store ou at_customer (a domicílio). Reserva: booking. Digital: digital. Use o que a oferta aceita (fulfillment em get_offers).
postalCodestringna entregaCódigo postal do consumidor (CEP no Brasil); a loja precisa entregar nele.
notestringnãoRecado para a loja (aparece no pedido).
scheduledForstringno serviçoData e hora pedidas, com fuso (ex.: 2026-11-05T14:00-03:00). Use um horário livre de check_availability; fora do expediente ou lotado é recusado.
checkIn / checkOutstringna reservaDatas AAAA-MM-DD. checkOut é obrigatório com preço por noite (a linha multiplica pelas noites).
guestsnúmerona reservaPessoas. Obrigatório com preço por pessoa (a linha multiplica pelos hóspedes).
externalReferencestringnãoSeu id para este carrinho (conversa ou usuário no seu sistema), até 100 caracteres. Volta em get_cart, em Pedidos dos agentes e nos webhooks do desenvolvedor.
{
  "id": "f4e2add5-0103-4fc2-aebb-77dbabff2241",
  "status": "OPEN",
  "store": { "id": "00000000-0000-0000-0000-0000000000a4", "name": "Farmácia Exemplo Savassi", … },
  "fulfillment": "PICKUP",
  "items": [
    { "productId": "00000000-0000-0000-0000-0000000000b6", "name": "Protetor Solar FPS 50 200ml",
      "unit": "UN", "quantity": 2, "unitPrice": 52.90, "lineTotal": 105.80 }
  ],
  "subtotal": 105.80,
  "deliveryFee": null,
  "total": 105.80,
  "expiresAt": "2026-10-27T02:39:54Z",
  "confirmUrl": "https://bridgesagents.com/compras/c/abc_…",
  "order": null
}

Produto com receita não entra em carrinho. Se algum item tiver problema (sem estoque, quantidade, loja que não vende), nada é criado e a resposta lista cada problema. Na confirmação, preço e estoque são conferidos de novo: se o preço mudou, o consumidor vê o valor novo antes de confirmar.

check_availability

Agenda em tempo real, antes de create_cart. Serviço: slots com os horários livres dentro do horário da loja (a cada duration_minutes) e as vagas que sobram (free, quando a loja informa capacity); use um start como scheduledFor. Reserva: nights com cada noite e as vagas livres (free, quando a loja informa units; 0 = lotado). O carrinho recusa horário fora do expediente ou lotado e sugere os próximos livres.

ParâmetroTipoObrigatórioDescrição
storeIdUUIDsimLoja (de get_offers).
productIdUUIDsimServiço ou reserva (de search_products).
fromAAAA-MM-DDnãoPrimeiro dia; hoje se vazio.
daysinteironãoQuantos dias olhar, de 1 a 31 (padrão 7).

get_cart

Situação de um carrinho criado pela sua chave: OPEN, CONFIRMED (com o número e o status do pedido) ou EXPIRED. O link vale 24 horas. Enquanto o pedido é NEW, order.respondBy diz até quando a loja aceita (2 h; 24 h para reserva e digital, contando a partir da abertura da loja): sem resposta, ele é cancelado sozinho com o motivo A loja não respondeu a tempo.

ParâmetroTipoObrigatórioDescrição
cartIdUUIDsimid devolvido por create_cart.

Próximas tools: quote_delivery e pagamento pelo agente.

UCP e ACP

O mesmo catálogo também sai nos formatos de comércio para agentes do Google e da OpenAI. Cada produto (o mesmo em qualquer loja, pelo GTIN) traz as ofertas de todas as lojas. A compra continua na loja: ainda não há checkout pelo agente.

UCP (Universal Commerce Protocol)

O perfil em https://api.sandbox.bridgesagents.com/.well-known/ucp anuncia as capacidades dev.ucp.shopping.catalog.search e dev.ucp.shopping.catalog.lookup (versão 2026-08-25). Cada oferta de loja é uma variante: a loja vem em seller, o preço em centavos ({"amount": 2490, "currency": "BRL"}), url é o link de compra. Distância, retirada, entrega e horário ficam em metadata. Informe context.postal_code para ver só as lojas que atendem o consumidor.

POST/ucp/v1/catalog/search

Busca por texto, com filtros categories e price (min/max em centavos) e paginação por cursor.

curl -X POST "https://api.sandbox.bridgesagents.com/ucp/v1/catalog/search" -H "Authorization: Bearer abd_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -d '{"query": "pilha aa", "context": {"postal_code": "30140-010"}, "pagination": {"limit": 5}}'

POST/ucp/v1/catalog/lookup

Produtos por ids; os que não existem voltam em messages com not_found.

POST/ucp/v1/catalog/product

Um produto por id, com todas as ofertas.

ACP (Agentic Commerce Protocol, OpenAI)

Feed de produtos do ChatGPT: um JSON por linha, uma linha por oferta de loja, com price no formato 19.90 BRL e is_eligible_checkout: false. Ofertas sem marca, sem imagem ou sem canal de compra ficam de fora (o formato exige esses campos), assim como produtos com receita. O header X-BridgesAgents-Skipped diz quantas ficaram de fora.

O feed é de uso interno: o próprio BridgesAgents o gera em /feeds/acp/products.jsonl.gz e envia à OpenAI. Agentes não precisam baixá-lo; para consultar o catálogo, use MCP, REST ou UCP com a chave de agente.

Importar catálogo

O lojista envia a planilha que já usa, em Excel (.xlsx) ou CSV, pelo painel ou pela API. Importar de novo atualiza preço e estoque, sem duplicar produtos.

  • A primeira linha é o cabeçalho. A ordem das colunas não importa, e colunas desconhecidas são ignoradas.
  • Os nomes são comparados sem acento, maiúsculas ou pontuação: Preço de Venda e preco_venda são a mesma coluna.
  • Números aceitam o formato brasileiro (1.234,56, R$ 27,90) e também 27.90.
  • CSV pode usar ponto e vírgula, vírgula ou tab, em UTF-8 ou no formato padrão do Excel em português.
  • Linhas com problema não impedem as outras: o resultado lista cada uma com o número da linha e o motivo.
  • Combustível (gasolina, etanol, diesel, GNV) é marcado sozinho como vendido só no local: sem carrinho, aparece pela distância até o posto. Armas, munição, explosivos, cigarro eletrônico e drogas são recusados.
  • Limites: 5 MB e 10.000 linhas por arquivo.
ColunaNomes aceitosObrigatóriaObservação
Nomenome, produto, descrição, itemsimAté 300 caracteres.
Preçopreço, valor, preço de venda, valor unitáriosimMaior que zero.
EANean, gtin, código de barrasnão8, 12, 13 ou 14 dígitos. É o que liga seu produto ao mesmo produto de outras lojas.
Marcamarca, fabricantenãoSem EAN, nome + marca identificam o produto.
Categoriacategoria, departamento, seçãonão
Unidadeunidade, un, undnãoUN (padrão), KG, G, L ou ML.
Preço promocionalpreço promocional, promoção, preço ofertanãoMenor que o preço.
Estoqueestoque, quantidade, qtd, saldonãoPadrão 0.
Vendido por pesovendido por peso, por peso, granelnãosim/não. Sem a coluna, unidade KG conta como vendido por peso.
Exige receitaexige receita, receita, tarja, venda sob prescriçãonãosim/não, ou "tarja vermelha"/"tarja preta". Os agentes não concluem a compra desses produtos.
Maior de 18maior de 18, maiores de 18, 18, adultonãosim/não. Bebida alcoólica e tabaco são marcados sozinhos; o agente controla a idade.
Imagemimagem, foto, url da imagem, image_linknãoEndereço https:// público da foto do produto.
Tipo de itemtipo de item, kindnãoproduto, combustível, serviço, reserva ou digital. Padrão: produto.
Moedamoeda, currencynãoCódigo de 3 letras (BRL, USD, EUR). Padrão: BRL. No formato do feed do Google, o preço pode vir com a moeda (27.90 BRL).
Tipo de preçotipo de preço, price_typenãofixo, a partir de, por noite ou por pessoa. Padrão: fixo.
Atendimentoatendimento, como chega, fulfillmentnãoentrega, retirada, no local, a domicílio, reserva ou digital (vários separados por vírgula). Padrão: o do tipo.
MPN e SKUmpn, código do fabricante / sku, código internonãoCódigo do fabricante (identifica o item sem EAN, junto com a marca) e o código no seu sistema.
Categoria do Googlegoogle_product_category, categoria googlenãoId ou caminho da Google Product Taxonomy (ex.: 2271 ou Apparel & Accessories > Clothing).
Atributosatributo_<nome> (ex.: atributo_duration_minutes), cor, tamanho, voltagemnãoDados do tipo: cor e tamanho do produto, duração do serviço, hóspedes da reserva. A planilha do feed do Google Merchant Center também é aceita (title, price, sale_price, image_link...).

Baixar planilha modelo

Cadastro de lojas

O lojista é responsável pelo cadastro de cada loja: endereço, localização, como atende e horário. É com isso que os agentes sabem se a loja entrega para o consumidor, se dá para retirar por perto e se está aberta. O painel mostra o que falta em cada loja.

  • Coordenada: informe a exata (no Google Maps, botão direito sobre a loja e clique nos números). Em branco, usamos uma aproximada pelo CEP, que pode ser só o centro da cidade.
  • Entrega: por raio (km a partir da loja), por faixas de CEP ou os dois. Taxa, prazo em minutos e pedido mínimo são por loja.
  • Canal de compra: WhatsApp de pedidos e/ou site. É para onde o link “comprar nesta loja” (buyUrl) leva o cliente. No site, {produto} na URL é trocado pelo nome do produto.
  • Horário: por dia da semana, no fuso da UF da loja. Dois turnos com vírgula, 24h ou fechado. Fechar depois da meia-noite (22:00-02:00) também vale.

Planilha de lojas (redes)

Em Lojas → Importar planilha, a rede cadastra e atualiza todas as lojas de uma vez. O código identifica cada loja: se existir, atualiza; se não, cria.

ColunaNomes aceitosObrigatóriaExemplo
Códigocódigo, código da loja, filialsimBH-SAVASSI
Nomenome, loja, unidadesimFarmácia Centro
CEP / código postalcep, código postal, zipsim30140-010
Endereçorua/logradouro, número, complemento, bairro, cidade, UFnão*Rua Pernambuco, 1200
Coordenadalatitude, longitudenão-19.937410 / -43.934170
Atendimentoretirada, entreganãosim / não
Entregaraio km, taxa de entrega, prazo (min), pedido mínimonão4 · 6,90 · 45 · 30,00
Faixas de CEPfaixas cep, ceps atendidosnão30100-000 a 30199-999; 30210-000
Horárioseg, ter, qua, qui, sex, sab, domnão08:00-12:00, 14:00-20:00 · 24h · fechado
Canal de comprawhatsapp, sitenão(31) 99999-0000 · https://minhaloja.com.br/busca?q={produto}
Chave Pixchave_pix, pixnãoSó no Brasil: CPF, CNPJ, e-mail, celular ou chave aleatória. O consumidor paga pelo link do pedido.

* Cidade e UF são completadas pelo CEP quando ficam em branco. Baixar planilha modelo de lojas

Conectar o Bling

Quem usa o Bling não precisa de planilha nem de programação: em Integrações, no painel, o administrador conecta a conta Bling, autoriza o BridgesAgents (só leitura de produtos, estoque e depósitos) e escolhe qual depósito abastece cada loja.

  • Preço, foto, código de barras, marca e categoria vêm do cadastro do produto no Bling.
  • O estoque de cada loja é a soma do saldo disponível dos depósitos ligados a ela.
  • Mudanças de preço e estoque chegam em segundos; uma sincronização completa roda todo dia de madrugada e aparece em Importações com a fonte Bling.
  • Produto inativado ou excluído no Bling fica com estoque zero.
  • Produtos com variação (cor, tamanho) ainda não entram: por enquanto, envie esses pela planilha.

Conectar a Nuvemshop

Quem vende numa loja virtual Nuvemshop também não precisa de planilha: em Integrações, no painel, o administrador conecta a loja, autoriza o BridgesAgents (só leitura de produtos e estoque) e escolhe qual loja do BridgesAgents recebe o estoque de cada local da Nuvemshop (com uma loja só, isso é automático).

  • Cada variação vira um item, com o nome do produto e os valores da variação (ex.: "Camiseta Básica – Azul / M").
  • Preço, preço promocional, foto, código de barras (quando é um GTIN válido), marca e categoria vêm da Nuvemshop.
  • Variação sem controle de estoque na Nuvemshop aparece como disponível, com 999 unidades.
  • Mudanças nos produtos chegam em segundos; uma sincronização completa roda todo dia de madrugada e aparece em Importações com a fonte Nuvemshop.
  • Produto oculto, fora da vitrine ou excluído na Nuvemshop fica com estoque zero. Variação sem preço ("consulte") não entra.

Webhooks de pedido

Para o pedido cair direto no sistema da loja (ERP, PDV, agenda, n8n, Zapier): em Integrações, a central ou o desenvolvedor da empresa cadastra um endereço https e escolhe a loja (ou todas) e os eventos. Os administradores recebem um e-mail a cada webhook novo.

  • order.created: o consumidor confirmou o pedido (o mesmo momento do e-mail para a loja).
  • order.status_changed: a loja aceitou, deixou pronto, concluiu ou cancelou (com cancelReason).
  • order.paid: a loja marcou o pedido como pago (ou desfez: paidAt volta vazio).
  • Cada envio é um POST com o pedido completo em data.order: itens com gtin e sku da loja, cliente, endereço, agenda da reserva ou do serviço, totais e moeda. Campos novos podem aparecer; nenhum é renomeado.
  • Os dados do cliente vão só para a loja do pedido. Para devolver o status, use POST /api/v1/me/orders/{id}/status com a chave da loja.
  • Número do pedido: escolha no cadastro o que o seu sistema aceita. Só números: "number": 1042. Letras e números: "number": "AB1042". O campo reference ("AB1042", o mesmo identificador do Pix e do link do consumidor) vai sempre.
  • Cabeçalho de acesso (opcional): se o seu sistema exige um token fixo (Authorization: Bearer …, X-Api-Key...), cadastre o nome e o valor: ele vai em todo envio, junto com a assinatura. O valor fica cifrado e o painel mostra só o final.
  • Conta de desenvolvedor: em Webhooks, o desenvolvedor do agente recebe os mesmos eventos dos pedidos feitos pelas chaves dele, com data.cart (id, externalReference, agentKeyId) e o pedido sem dados pessoais do comprador (customer e deliveryAddress vazios). É assim que o bot avisa o usuário sem ficar consultando get_cart.

Exemplo

POST https://seusistema.com.br/agentbridge/pedidos
Content-Type: application/json
X-BridgesAgents-Event: order.created
X-BridgesAgents-Event-Id: 6f1c0d2e-…
X-BridgesAgents-Delivery: 0b9a7c41-…
X-BridgesAgents-Signature: t=1793000000,v1=5d2c…
X-Api-Key: (o cabeçalho de acesso, se você cadastrou um)

{
  "id": "6f1c0d2e-…",
  "event": "order.created",
  "createdAt": "2026-10-27T14:03:11Z",
  "data": {
    "order": {
      "id": "…", "number": 1042, "reference": "AB1042", "status": "NEW",
      "storeId": "…", "storeCode": "CENTRO", "storeName": "Exemplo Centro",
      "fulfillment": "DELIVERY",
      "customer": { "name": "Ana Souza", "phone": "+5511988887777", "email": "ana@exemplo.com", "note": null },
      "deliveryAddress": { "postalCode": "01310100", "street": "Av. Paulista", "number": "1000", "complement": null,
                           "district": "Bela Vista", "city": "São Paulo", "region": "SP" },
      "items": [
        { "productId": "…", "gtin": "7890000000011", "sku": "ARZ-5", "name": "Arroz Branco Tipo 1 5kg", "unit": "UN",
          "quantity": 2, "unitPrice": 24.90, "multiplier": 1, "lineTotal": 49.80, "adult": false }
      ],
      "subtotal": 49.80, "deliveryFee": 8.00, "total": 57.80, "currency": "BRL", "estimated": false,
      "schedule": { "scheduledFor": null, "checkIn": null, "checkOut": null, "guests": null },
      "paidAt": null, "cancelReason": null, "agentNote": null,
      "createdAt": "2026-10-27T14:03:10Z", "updatedAt": "2026-10-27T14:03:10Z",
      "payment": { "status": "PENDING", "method": "PIX" }
    }
  }
}

Conferir a assinatura

Ao cadastrar, o painel mostra uma única vez o segredo (whsec_…). Cada envio traz X-BridgesAgents-Signature: t=<segundos>,v1=<hex>, em que v1 é o HMAC-SHA256, com o segredo, de t + "." + corpo (o corpo exatamente como chegou). Recuse se não bater ou se t tiver mais de 5 minutos.

// Node.js (Express): use o corpo cru, antes de qualquer JSON.parse
import crypto from "node:crypto";

app.post("/agentbridge/pedidos", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("X-BridgesAgents-Signature") ?? "";
  const t = header.match(/t=(\d+)/)?.[1];
  const v1 = header.match(/v1=([0-9a-f]+)/)?.[1] ?? "";
  const expected = crypto.createHmac("sha256", process.env.BRIDGESAGENTS_WEBHOOK_SECRET)
    .update(`${t}.${req.body}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  if (!fresh || v1.length !== expected.length
      || !crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body);
  // Guarde event.id: se chegar de novo (nova tentativa ou reenvio), ignore
  res.sendStatus(200);
});
# Python (Flask)
import hashlib, hmac, os, re, time
from flask import Flask, request, abort

app = Flask(__name__)

@app.post("/agentbridge/pedidos")
def pedidos():
    header = request.headers.get("X-BridgesAgents-Signature", "")
    t = re.search(r"t=(\d+)", header)
    v1 = re.search(r"v1=([0-9a-f]+)", header)
    if not t or not v1 or abs(time.time() - int(t[1])) > 300:
        abort(401)
    body = request.get_data(as_text=True)
    expected = hmac.new(os.environ["BRIDGESAGENTS_WEBHOOK_SECRET"].encode(), f"{t[1]}.{body}".encode(),
                        hashlib.sha256).hexdigest()
    if not hmac.compare_digest(v1[1], expected):
        abort(401)
    event = request.get_json()
    return "", 200

Respostas, novas tentativas e reenvio

  • Responda 2xx em até 10 segundos e processe depois, se for demorado. Redirecionamento não é seguido.
  • Sem 2xx, tentamos de novo em 1 min, 5 min, 30 min, 2 h e 12 h. Esgotadas (cerca de 15 h), o webhook é pausado e os administradores recebem e-mail; os pedidos continuam no painel.
  • O mesmo evento pode chegar mais de uma vez (nova tentativa ou reenvio): use X-BridgesAgents-Event-Id (igual ao id do corpo) para ignorar repetidos. A ordem não é garantida depois de uma falha: compare data.order.updatedAt.
  • No painel ficam os últimos envios (30 dias), com o erro de cada um, e os botões Enviar teste (pedido fictício, evento webhook.test), Reenviar, Pausar/Reativar e Trocar segredo.

Testar no sandbox

No sandbox você testa o fluxo inteiro do seu agente sem uma loja de verdade: as lojas, os produtos e os pedidos são fictícios, e você faz o papel da loja e do checkout de pagamento.

  • Pelo painel: em Pedidos dos agentes, abra um pedido e use os botões para aceitar, marcar como pronto, concluir ou cancelar, e para avisar o pagamento (pago no Pix, cartão ou boleto, recusado, estornado, contestado).
  • Pela API, com a chave de agente (para testes automatizados): POST /api/v1/carts/{id}/sandbox/status com {"status": "ACCEPTED"} (ACCEPTED, READY, COMPLETED, CANCELLED) e POST /api/v1/carts/{id}/sandbox/payment com {"status": "paid", "method": "pix"} (paid, refused, refunded, chargeback, pending; pix, card, boleto).
  • Cada simulação muda o pedido de verdade: o consumidor é avisado, os seus webhooks recebem order.status_changed e order.paid, e o get_cart mostra order.status, order.paymentStatus, order.paymentMethod e order.paidAt.
  • Só pedidos montados pelas suas chaves. Em produção essas rotas não existem: quem responde é a loja.
# Aceitar o pedido e avisar o pagamento aprovado (sandbox, com a chave de agente)
curl -X POST https://api.sandbox.bridgesagents.com/api/v1/carts/$CART_ID/sandbox/status \
  -H "Authorization: Bearer $BRIDGESAGENTS_KEY" -H "Content-Type: application/json" \
  -d '{"status": "ACCEPTED"}'

curl -X POST https://api.sandbox.bridgesagents.com/api/v1/carts/$CART_ID/sandbox/payment \
  -H "Authorization: Bearer $BRIDGESAGENTS_KEY" -H "Content-Type: application/json" \
  -d '{"status": "paid", "method": "pix"}'

Ferramenta completa: risco e regiões

Contas de desenvolvedor podem ver o nível de risco dos pedidos que os seus agentes geraram (GET /api/v1/me/agent-orders/{cartId}/risk) e os números por região das lojas da rede (GET /api/v1/me/insights/regions), e buscar pedidos por região (GET /api/v1/me/insights/orders?q=04001, pelo começo do CEP ou pelo nome da cidade ou do bairro): produtos e categoria, pagamento, recusas e o motivo, e o contato (nome, e-mail, telefone) só de pedidos com consentimento: o operador do bot colhe o consentimento do comprador, conforme a cláusula do contrato com a BridgesAgents. O que aparece depende do que o contrato libera, campo a campo, e cada busca fica registrada. No sandbox já está tudo liberado; em produção, mediante contrato com a BridgesAgents.

Enviar catálogo por API

Para o sistema da loja (ERP, loja virtual, script) mandar preços e estoque automaticamente, sem planilha. Mesmas regras e mensagens da importação por planilha.

1. Gere a chave

Há dois tipos:

  • Chave da empresa (POST /api/v1/me/api-keys): vale para todas as lojas. Cada item informa storeCode, o código da loja no seu sistema. Ideal para redes.
  • Chave de uma loja (POST /api/v1/stores/{storeId}/api-keys): tudo vai para aquela loja e storeCode é ignorado.

No painel, em Chaves de API, ou pela API com a sessão do lojista (veja Autenticação). A chave inteira só aparece nessa resposta: guarde-a com segurança.

curl -X POST "https://api.sandbox.bridgesagents.com/api/v1/stores/{storeId}/api-keys" \
  -H "Authorization: Bearer abs_…" \
  -H "Content-Type: application/json" -d '{"name": "ERP"}'
{
  "apiKey": { "id": "…", "name": "ERP", "keyPrefix": "abk_Xy12Ab34", "createdAt": "…", "lastUsedAt": null, "revokedAt": null },
  "key": "abk_Xy12Ab34…"
}

2. Envie os itens com a chave

A chave vai no header X-Api-Key. Com a chave da empresa, a loja de cada item vem do storeCode; com a chave de uma loja, é sempre aquela loja. Cada item cria ou atualiza a oferta da loja; itens que não forem enviados continuam como estão (para tirar de venda, envie estoque 0). Até 1.000 itens por requisição.

curl -X POST "https://api.sandbox.bridgesagents.com/api/v1/catalog/items" \
  -H "X-Api-Key: abk_…" -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "storeCode": "BH-SAVASSI", "gtin": "7890000000066", "name": "Pilha Alcalina AA com 4", "price": 17.50, "stock": 60 },
      { "storeCode": "BH-PAMPULHA", "gtin": "7890000000103", "name": "Amoxicilina 500mg 21 cápsulas",
        "price": 29.90, "stock": 8, "requiresPrescription": true }
    ]
  }'
CampoTipoObrigatórioObservação
nametextosimAté 300 caracteres.
pricenúmerosimMaior que zero, com ponto decimal (17.50).
gtintextonãoEAN com 8, 12, 13 ou 14 dígitos. Liga o item ao mesmo produto de outras lojas.
brandtextonãoSem GTIN, nome + marca identificam o produto.
categorytextonão
unittextonãoUN (padrão), KG, G, L ou ML.
promoPricenúmeronãoMenor que price.
stocknúmeronãoPadrão 0.
soldByWeighttrue/falsenãoSem o campo, unidade KG conta como vendido por peso.
requiresPrescriptiontrue/falsenãoMedicamento com receita. Os agentes não concluem a compra.
adulttrue/falsenãoMaiores de 18. Bebida alcoólica e tabaco já são marcados pelo nome e pela categoria.
storeCodetextocom chave da empresaCódigo da loja no seu sistema (cadastrado em Lojas).
kindtextonãoGOOD, FUEL, SERVICE, BOOKABLE ou DIGITAL. Padrão: GOOD.
currencytextonãoISO 4217 (BRL, USD, EUR). Padrão: BRL.
priceTypetextonãoFIXED, FROM, PER_NIGHT ou PER_PERSON (cada tipo aceita alguns).
fulfillmentlistanãoDELIVERY, PICKUP, IN_STORE, AT_CUSTOMER, BOOKING, DIGITAL. Padrão: o do tipo.
mpn / skutextonãoCódigo do fabricante e código no seu sistema.
googleCategorytextonãoId ou caminho da Google Product Taxonomy.
attributesobjetonãoDados do tipo, ex.: {"duration_minutes": 60, "capacity": 2} (serviço: duração e atendimentos ao mesmo tempo) ou {"max_guests": 2, "check_in": "14:00", "units": 3} (reserva: unidades iguais por noite).
imageUrltextonãoEndereço https:// público da foto do produto. Vale a primeira informada para o produto.

A resposta diz o que entrou. Itens com problema não impedem os outros e aparecem em errors com a posição em items (começando em 0):

{
  "received": 2, "imported": 1, "failed": 1,
  "productsCreated": 0, "offersCreated": 0, "offersUpdated": 1,
  "errors": [ { "index": 1, "gtin": null, "name": "Banana Prata", "message": "Preço em branco." } ]
}

Para trocar uma chave vazada, gere outra e revogue a antiga no painel (ou com DELETE /api/v1/stores/{storeId}/api-keys/{keyId}).

API REST

Respostas em JSON. Os campos de produto e oferta são os mesmos das tools MCP.

GET/api/v1/products?q={busca}&limit={1-50}&postalCode=&lat=&lng=

Busca produtos. limit é opcional (padrão 20); postalCode, lat e lng filtram pelas lojas que atendem o consumidor (veja Localização do consumidor).

curl -H "Authorization: Bearer abd_SUA_CHAVE" "https://api.sandbox.bridgesagents.com/api/v1/products?q=pilha%20aa"

GET/r/{offerId}

O buyUrl das ofertas: conta o clique e redireciona para o WhatsApp ou o site da loja.

GET/api/v1/products/{id}/offers

Ofertas do produto por loja, da mais barata para a mais cara.

curl -H "Authorization: Bearer abd_SUA_CHAVE" \
  "https://api.sandbox.bridgesagents.com/api/v1/products/00000000-0000-0000-0000-0000000000b7/offers?postalCode=30140-010"

GET/api/v1/products/{id}/availability

Mesmo que a tool check_availability: ?storeId=...&from=AAAA-MM-DD&days=7.

POST/api/v1/carts

Mesmo que a tool create_cart. Responde 201 com o carrinho e confirmUrl; itens com problema voltam em problems (HTTP 400).

curl -X POST "https://api.sandbox.bridgesagents.com/api/v1/carts" -H "Authorization: Bearer abd_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -d '{"storeId": "00000000-0000-0000-0000-0000000000a4", "fulfillment": "pickup",
       "items": [{"productId": "00000000-0000-0000-0000-0000000000b6", "quantity": 2}]}'

GET/api/v1/carts/{id}

Mesmo que a tool get_cart.

Com sessão do lojista (header Authorization: Bearer abs_…)

GET/api/v1/me

Lojista logado.

GET/api/v1/me/stores

Lojas do lojista logado, com o cadastro completo e o que falta em cada uma (pending).

POST/api/v1/me/stores

Cadastra uma loja. PUT /api/v1/me/stores/{id} atualiza.

POST/api/v1/me/stores/import

Planilha de lojas (multipart/form-data, campo file). Veja Cadastro de lojas.

GET/api/v1/me/offers?q=&storeId=&stale=&page=&size=

Ofertas das lojas que você enxerga (gerente: só as dele), por nome do produto: preço, estoque, quando foi atualizada, stale e hidden. q procura em nome, marca e código de barras; stale=true traz só as paradas há mais de 7 dias. Paginada (size até 100).

GET/api/v1/me/offers/freshness

Ofertas paradas por loja: total, sem atualização há mais de 7 dias (stale) e há mais de 30 (hidden, já fora dos agentes).

GET/api/v1/me/performance?days={1-90}

Desempenho das lojas que a pessoa enxerga: aparições para agentes, vezes com o menor preço e cliques no link de compra, por loja e por produto.

GET/api/v1/me/price-insights?days={1-90}&storeId=&limit=

Inteligência de preço: para cada produto seu, como o seu preço se compara ao de outras lojas da mesma cidade (sem identificar quais) e quantas vezes você deixou de ser o menor preço nas consultas dos agentes. days de 1 a 90.

GET/api/v1/me/agent-orders?status=&keyId=

Conta de desenvolvedor: carrinhos e pedidos das suas chaves de agente, sem dados do comprador (cartStatus OPEN, CONFIRMED ou EXPIRED; orderStatus, respondBy, paidAt, externalReference). status: open, confirmed, expired ou cancelled. GET .../summary?days=30 traz carrinhos, confirmados, conversão e pedidos que a loja não respondeu.

GET/api/v1/me/orders?status=&storeId=

Pedidos das lojas que você enxerga. status: novos, andamento, concluidos ou cancelados (sem ele, todos). GET .../summary traz quantos novos e em andamento; GET .../{id}, o pedido com dados do cliente.

POST/api/v1/me/orders/{id}/status

Muda o status: ACCEPTED, READY, COMPLETED ou CANCELLED (com reason, que o cliente vê). Só avança na ordem; o cliente é avisado por e-mail se informou um.

POST/api/v1/me/api-keys

Gera a chave da empresa (todas as lojas). GET lista e DELETE .../{keyId} revoga.

POST/api/v1/stores/{storeId}/catalog/import

Importa a planilha de uma loja sua. Envie como multipart/form-data no campo file.

curl -H "Authorization: Bearer abs_…" -F "file=@catalogo.xlsx" \
  "https://api.sandbox.bridgesagents.com/api/v1/stores/{storeId}/catalog/import"

GET/api/v1/me/imports?problems={true|false}&limit={1-100}

Histórico de importações (planilha e API) das suas lojas, da mais recente para a mais antiga. problems=true traz só as com itens rejeitados ou recusadas.

GET/api/v1/me/imports/{id}

Uma importação, com os itens rejeitados (errors): linha da planilha ou posição em items, e o motivo.

Com chave de API da loja (header X-Api-Key: abk_…)

POST/api/v1/catalog/items

Envia itens em JSON. Veja Enviar catálogo por API.

POST/api/v1/stores/{storeId}/api-keys

Com sessão do lojista: gera uma chave para uma loja sua. GET no mesmo caminho lista as chaves (só o começo de cada uma) e DELETE .../api-keys/{keyId} revoga.

{
  "rows": 120,
  "imported": 118,
  "failed": 2,
  "productsCreated": 40,
  "offersCreated": 100,
  "offersUpdated": 18,
  "ignoredColumns": ["Observações"],
  "errors": [
    { "line": 7, "message": "Preço inválido: \"abc\"." },
    { "line": 31, "message": "Nome do produto em branco." }
  ]
}

Para testar os endpoints direto no navegador, use a referência interativa (Swagger).

Erros e limites

SituaçãoResultado
Busca vaziaLista vazia.
limit fora de 1 a 50Ajustado para o limite mais próximo.
Produto sem ofertas ou id inexistenteLista vazia.
id que não é um UUID (REST)HTTP 400.
Parâmetro q ausente (REST)HTTP 400.
Importação: arquivo vazio, formato não suportado, sem colunas nome e preço ou mais de 10.000 linhasHTTP 400.
Importação: arquivo acima de 5 MBHTTP 413.
Consulta ao catálogo (REST, MCP ou UCP) sem chave de agente, ou com chave inválida ou revogadaHTTP 401.
Mais de 120 consultas por minuto com a mesma chaveHTTP 429, com Retry-After.
Sem sessão ou sessão expirada (rotas do painel)HTTP 401.
Conta de lojista criando chave de agente, ou conta de desenvolvedor criando lojaHTTP 403.
Loja ou importação que não é sua (ou não existe)HTTP 404.
Envio por API: sem X-Api-Key, chave inválida ou revogadaHTTP 401.
Busca: CEP inválido, só latitude ou só longitude, ou coordenada fora do BrasilHTTP 400.
Cadastro de loja com dado inválido (CEP, UF, coordenada, horário, faixa de CEP)HTTP 400, com o motivo.
Envio por API: JSON malformado, sem itens ou mais de 1.000 itensHTTP 400.

Erros vêm no formato application/problem+json (RFC 9457). O campo detail já traz uma mensagem em português que pode ser mostrada ao lojista.

Autenticação

QuemComoPara quê
Agente (conta de desenvolvedor)Chave de agente: Authorization: Bearer abd_…Buscar produtos e ofertas: REST, MCP e UCP. Obrigatória.
Assistente de IA do consumidorOAuth 2.1: Authorization: Bearer abo_…Buscar produtos e ofertas em nome de um consumidor que conectou o assistente. Escopo catalog.
LojistaSessão: Authorization: Bearer abs_…Painel, importar planilha, gerar chaves, ver o histórico. Só enxerga as próprias lojas.
Sistema do lojistaChave da loja: X-Api-Key: abk_…Enviar o catálogo em JSON para aquela loja.

Chave de agente

Nenhuma consulta ao catálogo é anônima. Crie a conta de desenvolvedor (sem CNPJ) e gere as chaves em Chaves de agente, no painel. A chave aparece uma vez só; o banco guarda apenas o hash. Use uma chave por agente ou ambiente: se uma vazar, revogue só ela. O painel mostra quantas consultas cada chave fez por dia, e o limite é de 120 por minuto por chave.

curl -X POST "https://api.sandbox.bridgesagents.com/api/v1/me/agent-keys" -H "Authorization: Bearer abs_…" \
  -H "Content-Type: application/json" -d '{"name": "Agente em produção"}'
# → { "apiKey": { "id": "…", "keyPrefix": "abd_Xk3…" }, "key": "abd_…" }   (só desta vez)

Planos e franquia

Cada requisição autenticada com chave de agente conta uma consulta (busca, ofertas, carrinho ou chamada MCP). O plano Grátis inclui 10.000 consultas por mês; ao chegar nelas, a API responde 429 com a explicação em detail até o dia 1º do mês seguinte. Os planos pagos (Pro e Escala) não bloqueiam: o excedente é cobrado por bloco de 1.000 consultas na fatura do fim do mês. Avisamos por e-mail aos 80% e aos 100% da franquia, e o uso do mês aparece em Plano e cobrança, no painel. O sandbox não tem franquia.

Sandbox

Para testar sem atingir lojas de verdade, use o sandbox: site em https://sandbox.bridgesagents.com e API em https://api.sandbox.bridgesagents.com (MCP em /mcp). Ele tem a mesma versão da produção (toda mudança é publicada nos dois), mas banco próprio, com lojas e produtos de exemplo. Crie lá uma conta de desenvolvedor: as chaves começam com abd_test_ e não valem na produção (nem as de produção valem lá). Carrinhos e pedidos funcionam de ponta a ponta, só que nas lojas de exemplo; os e-mails chegam com [Sandbox] no assunto.

OAuth 2.1 (servidor MCP)

Seguimos a autorização da especificação MCP: metadados em /.well-known/oauth-protected-resource e /.well-known/oauth-authorization-server, registro dinâmico em POST /oauth/register (só clientes públicos), código com PKCE S256 obrigatório em /oauth/authorize e POST /oauth/token. O token de acesso (abo_) vale 1 hora e o de renovação (abr_) 30 dias, trocado a cada uso; reusar um token de renovação ou um código derruba a conexão. POST /oauth/revoke revoga. O consumidor vê e desconecta os apps em Minhas compras › Apps conectados.

Ficam públicos só a descoberta (/.well-known/ucp), esta documentação e o link de compra /r/…, que o consumidor abre.

Sessão do painel

A sessão vem do login e vale 30 dias:

curl -X POST "https://api.sandbox.bridgesagents.com/api/v1/auth/login" -H "Content-Type: application/json" \
  -d '{"email": "voce@sualoja.com.br", "password": "…"}'
# → { "token": "abs_…", "expiresAt": "…", "user": { … } }

O cadastro de lojista (POST /api/v1/auth/signup) cria a empresa, a primeira loja e o usuário de uma vez; o de desenvolvedor (POST /api/v1/auth/signup/developer) pede só nome da empresa ou projeto, seu nome, e-mail e senha. Os dois já devolvem a sessão. POST /api/v1/auth/logout encerra a sessão.

A chave de agente continua valendo para agentes que não usam OAuth.