MyImmobilier.ch

Pour les agences et éditeurs de CRM

API & intégration CRM

Une seule API JSON pour piloter vos annonces depuis votre CRM (ImmoBridge, Immomig, Rexx, Flowfact…), recevoir les demandes des visiteurs en temps réel et récupérer les statistiques de chaque bien. Les biens suivent le standard SwissRETS JSON.

1. Authentification

Depuis Espace agence → Intégrations, créez une clé (onglet CRM). Elle est affichée une seule fois ; nous ne conservons que son empreinte SHA-256. Une clé donne accès uniquement aux données de son agence et se révoque à tout moment.

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

Droits d'une clé : listings:read, listings:write, leads:read, leads:write, stats:read.

2. Envoyer et gérer les biens

Le bien est un objet property SwissRETS. Son id est l'identifiant de votre CRM : il rend l'envoi idempotent (même id = mise à jour, jamais de doublon). Ajoutez l'en-tête X-CRM: immobridge pour identifier la source. Les photos (attachments.images[].url, https) sont téléchargées et hébergées chez nous (30 max, 8 Mo chacune).

GET/api/v1/listingsListe (status, updatedSince, limit, cursor).
PUT/api/v1/listings/{externalId}Crée ou met à jour un bien SwissRETS (upsert).
POST/api/v1/listingsIdem, l'id vient du corps (property.id).
GET/api/v1/listings/{ref}Détail. {ref} = id CRM, référence ANN-xxxxx ou uuid.
POST/api/v1/listings/{ref}/publishPublie (60 jours). L'agence doit être approuvée.
POST/api/v1/listings/{ref}/unpublishRepasse en brouillon.
POST/api/v1/listings/{ref}/renewRenouvelle 60 jours.
DELETE/api/v1/listings/{ref}Archive (retire du portail, garde l'historique).
POST/api/v1/importsImport en masse : { crm, properties: [≤ 50] }, résultat par bien (207 si échecs partiels).

availability.state pilote le statut : active/reserved → publié, private → brouillon, reference/taken → archivé. Cette correspondance permet de « publier depuis le CRM » simplement en renvoyant le bien.

Documents complémentaires (règlement, dossier…) : attachments.documents[] peut contenir plusieurs entrées, toutes affichées sur la fiche — pas seulement une brochure unique. Plan 3D / visite immersive (Matterport, iGuide…) : attachments.virtualTourLinks[], comme une visite virtuelle classique.

Adresse masquée : certaines agences ne publient pas l'adresse exacte (bien occupé, propriétaire discret…). Ajoutez l'en-tête X-Hide-Address: true sur PUT/POST /listings (ou {"hideAddress": true} dans le corps de /imports) : la rue, le numéro et les coordonnées exactes ne sont alors jamais publiés — seuls la ville et un périmètre approximatif (300 à 450 m, dont le centre n'est volontairement pas l'adresse réelle) s'affichent sur la carte, la vue de rue et la carte cadastrale. Omettez l'en-tête pour laisser ce réglage inchangé lors d'une mise à jour ; envoyez X-Hide-Address: false pour le désactiver.

Blocs affichés : par défaut, une annonce publiée montre la carte, la vue de rue, la simulation solaire et la carte cadastrale dès que les données le permettent (coordonnées, type de bien). Pour en masquer certains sans toucher à l'adresse, ajoutez l'en-tête X-Hide-Features avec une liste séparée par des virgules parmi map, streetview, solar, cadastre — par exemple X-Hide-Features: streetview,solar. Comme pour l'adresse, omettre l'en-tête laisse le réglage existant inchangé ; l'envoyer avec une liste vide (X-Hide-Features:) réaffiche tout. La réponse renvoie l'état courant dans hiddenFeatures (tableau) et hiddenAddress (booléen).

curl -X PUT https://www.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. Afficher vos biens sur votre site (vitrine)

Pas envie de coder l'affichage vous-même ? Un widget <iframe> clé en main, personnalisable (couleur, carte, filtres) et sans clé API, se génère depuis Intégrations → Site web. Pour un site codé sur-mesure qui gère lui-même l'affichage, utilisez plutôt cette route : elle renvoie vos biens publiés uniquement, au format affichage (photos, coordonnées déjà démasquées si besoin, médias, badges) — contrairement à /listings ci-dessus, orientée synchronisation CRM.

GET/api/v1/vitrineBiens publiés (scope listings:read, déjà suffisant — pas de clé dédiée).

Mêmes filtres que la recherche du portail : types (répétable), prixMin, prixMax, piecesMin, surfaceMin, q (texte libre), sort (pertinence, prix_asc, prix_desc, surface_desc, recent), page. Réponse paginée { data, total, totalPages, page }, mise en cache 60 s (Cache-Control: public, max-age=60), contrairement aux autres routes /api/v1.

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

{
  "data": [{
    "id": "…", "reference": "ANN-00012", "slug": "lausanne-appartement-…",
    "url": "https://www.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. Recevoir les demandes

Contact, demande de brochure, visite ou rappel : chaque demande est disponible par webhook lead.created (immédiat) et par polling GET /api/v1/leads?since=…&unprocessed=1. Accusez réception avec PATCH /api/v1/leads/{id} {"acknowledged": true, "status": "traitee"} pour ne pas la retraiter.

{
  "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": "…",
    "listing": { "reference": "ANN-00012", "externalId": "CRM-4711", "title": "…", "url": "https://www.myimmobilier.ch/bien/…" }
  }
}

5. Statistiques

Compteurs anonymes agrégés par jour et par bien : views, contacts, phoneReveals, brochureDownloads, virtualTours, videoPlays, favorites, shares. Aucune donnée personnelle de visiteur.

GET/api/v1/stats?from=&to=Toute l'agence : totaux, par bien, par jour (30 jours par défaut).
GET/api/v1/listings/{ref}/statsUn bien.

6. Webhooks

Événements : lead.created, listing.published, listing.updated, listing.expired, webhook.test. URL en https uniquement. Délai de réponse : 5 s ; sinon nouvelle tentative après 1 min, 5 min, 30 min, 2 h puis 12 h (rejeu manuel possible depuis l'espace agence). Dédoublonnez sur l'en-tête X-MyImmobilier-Delivery / id.

Vérifiez la signature : X-MyImmobilier-Signature: t=<timestamp>,v1=<hex> où v1 = HMAC_SHA256(secret, t + "." + corps_brut). Refusez si t a plus de 5 minutes.

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. Erreurs et limites

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

Codes : 400 invalid_request, 401 unauthorized, 403 forbidden / agency_not_approved, 404 not_found, 409 invalid_state, 413 payload_too_large, 422 validation_failed. Corps max 2 Mo (8 Mo pour les imports). Un bien publié expire après 60 jours : appelez renew ou recevez listing.expired pour le renouveler automatiquement.

Éditeur de CRM ? Écrivez-nous pour un accompagnement d'intégration : contact.