Discover your next chapter in Dubai real estate.

Explore exceptional properties crafted for modern living. Whether buying, renting, or selling, find your perfect match with us. Your next adventure starts here.

Featured this week

Featured developments

Get an instant property valuation

If you're considering selling your home, understanding its current value is a crucial first step. Obtain a precise, independent valuation along with a comprehensive report here.

We have grown to become a major player in the Dubai property sector by providing world-class client services.

Google Review

from over 125+ reviews

Check our live listings

From studios to Penthouses & Townhouses to Villas or Mansions, we’ve got them all for you!

Careers Submission

Tell Us About Yourself and Your Qualifications

By clicking Submit, you agree to our Terms & Conditions and Privacy Policy .

// ========================================================= // KORE M14 — Window Poster: Build HTML for Gotenberg (v9) // Round-2 changes: // - TITLE = `${type} IN ${location}, ${area}` (no bedroom prefix) // - Description from webhook.description (popup), 550-char limit // - Footer black (#000) for transparent paper printing // - White CSS gradient overlay at top of hero (no PNG dependency) // - Agent QR (bottom) = vCard QR generated via qrserver.com from user data // - Property DLD QR (top of hero) stays from property.qRCodeId attachment // ========================================================= const webhook = $('Webhook').item.json.body || $('Webhook').item.json; const property = $('Get Property').item.json; const user = $('Get User').item.json; const ASSETS = 'https://koredubai.net/kore-render/assets'; const DESC_LIMIT = 550; // ---------- Option label maps ---------- const AMENITY_LABELS = { AC: 'Central A/C & Heating', BA: 'Balcony', BK: 'Built-in Kitchen Appliances', BL: 'View of Landmark', BW: 'Built-in Wardrobes', CP: 'Covered Parking', CS: 'Concierge Service', LB: 'Lobby in Building', MR: "Maid's Room", MS: 'Maid Service', PA: 'Pets Allowed', PG: 'Private Garden', PJ: 'Private Jacuzzi', PP: 'Private Pool', PY: 'Private Gym', VC: 'Vastu-compliant', SE: 'Security', SP: 'Shared Pool', SS: 'Shared Spa', ST: 'Study', SY: 'Shared Gym', VW: 'View of Water', WC: 'Walk-in Closet', CO: "Children's Pool", PR: "Children's Play Area", BR: 'Barbecue Area', CR: 'Conference Room', AN: 'Available Networked', DN: 'Dining in building', PN: 'Pantry', MZ: 'Mezzanine', HR: 'Horse Riding', GC: 'Golf Course', CH: 'Club House', SA: 'Sports Academy', DR: "Drivers Room", SAN: 'Sanitation', NS: 'No Services', FP: 'Fixed Phone', FO: 'Fibre Optics', FD: 'Flood Drainage', NET: 'Networked', VOL: 'View of Landmark', }; // ---------- helpers ---------- const fmt = (n) => (n == null || n === '') ? '' : Number(n).toLocaleString('en-US'); const fmtInt = (n) => (n == null || n === '') ? '' : Math.round(Number(n)).toLocaleString('en-US'); const phoneFmt = (p) => { if (!p) return ''; const d = String(p).replace(/\D/g, ''); if (d.startsWith('971') && d.length === 12) return `+971 ${d.slice(3,5)} ${d.slice(5,8)} ${d.slice(8)}`; return p; }; const upper = (s) => (s || '').toString().toUpperCase(); const trimToSentence = (text, limit) => { text = String(text || '').trim().replace(/\s+/g, ' '); if (text.length <= limit) return text; const slice = text.slice(0, limit); const lastStop = slice.lastIndexOf('.'); if (lastStop > 60) return slice.slice(0, lastStop + 1); const lastSpace = slice.lastIndexOf(' '); return slice.slice(0, lastSpace > 0 ? lastSpace : limit); }; // ---------- field values ---------- const propertyRef = property.ref || property.id || ''; const bedCount = Number(property.bedroomCount) || 0; const isStudio = bedCount === 0; const bedBadgeText = isStudio ? 'STUDIO' : `${bedCount} BDRM`; // ---------- Title (TYPE IN LOCATION, AREA) ---------- const titleParts = [ [upper(property.type), upper(property.locationName || property.community)].filter(Boolean).join(' IN '), upper(property.aPILocation || ''), ].filter(Boolean); const TITLE = titleParts.join(', '); // ---------- Dynamic features list ---------- const rawCodes = [ ...(Array.isArray(property.amenities) ? property.amenities : []), ...(Array.isArray(property.features) ? property.features : []), ]; const seen = new Set(); const featureList = []; for (const code of rawCodes) { const label = AMENITY_LABELS[code] || code; if (!seen.has(label)) { seen.add(label); featureList.push(label); } if (featureList.length >= 5) break; } const FEATURES_LIST_HTML = featureList.length ? featureList.map(f => `
  • ${f}
  • `).join('') : '
  • '; // ---------- DLD QR (property) ---------- let qrPropertyUrl = ''; let qrDebug = 'no-binary'; try { const buffer = await this.helpers.getBinaryDataBuffer(0, 'qrBinary'); if (buffer && buffer.length > 100) { qrPropertyUrl = `data:image/png;base64,${buffer.toString('base64')}`; qrDebug = `ok-${buffer.length}b`; } else { qrDebug = `too-small (${buffer ? buffer.length : 0}b)`; } } catch (e) { qrDebug = 'error: ' + e.message; } // ---------- Agent vCard QR (generated dynamically via qrserver) ---------- const agentName = (user.name || '').trim(); // Display-name override: Peyman Javaherian shows as "Tony" on the poster (QR/vCard stays the real contact) const NAME_OVERRIDES = { '637dfc6a6b90a8f03': 'Tony' }; const agentDisplayName = (NAME_OVERRIDES[user.id] || agentName); const agentFirst = (user.firstName || '').trim(); const agentLast = (user.lastName || '').trim(); const agentTel = (user.phoneNumber || '').trim(); const agentEmail = (user.emailAddress || '').trim(); const agentTitle = (user.title || '').trim(); const agentOrg = 'KORE Real Estate'; const vcard = [ 'BEGIN:VCARD', 'VERSION:3.0', `N:${agentLast};${agentFirst}`, `FN:${agentName}`, `TEL;TYPE=CELL:${agentTel}`, `EMAIL:${agentEmail}`, `ORG:${agentOrg}`, 'END:VCARD', ].join('\n'); const qrAgentUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&margin=0&data=${encodeURIComponent(vcard)}`; // ---------- Price with /yr for Rent ---------- const isRent = upper(property.requestType || property.transactionType) === 'RENT'; const PRICE = fmt(property.price) + (isRent ? '/yr' : ''); // ---------- Get uploaded image URLs ---------- const heroUrl = $('Upload Hero').first().json.url || ''; const thumb1Url = $('Upload Thumb1').first().json.url || ''; const thumb2Url = $('Upload Thumb2').first().json.url || ''; const thumb3Url = $('Upload Thumb3').first().json.url || ''; // ---------- Description (from webhook popup, fallback to CRM) ---------- const descriptionRaw = (webhook.description || property.description || '').toString(); const DESCRIPTION = trimToSentence(descriptionRaw.slice(0, DESC_LIMIT), DESC_LIMIT); // ---------- Filenames for cleanup ---------- const filenamesToDelete = [ $('Upload Hero').first().json.filename, $('Upload Thumb1').first().json.filename, $('Upload Thumb2').first().json.filename, $('Upload Thumb3').first().json.filename, ].filter(Boolean); const data = { HERO: heroUrl, THUMB1: thumb1Url, THUMB2: thumb2Url, THUMB3: thumb3Url, QR_PROP: qrPropertyUrl, QR_AGENT: qrAgentUrl, TITLE: TITLE, SALE_OR_RENT: isRent ? 'RENT' : 'SALE', BED_BADGE: bedBadgeText, BATHS: property.bathroomCount || '', SQFT: fmtInt(property.square), MAID_LINE: property.hasMaidRoom ? '
    + MAID
    ' : '', PERMIT: property.rERAPermitNo || property.permitNumber || propertyRef, FEATURES_HEADLINE: upper(property.featuresHeadline || property.name || ''), DESCRIPTION: DESCRIPTION, FEATURES_LIST: FEATURES_LIST_HTML, AGENT_NAME: upper(agentDisplayName), AGENT_TITLE: upper(agentTitle), AGENT_PHONE: phoneFmt(agentTel), AGENT_EMAIL: agentEmail, PRICE, ASSETS, }; // ---------- HTML template ---------- const html = `
    {{TITLE}}
    FOR {{SALE_OR_RENT}}
    {{BED_BADGE}}
    {{MAID_LINE}}
    {{BATHS}} BTH
    {{SQFT}} SQFT
    Permit No: {{PERMIT}}
    {{FEATURES_HEADLINE}}

    Features:

    {{DESCRIPTION}}
    For more information or to book a viewing contact:
    {{AGENT_NAME}}
    {{AGENT_PHONE}}
    {{PRICE}}
    `; const final = Object.keys(data).reduce( (acc, key) => acc.split(`{{${key}}}`).join(data[key]), html ); return [{ json: { ok: true, propertyRef, qrDebug, descLen: DESCRIPTION.length, featureCount: featureList.length, htmlLength: final.length, filenamesToDelete }, binary: { files: { data: Buffer.from(final, 'utf8').toString('base64'), mimeType: 'text/html', fileName: 'index.html', }, }, }];