/** * SDK oficial da API ImobyFlow — TypeScript / Node 18+ (versão do contrato 1.0.0). * Official ImobyFlow API SDK — TypeScript / Node 18+. * * Gerado do mesmo contrato do portal: https://imobyflow.com.br/desenvolvedores * Uso / usage: * * import { ImobyFlow } from './imobyflow'; * const api = new ImobyFlow(process.env.IMOBYFLOW_API_KEY!); * const { data } = await api.getMe(); * for await (const imovel of api.listPropertiesAll({ status: 'ACTIVE' })) console.log(imovel.id); * * A chave é SÓ do servidor: nunca a ponha no navegador do seu cliente. * The key is SERVER-SIDE only: never ship it to your end user's browser. */ import { createHmac, randomUUID, timingSafeEqual } from 'node:crypto'; export const API_VERSION = "1.0.0"; /** Erro no formato RFC 9457 (`application/problem+json`). · Error in RFC 9457 format (`application/problem+json`). */ export interface Problem { /** Endereço da página deste erro, com a causa e o conserto. · URL of this error page, with the cause and the fix. */ type: string; /** O nome do erro, na língua do `Accept-Language`. · Error name, in the `Accept-Language` language. */ title: string; /** O código HTTP. · The HTTP status code. */ status: number; /** Código estável — é por ele que o seu sistema decide o que fazer. · Stable code — your system should branch on it. */ code: string; /** Id da requisição. Mande junto quando falar com o suporte. · Request id. Include it when contacting support. */ requestId: string; /** Explicação do caso (quando houver). · Explanation of this case (when available). */ detail?: string; /** Todos os problemas do pedido de uma vez — não só o primeiro. · All problems in the request at once — not just the first one. */ invalidParams?: Array<{ name: string; reason: string; [key: string]: unknown; }>; } /** Os limites da faixa de uso da conta. · The account's usage tier limits. */ export interface Limits { /** Chamadas por segundo. · Calls per second. */ ratePerSecond: number; /** Pico tolerado num instante. · Short burst allowed. */ burst: number; /** Chamadas por mês (mês de calendário, UTC). · Calls per month (calendar month, UTC). */ monthlyQuota: number; /** Chaves ativas ao mesmo tempo. · Active keys at the same time. */ maxKeys: number; /** Endereços de webhook. · Webhook endpoints. */ maxWebhooks: number; } /** Quem é a conta, o que a chave pode fazer e quanto da cota já foi usado. · Who the account is, what the key can do and how much of the quota is used. */ export interface Me { /** A conta dona da chave. · The account that owns the key. */ account: { id: string; name: string | null; type: string | null; plan: string | null; }; /** A liberação da API. · API access. */ access: { status: string | null; tier: string; pilotUntil: string | null; limits: Limits; }; /** A chave usada nesta chamada. · The key used in this call. */ key: { id: string | null; name: string | null; scopes: Array<"properties:read" | "properties:write" | "leads:read" | "leads:write" | "radar:read" | "radar:write" | "partnerships:read" | "partnerships:write" | "team:read" | "launches:read" | "launches:write" | "captacao:read" | "captacao:accept" | "events:read" | "webhooks:manage">; expiresAt: string | null; }; /** O uso do mês. · Monthly usage. */ usage: { month: string; calls: number; monthlyQuota: number; remaining: number; }; /** A cota de imóveis do plano. · The plan's property quota. */ properties: { active: number; limit: number | null; }; } /** Um valor aceito e o seu rótulo. · An accepted value and its label. */ export interface ValueLabel { /** O valor que vai no campo. · The value to send in the field. */ value: string; /** O rótulo, na língua do `Accept-Language`. · The label, in the `Accept-Language` language. */ label: string; } /** Uma categoria de imóvel e os seus subtipos. · A property category and its subtypes. */ export interface PropertyTypeGroup { /** A categoria. · The category. */ category: "RESIDENCIAL" | "COMERCIAL" | "TERRENO" | "RURAL"; /** Rótulo da categoria. · Category label. */ label: string; /** Os subtipos da categoria — é o subtipo que vai em `type`. · The subtypes in the category — the subtype is what goes in `type`. */ types: Array; } /** Uma permissão que uma chave pode ter. · A permission a key can have. */ export interface ScopeInfo { /** A permissão. · The permission. */ id: "properties:read" | "properties:write" | "leads:read" | "leads:write" | "radar:read" | "radar:write" | "partnerships:read" | "partnerships:write" | "team:read" | "launches:read" | "launches:write" | "captacao:read" | "captacao:accept" | "events:read" | "webhooks:manage"; /** O grupo, para exibir. · The group, for display. */ group: string; /** O nome, para exibir. · The name, for display. */ label: string; /** O que ela permite. · What it allows. */ description: string; } /** Um tipo de evento. · An event type. */ export interface EventTypeInfo { /** O tipo do evento. · The event type. */ type: "property.created" | "property.updated" | "property.deleted" | "property.restored" | "property.purged" | "lead.created" | "lead.updated" | "lead.deleted" | "lead.restored" | "lead.purged" | "radar.client_summary" | "partnership.created" | "partnership.updated" | "captacao.offer_received" | "captacao.offer_accepted" | "captacao.offer_withdrawn"; /** A permissão de leitura que dá acesso a ele. · The read permission that grants access to it. */ scope: "properties:read" | "properties:write" | "leads:read" | "leads:write" | "radar:read" | "radar:write" | "partnerships:read" | "partnerships:write" | "team:read" | "launches:read" | "launches:write" | "captacao:read" | "captacao:accept" | "events:read" | "webhooks:manage"; /** O que ele avisa. · What it signals. */ description: string; } /** Uma cidade do catálogo. · A catalog city. */ export interface City { /** Código do IBGE, 7 dígitos — a forma mais segura de mandar a cidade. · IBGE code, 7 digits — the safest way to send the city. */ cityId: string; /** Nome da cidade. · City name. */ name: string; /** Estado (UF). · State (UF). */ uf: string; /** Identificador da cidade no catálogo. · City identifier in the catalog. */ citySlug: string; } /** Um bairro do catálogo. · A catalog neighborhood. */ export interface Neighborhood { /** O identificador do bairro — o valor de `neighborhoodSlug` na escrita. · The neighborhood identifier — the `neighborhoodSlug` value for writes. */ neighborhoodSlug: string; /** Nome do bairro. · Neighborhood name. */ name: string; } /** O endereço completo — o imóvel é da própria conta. · The full address — the property belongs to the account itself. */ export interface Address { /** Nome do bairro. · Neighborhood name. */ neighborhood: string | null; /** Bairro do catálogo. Nulo quando o bairro ainda não foi reconhecido — aí o imóvel fica fora das recomendações até a curadoria decidir. · Catalog neighborhood. Null when the neighborhood is not recognized yet — the property stays out of recommendations until curation decides. */ neighborhoodSlug: string | null; /** Nome da cidade. · City name. */ city: string | null; /** Código do IBGE. · IBGE code. */ cityId: string | null; /** Identificador da cidade no catálogo. · City identifier in the catalog. */ citySlug: string | null; /** Estado (UF). · State (UF). */ state: string | null; /** Rua. · Street. */ street: string | null; /** Número. · Number. */ number: string | null; /** Complemento. · Complement. */ complement: string | null; } /** O endereço até o bairro. · The address down to the neighborhood. */ export interface AddressArea { /** Nome do bairro. · Neighborhood name. */ neighborhood: string | null; /** Bairro do catálogo. · Catalog neighborhood. */ neighborhoodSlug: string | null; /** Nome da cidade. · City name. */ city: string | null; /** Código do IBGE. · IBGE code. */ cityId: string | null; /** Identificador da cidade no catálogo. · City identifier in the catalog. */ citySlug: string | null; /** Estado (UF). · State (UF). */ state: string | null; } /** As condições para parceiros. · Terms for partners. */ export interface PartnershipTerms { /** Comissão de venda oferecida a parceiros, em %. · Sale commission offered to partners, in %. */ commissionPercent: number | null; /** Comissão de locação, em aluguéis. · Rent commission, in months of rent. */ rentCommissionMonths: number | null; /** Parte da comissão que fica com quem captou, em %. · Share of the commission kept by the listing side, in %. */ splitListingPercent: number | null; } /** A situação do imóvel nas recomendações (Radar). · The property's status in recommendations (Radar). */ export interface PropertyRadar { /** Entra nas recomendações agora (ativo e sem pendência). · Is in recommendations right now (active and with no pending item). */ eligible: boolean; /** O que falta para entrar nas recomendações. A capa é obrigatória. · What is missing to enter recommendations. A cover photo is required. */ pending: Array<"CITY_MISSING" | "CITY_NOT_IN_CATALOG" | "NEIGHBORHOOD_MISSING" | "NEIGHBORHOOD_NOT_IN_CATALOG" | "TYPE_MISSING" | "PURPOSE_MISSING" | "SALE_PRICE_MISSING" | "RENT_PRICE_MISSING" | "COVER_PHOTO_MISSING" | "OTHER">; /** As mesmas pendências, por extenso. · The same pending items, spelled out. */ labels: Array; } /** A análise do imóvel pela ImobyFlow antes de ele aparecer para parceiros. Imóvel novo pela API nasce em análise. · ImobyFlow's review before the property is shown to partners. New properties from the API start under review. */ export interface Curation { /** `PENDING`, `APPROVED` ou `REJECTED`. · `PENDING`, `APPROVED` or `REJECTED`. */ status: string | null; /** O motivo da recusa, em código. · The rejection reason, as a code. */ reason: "LANCAMENTO" | "SEM_VALOR" | "QUALIDADE_BAIXA" | "NAO_ANGARIACAO" | "MARCA_DAGUA" | "FOTOS_INSUFICIENTES" | "DADOS_INCOERENTES" | "OUTRO" | null; /** O motivo por extenso. · The reason, spelled out. */ label: string | null; } /** A régua dos 60 dias: imóvel não confirmado sai do ar. Gravar o imóvel pela API (ou confirmar) renova a data. · The 60-day rule: unconfirmed properties go offline. Writing the property through the API (or confirming it) renews the date. */ export interface Confirmation { /** Última confirmação de que segue disponível. · Last confirmation that it is still available. */ lastConfirmedAt: string | null; /** Quando a confirmação vence (a cada 60 dias). · When the confirmation is due (every 60 days). */ dueAt: string | null; /** Quando o aviso de confirmação foi mandado. · When the confirmation notice was sent. */ noticeAt: string | null; /** Quando sai do ar sem resposta (7 dias depois do aviso). · When it goes offline without an answer (7 days after the notice). */ offlineAt: string | null; } /** Dados de lançamento (empreendimento e tipologia). · New-development data (development and unit type). */ export interface LaunchBlock { /** Estágio da obra. · Construction stage. */ constructionStage: "LAUNCH" | "OFF_PLAN" | "NEW" | "READY" | null; /** Previsão de entrega. · Expected delivery. */ deliveryDate: string | null; /** Andamento da obra, em %. · Construction progress, in %. */ constructionProgress: number | null; /** Unidades no total. · Total units. */ unitsTotal: number | null; /** Unidades disponíveis. · Available units. */ unitsAvailable: number | null; /** Unidades reservadas. · Reserved units. */ unitsReserved: number | null; /** Unidades vendidas. · Sold units. */ unitsSold: number | null; /** As plantas. · The floor plans. */ floorPlans: Array; } /** O download das fotos mandadas por endereço. Ele acontece depois da resposta — é aqui que você vê se a galeria entrou. · Download of the photos sent by URL. It happens after the response — this is where you see whether the gallery made it. */ export interface PhotoSync { /** `QUEUED` (na fila), `DONE`, `PARTIAL` (algumas falharam), `FAILED` ou `NOT_QUEUED` (a fila recusou — o próximo pedido tenta de novo). · `QUEUED`, `DONE`, `PARTIAL` (some failed), `FAILED` or `NOT_QUEUED` (the queue refused — the next request retries). */ state: string; /** Fotos pedidas. · Photos requested. */ requested: number | null; /** Fotos baixadas e gravadas. · Photos downloaded and stored. */ ingested: number | null; /** Fotos que falharam. · Photos that failed. */ failed: number | null; /** Quando entrou na fila. · When it was queued. */ queuedAt: string | null; /** Quando terminou. · When it finished. */ finishedAt: string | null; } /** Um imóvel da carteira da conta. · A property in the account's portfolio. */ export interface Property { /** Id do imóvel. · Property id. */ id: string; /** O código do imóvel no SEU sistema. · The property's code in YOUR system. */ externalRef: string | null; /** `PROPERTY` (avulso), `DEVELOPMENT` (empreendimento) ou `TYPOLOGY` (tipologia de um empreendimento). · `PROPERTY` (standalone), `DEVELOPMENT` (new development) or `TYPOLOGY` (unit type of a development). */ kind: "PROPERTY" | "DEVELOPMENT" | "TYPOLOGY"; /** O empreendimento desta tipologia. · The development of this unit type. */ developmentId: string | null; /** Situação. `DELETED` = na lixeira (30 dias). · Status. `DELETED` = in the trash (30 days). */ status: "ACTIVE" | "INACTIVE" | "SOLD" | "RENTED" | "DELETED"; /** Subtipo (veja as listas de valores). · Subtype (see the reference lists). */ type: "APARTAMENTO" | "CASA_RUA" | "CASA_CONDOMINIO" | "CASA_VILA" | "STUDIO" | "FLAT" | "COBERTURA" | "SALA_COMERCIAL" | "LOJA_PONTO" | "GALPAO" | "PREDIO" | "TERRENO_RUA" | "TERRENO_CONDOMINIO" | "CHACARA_SITIO" | "FAZENDA" | null; /** Categoria do subtipo. · The subtype's category. */ category: "RESIDENCIAL" | "COMERCIAL" | "TERRENO" | "RURAL" | null; /** Finalidades: `VENDA`, `LOCACAO` ou as duas. · Purposes: `VENDA` (sale), `LOCACAO` (rent) or both. */ purposes: Array<"VENDA" | "LOCACAO">; /** Preço de venda, em reais. · Sale price, in BRL. */ salePrice: number | null; /** Aluguel mensal, em reais. · Monthly rent, in BRL. */ rentPrice: number | null; /** Título do anúncio. · Listing title. */ title: string | null; /** Descrição, em texto corrido. · Description, as plain text. */ description: string | null; /** Endereço. · Address. */ address: Address; /** Quartos. · Bedrooms. */ bedrooms: number | null; /** Suítes. · Suites. */ suites: number | null; /** Banheiros. · Bathrooms. */ bathrooms: number | null; /** Vagas de garagem. · Parking spots. */ parkingSpots: number | null; /** Área privativa total, em m² — a que se compara entre imóveis. · Total private area, in m² — the comparable one. */ area: number | null; /** Área coberta, em m² (só exibição). · Covered area, in m² (display only). */ privateArea: number | null; /** Itens do imóvel e do condomínio. · Property and building amenities. */ amenities: Array; /** A capa (a 1ª foto). Sem capa o imóvel não entra nas recomendações. · The cover (1st photo). Without a cover the property is left out of recommendations. */ coverPhoto: string | null; /** A galeria, na ordem. · The gallery, in order. */ photos: Array; /** Endereço do anúncio no seu site. · Listing URL on your website. */ listingUrl: string | null; /** O corretor responsável da equipe. · The responsible broker on the team. */ captador: { accountId: string; name: string | null; } | null; /** Nome do captador em texto (quem não está na equipe). · Listing agent name as text (someone not on the team). */ captadorName: string | null; /** Compartilhado com parceiros. · Shared with partners. */ sharedToNetwork: boolean; /** Condições para parceiros. · Partner terms. */ partnershipTerms: PartnershipTerms; /** Situação nas recomendações. · Recommendation status. */ radar: PropertyRadar; /** Análise da ImobyFlow. · ImobyFlow's review. */ curation: Curation; /** A confirmação de disponibilidade. · Availability confirmation. */ confirmation: Confirmation; /** Por que está arquivado (só com `status` = `INACTIVE`). · Why it is archived (only when `status` = `INACTIVE`). */ inactiveReason: string | null; /** Quando foi para a lixeira. · When it was moved to the trash. */ deletedAt: string | null; /** Dados de lançamento (só empreendimento e tipologia). · New-development data (development and unit type only). */ launch: LaunchBlock | null; /** O download das fotos mandadas por endereço. · Download of the photos sent by URL. */ photoSync: PhotoSync | null; /** Quando foi criado (UTC). · When it was created (UTC). */ createdAt: string; /** Última alteração (UTC). · Last change (UTC). */ updatedAt: string; } /** O endereço. Vem inteiro quando vem. · The address. Send it whole when you send it. */ export interface AddressInput { /** Código do IBGE (7 dígitos). Com ele, `city` e `state` são dispensáveis. · IBGE code (7 digits). With it, `city` and `state` are not needed. */ cityId?: string; /** Nome da cidade (com `state`). Nome desconhecido volta 400 com sugestões. · City name (with `state`). An unknown name returns 400 with suggestions. */ city?: string; /** UF, 2 letras. · State (UF), 2 letters. */ state?: string; /** Bairro pelo identificador do catálogo — o caminho garantido. · Neighborhood by catalog identifier — the guaranteed path. */ neighborhoodSlug?: string; /** Bairro em texto. Ele passa pelo dicionário de apelidos; o que não for reconhecido fica pendente para a curadoria, nunca vira um bairro inventado. · Neighborhood as text. It goes through the alias dictionary; anything unrecognized waits for curation and never becomes a made-up neighborhood. */ neighborhood?: string; /** Rua. · Street. */ street?: string; /** Número. · Number. */ number?: string; /** Complemento. · Complement. */ complement?: string; } /** Os campos que a API aceita no imóvel. Campo desconhecido é erro (400). Campo ausente não apaga; para limpar, mande `null`. · The fields the API accepts for a property. Unknown fields are an error (400). Absent fields are kept; to clear one, send `null`. */ export interface PropertyInput { /** O código do imóvel no seu sistema (só no POST; no PUT ele vai no caminho). · The property's code in your system (POST only; in PUT it goes in the path). */ externalRef?: string; /** Título. Marcação HTML é removida. · Title. HTML markup is stripped. */ title?: string; /** Descrição. `
` e `

` viram quebra de linha; o resto da marcação sai. · Description. `
` and `

` become line breaks; other markup is stripped. */ description?: string; /** Subtipo. · Subtype. */ type?: "APARTAMENTO" | "CASA_RUA" | "CASA_CONDOMINIO" | "CASA_VILA" | "STUDIO" | "FLAT" | "COBERTURA" | "SALA_COMERCIAL" | "LOJA_PONTO" | "GALPAO" | "PREDIO" | "TERRENO_RUA" | "TERRENO_CONDOMINIO" | "CHACARA_SITIO" | "FAZENDA"; /** Finalidades. · Purposes. */ purposes?: Array<"VENDA" | "LOCACAO">; /** Preço de venda, em reais. Obrigatório com `VENDA`. · Sale price, in BRL. Required with `VENDA`. */ salePrice?: number | null; /** Aluguel, em reais. Obrigatório com `LOCACAO`. · Rent, in BRL. Required with `LOCACAO`. */ rentPrice?: number | null; /** Endereço. · Address. */ address?: AddressInput; /** Quartos. · Bedrooms. */ bedrooms?: number | null; /** Suítes. · Suites. */ suites?: number | null; /** Banheiros. · Bathrooms. */ bathrooms?: number | null; /** Vagas. · Parking spots. */ parkingSpots?: number | null; /** Área privativa total, em m². · Total private area, in m². */ area?: number | null; /** Área coberta, em m². · Covered area, in m². */ privateArea?: number | null; /** Endereço do anúncio no seu site (http ou https). · Listing URL on your website (http or https). */ listingUrl?: string | null; /** Nome do captador em texto. · Listing agent name as text. */ captadorName?: string | null; /** Id do corretor responsável da equipe. · Id of the responsible broker on the team. */ captadorAccountId?: string | null; /** Condições para parceiros. · Partner terms. */ partnershipTerms?: PartnershipTerms; /** Até 30 endereços; a 1ª é a capa. Baixadas depois da resposta (veja `photoSync`). Ausente = a galeria fica como está; lista vazia = tirar as fotos. · Up to 30 URLs; the 1st is the cover. Downloaded after the response (see `photoSync`). Absent = gallery unchanged; empty list = remove the photos. */ photos?: Array; } /** O desfecho da escrita. · The write outcome. */ export interface PropertyWriteResult { /** O que aconteceu: `CREATED`, `UPDATED`, `UNCHANGED` (nada mudou, nada gravado), `CONFIRMED` (só a data de disponibilidade), `STATUS_SET`, `DELETED`, `ALREADY_DELETED`… · What happened: `CREATED`, `UPDATED`, `UNCHANGED` (nothing changed, nothing written), `CONFIRMED` (only the availability date), `STATUS_SET`, `DELETED`, `ALREADY_DELETED`… */ outcome: string; /** Foi um ensaio — nada foi gravado. · It was a dry run — nothing was written. */ dryRun: boolean; /** Campos que a equipe editou na tela e que por isso não foram sobrescritos — a edição na tela vence. · Fields the team edited in the dashboard, so they were not overwritten — dashboard edits win. */ conflicts: Array; /** A galeria: `UNCHANGED`, `QUEUED`, `ALREADY_QUEUED` ou `CLEARED`. Nulo quando o pedido não falou de fotos. · The gallery: `UNCHANGED`, `QUEUED`, `ALREADY_QUEUED` or `CLEARED`. Null when the request did not mention photos. */ photos: string | null; } /** Um telefone. · A phone number. */ export interface Phone { /** Só dígitos, com DDD (10 a 13). · Digits only, with area code (10 to 13). */ number: string; /** É WhatsApp. · Is WhatsApp. */ isWhatsApp: boolean; /** Rótulo livre (`celular`, `trabalho`…). · Free label (`mobile`, `work`…). */ label: string | null; } /** Uma região de interesse. · A region of interest. */ export interface Location { /** Código do IBGE. · IBGE code. */ cityId: string | null; /** Identificador da cidade. · City identifier. */ citySlug: string | null; /** Nome da cidade. · City name. */ cityName: string | null; /** Estado. · State. */ uf: string | null; /** Bairro do catálogo (nulo = a cidade inteira). · Catalog neighborhood (null = the whole city). */ neighborhoodSlug: string | null; /** Nome do bairro. · Neighborhood name. */ neighborhoodName: string | null; /** `PREFERRED` (preferido) ou `ACCEPTABLE` (aceitável) — pesa na nota da recomendação. · `PREFERRED` or `ACCEPTABLE` — it weighs on the recommendation score. */ preference: "PREFERRED" | "ACCEPTABLE"; } /** O perfil de busca — é ele que liga as recomendações. · The search profile — it is what drives recommendations. */ export interface Interest { /** Finalidades: `VENDA`, `LOCACAO` ou as duas. · Purposes: `VENDA` (sale), `LOCACAO` (rent) or both. */ purposes: Array<"VENDA" | "LOCACAO">; /** Tipos de imóvel procurados. · Property types wanted. */ propertyTypes: Array<"APARTAMENTO" | "CASA_RUA" | "CASA_CONDOMINIO" | "CASA_VILA" | "STUDIO" | "FLAT" | "COBERTURA" | "SALA_COMERCIAL" | "LOJA_PONTO" | "GALPAO" | "PREDIO" | "TERRENO_RUA" | "TERRENO_CONDOMINIO" | "CHACARA_SITIO" | "FAZENDA">; /** Regiões de interesse. · Regions of interest. */ locations: Array; /** Aceita bairros parecidos com os escolhidos. · Accepts neighborhoods similar to the chosen ones. */ similarNeighborhoods: boolean; /** Compra: piso, em reais. · Purchase: minimum, in BRL. */ salePriceMin: number | null; /** Compra: teto, em reais. · Purchase: maximum, in BRL. */ salePriceMax: number | null; /** Aluguel: piso, em reais. · Rent: minimum, in BRL. */ rentPriceMin: number | null; /** Aluguel: teto, em reais. · Rent: maximum, in BRL. */ rentPriceMax: number | null; /** Quartos, no mínimo. · Minimum bedrooms. */ bedrooms: number | null; /** Banheiros, no mínimo. · Minimum bathrooms. */ bathrooms: number | null; /** Vagas, no mínimo. · Minimum parking spots. */ parkingSpots: number | null; /** Área mínima, em m². · Minimum area, in m². */ areaMin: number | null; } /** A situação do cliente nas recomendações. · The client's status in recommendations. */ export interface LeadRadar { /** Recebe recomendações agora (ativo e sem pendência). · Receives recommendations right now (active and with no pending item). */ eligible: boolean; /** O que falta no perfil de busca. · What is missing in the search profile. */ pending: Array<"PURPOSE_MISSING" | "PROPERTY_TYPES_MISSING" | "CITY_MISSING" | "CITY_NOT_IN_CATALOG" | "BUDGET_MISSING" | "SALE_BUDGET_MISSING" | "RENT_BUDGET_MISSING" | "OTHER">; /** As mesmas pendências, por extenso. · The same pending items, spelled out. */ labels: Array; } /** Um cliente da conta. Nome, telefone e e-mail são dado pessoal — a imobiliária é a controladora. · A client of the account. Name, phone and e-mail are personal data — the agency is the controller. */ export interface Lead { /** Id do cliente. · Client id. */ id: string; /** O código do cliente no SEU sistema. · The client's code in YOUR system. */ externalRef: string | null; /** O corretor responsável (ou a própria imobiliária). · The responsible broker (or the agency itself). */ ownerAccountId: string | null; /** `ACTIVE`, `ARCHIVED` ou `DELETED` (lixeira). · `ACTIVE`, `ARCHIVED` or `DELETED` (trash). */ state: "ACTIVE" | "ARCHIVED" | "DELETED"; /** Etapa do atendimento. · Funnel stage. */ status: "NEW" | "ATTENDING" | "PROPOSAL" | "WON" | "LOST" | null; /** Nome. · Name. */ name: string | null; /** Telefones. · Phones. */ phones: Array; /** E-mail. · E-mail. */ email: string | null; /** Por onde chegou: `MANUAL`, `IMPORT_CSV`, `EMAIL`, `WEBHOOK`, `META_ADS`, `PORTAL_API`, `CRM_API` (pela API)… · Where it came from: `MANUAL`, `IMPORT_CSV`, `EMAIL`, `WEBHOOK`, `META_ADS`, `PORTAL_API`, `CRM_API` (through the API)… */ source: string; /** Detalhe da origem (o portal, o nome do sistema). · Source detail (the portal, the system name). */ sourceDetail: string | null; /** O imóvel que trouxe o cliente, quando houver. · The property that brought the client, if any. */ sourcePropertyId: string | null; /** Crédito pré-aprovado. · Pre-approved credit. */ preApproved: boolean; /** O perfil (sem nome nem contato) pode receber imóveis de parceiros. · The profile (without name or contact) may receive partner properties. */ sharedToNetwork: boolean; /** Anotações. · Notes. */ notes: string | null; /** Perfil de busca. · Search profile. */ interest: Interest; /** Situação nas recomendações. · Recommendation status. */ radar: LeadRadar; /** Última movimentação — anda pela data que o seu sistema manda e pelos atendimentos, nunca pela sincronização. · Last activity — moves with the date your system sends and with interactions, never with sync itself. */ lastActivityAt: string | null; /** Quando foi arquivado. · When it was archived. */ archivedAt: string | null; /** Por que foi arquivado (`MANUAL`, `INACTIVITY`, `IMPORT`…). · Why it was archived (`MANUAL`, `INACTIVITY`, `IMPORT`…). */ archivedReason: string | null; /** Quando foi para a lixeira. · When it was moved to the trash. */ deletedAt: string | null; /** Quando foi criado (UTC). · When it was created (UTC). */ createdAt: string; /** Última alteração (UTC). · Last change (UTC). */ updatedAt: string; } /** Um telefone. Também aceita só o número, em texto. · A phone. A plain string with the number is also accepted. */ export interface PhoneInput { /** Com DDD; só os dígitos contam. · With area code; only digits count. */ number?: string; /** É WhatsApp (padrão: sim). · Is WhatsApp (default: yes). */ isWhatsApp?: boolean; /** Rótulo livre. · Free label. */ label?: string; } /** Uma região de interesse. · A region of interest. */ export interface LocationInput { /** Código do IBGE. · IBGE code. */ cityId?: string; /** Nome da cidade (com `uf`). · City name (with `uf`). */ cityName?: string; /** Estado. · State. */ uf?: string; /** Bairro do catálogo. · Catalog neighborhood. */ neighborhoodSlug?: string; /** Bairro em texto. Área de mercado ("Ecoville") vira os bairros do catálogo que ela cobre. · Neighborhood as text. Market areas ("Ecoville") expand into the catalog neighborhoods they cover. */ neighborhoodName?: string; /** `PREFERRED` (padrão) ou `ACCEPTABLE`. · `PREFERRED` (default) or `ACCEPTABLE`. */ preference?: "PREFERRED" | "ACCEPTABLE"; } /** O perfil de busca. É mesclado campo a campo: mandar só o teto não apaga as regiões. · The search profile. Merged field by field: sending only the maximum does not erase the regions. */ export interface InterestInput { /** Finalidades. · Purposes. */ purposes?: Array<"VENDA" | "LOCACAO">; /** Tipos procurados. · Types wanted. */ propertyTypes?: Array<"APARTAMENTO" | "CASA_RUA" | "CASA_CONDOMINIO" | "CASA_VILA" | "STUDIO" | "FLAT" | "COBERTURA" | "SALA_COMERCIAL" | "LOJA_PONTO" | "GALPAO" | "PREDIO" | "TERRENO_RUA" | "TERRENO_CONDOMINIO" | "CHACARA_SITIO" | "FAZENDA">; /** Regiões (até 20). Quando vem, vem inteira. · Regions (up to 20). Send the whole list when you send it. */ locations?: Array; /** Aceita bairros parecidos. · Accepts similar neighborhoods. */ similarNeighborhoods?: boolean; /** Compra: piso. · Purchase: minimum. */ salePriceMin?: number | null; /** Compra: teto. Abaixo do piso é recusado. · Purchase: maximum. Below the minimum is refused. */ salePriceMax?: number | null; /** Aluguel: piso. · Rent: minimum. */ rentPriceMin?: number | null; /** Aluguel: teto. Abaixo do piso é recusado. · Rent: maximum. Below the minimum is refused. */ rentPriceMax?: number | null; /** Quartos, no mínimo. · Minimum bedrooms. */ bedrooms?: number | null; /** Banheiros, no mínimo. · Minimum bathrooms. */ bathrooms?: number | null; /** Vagas, no mínimo. · Minimum parking spots. */ parkingSpots?: number | null; /** Área mínima, em m². · Minimum area, in m². */ areaMin?: number | null; } /** Os campos que a API aceita no cliente. Campo desconhecido é erro; campo ausente não apaga. · The fields the API accepts for a client. Unknown fields are an error; absent fields are kept. */ export interface LeadInput { /** O código do cliente no seu sistema (só no POST). · The client's code in your system (POST only). */ externalRef?: string; /** Nome. Obrigatório na criação. · Name. Required on creation. */ name?: string; /** Até 5 telefones. Na criação, telefone ou e-mail. · Up to 5 phones. On creation, a phone or an e-mail. */ phones?: Array; /** E-mail. · E-mail. */ email?: string | null; /** Etapa do atendimento. · Funnel stage. */ status?: "NEW" | "ATTENDING" | "PROPOSAL" | "WON" | "LOST"; /** Anotações. · Notes. */ notes?: string; /** Crédito pré-aprovado. · Pre-approved credit. */ preApproved?: boolean; /** O perfil pode receber imóveis de parceiros. · The profile may receive partner properties. */ sharedToNetwork?: boolean; /** Perfil de busca. · Search profile. */ interest?: InterestInput; /** O corretor responsável, pelo id. · The responsible broker, by id. */ ownerAccountId?: string; /** O corretor responsável, pelo e-mail — é como o seu sistema o conhece. Sem responsável, vale a distribuição da imobiliária. · The responsible broker, by e-mail — the way your system knows them. Without one, the agency's distribution rule applies. */ ownerEmail?: string; /** A última movimentação no SEU sistema. Só anda para a frente. Mais de 60 dias atrás na criação = o cliente entra arquivado (base histórica). · The last activity in YOUR system. It only moves forward. More than 60 days ago on creation = the client is created archived (historical base). */ lastActivityAt?: string; /** Obrigatório `true` na criação: a imobiliária declara ter a base legal para tratar o dado (Termos da API). · Must be `true` on creation: the agency declares it has the legal basis to process the data (API Terms). */ consentGiven?: boolean; } /** O desfecho da escrita. · The write outcome. */ export interface LeadWriteResult { /** O que aconteceu: `CREATED`, `UPDATED`, `UNCHANGED`, `LINKED` (o código passou a apontar para um cliente que já existia pelo contato), `ARCHIVED` (criado arquivado, base histórica), `STATE_SET` (arquivado ou reativado), `ADDED` (atendimento registrado), `DELETED`, `ALREADY_DELETED`. · What happened: `CREATED`, `UPDATED`, `UNCHANGED`, `LINKED` (the code now points to a client that already existed by contact), `ARCHIVED` (created archived, historical base), `STATE_SET` (archived or reactivated), `ADDED` (interaction logged), `DELETED`, `ALREADY_DELETED`. */ outcome: string; /** Foi um ensaio — nada foi gravado. · It was a dry run — nothing was written. */ dryRun: boolean; /** O atendimento registrado (só em `POST /leads/{leadId}/interactions`). · The logged interaction (only in `POST /leads/{leadId}/interactions`). */ interaction?: { id: string; type: string; note: string | null; at: string | null; }; } /** O retrato do imóvel no par — o mesmo do card. Campos que a tela esconde saem nulos. · The property snapshot in the pair — the same as the card. Fields the dashboard hides come back null. */ export interface PropertySnapshot { /** Título. · Title. */ title: string | null; /** Subtipo. · Subtype. */ type: string | null; /** Finalidade do par. · Purpose of the pair. */ purpose: string | null; /** Finalidades do imóvel. · The property's purposes. */ purposes: Array; /** Preço (venda, ou aluguel quando o par é de locação). · Price (sale, or rent when the pair is a rental). */ price: number | null; /** O preço é "a partir de" (lançamento). · The price is a "starting at" price (new development). */ priceFrom: boolean; /** Aluguel. · Rent. */ rentPrice: number | null; /** Cidade. · City. */ city: string | null; /** Bairro. · Neighborhood. */ neighborhood: string | null; /** Quartos. · Bedrooms. */ bedrooms: number | null; /** Banheiros. · Bathrooms. */ bathrooms: number | null; /** Vagas. · Parking spots. */ parkingSpots: number | null; /** Área, em m². · Area, in m². */ area: number | null; /** Capa. · Cover photo. */ coverPhoto: string | null; /** Anúncio (nulo quando a tela também esconde). · Listing URL (null when the dashboard hides it too). */ listingUrl: string | null; /** Comissão de venda, em %. · Sale commission, in %. */ commissionPercent: number | null; /** Comissão de locação, em aluguéis. · Rent commission, in months. */ rentCommissionMonths: number | null; /** Parte de quem captou, em %. · Listing side share, in %. */ splitListingPercent: number | null; /** Última confirmação de disponibilidade. · Last availability confirmation. */ lastConfirmedAt: string | null; /** Entrega (lançamento). · Delivery (new development). */ deliveryDate: string | null; /** Empreendimento. · Development. */ developmentId: string | null; /** Nome do empreendimento. · Development name. */ developmentName: string | null; } /** O retrato do cliente no par. · The client snapshot in the pair. */ export interface ClientSnapshot { /** O cliente é de parceiro e está mascarado. · The client belongs to a partner and is masked. */ masked: boolean; /** Etapa do atendimento. · Funnel stage. */ status: string | null; /** Crédito pré-aprovado. · Pre-approved credit. */ preApproved: boolean; /** Finalidades procuradas. · Purposes wanted. */ purposes: Array; /** Tipos procurados. · Types wanted. */ propertyTypes: Array; /** Regiões, por extenso. · Regions, spelled out. */ regions: Array; /** Piso. · Minimum. */ priceMin: number | null; /** Teto. · Maximum. */ priceMax: number | null; /** Quartos. · Bedrooms. */ bedrooms: number | null; /** Vagas. · Parking spots. */ parkingSpots: number | null; /** Área mínima. · Minimum area. */ areaMin: number | null; /** Nome — só com a permissão `leads:read` na chave e só quando a tela também mostra. Telefone, nunca. · Name — only with the `leads:read` permission on the key and only when the dashboard shows it too. Phone, never. */ name: string | null; } /** O parceiro do outro lado. · The partner on the other side. */ export interface Counterpart { /** Id da conta do parceiro. · Partner's account id. */ accountId: string; /** Nome. · Name. */ name: string | null; /** Tipo de conta. · Account type. */ type: string | null; /** Imobiliária do parceiro. · Partner's agency. */ organization: string | null; /** CRECI. · Broker license (CRECI). */ creci: string | null; /** CRECI conferido. · License verified. */ creciVerified: boolean; /** Telefone. · Phone. */ phone: string | null; } /** Uma oportunidade: um imóvel que serve a um cliente. · An opportunity: a property that fits a client. */ export interface Match { /** Id da oportunidade (`m#{imóvel}#{cliente}#{lado}`). Tem `#`: mande codificado (`%23`) no caminho. · Opportunity id (`m#{property}#{client}#{side}`). It contains `#`: URL-encode it (`%23`) in the path. */ id: string; /** `INTERNAL` (da própria carteira), `NETWORK` (com parceiros) ou `LAUNCH` (lançamento). · `INTERNAL` (own portfolio), `NETWORK` (with partners) or `LAUNCH` (new development). */ scope: "INTERNAL" | "NETWORK" | "LAUNCH"; /** `NEW`, `FAVORITED`, `ARCHIVED` ou `REQUESTED` (virou pedido de parceria). · `NEW`, `FAVORITED`, `ARCHIVED` or `REQUESTED` (became a partnership request). */ status: string; /** Nota de 0 a 100. · Score from 0 to 100. */ score: number; /** Por que combina. · Why it matches. */ reasons: Array; /** O que não combina. · What doesn't match. */ gaps: Array; /** Imóvel. · Property. */ propertyId: string | null; /** Cliente. · Client. */ leadId: string | null; /** O corretor da casa que recebe a oportunidade. · The in-house broker who gets the opportunity. */ ownerAccountId: string | null; /** Nome dele. · Their name. */ ownerName: string | null; /** Oportunidade de parceiro sem assinatura ativa — o parceiro fica escondido, como na tela. · Partner opportunity without an active subscription — the partner stays hidden, as in the dashboard. */ locked: boolean; /** Deixou de combinar depois de uma mudança. · No longer matches after a change. */ lostRelevance: boolean; /** O imóvel. · The property. */ property: PropertySnapshot | null; /** O cliente. · The client. */ client: ClientSnapshot | null; /** O parceiro (nulo quando é da casa ou está escondido). · The partner (null when in-house or hidden). */ counterpart: Counterpart | null; /** Quando foi criado (UTC). · When it was created (UTC). */ createdAt: string; /** Última alteração (UTC). · Last change (UTC). */ updatedAt: string; } /** O desfecho. · The outcome. */ export interface MatchWriteResult { /** `APPLIED`; em ensaio, `WOULD_APPLY`. · `APPLIED`; in a dry run, `WOULD_APPLY`. */ outcome: string; /** Foi um ensaio. · It was a dry run. */ dryRun: boolean; } /** Uma ponta da parceria. · One side of the partnership. */ export interface Party { /** Id da conta. · Account id. */ accountId: string | null; /** Nome. · Name. */ name: string | null; /** Tipo de conta. · Account type. */ type: string | null; } /** Uma parceria. · A partnership. */ export interface Partnership { /** Id da parceria. · Partnership id. */ id: string; /** Etapa. · Stage. */ status: "PENDING" | "ACCEPTED" | "DECLINED" | "CANCELLED" | "LEAD_REGISTERED" | "CONTACTED" | "NEGOTIATING" | "PROPOSAL" | "WON" | "LOST"; /** `CLIENT_FOR_PROPERTY` ou `PROPERTY_FOR_CLIENT`. · `CLIENT_FOR_PROPERTY` or `PROPERTY_FOR_CLIENT`. */ kind: string | null; /** Imóvel. · Property. */ propertyId: string | null; /** Cliente. · Client. */ leadId: string | null; /** A oportunidade de origem. · The originating opportunity. */ matchId: string | null; /** Empreendimento (registro em lançamento). · Development (new-development registration). */ developmentId: string | null; /** Nome do empreendimento. · Development name. */ developmentTitle: string | null; /** Nota da oportunidade. · Opportunity score. */ score: number | null; /** Resumo. · Summary. */ summary: string | null; /** Por que combina. · Why it matches. */ reasons: Array; /** Mensagem do pedido. · Request message. */ message: string | null; /** O lado da conta: `REQUESTER` (pediu) ou `ADDRESSEE` (recebeu). · The account's side: `REQUESTER` or `ADDRESSEE`. */ ourSide: string | null; /** O corretor da casa na parceria. · The in-house broker in the partnership. */ ourBrokerId: string | null; /** A conta pode mover a etapa — só o lado que captou o cliente pode. · The account can move the stage — only the side that brought the client can. */ canMoveFunnel: boolean; /** Quem pediu. · Requester. */ requester: Party | null; /** Quem recebeu. · Addressee. */ addressee: Party | null; /** O parceiro do outro lado. · The partner on the other side. */ counterpart: { name: string | null; type: string | null; organization: string | null; contactName: string | null; phone: string | null; email: string | null; creci: string | null; creciVerified: boolean; }; /** O imóvel. · The property. */ property: PropertySnapshot | null; /** O cliente. · The client. */ client: ClientSnapshot | null; /** Último avanço de etapa. · Last stage move. */ lastProgressAt: string | null; /** Até quando o cliente fica protegido (lançamento). · Until when the client is protected (new development). */ protectedUntil: string | null; /** Quando o imóvel saiu do ar. · When the property went off the market. */ propertyOffMarketAt: string | null; /** Quando foi criado (UTC). · When it was created (UTC). */ createdAt: string; /** Última alteração (UTC). · Last change (UTC). */ updatedAt: string; } /** O desfecho. · The outcome. */ export interface PartnershipWriteResult { /** O que aconteceu. · What happened. */ outcome: string; /** Foi um ensaio. · It was a dry run. */ dryRun: boolean; } /** Uma pessoa da equipe. · A team member. */ export interface Member { /** Id da pessoa na plataforma. · The person's platform id. */ accountId: string; /** Nome. · Name. */ name: string | null; /** E-mail — é por ele que o seu sistema reconhece o corretor. · E-mail — your system matches the broker by it. */ email: string | null; /** Telefone. · Phone. */ phone: string | null; /** Foto. · Photo. */ photoUrl: string | null; /** CRECI (quem tem — a equipe também tem estagiários e administrativos). · Broker license (if any — the team also has interns and office staff). */ creci: string | null; /** CRECI conferido. · License verified. */ creciVerified: boolean; /** `active` ou `suspended`. · `active` or `suspended`. */ status: string; /** Pode cadastrar imóvel e ser o responsável por ele. · Can register properties and be responsible for them. */ canCaptureProperties: boolean; /** Supervisiona parte da equipe. · Supervises part of the team. */ isSupervisor: boolean; /** O supervisor desta pessoa. · This person's supervisor. */ supervisorId: string | null; /** Entra na distribuição de clientes. · Takes part in client distribution. */ receivesLeadDistribution: boolean; /** Primeiro acesso. · First access. */ firstAccessAt: string | null; } /** A sincronização do feed do CRM. Nunca traz o link nem o token. · The CRM feed sync. Never includes the link or the token. */ export interface FeedSync { /** O link do feed do CRM está cadastrado na conta. · The CRM feed link is set up on the account. */ configured: boolean; /** O CRM do link (o formato do feed). · The CRM behind the link (the feed format). */ connector: string | null; /** A sincronização de 6 em 6 horas está ligada. · The 6-hourly sync is on. */ autoSync: boolean; /** Como terminou a última sincronização (a mesma situação da tela) — `QUEUED` enquanto ela anda. · How the last sync ended (the same status as the dashboard) — `QUEUED` while it runs. */ status: string | null; /** A frase da última sincronização, a mesma da tela. · The last sync message, the same as in the dashboard. */ message: string | null; /** Quando a última terminou. · When the last one finished. */ lastSyncAt: string | null; /** Imóveis no feed na última leitura. · Properties in the feed at the last read. */ feedCount: number | null; /** O que a última sincronização fez. · What the last sync did. */ counts: { created: number | null; updated: number | null; archived: number | null; skipped: number | null; unchanged: number | null; confirmed: number | null; }; /** Imóveis com campo editado na tela que o feed quis mudar — esperam a decisão em Imóveis › Importar. · Properties with a field edited in the dashboard that the feed tried to change — awaiting a decision under Properties › Import. */ conflicts: number; /** A sincronização agendada por `POST /v1/feed/sync` para o fim da janela (nulo = nenhuma). · The sync scheduled by `POST /v1/feed/sync` for the end of the window (null = none). */ scheduledFor: string | null; } /** O desfecho. · The outcome. */ export interface FeedSyncResult { /** `QUEUED` (sincroniza já) · `SCHEDULED` (agendada para o fim da janela de 10 minutos) · `ALREADY_SCHEDULED` (já havia uma agendada — nada novo). · `QUEUED` (syncs now) · `SCHEDULED` (scheduled for the end of the 10-minute window) · `ALREADY_SCHEDULED` (one was already scheduled — nothing new). */ outcome: "QUEUED" | "SCHEDULED" | "ALREADY_SCHEDULED"; /** Foi ensaio (nada enfileirado). · It was a dry run (nothing queued). */ dryRun: boolean; } /** Uma tipologia do empreendimento. · A unit type of the development. */ export interface Typology { /** Id da tipologia. · Unit type id. */ id: string; /** Nome. · Name. */ title: string | null; /** Subtipo. · Subtype. */ type: string | null; /** Situação. · Status. */ status: string; /** Preço. · Price. */ salePrice: number | null; /** Quartos. · Bedrooms. */ bedrooms: number | null; /** Suítes. · Suites. */ suites: number | null; /** Banheiros. · Bathrooms. */ bathrooms: number | null; /** Vagas. · Parking spots. */ parkingSpots: number | null; /** Área privativa, em m². · Private area, in m². */ area: number | null; /** Área coberta, em m². · Covered area, in m². */ privateArea: number | null; /** Capa. · Cover photo. */ coverPhoto: string | null; /** Plantas. · Floor plans. */ floorPlans: Array; /** Itens da unidade. · Unit features. */ unitFeatures: Array; /** Unidades disponíveis. · Available units. */ unitsAvailable: number | null; /** Unidades no total. · Total units. */ unitsTotal: number | null; } /** Um empreendimento do catálogo de lançamentos. · A development in the new-development catalog. */ export interface Launch { /** Id do empreendimento. · Development id. */ id: string; /** Nome. · Name. */ title: string | null; /** Construtora. · Developer. */ developer: string | null; /** Endereço até o bairro (a rua não sai no catálogo). · Address down to the neighborhood (the street is not in the catalog). */ address: AddressArea; /** Estágio da obra. · Construction stage. */ constructionStage: "LAUNCH" | "OFF_PLAN" | "NEW" | "READY" | null; /** Previsão de entrega. · Expected delivery. */ deliveryDate: string | null; /** Andamento da obra, em %. · Construction progress, in %. */ constructionProgress: number | null; /** Capa. · Cover photo. */ coverPhoto: string | null; /** Itens do empreendimento. · Development amenities. */ amenities: Array; /** Comissão para o corretor, em %. · Broker commission, in %. */ commissionPercent: number | null; /** Menor preço entre as tipologias. · Lowest unit-type price. */ priceMin: number | null; /** Maior preço. · Highest price. */ priceMax: number | null; /** Quantas tipologias. · How many unit types. */ typologyCount: number; /** As tipologias. · The unit types. */ typologies: Array; } /** Um proprietário da Captação. Os campos depois de `createdAt` só existem depois do aceite. · A Captação owner. The fields after `createdAt` only exist after accepting. */ export interface CaptacaoOffer { /** Id do proprietário na mesa. · The owner's id on the desk. */ id: string; /** Situação. · Status. */ status: string | null; /** Venda ou locação. · Sale or rent. */ purpose: string | null; /** Subtipo. · Subtype. */ propertyType: string | null; /** Cidade. · City. */ city: string | null; /** Identificador da cidade. · City identifier. */ citySlug: string | null; /** Bairro. · Neighborhood. */ neighborhood: string | null; /** Quartos. · Bedrooms. */ bedrooms: number | null; /** Suítes. · Suites. */ suites: number | null; /** Banheiros. · Bathrooms. */ bathrooms: number | null; /** Vagas. · Parking spots. */ parkingSpots: number | null; /** Área útil que o proprietário declarou. · Usable area declared by the owner. */ usableAreaDeclared: number | null; /** Área total que o proprietário declarou. · Total area declared by the owner. */ areaDeclared: number | null; /** Valor que o proprietário espera. · Price the owner expects. */ priceExpected: number | null; /** Prazo para vender: `AGORA`, `TRES_MESES`, `SEIS_MESES` ou `PESQUISANDO`. · Time frame to sell: `AGORA` (now), `TRES_MESES` (3 months), `SEIS_MESES` (6 months) or `PESQUISANDO` (just researching). */ sellTimeframe: string | null; /** Ocupação: `PROPRIO` (o dono mora), `ALUGADO` ou `VAZIO`. · Occupancy: `PROPRIO` (owner lives there), `ALUGADO` (rented) or `VAZIO` (empty). */ occupancy: string | null; /** Quem cadastrou: `DONO`, `COPROPRIETARIO`, `REPRESENTANTE` ou `OUTRO` (não é o proprietário). · Who registered: `DONO` (owner), `COPROPRIETARIO` (co-owner), `REPRESENTANTE` (legal representative) or `OUTRO` (not the owner). */ ownerRelation: string | null; /** Aceita exclusividade: `SIM` ou `NAO`. · Accepts exclusivity: `SIM` (yes) or `NAO` (no). */ exclusivityOpenness: string | null; /** Disse que já anuncia em outro lugar. · Said it is already listed elsewhere. */ alreadyListedDeclared: boolean; /** O preço do contato, em centavos — o que o aceite debita. · The contact price, in cents — what accepting debits. */ priceCents: number | null; /** Até quando dá para aceitar (o prazo corre só das 9h às 17h, nos dias de atendimento). · Accept deadline (the clock runs only 9am–5pm on service days). */ deadline: string | null; /** Quantos clientes da conta combinam. · How many account clients match. */ matchingClients: { city: number | null; neighborhood: number | null; }; /** A procura medida pela plataforma. · Demand measured by the platform. */ demand: { available: boolean; people: number | null; scope: string | null; scopeName: string | null; asOf: string | null; label: string | null; } | null; /** Imóveis parecidos à venda no bairro. · Similar properties for sale in the neighborhood. */ stockNeighborhood: number | null; /** Nome mascarado (antes do aceite). · Masked name (before accepting). */ ownerNameMasked: string | null; /** Como o telefone do proprietário foi confirmado. · How the owner's phone was verified. */ ownerPhoneVerifiedVia: string | null; /** Quando. · When. */ ownerPhoneVerifiedAt: string | null; /** O contato já foi liberado para a conta (aceito). Antes disso, `owner` e `address` NÃO existem na resposta. · The contact was released to the account (accepted). Before that, `owner` and `address` do NOT exist in the response. */ revealed: boolean; /** Quando foi criado (UTC). · When it was created (UTC). */ createdAt: string; /** O proprietário (só depois do aceite). · The owner (only after accepting). */ owner?: { name: string | null; phone: string | null; email: string | null; }; /** O endereço (só depois do aceite). · The address (only after accepting). */ address?: { street: string | null; number: string | null; complement: string | null; neighborhood: string | null; city: string | null; }; /** Quando foi aceito. · When it was accepted. */ acceptedAt?: string | null; /** Como foi aceito: pela tela ou pela API (com a chave, o IP e a versão do termo). · How it was accepted: in the dashboard or through the API (with the key, the IP and the terms version). */ acceptedVia?: { [key: string]: unknown; } | null; /** Até quando cabe pedir devolução. · Refund request deadline. */ refundUntil?: string | null; /** Quando foi devolvido. · When it was refunded. */ refundedAt?: string | null; /** Quando o imóvel foi vendido. · When the property was sold. */ soldAt?: string | null; /** Etapa na mesa. · Stage on the desk. */ stage?: string | null; /** Etapa por extenso. · Stage, spelled out. */ stageLabel?: string | null; /** Motivo da perda. · Lost reason. */ lostReason?: string | null; /** Anotação. · Note. */ note?: string | null; /** Aba da mesa: `ACTIVE`, `CAPTURED` ou `ARCHIVED`. · Desk tab: `ACTIVE`, `CAPTURED` or `ARCHIVED`. */ tab?: string | null; } /** O resumo da mesa. · Desk summary. */ export interface CaptacaoSummary { /** Proprietários na vez da conta agora. · Owners offered to the account right now. */ pendingCount: number; /** Crédito, em centavos. · Credit, in cents. */ balanceCents: number; /** Assinatura do módulo em dia. · Module subscription active. */ subscriptionActive: boolean; /** O módulo está valendo na plataforma. · The module is on for the platform. */ platformActive: boolean; /** A conta pausou o recebimento. · The account paused receiving owners. */ paused: boolean; /** Pausa automática (prazos vencidos em sequência). · Automatic pause (deadlines missed in a row). */ pausedAuto: boolean; /** Prazos vencidos em sequência. · Deadlines missed in a row. */ expiredStreak: number; /** A pausa automática vem depois de quantos. · Automatic pause after how many. */ autoPauseAfter: number | null; /** Em ativação (antes da 1ª recarga). · In activation (before the first top-up). */ inActivation: boolean; /** Aceites até a 1ª recarga. · Accepts until the first top-up. */ leadsToFirstCharge: number | null; /** Recarga em andamento. · Top-up in progress. */ rechargePending: boolean; /** Cidades atendidas. · Cities served. */ operatingCities: Array; /** Finalidades atendidas. · Purposes served. */ purposes: Array; /** Dias de atendimento. · Service days. */ serviceDays: Array; } /** O desfecho do aceite. · The accept outcome. */ export interface CaptacaoAcceptResult { /** `ACCEPTED`, `ALREADY_ACCEPTED` (sem segundo débito) ou, em ensaio, `WOULD_ACCEPT`. · `ACCEPTED`, `ALREADY_ACCEPTED` (no second debit) or, in a dry run, `WOULD_ACCEPT`. */ outcome: string; /** Foi um ensaio — nada foi debitado. · It was a dry run — nothing was debited. */ dryRun: boolean; /** O valor debitado (ou que seria), em centavos. · The amount debited (or that would be), in cents. */ priceCents: number | null; } /** Um evento. · An event. */ export interface Event { /** Id do evento (`evt_…`) — o mesmo `webhook-id` da entrega. Use-o para não processar duas vezes. · Event id (`evt_…`) — the same `webhook-id` as the delivery. Use it to avoid processing twice. */ id: string; /** Tipo. · Type. */ type: "property.created" | "property.updated" | "property.deleted" | "property.restored" | "property.purged" | "lead.created" | "lead.updated" | "lead.deleted" | "lead.restored" | "lead.purged" | "radar.client_summary" | "partnership.created" | "partnership.updated" | "captacao.offer_received" | "captacao.offer_accepted" | "captacao.offer_withdrawn"; /** Quando foi registrado (UTC). · When it was recorded (UTC). */ createdAt: string; /** O que mudou, enxuto: ids, o estado e `changedFields` (os NOMES dos campos que mudaram). Nunca nome, telefone, e-mail ou endereço — busque o detalhe pela leitura do recurso. · What changed, lean: ids, state and `changedFields` (the NAMES of the changed fields). Never name, phone, e-mail or address — fetch the detail by reading the resource. */ data: { [key: string]: unknown; }; } /** Um endereço de webhook. O segredo nunca sai aqui. · A webhook endpoint. The secret never comes back here. */ export interface Webhook { /** Id do endereço (`wh_…`). · Endpoint id (`wh_…`). */ id: string; /** O endereço (só HTTPS). · The URL (HTTPS only). */ url: string; /** Os tipos que ele recebe (vazio = todos os que a conta lê). · The types it receives (empty = all the account can read). */ types: Array<"property.created" | "property.updated" | "property.deleted" | "property.restored" | "property.purged" | "lead.created" | "lead.updated" | "lead.deleted" | "lead.restored" | "lead.purged" | "radar.client_summary" | "partnership.created" | "partnership.updated" | "captacao.offer_received" | "captacao.offer_accepted" | "captacao.offer_withdrawn">; /** Descrição. · Description. */ description: string | null; /** `ACTIVE` ou `DISABLED`. · `ACTIVE` or `DISABLED`. */ status: "ACTIVE" | "DISABLED"; /** Quando foi desligado. · When it was disabled. */ disabledAt: string | null; /** `GONE_410` (o seu sistema respondeu 410), `FAILURE_RATE` (metade ou mais das entregas falhou em 48 h) ou `MANUAL`. · `GONE_410` (your system answered 410), `FAILURE_RATE` (half or more of the deliveries failed in 48 h) or `MANUAL`. */ disabledReason: string | null; /** Última troca do segredo. · Last secret rotation. */ secretRotatedAt: string | null; /** Até quando o segredo anterior ainda assina junto (24 h depois da troca). · Until when the previous secret still signs alongside (24 h after rotation). */ previousSecretValidUntil: string | null; /** Quando foi criado (UTC). · When it was created (UTC). */ createdAt: string; /** Última alteração (UTC). · Last change (UTC). */ updatedAt: string; } /** Um endereço novo. · A new endpoint. */ export interface WebhookCreate { /** O endereço, só HTTPS e público. Credencial na URL é recusada. · The URL, HTTPS and public only. Credentials in the URL are refused. */ url: string; /** Os tipos (ausente = todos os que a conta lê). · The types (absent = all the account can read). */ types?: Array<"property.created" | "property.updated" | "property.deleted" | "property.restored" | "property.purged" | "lead.created" | "lead.updated" | "lead.deleted" | "lead.restored" | "lead.purged" | "radar.client_summary" | "partnership.created" | "partnership.updated" | "captacao.offer_received" | "captacao.offer_accepted" | "captacao.offer_withdrawn">; /** Descrição. · Description. */ description?: string | null; } /** O que mudar no endereço. · What to change on the endpoint. */ export interface WebhookUpdate { /** O endereço. · The URL. */ url?: string; /** Os tipos. · The types. */ types?: Array<"property.created" | "property.updated" | "property.deleted" | "property.restored" | "property.purged" | "lead.created" | "lead.updated" | "lead.deleted" | "lead.restored" | "lead.purged" | "radar.client_summary" | "partnership.created" | "partnership.updated" | "captacao.offer_received" | "captacao.offer_accepted" | "captacao.offer_withdrawn">; /** Descrição. · Description. */ description?: string | null; /** `ACTIVE` religa (limpa o motivo do desligamento) e `DISABLED` desliga. · `ACTIVE` re-enables (clears the disable reason) and `DISABLED` disables. */ status?: "ACTIVE" | "DISABLED"; } /** Uma entrega de evento a um endereço. · One event delivery to an endpoint. */ export interface Delivery { /** Id da entrega (`dlv_…`). · Delivery id (`dlv_…`). */ id: string; /** O evento entregue. · The delivered event. */ eventId: string | null; /** Tipo do evento. · Event type. */ type: string | null; /** `PENDING`, `RETRYING`, `DELIVERED`, `FAILED` (esgotou as tentativas) ou `SKIPPED` (o endereço estava desligado). · `PENDING`, `RETRYING`, `DELIVERED`, `FAILED` (out of retries) or `SKIPPED` (the endpoint was disabled). */ status: string; /** Tentativas feitas. · Attempts made. */ attempts: number; /** Último código HTTP do seu sistema. · Last HTTP status from your system. */ lastStatusCode: number | null; /** Último erro. · Last error. */ lastError: string | null; /** Última tentativa. · Last attempt. */ lastAttemptAt: string | null; /** Próxima tentativa (1 min, 5 min, 30 min, 2 h, 6 h e 12 h). · Next attempt (1 min, 5 min, 30 min, 2 h, 6 h and 12 h). */ nextAttemptAt: string | null; /** Quando foi entregue. · When it was delivered. */ deliveredAt: string | null; /** Quando a entrega foi aberta. · When the delivery was opened. */ createdAt: string | null; } /** O resultado do evento de teste (`webhook.test`). · The test event result (`webhook.test`). */ export interface WebhookTestResult { /** O seu sistema respondeu 2xx. · Your system answered 2xx. */ delivered: boolean; /** O código que ele respondeu. · The status it answered. */ statusCode: number | null; /** O motivo da falha. · The failure reason. */ error: string | null; /** Tempo da entrega, em ms. · Delivery time, in ms. */ ms: number | null; } /** O lote recebido. · The received batch. */ export interface BatchCreated { /** Id do lote (`b-…`). · Batch id (`b-…`). */ id: string; /** `properties` ou `leads`. · `properties` or `leads`. */ type: "properties" | "leads"; /** `QUEUED`, ou `DONE` quando nenhum item era válido. · `QUEUED`, or `DONE` when no item was valid. */ status: "QUEUED" | "RUNNING" | "DONE"; /** Itens recebidos. · Items received. */ total: number; /** Itens que seguiram para o processamento. · Items queued for processing. */ accepted: number; /** Itens recusados na forma (veja o resultado). · Items rejected on shape (see the result). */ rejected: number; /** O lote inteiro é um ensaio. · The whole batch is a dry run. */ dryRun: boolean; } /** O resultado de um item. · One item result. */ export interface BatchItem { /** Posição no pedido. · Position in the request. */ index: number; /** O código do item. · The item's code. */ externalRef: string | null; /** O desfecho do item — os mesmos do PUT pelo código, mais `PENDING` (ainda na fila) e `REJECTED`. · The item's outcome — the same as the PUT by code, plus `PENDING` (still queued) and `REJECTED`. */ outcome: string; /** O id do imóvel ou cliente. · The property or client id. */ id: string | null; /** O código de erro, quando recusado. · The error code, when refused. */ code: string | null; /** Detalhe. · Detail. */ detail: string | null; /** Os problemas de forma. · Shape problems. */ invalidParams: Array<{ [key: string]: unknown; }> | null; /** A galeria (imóveis). · The gallery (properties). */ photos: string | null; } /** Um lote, com o desfecho de cada item. · A batch, with each item outcome. */ export interface Batch { /** Id do lote. · Batch id. */ id: string; /** `properties` ou `leads`. · `properties` or `leads`. */ type: "properties" | "leads"; /** `QUEUED`, `RUNNING` ou `DONE`. · `QUEUED`, `RUNNING` or `DONE`. */ status: "QUEUED" | "RUNNING" | "DONE"; /** Ensaio. · Dry run. */ dryRun: boolean; /** Itens. · Items. */ total: number | null; /** Itens com desfecho. · Items with an outcome. */ processed: number | null; /** Contagem por desfecho (`CREATED`, `UPDATED`, `REJECTED`…). · Count per outcome (`CREATED`, `UPDATED`, `REJECTED`…). */ counts: Record; /** Quando foi recebido. · When it was received. */ createdAt: string | null; /** Quando começou. · When it started. */ startedAt: string | null; /** Quando terminou. · When it finished. */ finishedAt: string | null; /** O desfecho de cada item, na ordem do pedido. · Each item outcome, in request order. */ results: Array; } /** O desfecho de um imóvel na confirmação. · One property outcome in the confirmation. */ export interface ConfirmItem { /** O id pedido (quando o pedido veio por `ids`). · The requested id (when the request used `ids`). */ id?: string; /** O código pedido (quando o pedido veio por `refs`). · The requested code (when the request used `refs`). */ ref?: string; /** O imóvel achado (nulo quando não achou). · The property found (null when not found). */ propertyId: string | null; /** `CONFIRMED`, `ALREADY_CONFIRMED` (há menos de 24 h — não regrava) ou `NOT_FOUND`. · `CONFIRMED`, `ALREADY_CONFIRMED` (less than 24 h ago — not rewritten) or `NOT_FOUND`. */ outcome: "CONFIRMED" | "ALREADY_CONFIRMED" | "NOT_FOUND"; } /** O erro da API (application/problem+json). Decida pelo `code`, nunca pelo texto. */ export class ImobyFlowError extends Error { readonly status: number; readonly code: string | null; readonly title: string | null; readonly type: string | null; readonly requestId: string | null; readonly detail: string | null; readonly invalidParams: Array<{ name: string; reason: string }>; readonly body: unknown; constructor(status: number, body: unknown, requestId: string | null) { const p = (body && typeof body === 'object' ? body : {}) as Record; super(`${status} ${String(p.code ?? '')}: ${String(p.title ?? '')}`.trim()); this.name = 'ImobyFlowError'; this.status = status; this.code = typeof p.code === 'string' ? p.code : null; this.title = typeof p.title === 'string' ? p.title : null; this.type = typeof p.type === 'string' ? p.type : null; this.requestId = typeof p.requestId === 'string' ? p.requestId : requestId; this.detail = typeof p.detail === 'string' ? p.detail : null; this.invalidParams = Array.isArray(p.invalidParams) ? (p.invalidParams as Array<{ name: string; reason: string }>) : []; this.body = body; } } export interface ImobyFlowOptions { /** Padrão / default: https://api.imobyflow.com.br/v1 */ baseUrl?: string; /** `pt-BR` (padrão) ou `en`: a língua dos rótulos e das mensagens de erro. */ language?: 'pt-BR' | 'en'; /** Tempo máximo de cada tentativa, em ms (padrão 30000). */ timeoutMs?: number; /** Novas tentativas em 429, 502, 503, 504 e falha de rede (padrão 2). */ maxRetries?: number; /** A primeira espera, em ms; dobra a cada tentativa (padrão 500). */ retryBaseMs?: number; /** Outro `fetch` (testes, proxy). */ fetch?: typeof fetch; } interface OpMeta { method: string; path: string; query: readonly string[]; dryRun: boolean; idempotency: boolean; } const RETRY_STATUS = new Set([429, 502, 503, 504]); const espera = (ms: number) => new Promise((r) => setTimeout(r, ms)); export class ImobyFlow { readonly baseUrl: string; readonly language: string; readonly timeoutMs: number; readonly maxRetries: number; readonly retryBaseMs: number; #key: string; #fetch: typeof fetch; constructor(apiKey: string, options: ImobyFlowOptions = {}) { // "Bearer " colado junto sai ANTES de conferir: é o erro de cópia mais comum. const chave = String(apiKey ?? '').trim().replace(/^bearer\s+/i, ''); if (!chave || /\s/.test(chave)) throw new Error('ImobyFlow: chave da API ausente ou inválida.'); this.#key = chave; this.baseUrl = (options.baseUrl ?? "https://api.imobyflow.com.br/v1").replace(/\/+$/, ''); this.language = options.language ?? 'pt-BR'; this.timeoutMs = options.timeoutMs ?? 30000; this.maxRetries = options.maxRetries ?? 2; this.retryBaseMs = options.retryBaseMs ?? 500; this.#fetch = options.fetch ?? fetch; } /** Chamada crua, para quem precisar de uma rota antes de ela entrar no SDK. */ async call(op: OpMeta, path: Record, options: Record, body?: unknown): Promise { let caminho = op.path; for (const [nome, valor] of Object.entries(path)) { if (valor === undefined || valor === null || String(valor) === '') throw new Error(`ImobyFlow: ${nome} é obrigatório.`); caminho = caminho.replace(`{${nome}}`, encodeURIComponent(String(valor))); } const qs = new URLSearchParams(); for (const nome of op.query) { const v = options[nome]; if (v !== undefined && v !== null && v !== '') qs.set(nome, String(v)); } if (op.dryRun && options.dryRun) qs.set('dryRun', 'true'); const url = this.baseUrl + caminho + (qs.toString() ? `?${qs}` : ''); const headers: Record = { Authorization: `Bearer ${this.#key}`, Accept: 'application/json', 'Accept-Language': this.language, 'User-Agent': `imobyflow-sdk-typescript/${API_VERSION}`, }; // A MESMA chave em todas as tentativas: a re-tentativa devolve a resposta da primeira. const chave = op.idempotency ? String(options.idempotencyKey ?? randomUUID()) : undefined; if (chave) headers['Idempotency-Key'] = chave; if (body !== undefined) headers['Content-Type'] = 'application/json'; const podeRepetir = op.method === 'GET' || Boolean(chave); for (let tentativa = 0; ; tentativa++) { const abort = new AbortController(); const relogio = setTimeout(() => abort.abort(), this.timeoutMs); let res: Response; try { res = await this.#fetch(url, { method: op.method, headers, body: body === undefined ? undefined : JSON.stringify(body), signal: abort.signal, }); } catch (erro) { clearTimeout(relogio); if (podeRepetir && tentativa < this.maxRetries) { await espera(this.retryBaseMs * 2 ** tentativa); continue; } throw erro; } clearTimeout(relogio); const texto = await res.text(); let dado: unknown = null; try { dado = texto ? JSON.parse(texto) : null; } catch { dado = texto; } if (res.ok) return dado; if (podeRepetir && RETRY_STATUS.has(res.status) && tentativa < this.maxRetries) { const depois = Number(res.headers.get('retry-after')); await espera(Number.isFinite(depois) && depois > 0 ? depois * 1000 : this.retryBaseMs * 2 ** tentativa); continue; } throw new ImobyFlowError(res.status, dado, res.headers.get('x-request-id')); } } /** Quem sou eu · Who am I * `GET /v1/me` */ getMe(): Promise<{ data: Me; }> { return this.call({"method":"GET","path":"/me","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Me; }>; } /** Tipos de imóvel · Property types * `GET /v1/reference/property-types` */ getPropertyTypes(): Promise<{ data: Array; }> { return this.call({"method":"GET","path":"/reference/property-types","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; }>; } /** Finalidades · Purposes * `GET /v1/reference/purposes` */ getPurposes(): Promise<{ data: Array; }> { return this.call({"method":"GET","path":"/reference/purposes","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; }>; } /** Status do imóvel · Property statuses * `GET /v1/reference/property-statuses` */ getPropertyStatuses(): Promise<{ data: Array; }> { return this.call({"method":"GET","path":"/reference/property-statuses","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; }>; } /** Etapas do cliente · Client stages * `GET /v1/reference/lead-statuses` */ getLeadStatuses(): Promise<{ data: Array; }> { return this.call({"method":"GET","path":"/reference/lead-statuses","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; }>; } /** Etapas da parceria · Partnership stages * `GET /v1/reference/partnership-statuses` */ getPartnershipStatuses(): Promise<{ data: Array; }> { return this.call({"method":"GET","path":"/reference/partnership-statuses","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; }>; } /** Estágios da obra · Construction stages * `GET /v1/reference/construction-stages` */ getConstructionStages(): Promise<{ data: Array; }> { return this.call({"method":"GET","path":"/reference/construction-stages","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; }>; } /** Permissões · Permissions * `GET /v1/reference/scopes` */ getScopes(): Promise<{ data: Array; }> { return this.call({"method":"GET","path":"/reference/scopes","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; }>; } /** Tipos de evento · Event types * `GET /v1/reference/event-types` */ getEventTypes(): Promise<{ data: Array; }> { return this.call({"method":"GET","path":"/reference/event-types","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; }>; } /** Cidades · Cities * `GET /v1/geo/cities` */ listCities(options: { uf?: string; q?: string; limit?: number; cursor?: string } = {}): Promise<{ data: Array; nextCursor: string | null; total: number; }> { return this.call({"method":"GET","path":"/geo/cities","query":["uf","q","limit","cursor"],"dryRun":false,"idempotency":false}, { }, options) as Promise<{ data: Array; nextCursor: string | null; total: number; }>; } /** Todas as páginas de `listCities`, item a item · every page of `listCities`, item by item. */ async *listCitiesAll(options: Omit<{ uf?: string; q?: string; limit?: number; cursor?: string }, 'cursor'> = {}): AsyncGenerator { const vistos = new Set(); let cursor: string | null | undefined; do { const pagina = await this.listCities({ ...options, cursor: cursor ?? undefined }); for (const item of pagina.data) yield item; cursor = pagina.nextCursor; if (cursor && vistos.has(cursor)) break; // cursor repetido: para em vez de rodar para sempre if (cursor) vistos.add(cursor); } while (cursor); } /** Bairros de uma cidade · A city's neighborhoods * `GET /v1/geo/cities/{cityId}/neighborhoods` */ listNeighborhoods(cityId: string, options: { limit?: number; cursor?: string } = {}): Promise<{ data: Array; nextCursor: string | null; total: number; }> { return this.call({"method":"GET","path":"/geo/cities/{cityId}/neighborhoods","query":["limit","cursor"],"dryRun":false,"idempotency":false}, { cityId }, options) as Promise<{ data: Array; nextCursor: string | null; total: number; }>; } /** Todas as páginas de `listNeighborhoods`, item a item · every page of `listNeighborhoods`, item by item. */ async *listNeighborhoodsAll(cityId: string, options: Omit<{ limit?: number; cursor?: string }, 'cursor'> = {}): AsyncGenerator { const vistos = new Set(); let cursor: string | null | undefined; do { const pagina = await this.listNeighborhoods(cityId, { ...options, cursor: cursor ?? undefined }); for (const item of pagina.data) yield item; cursor = pagina.nextCursor; if (cursor && vistos.has(cursor)) break; // cursor repetido: para em vez de rodar para sempre if (cursor) vistos.add(cursor); } while (cursor); } /** Listar a carteira · List the portfolio * `GET /v1/properties` — permissão `properties:read` */ listProperties(options: { limit?: number; cursor?: string; status?: "ACTIVE" | "INACTIVE" | "SOLD" | "RENTED" | "DELETED"; purpose?: "VENDA" | "LOCACAO"; type?: "APARTAMENTO" | "CASA_RUA" | "CASA_CONDOMINIO" | "CASA_VILA" | "STUDIO" | "FLAT" | "COBERTURA" | "SALA_COMERCIAL" | "LOJA_PONTO" | "GALPAO" | "PREDIO" | "TERRENO_RUA" | "TERRENO_CONDOMINIO" | "CHACARA_SITIO" | "FAZENDA"; citySlug?: string; updatedSince?: string; radar?: "pending" | "ready" } = {}): Promise<{ data: Array; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/properties","query":["limit","cursor","status","purpose","type","citySlug","updatedSince","radar"],"dryRun":false,"idempotency":false}, { }, options) as Promise<{ data: Array; nextCursor: string | null; }>; } /** Todas as páginas de `listProperties`, item a item · every page of `listProperties`, item by item. */ async *listPropertiesAll(options: Omit<{ limit?: number; cursor?: string; status?: "ACTIVE" | "INACTIVE" | "SOLD" | "RENTED" | "DELETED"; purpose?: "VENDA" | "LOCACAO"; type?: "APARTAMENTO" | "CASA_RUA" | "CASA_CONDOMINIO" | "CASA_VILA" | "STUDIO" | "FLAT" | "COBERTURA" | "SALA_COMERCIAL" | "LOJA_PONTO" | "GALPAO" | "PREDIO" | "TERRENO_RUA" | "TERRENO_CONDOMINIO" | "CHACARA_SITIO" | "FAZENDA"; citySlug?: string; updatedSince?: string; radar?: "pending" | "ready" }, 'cursor'> = {}): AsyncGenerator { const vistos = new Set(); let cursor: string | null | undefined; do { const pagina = await this.listProperties({ ...options, cursor: cursor ?? undefined }); for (const item of pagina.data) yield item; cursor = pagina.nextCursor; if (cursor && vistos.has(cursor)) break; // cursor repetido: para em vez de rodar para sempre if (cursor) vistos.add(cursor); } while (cursor); } /** Um imóvel pelo seu código · One property by your code * `GET /v1/properties/by-ref/{ref}` — permissão `properties:read` */ getPropertyByRef(ref: string): Promise<{ data: Property; }> { return this.call({"method":"GET","path":"/properties/by-ref/{ref}","query":[],"dryRun":false,"idempotency":false}, { ref }, {}) as Promise<{ data: Property; }>; } /** Um imóvel · One property * `GET /v1/properties/{propertyId}` — permissão `properties:read` */ getProperty(propertyId: string): Promise<{ data: Property; }> { return this.call({"method":"GET","path":"/properties/{propertyId}","query":[],"dryRun":false,"idempotency":false}, { propertyId }, {}) as Promise<{ data: Property; }>; } /** Oportunidades de um imóvel · A property's opportunities * `GET /v1/properties/{propertyId}/matches` — permissão `radar:read` */ listPropertyMatches(propertyId: string): Promise<{ data: Array; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/properties/{propertyId}/matches","query":[],"dryRun":false,"idempotency":false}, { propertyId }, {}) as Promise<{ data: Array; nextCursor: string | null; }>; } /** Listar clientes · List clients * `GET /v1/leads` — permissão `leads:read` */ listLeads(options: { limit?: number; cursor?: string; state?: "ACTIVE" | "ARCHIVED" | "DELETED"; status?: "NEW" | "ATTENDING" | "PROPOSAL" | "WON" | "LOST"; ownerAccountId?: string; updatedSince?: string; radar?: "pending" | "ready" } = {}): Promise<{ data: Array; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/leads","query":["limit","cursor","state","status","ownerAccountId","updatedSince","radar"],"dryRun":false,"idempotency":false}, { }, options) as Promise<{ data: Array; nextCursor: string | null; }>; } /** Todas as páginas de `listLeads`, item a item · every page of `listLeads`, item by item. */ async *listLeadsAll(options: Omit<{ limit?: number; cursor?: string; state?: "ACTIVE" | "ARCHIVED" | "DELETED"; status?: "NEW" | "ATTENDING" | "PROPOSAL" | "WON" | "LOST"; ownerAccountId?: string; updatedSince?: string; radar?: "pending" | "ready" }, 'cursor'> = {}): AsyncGenerator { const vistos = new Set(); let cursor: string | null | undefined; do { const pagina = await this.listLeads({ ...options, cursor: cursor ?? undefined }); for (const item of pagina.data) yield item; cursor = pagina.nextCursor; if (cursor && vistos.has(cursor)) break; // cursor repetido: para em vez de rodar para sempre if (cursor) vistos.add(cursor); } while (cursor); } /** Um cliente · One client * `GET /v1/leads/{leadId}` — permissão `leads:read` */ getLead(leadId: string): Promise<{ data: Lead; }> { return this.call({"method":"GET","path":"/leads/{leadId}","query":[],"dryRun":false,"idempotency":false}, { leadId }, {}) as Promise<{ data: Lead; }>; } /** Um cliente pelo seu código · One client by your code * `GET /v1/leads/by-ref/{ref}` — permissão `leads:read` */ getLeadByRef(ref: string): Promise<{ data: Lead; }> { return this.call({"method":"GET","path":"/leads/by-ref/{ref}","query":[],"dryRun":false,"idempotency":false}, { ref }, {}) as Promise<{ data: Lead; }>; } /** Oportunidades de um cliente · A client's opportunities * `GET /v1/leads/{leadId}/matches` — permissão `radar:read` */ listLeadMatches(leadId: string): Promise<{ data: Array; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/leads/{leadId}/matches","query":[],"dryRun":false,"idempotency":false}, { leadId }, {}) as Promise<{ data: Array; nextCursor: string | null; }>; } /** Oportunidades da conta · The account's opportunities * `GET /v1/matches` — permissão `radar:read` */ listMatches(options: { limit?: number; cursor?: string; status?: "NEW" | "FAVORITED" | "ARCHIVED" | "REQUESTED"; scope?: "INTERNAL" | "NETWORK" | "LAUNCH"; ownerAccountId?: string } = {}): Promise<{ data: Array; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/matches","query":["limit","cursor","status","scope","ownerAccountId"],"dryRun":false,"idempotency":false}, { }, options) as Promise<{ data: Array; nextCursor: string | null; }>; } /** Todas as páginas de `listMatches`, item a item · every page of `listMatches`, item by item. */ async *listMatchesAll(options: Omit<{ limit?: number; cursor?: string; status?: "NEW" | "FAVORITED" | "ARCHIVED" | "REQUESTED"; scope?: "INTERNAL" | "NETWORK" | "LAUNCH"; ownerAccountId?: string }, 'cursor'> = {}): AsyncGenerator { const vistos = new Set(); let cursor: string | null | undefined; do { const pagina = await this.listMatches({ ...options, cursor: cursor ?? undefined }); for (const item of pagina.data) yield item; cursor = pagina.nextCursor; if (cursor && vistos.has(cursor)) break; // cursor repetido: para em vez de rodar para sempre if (cursor) vistos.add(cursor); } while (cursor); } /** Parcerias da equipe · The team's partnerships * `GET /v1/partnerships` — permissão `partnerships:read` */ listPartnerships(options: { status?: "PENDING" | "ACCEPTED" | "DECLINED" | "CANCELLED" | "LEAD_REGISTERED" | "CONTACTED" | "NEGOTIATING" | "PROPOSAL" | "WON" | "LOST"; updatedSince?: string } = {}): Promise<{ data: Array; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/partnerships","query":["status","updatedSince"],"dryRun":false,"idempotency":false}, { }, options) as Promise<{ data: Array; nextCursor: string | null; }>; } /** Uma parceria · One partnership * `GET /v1/partnerships/{partnershipId}` — permissão `partnerships:read` */ getPartnership(partnershipId: string): Promise<{ data: Partnership; }> { return this.call({"method":"GET","path":"/partnerships/{partnershipId}","query":[],"dryRun":false,"idempotency":false}, { partnershipId }, {}) as Promise<{ data: Partnership; }>; } /** A equipe · The team * `GET /v1/team` — permissão `team:read` */ listTeam(): Promise<{ data: Array; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/team","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; nextCursor: string | null; }>; } /** Estado da sincronização do feed · Feed sync status * `GET /v1/feed` — permissão `properties:read` */ getFeed(): Promise<{ data: FeedSync; }> { return this.call({"method":"GET","path":"/feed","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: FeedSync; }>; } /** Sincronizar o feed agora · Sync the feed now * `POST /v1/feed/sync` — permissão `properties:write` — aceita `dryRun` */ syncFeed(options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: FeedSync; result: FeedSyncResult; }> { return this.call({"method":"POST","path":"/feed/sync","query":[],"dryRun":true,"idempotency":true}, { }, options) as Promise<{ data: FeedSync; result: FeedSyncResult; }>; } /** Lançamentos da cidade · The city's new developments * `GET /v1/launches` — permissão `launches:read` */ listLaunches(options: { limit?: number; cursor?: string; citySlug?: string; neighborhood?: string; type?: "APARTAMENTO" | "CASA_RUA" | "CASA_CONDOMINIO" | "CASA_VILA" | "STUDIO" | "FLAT" | "COBERTURA" | "SALA_COMERCIAL" | "LOJA_PONTO" | "GALPAO" | "PREDIO" | "TERRENO_RUA" | "TERRENO_CONDOMINIO" | "CHACARA_SITIO" | "FAZENDA"; stage?: "LAUNCH" | "OFF_PLAN" | "NEW" | "READY"; priceMin?: number; priceMax?: number; q?: string } = {}): Promise<{ data: Array; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/launches","query":["limit","cursor","citySlug","neighborhood","type","stage","priceMin","priceMax","q"],"dryRun":false,"idempotency":false}, { }, options) as Promise<{ data: Array; nextCursor: string | null; }>; } /** Todas as páginas de `listLaunches`, item a item · every page of `listLaunches`, item by item. */ async *listLaunchesAll(options: Omit<{ limit?: number; cursor?: string; citySlug?: string; neighborhood?: string; type?: "APARTAMENTO" | "CASA_RUA" | "CASA_CONDOMINIO" | "CASA_VILA" | "STUDIO" | "FLAT" | "COBERTURA" | "SALA_COMERCIAL" | "LOJA_PONTO" | "GALPAO" | "PREDIO" | "TERRENO_RUA" | "TERRENO_CONDOMINIO" | "CHACARA_SITIO" | "FAZENDA"; stage?: "LAUNCH" | "OFF_PLAN" | "NEW" | "READY"; priceMin?: number; priceMax?: number; q?: string }, 'cursor'> = {}): AsyncGenerator { const vistos = new Set(); let cursor: string | null | undefined; do { const pagina = await this.listLaunches({ ...options, cursor: cursor ?? undefined }); for (const item of pagina.data) yield item; cursor = pagina.nextCursor; if (cursor && vistos.has(cursor)) break; // cursor repetido: para em vez de rodar para sempre if (cursor) vistos.add(cursor); } while (cursor); } /** Criar ou atualizar pelo seu código · Create or update by your code * `PUT /v1/properties/by-ref/{ref}` — permissão `properties:write` — aceita `dryRun` */ putPropertyByRef(ref: string, body: PropertyInput, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Property; result: PropertyWriteResult; }> { return this.call({"method":"PUT","path":"/properties/by-ref/{ref}","query":[],"dryRun":true,"idempotency":true}, { ref }, options, body) as Promise<{ data: Property; result: PropertyWriteResult; }>; } /** Cadastrar um imóvel · Create a property * `POST /v1/properties` — permissão `properties:write` — aceita `dryRun` */ createProperty(body: PropertyInput, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Property; result: PropertyWriteResult; }> { return this.call({"method":"POST","path":"/properties","query":[],"dryRun":true,"idempotency":true}, { }, options, body) as Promise<{ data: Property; result: PropertyWriteResult; }>; } /** Alterar parte de um imóvel · Partially update a property * `PATCH /v1/properties/{propertyId}` — permissão `properties:write` — aceita `dryRun` */ patchProperty(propertyId: string, body: PropertyInput, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Property; result: PropertyWriteResult; }> { return this.call({"method":"PATCH","path":"/properties/{propertyId}","query":[],"dryRun":true,"idempotency":true}, { propertyId }, options, body) as Promise<{ data: Property; result: PropertyWriteResult; }>; } /** Mudar o status · Change the status * `POST /v1/properties/{propertyId}/status` — permissão `properties:write` — aceita `dryRun` */ setPropertyStatus(propertyId: string, body: { status: "ACTIVE" | "INACTIVE" | "SOLD" | "RENTED"; }, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Property; result: PropertyWriteResult; }> { return this.call({"method":"POST","path":"/properties/{propertyId}/status","query":[],"dryRun":true,"idempotency":true}, { propertyId }, options, body) as Promise<{ data: Property; result: PropertyWriteResult; }>; } /** Confirmar disponibilidade em lote · Confirm availability in bulk * `POST /v1/properties/confirm` — permissão `properties:write` — aceita `dryRun` */ confirmProperties(body: { ids?: Array; refs?: Array; }, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Array; result: { dryRun: boolean; summary: Record; }; }> { return this.call({"method":"POST","path":"/properties/confirm","query":[],"dryRun":true,"idempotency":true}, { }, options, body) as Promise<{ data: Array; result: { dryRun: boolean; summary: Record; }; }>; } /** Criar, atualizar ou vincular pelo seu código · Create, update or link by your code * `PUT /v1/leads/by-ref/{ref}` — permissão `leads:write` — aceita `dryRun` */ putLeadByRef(ref: string, body: LeadInput, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Lead; result: LeadWriteResult; }> { return this.call({"method":"PUT","path":"/leads/by-ref/{ref}","query":[],"dryRun":true,"idempotency":true}, { ref }, options, body) as Promise<{ data: Lead; result: LeadWriteResult; }>; } /** Cadastrar um cliente · Create a client * `POST /v1/leads` — permissão `leads:write` — aceita `dryRun` */ createLead(body: LeadInput, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Lead; result: LeadWriteResult; }> { return this.call({"method":"POST","path":"/leads","query":[],"dryRun":true,"idempotency":true}, { }, options, body) as Promise<{ data: Lead; result: LeadWriteResult; }>; } /** Alterar parte de um cliente · Partially update a client * `PATCH /v1/leads/{leadId}` — permissão `leads:write` — aceita `dryRun` */ patchLead(leadId: string, body: LeadInput, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Lead; result: LeadWriteResult; }> { return this.call({"method":"PATCH","path":"/leads/{leadId}","query":[],"dryRun":true,"idempotency":true}, { leadId }, options, body) as Promise<{ data: Lead; result: LeadWriteResult; }>; } /** Arquivar ou reativar · Archive or reactivate * `POST /v1/leads/{leadId}/state` — permissão `leads:write` — aceita `dryRun` */ setLeadState(leadId: string, body: { state: "ACTIVE" | "ARCHIVED"; }, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Lead; result: LeadWriteResult; }> { return this.call({"method":"POST","path":"/leads/{leadId}/state","query":[],"dryRun":true,"idempotency":true}, { leadId }, options, body) as Promise<{ data: Lead; result: LeadWriteResult; }>; } /** Registrar um atendimento · Log an interaction * `POST /v1/leads/{leadId}/interactions` — permissão `leads:write` — aceita `dryRun` */ addLeadInteraction(leadId: string, body: { type: "CONTACT" | "VISIT" | "PROPOSAL" | "NOTE"; note?: string; at?: string; }, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Lead; result: LeadWriteResult; }> { return this.call({"method":"POST","path":"/leads/{leadId}/interactions","query":[],"dryRun":true,"idempotency":true}, { leadId }, options, body) as Promise<{ data: Lead; result: LeadWriteResult; }>; } /** Mandar para a lixeira · Move to the trash * `DELETE /v1/properties/{propertyId}` — permissão `properties:write` — aceita `dryRun` */ deleteProperty(propertyId: string, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Property; result: PropertyWriteResult; }> { return this.call({"method":"DELETE","path":"/properties/{propertyId}","query":[],"dryRun":true,"idempotency":true}, { propertyId }, options) as Promise<{ data: Property; result: PropertyWriteResult; }>; } /** Mandar para a lixeira · Move to the trash * `DELETE /v1/leads/{leadId}` — permissão `leads:write` — aceita `dryRun` */ deleteLead(leadId: string, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Lead; result: LeadWriteResult; }> { return this.call({"method":"DELETE","path":"/leads/{leadId}","query":[],"dryRun":true,"idempotency":true}, { leadId }, options) as Promise<{ data: Lead; result: LeadWriteResult; }>; } /** Descartar oportunidade · Dismiss an opportunity * `POST /v1/matches/{matchId}/dismiss` — permissão `radar:write` — aceita `dryRun` */ dismissMatch(matchId: string, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Match; result: MatchWriteResult; }> { return this.call({"method":"POST","path":"/matches/{matchId}/dismiss","query":[],"dryRun":true,"idempotency":true}, { matchId }, options) as Promise<{ data: Match; result: MatchWriteResult; }>; } /** Restaurar oportunidade · Restore an opportunity * `POST /v1/matches/{matchId}/restore` — permissão `radar:write` — aceita `dryRun` */ restoreMatch(matchId: string, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Match; result: MatchWriteResult; }> { return this.call({"method":"POST","path":"/matches/{matchId}/restore","query":[],"dryRun":true,"idempotency":true}, { matchId }, options) as Promise<{ data: Match; result: MatchWriteResult; }>; } /** Mover a etapa da parceria · Move the partnership stage * `POST /v1/partnerships/{partnershipId}/status` — permissão `partnerships:write` — aceita `dryRun` */ setPartnershipStatus(partnershipId: string, body: { status: "PENDING" | "ACCEPTED" | "DECLINED" | "CANCELLED" | "LEAD_REGISTERED" | "CONTACTED" | "NEGOTIATING" | "PROPOSAL" | "WON" | "LOST"; }, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: Partnership; result: PartnershipWriteResult; }> { return this.call({"method":"POST","path":"/partnerships/{partnershipId}/status","query":[],"dryRun":true,"idempotency":true}, { partnershipId }, options, body) as Promise<{ data: Partnership; result: PartnershipWriteResult; }>; } /** Proprietários na vez da conta · Owners offered to the account * `GET /v1/captacao/offers` — permissão `captacao:read` */ listCaptacaoOffers(): Promise<{ data: Array; summary: CaptacaoSummary; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/captacao/offers","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; summary: CaptacaoSummary; nextCursor: string | null; }>; } /** Um proprietário · One owner * `GET /v1/captacao/offers/{offerId}` — permissão `captacao:read` */ getCaptacaoOffer(offerId: string): Promise<{ data: CaptacaoOffer; }> { return this.call({"method":"GET","path":"/captacao/offers/{offerId}","query":[],"dryRun":false,"idempotency":false}, { offerId }, {}) as Promise<{ data: CaptacaoOffer; }>; } /** Proprietários aceitos · Accepted owners * `GET /v1/captacao/owners` — permissão `captacao:read` */ listCaptacaoOwners(options: { tab?: "ACTIVE" | "CAPTURED" | "ARCHIVED"; cursor?: string } = {}): Promise<{ data: Array; nextCursor: string | null; total: number | null; counts: Record | null; }> { return this.call({"method":"GET","path":"/captacao/owners","query":["tab","cursor"],"dryRun":false,"idempotency":false}, { }, options) as Promise<{ data: Array; nextCursor: string | null; total: number | null; counts: Record | null; }>; } /** Todas as páginas de `listCaptacaoOwners`, item a item · every page of `listCaptacaoOwners`, item by item. */ async *listCaptacaoOwnersAll(options: Omit<{ tab?: "ACTIVE" | "CAPTURED" | "ARCHIVED"; cursor?: string }, 'cursor'> = {}): AsyncGenerator { const vistos = new Set(); let cursor: string | null | undefined; do { const pagina = await this.listCaptacaoOwners({ ...options, cursor: cursor ?? undefined }); for (const item of pagina.data) yield item; cursor = pagina.nextCursor; if (cursor && vistos.has(cursor)) break; // cursor repetido: para em vez de rodar para sempre if (cursor) vistos.add(cursor); } while (cursor); } /** Aceitar um proprietário · Accept an owner * `POST /v1/captacao/offers/{offerId}/accept` — permissão `captacao:accept` — aceita `dryRun` */ acceptCaptacaoOffer(offerId: string, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: CaptacaoOffer; result: CaptacaoAcceptResult; }> { return this.call({"method":"POST","path":"/captacao/offers/{offerId}/accept","query":[],"dryRun":true,"idempotency":true}, { offerId }, options) as Promise<{ data: CaptacaoOffer; result: CaptacaoAcceptResult; }>; } /** O que mudou · What changed * `GET /v1/events` — permissão `events:read` */ listEvents(options: { limit?: number; cursor?: string; since?: string; types?: string } = {}): Promise<{ data: Array; nextCursor: string; hasMore: boolean; }> { return this.call({"method":"GET","path":"/events","query":["limit","cursor","since","types"],"dryRun":false,"idempotency":false}, { }, options) as Promise<{ data: Array; nextCursor: string; hasMore: boolean; }>; } /** Todas as páginas de `listEvents`, item a item · every page of `listEvents`, item by item. */ async *listEventsAll(options: Omit<{ limit?: number; cursor?: string; since?: string; types?: string }, 'cursor'> = {}): AsyncGenerator { const vistos = new Set(); let cursor: string | null | undefined; do { const pagina = await this.listEvents({ ...options, cursor: cursor ?? undefined }); for (const item of pagina.data) yield item; cursor = pagina.nextCursor; if (cursor && vistos.has(cursor)) break; // cursor repetido: para em vez de rodar para sempre if (cursor) vistos.add(cursor); } while (cursor); } /** Endereços cadastrados · Registered endpoints * `GET /v1/webhooks` — permissão `webhooks:manage` */ listWebhooks(): Promise<{ data: Array; }> { return this.call({"method":"GET","path":"/webhooks","query":[],"dryRun":false,"idempotency":false}, { }, {}) as Promise<{ data: Array; }>; } /** Cadastrar um endereço · Register an endpoint * `POST /v1/webhooks` — permissão `webhooks:manage` */ createWebhook(body: WebhookCreate): Promise<{ data: Webhook; secret: string; }> { return this.call({"method":"POST","path":"/webhooks","query":[],"dryRun":false,"idempotency":false}, { }, {}, body) as Promise<{ data: Webhook; secret: string; }>; } /** Alterar um endereço · Update an endpoint * `PATCH /v1/webhooks/{webhookId}` — permissão `webhooks:manage` */ updateWebhook(webhookId: string, body: WebhookUpdate, options: { idempotencyKey?: string } = {}): Promise<{ data: Webhook; }> { return this.call({"method":"PATCH","path":"/webhooks/{webhookId}","query":[],"dryRun":false,"idempotency":true}, { webhookId }, options, body) as Promise<{ data: Webhook; }>; } /** Apagar um endereço · Delete an endpoint * `DELETE /v1/webhooks/{webhookId}` — permissão `webhooks:manage` */ deleteWebhook(webhookId: string, options: { idempotencyKey?: string } = {}): Promise<{ data: Webhook; result: { outcome: string; }; }> { return this.call({"method":"DELETE","path":"/webhooks/{webhookId}","query":[],"dryRun":false,"idempotency":true}, { webhookId }, options) as Promise<{ data: Webhook; result: { outcome: string; }; }>; } /** Trocar o segredo · Rotate the secret * `POST /v1/webhooks/{webhookId}/rotate-secret` — permissão `webhooks:manage` */ rotateWebhookSecret(webhookId: string): Promise<{ data: Webhook; secret: string; }> { return this.call({"method":"POST","path":"/webhooks/{webhookId}/rotate-secret","query":[],"dryRun":false,"idempotency":false}, { webhookId }, {}) as Promise<{ data: Webhook; secret: string; }>; } /** Mandar um evento de teste · Send a test event * `POST /v1/webhooks/{webhookId}/test` — permissão `webhooks:manage` */ testWebhook(webhookId: string, options: { idempotencyKey?: string } = {}): Promise<{ data: WebhookTestResult; }> { return this.call({"method":"POST","path":"/webhooks/{webhookId}/test","query":[],"dryRun":false,"idempotency":true}, { webhookId }, options) as Promise<{ data: WebhookTestResult; }>; } /** Entregas de um endereço · An endpoint's deliveries * `GET /v1/webhooks/{webhookId}/deliveries` — permissão `webhooks:manage` */ listWebhookDeliveries(webhookId: string, options: { limit?: number; cursor?: string } = {}): Promise<{ data: Array; nextCursor: string | null; }> { return this.call({"method":"GET","path":"/webhooks/{webhookId}/deliveries","query":["limit","cursor"],"dryRun":false,"idempotency":false}, { webhookId }, options) as Promise<{ data: Array; nextCursor: string | null; }>; } /** Todas as páginas de `listWebhookDeliveries`, item a item · every page of `listWebhookDeliveries`, item by item. */ async *listWebhookDeliveriesAll(webhookId: string, options: Omit<{ limit?: number; cursor?: string }, 'cursor'> = {}): AsyncGenerator { const vistos = new Set(); let cursor: string | null | undefined; do { const pagina = await this.listWebhookDeliveries(webhookId, { ...options, cursor: cursor ?? undefined }); for (const item of pagina.data) yield item; cursor = pagina.nextCursor; if (cursor && vistos.has(cursor)) break; // cursor repetido: para em vez de rodar para sempre if (cursor) vistos.add(cursor); } while (cursor); } /** Reenviar uma entrega · Redeliver * `POST /v1/webhooks/{webhookId}/deliveries/{deliveryId}/retry` — permissão `webhooks:manage` */ retryWebhookDelivery(webhookId: string, deliveryId: string, options: { idempotencyKey?: string } = {}): Promise<{ data: Delivery; result: { outcome: "SCHEDULED" | "ALREADY_SCHEDULED"; }; }> { return this.call({"method":"POST","path":"/webhooks/{webhookId}/deliveries/{deliveryId}/retry","query":[],"dryRun":false,"idempotency":true}, { webhookId, deliveryId }, options) as Promise<{ data: Delivery; result: { outcome: "SCHEDULED" | "ALREADY_SCHEDULED"; }; }>; } /** Lote de imóveis · Batch of properties * `POST /v1/properties/batch` — permissão `properties:write` — aceita `dryRun` */ createPropertyBatch(body: { items: Array; }, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: BatchCreated; }> { return this.call({"method":"POST","path":"/properties/batch","query":[],"dryRun":true,"idempotency":true}, { }, options, body) as Promise<{ data: BatchCreated; }>; } /** Acompanhar o lote · Track the batch * `GET /v1/properties/batch/{batchId}` — permissão `properties:read` */ getPropertyBatch(batchId: string): Promise<{ data: Batch; }> { return this.call({"method":"GET","path":"/properties/batch/{batchId}","query":[],"dryRun":false,"idempotency":false}, { batchId }, {}) as Promise<{ data: Batch; }>; } /** Lote de clientes · Batch of clients * `POST /v1/leads/batch` — permissão `leads:write` — aceita `dryRun` */ createLeadBatch(body: { items: Array; }, options: { dryRun?: boolean; idempotencyKey?: string } = {}): Promise<{ data: BatchCreated; }> { return this.call({"method":"POST","path":"/leads/batch","query":[],"dryRun":true,"idempotency":true}, { }, options, body) as Promise<{ data: BatchCreated; }>; } /** Acompanhar o lote · Track the batch * `GET /v1/leads/batch/{batchId}` — permissão `leads:read` */ getLeadBatch(batchId: string): Promise<{ data: Batch; }> { return this.call({"method":"GET","path":"/leads/batch/{batchId}","query":[],"dryRun":false,"idempotency":false}, { batchId }, {}) as Promise<{ data: Batch; }>; } /** * Confere a assinatura de um webhook (Standard Webhooks). `rawBody`: o corpo EXATAMENTE como chegou, * antes de qualquer JSON.parse. Aceita qualquer uma das assinaturas (na troca de segredo vêm duas) e * recusa mensagem com mais de 5 minutos. */ static verifyWebhook(secret: string, msgId: string, timestamp: string, signatureHeader: string, rawBody: string): boolean { const agora = Math.floor(Date.now() / 1000); if (!/^\d+$/.test(timestamp) || Math.abs(agora - Number(timestamp)) > 5 * 60) return false; const chave = Buffer.from(secret.slice('whsec_'.length), 'base64'); const esperado = createHmac('sha256', chave).update(`${msgId}.${timestamp}.${rawBody}`, 'utf8').digest(); return signatureHeader.split(' ').some((parte) => { const [versao, assinatura] = parte.split(','); if (versao !== 'v1' || !assinatura) return false; const dada = Buffer.from(assinatura, 'base64'); return dada.length === esperado.length && timingSafeEqual(dada, esperado); }); } }