/** * iMatch Connect embed SDK v1 — type declarations for connect.js. * Load the script from https://app.imatch.ae/connect/v1/connect.js; it * defines `window.IMatchConnect`. Copy this file next to your code, or * `/// `. */ /** * The creators (`offer`, `poster`, `copilot`) re-host iMatch's studios; the * screens (`listings`, `projects`, `matches`) re-host its browse surfaces — * market listings search, the Dubai projects catalogue, your own matches — * read with exactly the rules of `GET /v1/listings`, `/v1/projects` and * `/v1/matches` (your allowed sources, distress only with `distress_read`, * contact only through a charged reveal, matches on your rows only). */ export type IMatchConnectTool = "offer" | "poster" | "copilot" | "listings" | "projects" | "matches"; export interface IMatchConnectListingRef { /** iMatch listing id. */ id?: number | string; /** Your own id for a listing you created through /v1 (external_id). */ external_id?: string; } /** * A listing that lives only in your CRM, handed to POST /api/v1/embed/sessions * as `listing: { inline: … }` (server side — never through this SDK, which * passes only the token; the token already names the listing, so leave the * `listing` mount option out). Informational: the shape your server sends. * iMatch keeps it for the session hour only; `external_id` is what the * `created` event's `reference_no` carries back. */ export interface IMatchConnectInlineListing { /** Your own id, ≤ 80 — printed as the reference and returned as `reference_no`. */ external_id: string; /** At least one of `title` / `description` (≤ 4000). */ title?: string | null; description?: string | null; purpose?: "Sale" | "Rent" | null; property_type?: string | null; area?: string | null; community?: string | null; project?: string | null; developer?: string | null; floor?: string | null; /** 0 = Studio. */ bedrooms?: number | string | null; bathrooms?: number | string | null; bua_sqft?: number | null; plot_sqft?: number | null; selling_price?: number | null; /** AED per year. */ rent_price?: number | null; /** Only AED today. */ currency?: "AED"; furnished?: string | null; completion_status?: string | null; handover_date?: string | null; payment_plan?: string | null; view?: string | null; permit_number?: string | null; /** Up to 20, https only, fetched at render time. */ photos?: Array<{ url: string; kind?: "photo" | "floor_plan" | "master_plan" }>; agent?: { name?: string | null; phone?: string | null; brokerage?: string | null }; } /** The `listing` field of the session mint body: by reference, or inline (never both). */ export type IMatchConnectEmbedSessionListing = | { id: number; external_id?: undefined; inline?: undefined } | { external_id: string; id?: undefined; inline?: undefined } | { inline: IMatchConnectInlineListing; id?: undefined; external_id?: undefined }; export interface IMatchConnectCreated { kind: "offer" | "poster"; /** Public share URL of the stored PDF / PNG. */ url: string; reference_no: string | null; artifact_id: number | null; } /** * What a screen hands you when your user clicks "Use this listing / project / * match" — ids and the one label you need to show what was picked. Fetch the * rest through `/v1` if you need it, or hand the id straight to the offer / * poster tool (`listing: { id }`). */ export type IMatchConnectSelected = | { kind: "listing"; /** iMatch listing id — what `listing: { id }` on the offer / poster mount takes. */ id: number; reference_no: string | null; /** Your own id when the listing is one you created through /v1; null on market rows. */ external_id: string | null; } | { kind: "project"; id: number; name: string } | { kind: "match"; id: number; listing_id: number | null; inquiry_id: number | null }; /** * A failed save (`code: "save_failed"`) — the only error the frame can report. * A rejected, expired or missing token never produces one: the embed page is * delivered with `frame-ancestors 'none'` in that case and the browser refuses * to render it, so nothing inside the frame runs. Watch the session's * `expires_at` and re-mint (then `setToken()`) before it passes. */ export interface IMatchConnectError { code: string; message: string; } export interface IMatchConnectMountOptions { /** * Container element or a selector. The page doing the mounting must be * served from an origin listed on your partner account (allowed origins); * the frame is delivered with `frame-ancestors` naming exactly those, and * a browser refuses to render it anywhere else. */ el: Element | string; tool: IMatchConnectTool; /** Embed token from POST /api/v1/embed/sessions (mint it on your server). */ token: string; /** Pre-select a listing; overrides the token's listing claim. */ listing?: IMatchConnectListingRef | string | number; theme?: "light" | "dark"; /** "auto" (default) grows the iframe to the content; a number fixes it in px. */ height?: "auto" | number; onReady?: (info: { tool: IMatchConnectTool }) => void; /** The offer / poster tools: the agent produced a document. */ onCreated?: (created: IMatchConnectCreated) => void; /** The listings / projects / matches screens: your user picked a row. */ onSelected?: (selected: IMatchConnectSelected) => void; /** A save failed. Never fires for a bad or expired token — see IMatchConnectError. */ onError?: (error: IMatchConnectError) => void; /** Override the embed base (default https://app.imatch.ae/embed/v1). */ embedBase?: string; } export interface IMatchConnectHandle { iframe: HTMLIFrameElement; destroy(): void; /** * Hand the frame a freshly minted token BEFORE the old one expires (track * the session's `expires_at`). The frame's later API calls use the new * token; the page itself is not re-verified, so this cannot revive a frame * the browser already refused to render. */ setToken(token: string): void; /** Free-form message to the embed (delivered as { type: "host", data }). */ postMessage(data: unknown): void; } export interface IMatchConnectOpenOptions extends Omit { onClose?: () => void; } export interface IMatchConnectOpenHandle extends IMatchConnectHandle { close(): void; } export interface IMatchConnectStatic { mount(options: IMatchConnectMountOptions): IMatchConnectHandle; /** Modal overlay with the tool; Esc, the backdrop or the × closes it. */ open(options: IMatchConnectOpenOptions): IMatchConnectOpenHandle; readonly version: string; /** The `source` field on every message the embed posts: "imatch-connect". */ readonly source: "imatch-connect"; } /** * Native apps (iOS / Android) — no SDK, no iframe. Open * `https://app.imatch.ae/embed/v1/?token=…` top-level in a web view; * the token (minted by YOUR server exactly as for the web) is the whole gate. * The page delivers every event above as a JSON STRING to a native handler * when one is present, in addition to `window.parent`: * iOS a `WKScriptMessageHandler` named `imatch` * → `window.webkit.messageHandlers.imatch.postMessage(json)` * Android a `@JavascriptInterface` object registered as `IMatchAndroid` * → `window.IMatchAndroid.onMessage(json)` * Parse the string: `{ source: "imatch-connect", type, ... }` with the same * `type`s and payloads as the SDK callbacks. To hand the page a re-minted * token, evaluate `window.IMatchConnectEmbed.setToken("")`. */ export interface IMatchConnectEmbedPage { setToken(token: string): void; } declare global { interface Window { IMatchConnect: IMatchConnectStatic; /** Optional override read by the SDK before mounting. */ IMATCH_CONNECT_EMBED_BASE?: string; /** Present INSIDE an embed page (not on your page) — the native app's way in. */ IMatchConnectEmbed?: IMatchConnectEmbedPage; } } declare const IMatchConnect: IMatchConnectStatic; export default IMatchConnect;