Pular para o conteúdo

Listings por Canal (Feed JSON)

Endpoint público, por canal, para sistemas externos consumirem seus imóveis filtrados em JSON. Autenticado por Bearer token gerado no próprio canal.

É o complemento simétrico dos Webhooks de Canais (Inbound): o mesmo pathToken que recebe leads agora também entrega imóveis.

Quando usar: quando um parceiro, landing page ou sistema externo precisa listar seus imóveis respeitando critérios que você definiu uma única vez no Garagem — sem precisar gerenciar uma API key por usuário, e sem expor a sua base inteira.


  1. Acesse Configurações → Canais e selecione o canal (ex.: o canal “API”).
  2. Vá em Recebimento → Saída de Imóveis (API) e ligue o toggle.
  3. Copie o Bearer token (mostrado apenas no momento da geração — guarde em local seguro).
  4. Copie a URL do endpoint.

Canais que já têm capabilities.outboundListings = true expõem essa seção. O canal “API” padrão vem pronto.


GET https://app.garagem.ai/api/webhooks/channels/{pathToken}/listings
POST https://app.garagem.ai/api/webhooks/channels/{pathToken}/listings
  • pathToken — o mesmo token do webhook de leads do canal (ex.: chnl_2xxxxxxxxxxxxxxxxxxxxxxx).
Authorization: Bearer <TOKEN>
  • O token é gerado uma única vez. Para trocar, use Rotacionar na UI — isso invalida o anterior.
  • Na UI, o token aparece mascarado como ••••••••••••abcd. O valor completo só é exibido logo após gerar/rotacionar.

O endpoint usa o mesmo motor de busca do site público (Typesense), respeitando os critérios base do canal (configurados em Exportação → Critérios Base).

ParâmetroTipoDescrição
qstringTexto livre (default: * = todos). Usa fullAddress, searchText, title, neighborhood, city....
pagenumberPágina (1-indexed).
perPagenumberItens por página (máx. 100, default ~12).
sortstringEx.: price:asc, updatedAt:desc.
query_bystringOverride do query_by do Typesense.
filter_bystringFiltros adicionais (combinados com os critérios base por AND).
slotstring(s)Filtrar por slot do canal. Aceita <slug>, <slug1>,<slug2> ou none (itens sem slot).
facet_bystringCampos para facetamento.

Aceita qualquer campo do ClientSearchParams do @garagem/site-search:

{
"q": "*",
"page": 1,
"per_page": 20,
"sort_by": "updatedAt:desc",
"filter_by": "type:=rental && bedrooms:>=2",
"slots": ["destaque_venda", "destaque_locacao"]
}
  • slots: string[] — equivalente ao ?slot= no GET.

O canal usa os mesmos slots (publicationTypes) do resto do sistema: você os define em Exportação → Slots de Publicação com o slug e os critérios que quiser — incluindo filtros por tipo de anúncio (sale, rental, seasonal), preço, bairro etc.

Auto-provisionamento via integrações externas (Vista)

Seção intitulada “Auto-provisionamento via integrações externas (Vista)”

Tenants com integração de provedor que marca imóveis como destaque (por exemplo, os campos DestaqueWeb e SuperDestaqueWeb do Vista) têm o canal “API” provisionado automaticamente:

  • Na primeira sincronização que traga esses flags, o canal API é criado com capabilities.outboundListings = true.
  • São semeados três slots: super_destaque, destaque e standard — já com critério padrão stage = <estágio ativo do tenant> (apenas imóveis no estágio ativo são considerados).
  • Os imóveis com flag de destaque são atribuídos automaticamente como override_type: include no slot correspondente.
  • Nenhuma configuração manual é necessária; parceiros podem consultar imediatamente ?slot=destaque e ?slot=super_destaque.

Visibilidade é sempre aplicada globalmente (visibility:=public && stage:=active no filtro base), então imóveis privados nunca aparecem — mesmo que tenham sido marcados como destaque no provedor de origem.

Exemplos de configuração que tenants costumam usar:

  • Slot destaque_venda → criteria.type = "sale", totalSlots = 5
  • Slot destaque_locacao → criteria.type = "rental", totalSlots = 10
  • Slot standard → sem criteria, pega o resto.

Cada item da resposta ganha o campo calculado:

"slot": "<slug>" | null

(ou null se o listing passou nos critérios base mas não foi atribuído a nenhum slot).

Liste os slots configurados do canal para construir filtros dinâmicos:

GET /api/webhooks/channels/{pathToken}/listings/slots
Authorization: Bearer <TOKEN>

Resposta:

{
"channelId": "uuid-do-canal",
"slots": [
{ "slug": "destaque_venda", "totalSlots": 5, "usedSlots": 3 },
{ "slug": "destaque_locacao", "totalSlots": 10, "usedSlots": 7 },
{ "slug": "standard", "totalSlots": 0, "usedSlots": 0 }
]
}

Mesmo shape sanitizado do endpoint de site público (Typesense), com slot injetado em cada document:

{
"found": 127,
"page": 1,
"search_time_ms": 4,
"hits": [
{
"document": {
"id": "uuid-do-listing",
"title": "Apartamento 2 quartos - Pinheiros",
"title_en": "2-bedroom apartment - Pinheiros",
"description_en": "Bright unit near the subway...",
"title_es": "",
"description_es": "",
"type": "rental",
"price": 3500,
"currency": "BRL",
"bedrooms": 2,
"bathrooms": 1,
"usableArea": 68,
"neighborhood": "Pinheiros",
"city": "São Paulo",
"state": "SP",
"mediaUrls": ["https://cdn.fndn.ai/..."],
"slot": "destaque_locacao",
"updatedAt": 1713800000
},
"highlights": [],
"text_match": 0
}
],
"facet_counts": []
}

Campos sensíveis (contatos, números exatos de endereço, coordenadas precisas) são automaticamente removidos/mascarados — mesma lógica de sanitização do site público.

Copy por idioma: o documento é o mesmo do Sites API — busca, incluindo title_en, description_en, title_es e description_es. Resolva título/descrição no cliente com fallback para português — veja Título e descrição por idioma.

Hits de lançamento incluem os mesmos campos Typesense da Sites API (isPrimaryMarket, marketType, typologyId, constructionStatus, expectedDelivery, availableUnits, hasAvailableUnits, etc.) — veja Campos de mercado primário na busca.

Em exportações de canal no formato canônico Babelfish, lançamentos com tipologia também podem trazer um bloco extensions.garagem:

{
"extensions": {
"garagem": {
"isPrimaryMarket": true,
"typologyId": "typology-uuid",
"typology": {
"name": "Tipo A - 2 Dormitórios",
"code": "TIP-A",
"description": "...",
"bedrooms": 2,
"bathrooms": 2,
"suites": 1,
"totalArea": 68,
"privateArea": 62,
"garageSpots": 1,
"basePrice": 650000,
"pricePerSqm": 9500,
"features": {},
"files": [],
"totalUnits": 40,
"expectedDelivery": "Dezembro 2026",
"constructionStatus": "structure"
}
}
}
}

extensions.garagem espelha a tipologia no feed de saída; preferência de intake é canonical.primaryMarket em integrações Babelfish. Quartos e área no nível raiz do listing continuam refletindo a tipologia quando isPrimaryMarket é verdadeiro.


Janela do terminal
curl -H "Authorization: Bearer <TOKEN>" \
"https://app.garagem.ai/api/webhooks/channels/chnl_seu_token/listings?page=1&perPage=20"
Janela do terminal
curl -H "Authorization: Bearer <TOKEN>" \
"https://app.garagem.ai/api/webhooks/channels/chnl_seu_token/listings?slot=destaque_locacao&perPage=50"
Janela do terminal
curl -H "Authorization: Bearer <TOKEN>" \
"https://app.garagem.ai/api/webhooks/channels/chnl_seu_token/listings?slot=destaque_venda,destaque_locacao"
Janela do terminal
curl -H "Authorization: Bearer <TOKEN>" \
"https://app.garagem.ai/api/webhooks/channels/chnl_seu_token/listings?slot=none"
Janela do terminal
curl -X POST \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"q": "*",
"page": 1,
"per_page": 20,
"sort_by": "price:asc",
"filter_by": "type:=sale && bedrooms:>=3",
"slots": ["destaque_venda"]
}' \
"https://app.garagem.ai/api/webhooks/channels/chnl_seu_token/listings"
Janela do terminal
curl -H "Authorization: Bearer <TOKEN>" \
"https://app.garagem.ai/api/webhooks/channels/chnl_seu_token/listings/slots"

StatusCodeSignificado
401missing_bearerHeader Authorization: Bearer ausente ou mal formado.
401invalid_bearerToken não confere com o hash armazenado. Pode ter sido rotacionado.
404endpoint_not_foundpathToken inválido ou canal sem saída de imóveis habilitada.
400invalid_bodyBody JSON inválido no POST.
503search_config_incompleteInfra de busca indisponível (coleção Typesense faltando).
503search_key_missingConfiguração de Typesense ausente no servidor.
500search_failedErro inesperado no Typesense.

  • Sanitização automática: mesmo conjunto de campos sensíveis removidos pelo site público (contatos, números de endereço, coordenadas mascaradas em ~100 m).
  • Visibilidade: o filtro base aplica visibility:=public && stage:=active — imóveis privados não vazam pelo endpoint, mesmo sem filtro explícito.
  • Cache: Cache-Control: private, no-store — a resposta depende do token e do tenant, não é cacheada pelo CDN.
  • Rate limit: aplicado no endpoint (reusa channelInboundEndpoint.rateLimits).
  • Logs: toda requisição gera log estruturado (tenantId, channelInstanceId, found, durationMs) — útil pra auditoria.

GET /api/v1/real-estate/listings/api/webhooks/channels/{pathToken}/listings
AuthAPI key do usuário (Bearer)Bearer token do canal
CritériosDefinidos na query stringSalvos no canal (UI)
Slots/DestaqueNãoSim (mesmo modelo dos portais)
EscopoRBAC do usuário (tudo que ele pode ver)Só imóveis que batem os critérios do canal
Ideal paraAutomação interna, scripts do tenantSites parceiros, landing pages, agregadores

Ver também: 05 - Anúncios (API interna por usuário) e 08 - Webhooks e Eventos (inbound de leads).