@horizon-integrations/tecimob-crm
v1.1.4
Published
Adapter de integração com o CRM Tecimob — padrão Airbyte (@horizon-js/integrations-core)
Downloads
54
Maintainers
Readme
@horizon-integrations/tecimob-crm
Adapter de integração com o CRM Tecimob, seguindo o padrão Airbyte do
@horizon-js/integrations-core.
Lê imóveis da API pública da Tecimob e devolve no formato canônico
HorizonProperty. Só-leitura.
pnpm add @horizon-integrations/tecimob-crmUso rápido
import { fetchAll, getListing, fetchByRef } from "@horizon-integrations/tecimob-crm"
const credentials = { token: process.env.TECIMOB_TOKEN! }
// Todos os imóveis, já convertidos (inclui busca de imagens)
const { properties, errors } = await fetchAll({ credentials })
// Lista leve pra delta
const index = await getListing({ credentials })
// Um imóvel — aceita o UUID ou o código legível (ex: "APB1344")
const imovel = await fetchByRef({ credentials }, "APB1344")Ou via contrato Airbyte, com streaming:
import { TecimobSource } from "@horizon-integrations/tecimob-crm"
import { SyncMode } from "@horizon-js/integrations-core"
const source = new TecimobSource()
const { ok, message } = await source.check(credentials)
if (!ok) throw new Error(message)
const [stream] = source.streams(credentials)
for await (const property of stream.readRecords(SyncMode.FULL_REFRESH)) {
await db.properties.upsert(property)
}Credenciais
Um único campo — o token da API pública, gerado no painel Tecimob da imobiliária:
{ token: "<uuid>|<hash>" }O segredo é a string inteira, incluindo o | (formato Laravel Sanctum). Vai
no header Authorization: Bearer <token>.
Duas coisas que você precisa saber antes de plugar
1. Não existe sync incremental (TEC-001)
A API Tecimob não expõe timestamp de atualização — não há updated_at nem
created_at em imóvel. Consequências:
- o stream suporta apenas
FULL_REFRESH; getListing()devolveupdatedAt: nullsempre;- o delta tem que ser por
sync_hash: compare o hash do record convertido com o que você guardou. É o único sinal de mudança disponível.
O sync_hash é determinístico e foi verificado estável entre execuções (nenhum
dos 290 imóveis da base de referência mudou de hash entre dois fetchAll).
2. As imagens só existem no detalhe (TEC-002)
GET /properties (listagem) não retorna images. O adapter resolve isso
buscando o detalhe de cada imóvel em paralelo — já é automático em fetchAll()
e readRecords(). Medido: 290 imóveis em ~7s com concorrência 8, sem 429.
Se um detalhe falhar, o imóvel segue no lote (sem imagens) com console.warn —
uma falha isolada não derruba o sync.
3. Em lead, mande phone_number sempre que houver (TEC-022)
A API deduplica corretamente por telefone (faz upsert e devolve o id da pessoa existente), mas devolve HTTP 500 quando o e-mail já existe.
O writer contorna isso: antes de criar, procura a pessoa por telefone/e-mail; se
já existe, vincula os imóveis de interesse (relate-person) ou registra uma
anotação, em vez de chamar o endpoint que quebra. O outcome do retorno diz o
que aconteceu:
| outcome | Significado |
|---|---|
| created | pessoa nova cadastrada |
| related | já existia → imóveis de interesse vinculados |
| noted | já existia → contato registrado como anotação |
| already-exists | já existia e não havia o que registrar |
Erro de negócio não lança: devolve { success: false, error } pra não
derrubar o formulário do site. Também valide o telefone antes de enviar — número
implausível é reprovado com 422 e derruba o lead inteiro (TEC-023).
O que o adapter faz por você
| Peculiaridade da API | O que o adapter faz |
|---|---|
| Valores como "R$ 1.480,00" | parseBRL() → número |
| Sem campo de título | Compõe determinístico: "Casa à venda em Centro, Rio Negro - PR" |
| 68% da base são imóveis excluídos | Filtra por status; nunca busca excluido |
| filter[status] aceita 1 valor só | Uma passada de paginação por status |
| Paginação repete registros | Dedup por id |
| Endereço aninhado em neighborhood.city.state | Achata em endereco_* |
| rooms[] / areas[] como arrays | Vira dormitorios, area_total, etc. |
| Enums não documentados | Preserva desconhecidos em *_adicionais (TEC-015) |
| Detalhe traz CPF/telefone do proprietário | Nunca converte dado pessoal (TEC-007) |
Privacidade
O endpoint de detalhe devolve dados do proprietário (person: nome,
CPF/CNPJ, e-mail, telefones, endereço) e anotações internas da imobiliária
(private_note, negotiation_note, keys_location, matriculation…).
Esses campos nunca são convertidos — existem no schema de entrada só pra validação não quebrar. O payload de saída é seguro pra vitrine pública. Há teste automatizado garantindo isso.
Dados de contato do corretor (corretor_email, corretor_telefone,
corretor_creci) são convertidos: são públicos por natureza.
Documentação
docs/PLUGAR_NUM_SITE.md— vai instalar num site cliente? comece aquidocs/API_REFERENCE.md— métodos públicos do pacotedocs/MODELO_TECIMOB.md— o que o modelo comporta e o que não comportadocs/API_TECIMOB.md— a API externa, testada campo a campodocs/FICHA_TECNICA_INTEGRACAO.md— mapeamento campo-a-campo + cobertura realdocs/LEAD_SENDER.md— envio de leads pro CRMdocs/ORGANIZACAO_LISTAS.md— como organizar as listas de exibição no frontendtecimobManifest.knownIssues— as peculiaridades da API, com workaround de cada
Licença
MIT
