Pular para o conteúdo

Condomínios por Canal (Importação + Feed)

Endpoint público, por canal, para sistemas externos enviarem condomínios/edifícios em lote para o Garagem (e, opcionalmente, consumi-los de volta em JSON). Usa o mesmo pathToken dos Webhooks de Canais e segue exatamente os mesmos princípios da importação de anúncios.

Quando usar: quando um provedor de dados, construtora ou sistema externo precisa cadastrar e manter atualizados os condomínios (edifícios) de uma imobiliária — em lote, de forma idempotente, sem duplicar registros a cada reenvio.


DireçãoEndpointAuthUso
Entrada (push em lote)POST /api/webhooks/channels/{pathToken}/buildings/importToken do canalCria ou atualiza condomínios em lote
Entrada (pull de feed)GET /api/webhooks/channels/{pathToken}/buildings/import?feedUrl=Token do canalGaragem busca um feed JSON externo e importa
Atualização parcialPATCH /api/webhooks/channels/{pathToken}/buildings/{buildingKey}Token do canalAtualiza um condomínio existente, sem criar
Saída (leitura em lote)GET/POST /api/webhooks/channels/{pathToken}/buildingsBearer de edifíciosConsome os condomínios da imobiliária, paginados

Todo condomínio importado passa pelo mesmo caminho do produto (findOrCreateLocation + upsertBuilding): multi-tenancy, geocodificação e deduplicação se comportam exatamente como no cadastro manual.


POST https://app.garagem.ai/api/webhooks/channels/{pathToken}/buildings/import
  • pathToken — o mesmo token do webhook do canal (ex.: chnl_2xxxxxxxxxxxxxxxxxxxxxxx).

Mesma do webhook de leads do canal (depende do provedor configurado):

  • Bearer Token — Authorization: Bearer sua_api_key
  • API Key — para canais API customizados
  • HMAC Signature — para provedores que assinam o payload

Aceita um array de condomínios, ou um objeto com um dos invólucros buildings, condominios, condominiums, items, data, edificios, ou ainda um único objeto de condomínio.

{
"buildings": [
{
"providerBuildingId": "EDF-1024",
"name": "Edifício Paulista",
"description": "Prédio comercial AAA",
"totalUnits": 120,
"floors": 30,
"numberOfTowers": 2,
"yearBuilt": 2015,
"developer": "Construtora X",
"administrator": "Admin Y",
"unitsPerFloor": 4,
"amenities": {
"piscina": true,
"academia": true,
"portaria24h": true
},
"photos": [
{
"url": "https://cdn.example.com/fachada.jpg",
"description": "Fachada",
"isCover": true
},
"https://cdn.example.com/lazer.jpg"
],
"floorPlans": [
{
"url": "https://cdn.example.com/planta.jpg",
"description": "Planta tipo"
}
],
"location": {
"street": "Avenida Paulista",
"number": "1578",
"neighborhood": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"postalCode": "01310-200",
"latitude": -23.5615,
"longitude": -46.6562
}
}
]
}
CampoTipoObrigatórioDescrição
namestringsimNome do condomínio/edifício.
providerBuildingIdstringrecomendadoID estável do seu sistema. Chave de deduplicação (veja abaixo).
externalId / idstring—Alternativas ao providerBuildingId (usados se ele não for enviado).
providerSourcestring—Origem dos dados. Default: a chave do provedor do canal.
descriptionstring—Descrição livre.
totalUnitsnumber—Total de unidades. Aceita total_units / unidades.
floorsnumber—Andares. Aceita andares.
numberOfTowersnumber—Torres. Aceita torres.
yearBuiltnumber—Ano de construção. Aceita ano.
developerstring—Construtora/incorporadora. Aceita builder / construtora.
administratorstring—Administradora.
publisherstring—Quem comercializa, quando for diferente da construtora.
classificationstring—Classificação de catálogo.
marketSquarestring—Praça. Aceita praca.
launchDatestring (YYYY-MM-DD)—Data de lançamento.
openingDatestring (YYYY-MM-DD)—Data de entrega. Aceita entrega.
buildingPermitstring—Referência de alvará/matrícula.
portfoliostring ou array—Tags de portfólio, por exemplo ["Lançamento"].
videosarray—Vídeos externos (url, source, description, thumbnailUrl). Não são arquivos hospedados.
unitsPerFloornumber—Unidades por andar. Gravado dentro de amenities.
amenities / featuresobjeto ou array—Lazer, segurança e acessibilidade. Veja Amenities.
photosarray—Fotos do condomínio. Veja Fotos. Aceita images / fotos.
floorPlansarray—Plantas. Aceita plantas.
location/address/enderecoobjetosimEndereço. street, city e state são obrigatórios (veja abaixo).

Campos numéricos aceitam string numérica ("120" vira 120). latitude/longitude aceitam lat/lng. postalCode aceita zip/cep. No endereço, street/rua, city/cidade, state/estado/uf e neighborhood/bairro são equivalentes.

O campo complement é aceito e ignorado: o endereço persistido não tem coluna de complemento.

No push, um campo omitido não apaga o valor já salvo. Enviar amenities ou features substitui o objeto de amenities. unitsPerFloor sozinho (inclusive null) atualiza só essa chave e mantém o restante do catálogo. Enviar null num campo de texto não apaga no push — use o PATCH para limpar.

CampoObrigatórioObservação
street / ruasimSem rua/cidade/estado o condomínio é ignorado.
city / cidadesim
state / estado / ufsim
number / numero—Default "".
neighborhood / bairro—Default "".
postalCode / cep / zip—Default "".
country—Default "BR".

Se latitude/longitude não forem enviados, o Garagem tenta geocodificar o endereço (não bloqueante). Um reenvio com endereço novo aponta o condomínio para o endereço resolvido.

A chave estável é (tenant, providerSource, providerBuildingId) — exatamente o que a tabela de edifícios foi desenhada para armazenar:

  • Reenviar o mesmo providerBuildingId atualiza o registro existente (não cria duplicata), mesmo que o endereço tenha mudado entre as sincronizações.
  • Se você não enviar nenhum id estável (providerBuildingId/externalId/id), a deduplicação cai para o endereço (mesma rua/número/bairro/cidade).

Recomendado: sempre envie providerBuildingId. É o que garante que sincronizações repetidas convirjam para o mesmo registro.

{
"ok": true,
"message": "Building import processed",
"results": {
"total": 50,
"created": 12,
"updated": 37,
"skipped": 1,
"errors": 0,
"published": 12
}
}
  • skipped — condomínios sem name ou sem rua/cidade/estado.
  • published — quantos foram propagados para parceiros (veja Sincronização em rede).
Janela do terminal
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sua_api_key" \
"https://app.garagem.ai/api/webhooks/channels/chnl_seu_token/buildings/import" \
-d '{
"buildings": [
{
"providerBuildingId": "EDF-1024",
"name": "Edifício Paulista",
"totalUnits": 120,
"floors": 30,
"location": {
"street": "Avenida Paulista",
"number": "1578",
"neighborhood": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"postalCode": "01310-200"
}
}
]
}'

Em vez de enviar o lote, você pode expor um feed JSON e deixar o Garagem buscá-lo:

GET https://app.garagem.ai/api/webhooks/channels/{pathToken}/buildings/import?feedUrl=https://seu-sistema.com/condominios.json

O Garagem faz GET no feedUrl, extrai os condomínios (mesmos invólucros aceitos no push) e importa com a mesma lógica de deduplicação. A resposta é idêntica à do push.


Se o canal que recebe a importação for uma rede com compartilhamento de condomínios habilitado (enabledResources inclui "buildings"), os condomínios importados são publicados automaticamente na rede — cada membro recebe uma cópia sincronizada.

  • Condomínios novos são publicados na rede (contabilizados em published).
  • Condomínios já vinculados a uma referência de rede disparam o evento building.updated, que propaga a edição para os membros pelo workflow de sincronização — sem dupla publicação.

Veja 07 - Compartilhamento para o modelo de redes.


Fotos e plantas são URLs públicas (https://...). O Garagem guarda a URL como mídia externa do condomínio; não baixa o arquivo para o CDN.

CampoAliasesItem
photosimages, fotos, imagens, mediastring (URL) ou { "url", "description", "isCover", "order", "visibility" }
floorPlansplantas, floor_plansmesmo formato. Em media, "type": "floor_plan" separa a planta das fotos
  • isCover / isPrimary marca a capa. visibility é public (default) ou private.
  • Campo omitido: a galeria atual fica como está.
  • Array enviado: substitui as fotos/plantas de origem externa, na ordem enviada. Uploads feitos no CRM (não externos) permanecem.
  • Array vazio ([]): remove as fotos/plantas externas daquele tipo.

Use URLs estáveis. Trocar a URL é uma foto nova.


O condomínio guarda um objeto JSON. A chave canônica é o id do catálogo de condomínio (piscina, academia, salaoFestas, portaria24h, elevador, …). Aliases em inglês e o rótulo em português são reescritos para esse id:

Você enviaGravado
pool, swimmingPool, Piscinapiscina
gymacademia
concierge24h, 24hConcierge, Portaria 24hportaria24h
partyRoom, Salão de festassalaoFestas
elevatorelevador

Chaves que o catálogo não conhece são gravadas como vieram (heliponto: true), para features próprias do tenant. Valores aceitam true/false, 1/0 e "sim"/"não".

Formatos aceitos, sozinhos ou juntos:

"amenities": { "pool": true, "leisure": { "piscinaInfantil": true } }
"features": ["academia", "Portaria 24h", { "id": "coworking", "value": false }]

No push, o objeto enviado em amenities ou features substitui amenities. No PATCH, as chaves são mescladas; "piscina": null remove só essa chave, e "amenities": null zera o objeto antes de aplicar features.

unitsPerFloor pode ir na raiz ou dentro de amenities. Na raiz, sem amenities nem features, ele só mescla essa chave — no push e no PATCH. null remove a chave e deixa o catálogo.


Use quando só uma parte do condomínio mudou. O PATCH não cria registro.

PATCH https://app.garagem.ai/api/webhooks/channels/{pathToken}/buildings/{buildingKey}

buildingKey é o providerBuildingId (URL-encoded) ou o UUID do condomínio no Garagem. A autenticação é a mesma do push, não o bearer de leitura.

Janela do terminal
curl -X PATCH \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sua_api_key" \
"https://app.garagem.ai/api/webhooks/channels/chnl_seu_token/buildings/EDF-1024" \
-d '{
"description": "Torre reformada em 2024",
"developer": null,
"amenities": { "coworking": true, "piscina": null },
"photos": [
{ "url": "https://cdn.example.com/fachada-nova.jpg", "isCover": true }
],
"location": {
"street": "Avenida Paulista",
"number": "1600"
}
}'
  • Campo omitido: permanece.
  • null em campo anulável (description, developer, administrator, datas, portfolio, videos): apaga.
  • name em branco é rejeitado. O nome não pode ser apagado.
  • location completa o endereço já salvo com as partes enviadas e resolve uma localização nova ou existente. O condomínio passa a apontar para ela. A linha de localização compartilhada com outros imóveis não é reescrita. O PATCH não geocodifica; latitude e longitude juntos entram na localização resolvida quando ela ainda não tem pino. String vazia no endereço é rejeitada.
  • Fotos seguem a regra da seção Fotos.
{
"ok": true,
"message": "Building patched",
"id": "uuid-do-condominio",
"providerBuildingId": "EDF-1024",
"updated": ["description", "developer", "amenities", "location", "photos"]
}
StatuserrorQuando
404not_foundNenhum condomínio com esse id no tenant.
409ambiguousO providerBuildingId casa com mais de um registro.
400empty_patchCorpo sem campos reconhecidos.
400invalid_namename vazio ou null.
400invalid_locationEndereço em branco, ou só uma coordenada.
400invalid_amenitiesamenities/features não é objeto nem array.
400invalid_mediaphotos/floorPlans não é array nem null.
400invalid_payloadJSON inválido ou chave vazia.

O limite de taxa é o mesmo do push (default 600 requisições/minuto por canal).


Complemento simétrico: um parceiro pode consumir os condomínios da imobiliária, paginados.

GET https://app.garagem.ai/api/webhooks/channels/{pathToken}/buildings?page=1&perPage=50
POST https://app.garagem.ai/api/webhooks/channels/{pathToken}/buildings { "page": 1, "perPage": 50 }
Authorization: Bearer <TOKEN>
  • Bearer dedicado para edifícios (distinto do bearer de listings). Gerado/rotacionado nas configurações do canal; exibido apenas uma vez.
ParâmetroTipoDescrição
pagenumberPágina (1-indexed, default 1).
perPagenumberItens por página (máx. 100, default 50).
{
"buildings": [
{
"id": "uuid-do-condominio",
"name": "Edifício Paulista",
"description": "Prédio comercial AAA",
"totalUnits": 120,
"floors": 30,
"numberOfTowers": 2,
"yearBuilt": 2015,
"developer": "Construtora X",
"administrator": "Admin Y",
"amenities": { "piscina": true, "unitsPerFloor": 4 },
"photos": [
{
"url": "https://cdn.example.com/fachada.jpg",
"description": "Fachada",
"order": 0,
"isPrimary": true,
"visibility": "public"
}
],
"floorPlans": [],
"providerSource": "meu-sistema",
"providerBuildingId": "EDF-1024",
"location": {
"street": "Avenida Paulista",
"number": "1578",
"neighborhood": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"postalCode": "01310-200",
"country": "BR",
"coordinates": { "x": -46.6562, "y": -23.5615 }
}
}
],
"total": 84,
"page": 1,
"perPage": 50
}

StatusCodeSignificado
404not_foundpathToken inválido.
401(verificação de auth)Assinatura/token inválido na importação.
400no_buildings_foundNenhum condomínio encontrado no payload (push).
400feedUrl parameter requiredfeedUrl ausente no pull.
500no_system_userCanal sem usuário de sistema para gravar os dados.
401missing_bearerHeader Authorization: Bearer ausente (leitura de saída).
401invalid_bearerToken não confere (leitura de saída).
404endpoint_not_foundCanal sem saída de edifícios habilitada (leitura de saída).
429rate_limitedLimite de requisições atingido (leitura, push ou PATCH).
500query_failedErro inesperado ao listar condomínios (leitura de saída).

  • Idempotência por (tenant, providerSource, providerBuildingId) — reenvios não duplicam. O PATCH só altera um registro já existente.
  • Cache: Cache-Control: private, no-store na leitura de saída.
  • Rate limit: push e PATCH compartilham 600 req/min por canal (BUILDINGS_IMPORT_RPM). A leitura reusa channelInboundEndpoint.rateLimits.
  • Logs: cada importação e cada PATCH geram um log estruturado no canal.

Ver também: 08 - Webhooks e Eventos (inbound de leads) e 13 - Listings por Canal (feed de anúncios).