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.
Visão geral
Seção intitulada “Visão geral”| Direção | Endpoint | Auth | Uso |
|---|---|---|---|
| Entrada (push em lote) | POST /api/webhooks/channels/{pathToken}/buildings/import | Token do canal | Cria ou atualiza condomínios em lote |
| Entrada (pull de feed) | GET /api/webhooks/channels/{pathToken}/buildings/import?feedUrl= | Token do canal | Garagem busca um feed JSON externo e importa |
| Atualização parcial | PATCH /api/webhooks/channels/{pathToken}/buildings/{buildingKey} | Token do canal | Atualiza um condomínio existente, sem criar |
| Saída (leitura em lote) | GET/POST /api/webhooks/channels/{pathToken}/buildings | Bearer de edifícios | Consome 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.
Importação em lote (push)
Seção intitulada “Importação em lote (push)”POST https://app.garagem.ai/api/webhooks/channels/{pathToken}/buildings/importpathToken— o mesmo token do webhook do canal (ex.:chnl_2xxxxxxxxxxxxxxxxxxxxxxx).
Autenticação
Seção intitulada “Autenticação”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
Formato do corpo
Seção intitulada “Formato do corpo”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 } } ]}Campos por condomínio
Seção intitulada “Campos por condomínio”| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | sim | Nome do condomínio/edifício. |
providerBuildingId | string | recomendado | ID estável do seu sistema. Chave de deduplicação (veja abaixo). |
externalId / id | string | — | Alternativas ao providerBuildingId (usados se ele não for enviado). |
providerSource | string | — | Origem dos dados. Default: a chave do provedor do canal. |
description | string | — | Descrição livre. |
totalUnits | number | — | Total de unidades. Aceita total_units / unidades. |
floors | number | — | Andares. Aceita andares. |
numberOfTowers | number | — | Torres. Aceita torres. |
yearBuilt | number | — | Ano de construção. Aceita ano. |
developer | string | — | Construtora/incorporadora. Aceita builder / construtora. |
administrator | string | — | Administradora. |
publisher | string | — | Quem comercializa, quando for diferente da construtora. |
classification | string | — | Classificação de catálogo. |
marketSquare | string | — | Praça. Aceita praca. |
launchDate | string (YYYY-MM-DD) | — | Data de lançamento. |
openingDate | string (YYYY-MM-DD) | — | Data de entrega. Aceita entrega. |
buildingPermit | string | — | Referência de alvará/matrícula. |
portfolio | string ou array | — | Tags de portfólio, por exemplo ["Lançamento"]. |
videos | array | — | Vídeos externos (url, source, description, thumbnailUrl). Não são arquivos hospedados. |
unitsPerFloor | number | — | Unidades por andar. Gravado dentro de amenities. |
amenities / features | objeto ou array | — | Lazer, segurança e acessibilidade. Veja Amenities. |
photos | array | — | Fotos do condomínio. Veja Fotos. Aceita images / fotos. |
floorPlans | array | — | Plantas. Aceita plantas. |
location/address/endereco | objeto | sim | Endereç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.
Endereço (location, address ou endereco)
Seção intitulada “Endereço (location, address ou endereco)”| Campo | Obrigatório | Observação |
|---|---|---|
street / rua | sim | Sem rua/cidade/estado o condomínio é ignorado. |
city / cidade | sim | |
state / estado / uf | sim | |
number / numero | — | Default "". |
neighborhood / bairro | — | Default "". |
postalCode / cep / zip | — | Default "". |
country | — | Default "BR". |
Se
latitude/longitudenã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.
Deduplicação e idempotência
Seção intitulada “Deduplicação e idempotência”A chave estável é (tenant, providerSource, providerBuildingId) — exatamente o que a tabela de edifícios foi desenhada para armazenar:
- Reenviar o mesmo
providerBuildingIdatualiza 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.
Resposta
Seção intitulada “Resposta”{ "ok": true, "message": "Building import processed", "results": { "total": 50, "created": 12, "updated": 37, "skipped": 1, "errors": 0, "published": 12 }}skipped— condomínios semnameou sem rua/cidade/estado.published— quantos foram propagados para parceiros (veja Sincronização em rede).
Exemplo
Seção intitulada “Exemplo”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" } } ] }'Importação por feed (pull)
Seção intitulada “Importação por feed (pull)”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.jsonO 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.
Sincronização em rede (fan-out)
Seção intitulada “Sincronização em rede (fan-out)”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
Seção intitulada “Fotos e plantas”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.
| Campo | Aliases | Item |
|---|---|---|
photos | images, fotos, imagens, media | string (URL) ou { "url", "description", "isCover", "order", "visibility" } |
floorPlans | plantas, floor_plans | mesmo formato. Em media, "type": "floor_plan" separa a planta das fotos |
isCover/isPrimarymarca a capa.visibilityépublic(default) ouprivate.- 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.
Amenities e features
Seção intitulada “Amenities e features”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ê envia | Gravado |
|---|---|
pool, swimmingPool, Piscina | piscina |
gym | academia |
concierge24h, 24hConcierge, Portaria 24h | portaria24h |
partyRoom, Salão de festas | salaoFestas |
elevator | elevador |
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.
Atualização parcial (PATCH)
Seção intitulada “Atualização parcial (PATCH)”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.
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.
nullem campo anulável (description,developer,administrator, datas,portfolio,videos): apaga.nameem branco é rejeitado. O nome não pode ser apagado.locationcompleta 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;latitudeelongitudejuntos 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"]}| Status | error | Quando |
|---|---|---|
| 404 | not_found | Nenhum condomínio com esse id no tenant. |
| 409 | ambiguous | O providerBuildingId casa com mais de um registro. |
| 400 | empty_patch | Corpo sem campos reconhecidos. |
| 400 | invalid_name | name vazio ou null. |
| 400 | invalid_location | Endereço em branco, ou só uma coordenada. |
| 400 | invalid_amenities | amenities/features não é objeto nem array. |
| 400 | invalid_media | photos/floorPlans não é array nem null. |
| 400 | invalid_payload | JSON inválido ou chave vazia. |
O limite de taxa é o mesmo do push (default 600 requisições/minuto por canal).
Leitura em lote (saída)
Seção intitulada “Leitura em lote (saída)”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=50POST https://app.garagem.ai/api/webhooks/channels/{pathToken}/buildings { "page": 1, "perPage": 50 }Autenticação
Seção intitulada “Autenticação”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âmetros
Seção intitulada “Parâmetros”| Parâmetro | Tipo | Descrição |
|---|---|---|
page | number | Página (1-indexed, default 1). |
perPage | number | Itens por página (máx. 100, default 50). |
Resposta
Seção intitulada “Resposta”{ "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}Códigos de Erro
Seção intitulada “Códigos de Erro”| Status | Code | Significado |
|---|---|---|
| 404 | not_found | pathToken inválido. |
| 401 | (verificação de auth) | Assinatura/token inválido na importação. |
| 400 | no_buildings_found | Nenhum condomínio encontrado no payload (push). |
| 400 | feedUrl parameter required | feedUrl ausente no pull. |
| 500 | no_system_user | Canal sem usuário de sistema para gravar os dados. |
| 401 | missing_bearer | Header Authorization: Bearer ausente (leitura de saída). |
| 401 | invalid_bearer | Token não confere (leitura de saída). |
| 404 | endpoint_not_found | Canal sem saída de edifícios habilitada (leitura de saída). |
| 429 | rate_limited | Limite de requisições atingido (leitura, push ou PATCH). |
| 500 | query_failed | Erro inesperado ao listar condomínios (leitura de saída). |
Segurança e Limites
Seção intitulada “Segurança e Limites”- Idempotência por
(tenant, providerSource, providerBuildingId)— reenvios não duplicam. O PATCH só altera um registro já existente. - Cache:
Cache-Control: private, no-storena leitura de saída. - Rate limit: push e PATCH compartilham 600 req/min por canal (
BUILDINGS_IMPORT_RPM). A leitura reusachannelInboundEndpoint.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).