/**
* 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;