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/listings | Elenco (status, updatedSince, limit, cursor). |
| PUT | /api/v1/listings/{externalId} | Crea o aggiorna un immobile SwissRETS (upsert). |
| POST | /api/v1/listings | Idem, 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}/publish | Pubblica (60 giorni). L'agenzia deve essere approvata. |
| POST | /api/v1/listings/{ref}/unpublish | Riporta in bozza. |
| POST | /api/v1/listings/{ref}/renew | Rinnova per 60 giorni. |
| DELETE | /api/v1/listings/{ref} | Archivia (rimuove dal portale, conserva lo storico). |
| POST | /api/v1/imports | Importazione 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/vitrine | Immobili 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}/stats | Un 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.