MyImmobilier.ch

Per agenzie e fornitori di CRM

API e integrazione CRM

Un'unica API JSON per gestire i vostri annunci dal vostro CRM (ImmoBridge, Immomig, Rexx, Flowfact…), ricevere in tempo reale le richieste dei visitatori e recuperare le statistiche di ogni immobile. Gli immobili seguono lo standard SwissRETS JSON.

1. Autenticazione

Da Area agenzia → Integrazioni, create una chiave (scheda CRM). Viene mostrata una sola volta; conserviamo soltanto la sua impronta SHA-256. Una chiave dà accesso esclusivamente ai dati della propria agenzia e può essere revocata in qualsiasi momento.

curl https://myimmobilier.ch/api/v1/me \
  -H "Authorization: Bearer mi_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Diritti di una chiave: listings:read, listings:write, leads:read, leads:write, stats:read.

2. Inviare e gestire gli immobili

L'immobile è un oggetto property SwissRETS. Il suo id è l'identificativo del vostro CRM: rende l'invio idempotent (stesso id = aggiornamento, mai duplicati). Aggiungete l'header X-CRM: immobridge per identificare la fonte. Le foto (attachments.images[].url, https) vengono scaricate e ospitate da noi (max 30, 8 MB ciascuna).

GET/api/v1/listingsElenco (status, updatedSince, limit, cursor).
PUT/api/v1/listings/{externalId}Crea o aggiorna un immobile SwissRETS (upsert).
POST/api/v1/listingsIdem, l'id proviene dal corpo (property.id).
GET/api/v1/listings/{ref}Dettaglio. {ref} = id CRM, riferimento MY-xxxxx o uuid.
POST/api/v1/listings/{ref}/publishPubblica (60 giorni). L'agenzia deve essere approvata.
POST/api/v1/listings/{ref}/unpublishRiporta in bozza.
POST/api/v1/listings/{ref}/renewRinnova per 60 giorni.
DELETE/api/v1/listings/{ref}Archivia (rimuove dal portale, conserva lo storico).
POST/api/v1/importsImportazione in blocco: { crm, properties: [≤ 50] }, risultato per immobile (207 in caso di errori parziali).

availability.state determina lo stato: active/reserved → pubblicato, private → bozza, reference/taken → archiviato. Questa corrispondenza permette di «pubblicare dal CRM» semplicemente rinviando l'immobile.

Documenti complementari (regolamento, dossier…): attachments.documents[] può contenere più voci, tutte visualizzate sulla scheda — non soltanto un'unica brochure. Planimetria 3D / visita immersiva (Matterport, iGuide…): attachments.virtualTourLinks[], come una normale visita virtuale.

Indirizzo nascosto: alcune agenzie non pubblicano l'indirizzo esatto (immobile occupato, proprietario riservato…). Aggiungete l'header X-Hide-Address: true su PUT/POST /listings (oppure {"hideAddress": true} nel corpo di /imports): la via, il numero civico e le coordinate esatte non vengono più pubblicati — sulla mappa, sulla vista stradale e sulla mappa catastale compaiono soltanto la località e un perimetro approssimativo (da 300 a 450 m, il cui centro non coincide volutamente con l'indirizzo reale). Omettete l'header per lasciare invariata questa impostazione durante un aggiornamento; inviate X-Hide-Address: false per disattivarla.

Blocchi visualizzati: per impostazione predefinita, un annuncio pubblicato mostra la mappa, la vista stradale, la simulazione solare e la mappa catastale non appena i dati lo consentono (coordinate, tipo di immobile). Per nasconderne alcuni senza toccare l'indirizzo, aggiungete l'header X-Hide-Features con un elenco separato da virgole tra map, streetview, solar, cadastre — ad esempio X-Hide-Features: streetview,solar. Come per l'indirizzo, omettere l'header lascia invariata l'impostazione esistente; inviarlo con un elenco vuoto (X-Hide-Features:) mostra di nuovo tutto. La risposta restituisce lo stato corrente in hiddenFeatures (array) e hiddenAddress (booleano).

curl -X PUT https://myimmobilier.ch/api/v1/listings/CRM-4711 \
  -H "Authorization: Bearer mi_live_…" -H "X-CRM: immobridge" \
  -H "Content-Type: application/json" -d '{
  "id": "CRM-4711", "referenceId": "R-4711", "type": "buy",
  "categories": ["apartment"],
  "availability": { "state": "active", "start": "2026-11-01" },
  "address": { "countryCode": "CH", "locality": "Lausanne", "region": "VD", "postalCode": "1003", "street": "Rue du Lac", "streetNumber": "4" },
  "characteristics": { "numberOfRooms": 3.5, "areaBwf": 82 },
  "prices": { "currency": "CHF", "buy": { "price": 895000 } },
  "localizations": [{ "languageCode": "fr", "title": "Appartement 3.5 pièces", "description": "…",
    "attachments": { "images": [{ "url": "https://…/1.jpg" }], "documents": [{ "url": "https://…/brochure.pdf" }] } }]
}'

3. Mostrare i vostri immobili sul vostro sito (vetrina)

Non volete programmare voi stessi la visualizzazione? Un widget <iframe> chiavi in mano, personalizzabile (colore, mappa, filtri) e senza chiave API, si genera da Integrazioni → Sito web. Per un sito sviluppato su misura che gestisce da sé la visualizzazione, utilizzate piuttosto questa route: restituisce solo gli immobili pubblicati, in formato di visualizzazione (foto, coordinate già rese approssimative se necessario, media, badge) — a differenza di /listings sopra, orientata alla sincronizzazione del CRM.

GET/api/v1/vitrineImmobili pubblicati (scope listings:read, già sufficiente — nessuna chiave dedicata).

Stessi filtri della ricerca del portale: types (ripetibile), prixMin, prixMax, piecesMin, surfaceMin, q (testo libero), sort (pertinence, prix_asc, prix_desc, surface_desc, recent), page. Risposta paginata { data, total, totalPages, page }, memorizzata in cache per 60 s (Cache-Control: public, max-age=60), a differenza delle altre route /api/v1.

curl "https://myimmobilier.ch/api/v1/vitrine?types=appartement&prixMax=900000&sort=recent" \
  -H "Authorization: Bearer mi_live_…"

{
  "data": [{
    "id": "…", "reference": "MY-01012", "slug": "lausanne-appartement-…",
    "url": "https://myimmobilier.ch/bien/lausanne-appartement-…",
    "type": "appartement", "offre": "vente", "titre": "Appartement 3.5 pièces",
    "prix": 895000, "pieces": 3.5, "surface": 82, "npa": "1003", "ville": "Lausanne",
    "latitude": 46.52, "longitude": 6.63,
    "photoPrincipale": "https://…/1.jpg", "photos": ["https://…/1.jpg"],
    "medias": { "visiteVirtuelle": null, "video": null, "plan": null, "brochure": null },
    "badges": ["nouveau"], "agenceNom": "Mon Agence", "publishedAt": "2026-09-20T08:00:00Z"
  }],
  "total": 12, "totalPages": 1, "page": 1
}

4. Ricevere le richieste

Contatto, richiesta di brochure, visita o richiamata: ogni richiesta è disponibile tramite il webhook lead.created (immediato) e tramite polling GET /api/v1/leads?since=…&unprocessed=1. Confermate la ricezione con PATCH /api/v1/leads/{id} {"acknowledged": true, "status": "traitee"} per non rielaborarla.

{
  "id": "evt_…", "type": "lead.created", "created": "2026-09-26T10:12:00Z",
  "data": {
    "id": "…", "type": "brochure", "status": "nouvelle",
    "contact": { "firstName": "Anna", "lastName": "Muller", "email": "anna@…", "phone": "+41 79 …" },
    "message": "…",
    "source": "site", "stage": "nouveau",
    "budget": { "amount": 850000, "currency": "CHF", "verified": false },
    "visit": { "startsAt": "2026-10-12T14:00:00Z", "endsAt": "2026-10-12T14:30:00Z" },
    "broker": { "id": "…", "name": "Marie Dupont", "email": "marie@…", "title": "Courtière" },
    "listing": { "id": "…", "reference": "MY-01012", "externalId": "CRM-4711", "title": "…", "city": "Lausanne", "price": 895000, "offer": "vente", "type": "appartement", "url": "https://myimmobilier.ch/bien/…" }
  }
}

source: site (modulo di myimmobilier.ch) oppure widget (modulo dell'iframe integrato nel vostro sito web). budget è il budget stimato allegato dal visitatore: calcolato da lui, mai verificato da una banca. visit esiste solo per una prenotazione di visita; broker è l'agente responsabile dell'immobile, se presente. stage: nouveau, contacte, visite, offre, conclu, perdu.

Gli eventi listing.* riportano l'immobile così come restituito da GET /listings/{ref}: status (brouillon, publie, expire, archive), exclusive, featured («Selezione»), boostedUntil (messa in evidenza a pagamento), contactDisplay (agence, courtier, les_deux), broker, outcome ({ "type": "vendu" | "loue", "at": … }), nonché prezzo, superfici, indirizzo (nascosto se richiesto), date di pubblicazione e di scadenza.

5. Statistiche

Contatori anonimi aggregati per giorno e per immobile: views, contacts, phoneReveals, brochureDownloads, virtualTours, videoPlays, favorites, shares. Nessun dato personale dei visitatori.

GET/api/v1/stats?from=&to=Intera agenzia: totali, per immobile, per giorno (30 giorni per impostazione predefinita).
GET/api/v1/listings/{ref}/statsUn immobile.

6. Webhooks

Eventi: lead.created, lead.updated (fase modificata dall'agenzia), listing.published (pubblicazione o rinnovo), listing.updated, listing.expired (scadenza raggiunta, verificata ogni giorno), listing.archived (archiviato o riportato in bozza), listing.concluded (venduto / affittato), listing.featured (messa in evidenza a pagamento applicata), webhook.test. Le azioni eseguite dall'area agenzia di MyImmobilier.ch vengono inviate come quelle effettuate tramite l'API (con qualche secondo di ritardo); quelle che il vostro CRM provoca da sé tramite l'API non gli vengono rinviate in eco, tranne listing.published/listing.updated. Solo URL https. Tempo di risposta: 5 s; altrimenti nuovo tentativo dopo 1 min, 5 min, 30 min, 2 h e poi 12 h (riesecuzione manuale possibile dall'area agenzia). Eliminate i duplicati in base all'header X-MyImmobilier-Delivery / id.

Verificate la firma: X-MyImmobilier-Signature: t=<timestamp>,v1=<hex> dove v1 = HMAC_SHA256(secret, t + "." + corps_brut). Rifiutate la richiesta se t ha più di 5 minuti.

import { createHmac, timingSafeEqual } from "node:crypto";

function verifier(secret, enteteSignature, corpsBrut) {
  const { t, v1 } = Object.fromEntries(enteteSignature.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const attendu = createHmac("sha256", secret).update(t + "." + corpsBrut).digest("hex");
  return attendu.length === v1.length && timingSafeEqual(Buffer.from(attendu), Buffer.from(v1));
}

7. Errori e limiti

{ "error": { "code": "validation_failed", "message": "…", "details": [{ "chemin": "/characteristics/areaBwf", "message": "must be number" }] } }

Codici: 400 invalid_request, 401 unauthorized, 403 forbidden / agency_not_approved, 404 not_found, 409 invalid_state, 413 payload_too_large, 422 validation_failed. Corpo max 2 MB (8 MB per le importazioni). Un immobile pubblicato scade dopo 60 giorni: chiamate renew oppure ricevete listing.expired per rinnovarlo automaticamente.

Fornitore di CRM? Scriveteci per un supporto all'integrazione: contatto.