---
name: crossup
description: Integrar o motor de recomendações da CrossUp em uma loja com @crossup/storefront ou a API, e verificar cada passo com evidência. Usar quando pedirem para integrar a CrossUp, mostrar recomendações, mandar sinais ou depurar uma integração.
---
# CrossUp — integrar e verificar
Documentação: https://docs.crossup.ai/llms.txt · Referência de erros: https://docs.crossup.ai/pt/api/errores.md
## Regras que não se negociam
1. A loja sai da chave. Nenhuma requisição leva `store_id`.
2. `results: []` é uma resposta válida; `reason_codes` diz por quê. Não se mostra nada.
3. Um campo ausente é desconhecido, não zero. `sellable` é uma string; `title` é um objeto por idioma.
4. Nunca quebrar a página: usar `tryGetRecommendations`, nunca `await` em `track.*`.
5. Diante de `429`, esperar `Retry-After`. Nunca tentar de novo em loop.
6. Não inventar endpoints, campos nem valores. Se a doc não diz, perguntar.
## Percurso
1. **Dados.** Pedir a chave `spk_…` e o `connection_id` (`cn_…`). Nunca commitar a chave em um arquivo que não seja de configuração pública; é pública por design, mas mesmo assim vai em variáveis de ambiente.
2. **Verificar a chave.** `GET {base}/v1/catalog/items/{item_id}` com `Authorization: Bearer`. Um `200` com `item.item_id` confirma chave e conexão. `401` → chave; `403 forbidden_connection` → conexão de outra loja; `404` → item de outra loja ou inexistente.
3. **Instalar.** `npm install @crossup/storefront`. Uma instância por página: `createStorefront({ apiKey, connectionId })`.
4. **Pedir.** `tryGetRecommendations({ surface, placementKey, contextItemId?, cartItems? })`. `pdp` e `cart` precisam de `contextItemId`; `carousel`, `in-checkout` e `success-page` precisam de `cartItems`; `checkout` aceita qualquer um dos dois.
5. **Mostrar.** Percorrer `results[]`; omitir os que não trazem `product`; usar `product.item.title[lang]` com fallback, `variants[].price.amount` como string, `availability[].sellable` como texto (nunca como booleano). Sanitizar `item.description` se for HTML. O design é o da loja: não trazer componentes da CrossUp.
6. **Sinais.** `track.impression(decision, { position })` quando a recomendação entra na tela (IntersectionObserver, uma vez). `track.click(decision, result)` ao abrir. `track.addToCart` com a quantidade somada. `track.checkoutStarted(decision)` ao ir para o checkout. Criar a instância com `cartId` (o id do carrinho da loja): sem ele a venda não pode ser atribuída. Não existe sinal de compra: a venda é confirmada pelo pedido da loja.
7. **Evidência.** Mostrar um `decision_id` de uma decisão de teste e um `signal_id` com `status: "accepted"`. Sem os dois, a integração não está terminada.
## Quando algo falha
- `results: []` com `module_not_configured`: o momento não está ativado no admin. Não é um bug do código.
- `results: []` com `no_offer` ou `brain_unavailable`: não mostrar nada. A loja continua vendendo.
- `rejected` com `unknown_decision` em um sinal: foi mandada uma decisão que não é desta conexão.
- `429 rate_limited`: procurar o loop (uma requisição por render, uma requisição por card).