Skip to content

Integrazione Market Scanner

Integra con un assistente AI
Scarica o copia il prompt di integrazione per Market Scanner e incollalo in Claude Code, Cursor o Copilot: contiene auth, esempi end-to-end e checklist.
Apri

Le API dello Scanner di Dokicasa permettono di ottenere visure, ispezioni, note, mappe catastali e ricerche da indirizzo. Sono asincrone: questa guida ti mostra come riceverne i risultati nel modo più efficiente, evitando il polling quando possibile.

🎮 Prova le API dal vivo

Apri il Playground interattivo: incolli il tuo token, lanci ogni chiamata in sandbox e vedi arrivare socket e polling in tempo reale, testi i webhook, e copi il codice pronto in cURL, Python, Node, JavaScript e PHP.

Base URLhttps://api.dokicasa.it
AuthAuthorization: Bearer <api_token>
Content-Typeapplication/json

Documentazione e autenticazione

La documentazione interattiva di tutti gli endpoint è su Swagger, sezione Scanner: api.dokicasa.it/api/documentation#/Scanner (ricerche, polling, recupero risultati).

Tutte le chiamate vanno autenticate con un Bearer token:

http
Authorization: Bearer YOUR_API_TOKEN

WARNING

Il token non deve mai finire in repo pubblici o sul frontend: trattalo come una password.

Endpoint principali

Tabella riassuntiva — lo schema completo è su Swagger.

MetodoPathCosa fa
POST/api/scanner/{type}Crea una richiesta scanner (visura / ispezione / mappa / list-addresses / …). Ritorna scanner_request_id.
GET/api/scanner-request/{id}Stato richiesta + risultato. Per i PDF qui trovi il link result.pdf_path (il file NON è nel JSON: si scarica dal link); per list-addresses i dati sono già nel JSON.
GET/api/scanner-request/{id}/resultsRisultati strutturati per le ricerche "normali" (es. immobili da indirizzo).
WSprivate-scanner_request.{id}Canale WebSocket privato per gli aggiornamenti realtime.

Stati della richiesta

ENQUEUED  →  SCANNING  →  SUCCESS
                       ╲→  ERROR

ENQUEUED e SCANNING sono transitori; SUCCESS ed ERROR sono finali.

Tipi di interrogazione

Ogni interrogazione è un POST /api/scanner/{type}. Qui sotto i type disponibili (lo schema dei parametri di ognuno è sullo Swagger, sezione Scanner).

Ricerche dati — il risultato è JSON nel campo result:

typeCosa faCampo in result
search-fiscal-codeCodice fiscale da nome, cognome e provinciaanagrafiche
tax-dataImmobili posseduti da un CF / P.IVAimmobili
catastoImmobili da foglio / particellaimmobili
intestatari-catastoProprietari di una particellaintestatari
list-addressesNormalizza un indirizzo → lista di vieaddresses
immobili-by-nameImmobili a livello nazionale da nome/cognome (2 fasi)immobili

Documenti — nessuna chiamata API restituisce il PDF: nel campo result.pdf_path trovi il link da cui scaricarlo (vedi nota sotto):

typeDocumento
visura-catasto / visura-tax-dataVisura catastale (da dati catastali / da CF-P.IVA)
ispezione-catasto / ispezione-tax-dataIspezione ipotecaria
nota-catasto / nota-tax-dataNota di trascrizione
mappa-catastoMappa catastale
elaborato-planimetricoElaborato planimetrico
elenco-immobiliElenco immobili

IMPORTANT

catasto ≠ visura catastale. catasto è la ricerca immobili da foglio/particella e restituisce l'elenco immobili (dati JSON). La visura catastale in PDF è invece visura-catasto (da dati catastali) o visura-tax-data (da CF/P.IVA). Analogamente la visura/ispezione ipotecaria è ispezione-catasto / ispezione-tax-data.

TIP

Le ispezioni e le note richiedono il comune nel body: serve a risolvere la conservatoria competente.

IMPORTANT

Le chiamate API non restituiscono mai il PDF: restituiscono solo il link result.pdf_path. Il file va scaricato con una GET separata su quel link, passando il tuo Bearer token (endpoint autenticato, non un link pubblico anonimo). Il link è stabile e permanente (non scade): puoi salvarlo e riutilizzarlo come riferimento in una tua procedura.

⚠️ Con curl la risposta è binaria (Content-Type: application/pdf): senza -o curl non stampa nulla sul terminale (sembra un "200 vuoto"), ma il file c'è. Salvalo:

bash
curl -o visura.pdf "<pdf_path>" -H "Authorization: Bearer <TOKEN>"

immobili-by-name — ricerca in due fasi

immobili-by-name cerca gli immobili di una persona a livello nazionale partendo da nome e cognome. Poiché possono esserci più omonimi, funziona in due fasi.

Fase 1 — lista degli omonimi

POST /api/scanner/immobili-by-name con solo nome e cognome:

json
{ "first_name": "CHRISTIAN", "last_name": "CANNATA" }

Al termine (via socket/polling, come ogni chiamata async) il result contiene la lista degli omonimi trovati:

json
{
  "needs_selection": true,
  "omonimi": [
    {
      "value": "9800054753#0#CANNATA#CHRISTIAN#CNNCRS91D08E625S#LIVORNO#08/04/1991#LI",
      "nome": "CHRISTIAN", "cognome": "CANNATA",
      "codice_fiscale": "CNNCRS91D08E625S",
      "data_nascita": "08/04/1991", "luogo_nascita": "LIVORNO (LI)", "sesso": "M"
    }
    // … altri omonimi
  ]
}
  • needs_selection: true → devi scegliere una persona dalla lista.
  • Se c'è un solo omonimo la selezione è automatica: ottieni subito il risultato della Fase 2.

Fase 2 — immobili della persona scelta

Richiama lo stesso endpoint aggiungendo selected_value = il campo value dell'omonimo scelto nella Fase 1 (copialo tale e quale):

json
{
  "first_name": "CHRISTIAN",
  "last_name": "CANNATA",
  "selected_value": "9800054753#0#CANNATA#CHRISTIAN#CNNCRS91D08E625S#LIVORNO#08/04/1991#LI"
}

Al termine il result contiene:

json
{
  "omonimo_selezionato": { "value": "…", "nome": "CHRISTIAN", "cognome": "CANNATA" },
  "immobili": { "…": "…" },
  "has_soppressi": false
}

Gli immobili sono persistiti: la lista completa (con intestatari) si recupera paginata da GET /api/scanner-request/{id}/results.

NOTE

Entrambe le fasi sono normali chiamate async: la POST torna ENQUEUED, poi via socket o polling arrivi a SUCCESS e leggi il result. Nomi molto comuni possono superare il limite di risultati della Fase 1: restringi con dati più specifici.

Come funziona

Tutte le chiamate scanner sono asincrone. Il flusso è sempre lo stesso, cambia solo come ricevi l'aggiornamento di stato.

  1. Crei la richiestaPOST /api/scanner/{type} → ricevi uno scanner_request_id e lo stato iniziale ENQUEUED.
  2. Attendi il completamento — scegli uno di questi modi (in ordine di preferenza):
    • WebSocket — push istantaneo, nessun polling.
    • Webhook callback — il nostro server chiama il tuo a fine elaborazione.
    • Polling (fallback) — solo se non puoi usare i due sopra.
  3. Ottieni il risultato — quando lo stato è SUCCESS:
    • PDF (visure, ispezioni, mappe, note): il file è già nella risposta di GET /api/scanner-request/{id}.
    • Ricerche strutturate: chiama GET /api/scanner-request/{id}/results.

Aggiornamenti realtime via WebSocket

⭐ Consigliato per app frontend

Approccio raccomandato per web/mobile: nessun polling, aggiornamenti istantanei, barre di avanzamento e notifiche in push.

Ogni richiesta pubblica eventi su un canale privato dedicato:

private-scanner_request.{id}

dove {id} è lo scanner_request_id ritornato dal POST iniziale.

Ambienti — host e chiave del socket

Il socket è separato per ambiente: usa host e chiave in base a dove chiami.

AmbienteBase URL APIwsHost (socket)keyauthEndpoint
Sandbox / testhttps://api-dev.dokicasa.itapi-dev.dokicasa.itstaginghttps://api-dev.dokicasa.it/broadcasting/auth
Produzionehttps://api.dokicasa.itsocket.dokicasa.itra9jcihuz0sx7vtw8yuthttps://api.dokicasa.it/broadcasting/auth

WARNING

Non mischiare gli ambienti: se ti connetti al socket di produzione ma fai le chiamate in sandbox (o viceversa), il canale si iscrive ma non ricevi eventi (è un Reverb diverso). L'esempio sotto usa i valori di produzione; per la sandbox sostituisci key, wsHost e authEndpoint con la riga Sandbox.

Esempio completo (JavaScript + Pusher/Reverb)

js
import Pusher from 'pusher-js'

const apiToken = 'YOUR_API_TOKEN'

// 1. Client WebSocket (Reverb sul backend, API Pusher sul client)
const pusher = new Pusher('ra9jcihuz0sx7vtw8yut', {
  wsHost: 'socket.dokicasa.it',
  wsPort: 443,
  wssPort: 443,
  forceTLS: true,
  cluster: 'mt1',
  disableStats: true,
  enabledTransports: ['ws', 'wss'],
  authEndpoint: 'https://api.dokicasa.it/broadcasting/auth',
  auth: { headers: { Authorization: 'Bearer ' + apiToken } },
})

// 2. Creo la richiesta scanner
const { scanner_request_id } = await fetch(
  'https://api.dokicasa.it/api/scanner/intestatari-catasto',
  {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + apiToken,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      comune: 'Roma', provincia: 'RM',
      foglio: '123', particella: '45',
      search_catasto_type: 'F',
    }),
  }
).then((r) => r.json())

// 3. Mi iscrivo al canale e ascolto gli stati
const channel = pusher.subscribe(`private-scanner_request.${scanner_request_id}`)
channel.bind('ScannerRequestNotification', ({ message }) => {
  const { status, percentage } = message
  if (status === 'SCANNING') updateProgressBar(percentage)
  if (status === 'SUCCESS') fetchResult(scanner_request_id)
  if (status === 'ERROR') handleError(message)
})

async function fetchResult(id) {
  const data = await fetch(`https://api.dokicasa.it/api/scanner-request/${id}`, {
    headers: { Authorization: 'Bearer ' + apiToken },
  }).then((r) => r.json())
  console.log('Risultato finale:', data)
}

Pro: nessun polling, ricezione istantanea, UX migliore. Funziona ovunque ci sia un client WS (web, mobile, server con client Pusher).

Webhook e firma

⭐ Consigliato per integrazioni backend

Passi un URL nella creazione della richiesta: a fine elaborazione Dokicasa ti POSTa il risultato. Invio asincrono con retry — nessuna configurazione nel pannello.

Ci sono due campi che puoi mettere nel body del POST iniziale (anche entrambi insieme):

Campo (nel body POST)Payload che riceviQuando parte
callbackcompatto: { id, type, status, percentage, result, created_at, updated_at, finished_at }a fine scansione
webhookcompleto: { scanner_request: { …oggetto richiesta intero, con paramseresult… } }sull'evento di completamento
json
{
  "type": "catasto",
  "comune": "Roma", "foglio": "123", "particella": "45", "search_catasto_type": "F",
  "callback": "https://tuo-server.com/dokicasa/scanner-callback",
  "webhook":  "https://tuo-server.com/dokicasa/scanner-webhook"
}

Comuni divisi in sezioni catastali

In alcuni comuni — Roma, Napoli, Genova — foglio e particella da soli non bastano a individuare un immobile: serve anche la sezione catastale. In quei casi aggiungi sezione_comune alla richiesta.

json
{
  "type": "catasto",
  "comune": "Napoli", "provincia": "NA",
  "sezione_comune": "SAN FERDINANDO",
  "foglio": "123", "particella": "45"
}

È facoltativo e concorre a identificare l'immobile insieme a foglio, particella, subalterno e comune: due immobili con la stessa particella ma sezione diversa restano distinti. Se lo ometti in un comune che le usa, la ricerca può restituire l'immobile sbagliato o non trovarlo.

Il nome del campo è sezione_comune: un sezione viene ignorato senza errore.

Il result che ricevi ha la forma che dipende dal tipo — vedi Formato dei risultati per tipo.

callback vs webhook — che differenza c'è

Stesso meccanismo (un POST firmato al tuo URL a fine elaborazione, con retry): cambia solo la forma del payload.

json
{
  "id": 2615190,
  "type": "VISURA-CATASTO",
  "status": "SUCCESS",
  "percentage": 98,
  "result": { "pdf_path": "https://.../sample-visura-catasto", "is_empty": false },
  "created_at": "2026-07-02T23:22:59+02:00",
  "updated_at": "2026-07-02T23:23:02+02:00",
  "finished_at": "2026-07-02T23:23:02+02:00"
}
json
{
  "scanner_request": {
    "id": 2615190,
    "user_id": 5128,
    "type": "VISURA-CATASTO",
    "params": { "comune": "ROMA", "provincia": "RM", "foglio": "123", "particella": "45", "tipo_visura": "ANALITICA" },
    "result": { "pdf_path": "https://.../sample-visura-catasto", "is_empty": false },
    "status": "SUCCESS"
  }
}
callbackwebhook
Payloadcompatto, campi flatoggetto scanner_request intero
Include i params d'inputNo
Header Signature (HMAC)
Quando partea fine scansionea fine scansione

Quale usare

  • callback → payload leggero: ti basta reagire all'esito (id, status, result). È la scelta più comune.
  • webhook → l'intero record, inclusi i params originali della richiesta: comodo per riconciliare/loggare senza tenere stato lato tuo.

Sono indipendenti: puoi passarne uno, l'altro, o entrambi (in quel caso ricevi due POST distinti). Entrambi firmati con l'header Signature.

Firma — verifica l'autenticità

Ogni webhook include l'header Signature: è l'HMAC-SHA256 del body grezzo, firmato con la tua api_token (la stessa che usi come Bearer). Così non devi gestire nessuna chiave nuova — verifichi con la tua api_key.

Signature = hash_hmac('sha256', <body_grezzo>, <la_tua_api_token>)
php
$raw = file_get_contents('php://input');                 // body GREZZO, non ri-serializzato
$expected = hash_hmac('sha256', $raw, $MY_API_TOKEN);
if (!hash_equals($expected, $_SERVER['HTTP_SIGNATURE'] ?? '')) {
    http_response_code(401); exit;                        // firma non valida
}
js
const crypto = require('crypto')
const raw = req.rawBody                                   // body GREZZO (Buffer/stringa)
const expected = crypto.createHmac('sha256', MY_API_TOKEN).update(raw).digest('hex')
if (expected !== req.get('Signature')) return res.sendStatus(401)

Firma sul body **grezzo**

Calcola l'HMAC sui byte esatti ricevuti, prima di parsare/ri-serializzare il JSON: reincodare cambia spazi e ordine delle chiavi, e la firma non combacia più.

Idempotenza

In caso di errori di rete l'invio viene ritentato: identifica la richiesta con l'id nel body e rispondi 2xx anche sui duplicati, così eviti retry inutili.

Formato dei risultati per tipo

Il campo result (lo trovi in GET /api/scanner-request/{id}, ed è lo stesso che arriva via socket e webhook) cambia forma a seconda del tipo di richiesta. Qui sotto le forme principali, con esempi reali.

`result` vs `/results`

  • result = payload completo della scansione (immobili, anagrafiche, PDF…), già pronto.
  • GET /api/scanner-request/{id}/results = vista paginata dei soli immobili persistiti (con intestatari), comoda quando sono tanti. Vale per i tipi che listano immobili (catasto, tax-data, list-addresses, intestatari-catasto); restituisce un paginatore Laravel { current_page, data: [...], links, ... }.
json
{
  "has_soppressi": true,
  "immobili": [
    {
      "foglio": "368", "particella": "435", "subalterno": "5",
      "indirizzo": "VIA ESEMPIO n. 11 Interno 1 Piano S1 - T",
      "comune": "ROMA", "provincia": "RM", "numero_civico": "11", "piano": "S1",
      "categoria": "A02", "classe": "02", "consistenza": "6 vani", "rendita": "1239,50",
      "catasto": "F", "codice_belfiore": "H501", "partita": "", "zona_cens": "004",
      "scanner_immobili_id": 82659
    },
    {
      "foglio": "368", "particella": "435", "subalterno": "24",
      "indirizzo": "VIA ESEMPIO n. 19 Piano T",
      "comune": "ROMA", "provincia": "RM", "numero_civico": "19", "piano": "T",
      "categoria": "C01", "classe": "10", "consistenza": "46 m2", "rendita": "3594,44",
      "catasto": "F", "codice_belfiore": "H501", "partita": "", "zona_cens": "004",
      "scanner_immobili_id": 82678
    }
  ]
}
json
{
  "intestatari": [
    {
      "nome_cognome": "ROSSI MARIO", "codice_fiscale": "RSSMRA80A01H501U",
      "quota": "1/2", "titolarita": "Proprieta'",
      "luogo_nascita": "ROMA", "data_nascita": "01/01/1980",
      "comune": "ROMA", "provincia": "RM"
    },
    {
      "nome_cognome": "ESEMPIO IMMOBILIARE SRL", "codice_fiscale": "01234567890",
      "quota": "1/2", "titolarita": "Nuda proprieta'",
      "luogo_nascita": "", "data_nascita": "",
      "comune": "ROMA", "provincia": null
    }
  ]
}
json
{
  "immobili": [],
  "anagrafiche": [
    {
      "cognome": "ROSSI", "nome": "MARIO",
      "data_di_nascita": "17/05/1938", "luogo_di_nascita": "MILANO (MI)",
      "sesso": "M", "codice_fiscale": "RSSMRA38E17F205T"
    }
  ]
}
json
{
  "addresses": {
    "123494##VIA ROMA": " VIA ROMA ",
    "337696##VIA ROMAGNOSI": " VIA ROMAGNOSI ",
    "12428##VIALE ROMAGNA": " VIALE ROMAGNA "
  }
}
json
{
  "pdf_path": "https://.../visure/<request_id>.pdf",
  "request_id": "…",
  "is_nota_negativa": false,
  "is_empty": false
}

Dove trovi lo schema di ogni singola chiamata

I parametri di input e il tipo esatto di ogni interrogazione (foglio/particella, tax_code, tipo_visura, ecc.) sono sullo Swagger, sezione Scanner: api.dokicasa.it/api/documentation#/Scanner. Questa pagina copre invece come ricevere e interpretare i risultati.

Come testare l'integrazione

C'è un ambiente sandboxhttps://api-dev.dokicasa.it — che rispecchia le API di produzione ma non fa interrogazioni reali: ogni chiamata torna dati di esempio realistici, non consuma credito e non genera documenti reali. Rispetto alla produzione cambia solo il base URL: endpoint, parametri, formato risposte, WebSocket e webhook sono identici.

🔑 Chiave API di test (sandbox)

Usa questo token: è un utente con tutte le chiamate abilitate e gratuite, pensato apposta per provare. In sandbox la whitelist degli origin è disattivata, quindi puoi chiamare anche da localhost o da qualsiasi dominio di test.

HfBfYShns3E02no3kkwCRWQugkVNcJCtrRN2BPoI5NxhqsiXaxxnSPpIHY2O

Passalo come header Authorization: Bearer <token> su ogni chiamata.

È già pronto nel Playground

Nel Playground interattivo questo token è precompilato (e copiabile): apri, premi Lancia e vedi socket + polling in tempo reale, senza configurare nulla.

Endpoint da testare

API — base https://api-dev.dokicasa.it

CosaEndpoint
Avvia una ricercaPOST /api/scanner/{type}{ scanner_request_id, status }
Stato + risultatoGET /api/scanner-request/{id} · …/{id}/status
Risultati paginatiGET /api/scanner-request/{id}/results
Crea monitoraggioPOST /api/monitoring/from-catasto
Simula webhook (solo sandbox)POST /api/user-monitorings/{id}/simulate-webhook

Socket — sandbox (vedi anche Ambienti del socket)

ParametroValore
wsHostapi-dev.dokicasa.it
keystaging
authEndpointhttps://api-dev.dokicasa.it/broadcasting/auth
canale · eventoprivate-scanner_request.{id} · ScannerRequestNotification

Prova rapida (curl)

bash
curl -X POST https://api-dev.dokicasa.it/api/scanner/list-addresses \
  -H "Authorization: Bearer HfBfYShns3E02no3kkwCRWQugkVNcJCtrRN2BPoI5NxhqsiXaxxnSPpIHY2O" \
  -H "Content-Type: application/json" \
  -d '{"indirizzo":"via roma","comune":"Milano","provincia":"MI"}'
# → { "scanner_request_id": 12345, "status": "ENQUEUED" }
# poi:  GET /api/scanner-request/12345/status   fino a  status = SUCCESS

Cosa verificare:

  1. Flusso async — la POST torna ENQUEUED, poi via socket (istantaneo) o polling arrivi a SUCCESS.
  2. Forma delle risposte — mappa i campi del result (vedi Formato risultati per tipo).
  3. Webhook — punta webhook_url a un inspector (webhook.site); in sandbox il monitoraggio invia subito un evento d'esempio alla creazione (o richiamalo con /simulate-webhook).
  4. Firma — valida l'header Signature (vedi sezione Firma).

NOTE

In sandbox i dati sono rappresentativi e fissi (non variano in base ai parametri): mostrano la struttura del result. Quando sei pronto, passa al base URL e al socket di produzione (tabella Ambienti del socket).

Monitoraggio immobili

Oltre alle ricerche una-tantum, puoi monitorare un immobile nel tempo: Dokicasa ricontrolla periodicamente i proprietari al catasto e, quando cambiano, ti invia una notifica webhook con la variazione.

Attivare il monitoraggio

POST /api/monitoring/from-catasto — parti direttamente dai dati catastali (non serve un id immobile). Se l'immobile non è ancora a sistema viene prima risolto con una ricerca catastale (proprietari inclusi) e salvato, poi il monitoraggio viene attivato.

json
{
  "foglio": "123",
  "particella": "45",
  "subalterno": "7",
  "comune": "Roma",
  "provincia": "RM",
  "webhook_url": "https://tuo-server.com/dokicasa/monitoring-changed"
}
CampoObbligatorioNote
foglio, particellala "tripletta" catastale
subalternonumerico, opzionale
comunecodice catastale o nome comune
provinciasigla (no TN/BZ)
search_catasto_typeF fabbricati (default) / T terreni
sezione_comuneper comuni con sezioni (Napoli/Roma/Genova)
webhook_urlURL a cui inviare le variazioni (vedi sotto)
name, noteetichette libere

Risposta: { immobile_id, user_monitoring_id, created, scanned, last_monitoring, next_monitoring } (last_monitoring = ultima esecuzione, null se mai girato; next_monitoring = prossima esecuzione).

Webhook di variazione proprietari

Se valorizzi webhook_url, ad ogni cambio proprietari rilevato dal monitoraggio Dokicasa fa una POST asincrona a quell'URL con il JSON completo della variazione:

json
{
  "event": "immobile.owners_changed",
  "user_monitoring_id": 12345,
  "immobile": {
    "id": 987, "foglio": "123", "particella": "45",
    "subalterno": "7", "comune": "Roma", "provincia": "RM"
  },
  "changes": [
    {
      "date": "2026-07-01",
      "differences": {
        "added": [
          {
            "quota": "1/2", "nome": "Mario", "cognome": "Rossi", "denominazione": null,
            "codice_fiscale": "RSSMRA80A01H501U", "partita_iva": null,
            "dt_nascita": "1980-01-01", "comune_nascita": "Roma", "provincia_nascita": "RM", "sesso": "M"
          }
        ],
        "removed": [
          {
            "quota": "1/2", "nome": "Luigi", "cognome": "Verdi", "denominazione": null,
            "codice_fiscale": "VRDLGU75M15F205X", "partita_iva": null,
            "dt_nascita": "1975-08-15", "comune_nascita": "Milano", "provincia_nascita": "MI", "sesso": "M"
          }
        ],
        "modified": [
          {
            "id": "CF:BNCLNA82H41H501Y",
            "before": {
              "quota": "1/3", "nome": "Elena", "cognome": "Bianchi", "denominazione": null,
              "codice_fiscale": "BNCLNA82H41H501Y", "partita_iva": null,
              "dt_nascita": "1982-06-01", "comune_nascita": "Roma", "provincia_nascita": "RM", "sesso": "F"
            },
            "after": {
              "quota": "1/2", "nome": "Elena", "cognome": "Bianchi", "denominazione": null,
              "codice_fiscale": "BNCLNA82H41H501Y", "partita_iva": null,
              "dt_nascita": "1982-06-01", "comune_nascita": "Roma", "provincia_nascita": "RM", "sesso": "F"
            }
          }
        ]
      }
    }
  ]
}

Ogni elemento di added / removed (e i blocchi before / after di modified) riporta i dati anagrafici base del soggetto: quota, nome, cognome, denominazione (persona fisica → nome+cognome, azienda → denominazione), codice_fiscale, partita_iva, dt_nascita, comune_nascita, provincia_nascita, sesso. I campi non disponibili sono null. Il codice_fiscale è anche incapsulato nell'id di modified (prefisso CF:, oppure PI: per la P.IVA o NM: se manca l'identificativo fiscale).

NOTE

Il webhook parte in automatico dal cron di monitoraggio, con retry in caso di errori di rete. Rispondi 2xx per confermare la ricezione.

TIP

Il webhook_url è opzionale in fase di creazione: se non lo passi non viene inviato nulla. Puoi aggiungerlo o cambiarlo in un secondo momento (vedi sotto).

Provare il webhook in **sandbox** (staging)

Sull'ambiente di test (https://api-dev.dokicasa.it) non devi aspettare un cambio proprietari reale:

  • Alla creazione, se passi un webhook_url, inviamo subito un evento immobile.owners_changed d'esempio a quell'URL (la risposta della create include simulated_webhook_sent: true).
  • Puoi re-inviarlo quando vuoi con POST /api/user-monitorings/{id}/simulate-webhook.

Il payload ha lo stesso identico formato dell'evento reale: punta webhook_url a un inspector (webhook.site) per vederlo arrivare. In produzione questi automatismi sono disattivati — il webhook parte solo su un cambio proprietari reale.

Elenco dei monitoraggi

GET /api/users/me/monitorings — ritorna i tuoi monitoraggi (paginati). Filtri via query string:

ParametroValoriNote
filter.typeIMMOBILE (default) / SOGGETTOtipo di monitoraggio
filter.statusACTIVE, EXPIRED, CANCELLEDanche multipli, separati da virgola
filter.sourceAPI / CENSIMENTOorigine del monitoraggio
filter.change_owner1solo quelli con variazioni proprietari
filter.texttesto liberocerca in note + indirizzo/dati catastali
sort_ends_atASC / DESCordina per scadenza (default: id DESC)
pagenumeropagina (risposta paginata)

Se ti servono solo gli id: GET /api/users/me/monitorings-ids.

NOTE

È un endpoint di sola lettura: funziona sempre col tuo Bearer token, anche senza whitelist origin configurata. Il default filter.type=IMMOBILE ritorna solo i monitoraggi immobile — per i soggetti passa filter.type=SOGGETTO.

Gestione del monitoraggio

MetodoPathCosa fa
GET/api/user-monitorings/{id}Dettaglio del monitoraggio: dati del monitoring, immobile collegato (model), proprietari attuali (model.soggetti), eventi di variazione (events) e storico letture proprietari (proprietari_history). Vedi esempio sotto.
PATCH/api/user-monitorings/{id}/webhookImposta o rimuove (passando null) la webhook_url. Body: { "webhook_url": "https://…" }.
PATCH/api/user-monitorings/{id}Cambia lo status: ACTIVE (attivo), DISACTIVE (in pausa), CANCELED (annullato).
POST/api/user-monitorings/{id}/renewRinnova il monitoraggio.
POST/api/user-monitorings/{id}/simulate-webhookSolo sandbox: invia subito al webhook_url un evento immobile.owners_changed di esempio, per provare la ricezione senza attendere un cambio reale. In produzione risponde 404.
DELETE/api/user-monitorings/{id}Elimina il monitoraggio.

Esempio risposta — dettaglio monitoraggio

json
{
  "id": 6004,
  "user_id": 5101,
  "status": "ACTIVE",
  "ends_at": "2026-07-10 11:08:01",
  "model_type": "App\\Models\\ScannerImmobile",
  "model_id": 2013602,
  "model": {
    "id": 2013602,
    "comune": "Nardo'", "provincia": "LE",
    "foglio": "129", "particella": "1474", "subalterno": "1",
    "categoria": "A03",
    "indirizzo_completo": "VIA CAVALIERI TEUTONICI n. 6 Piano T",
    "soggetti": [
      {
        "denominazione": "COLELLA ANNA MARIA",
        "codice_fiscale": "CLLNMR35P44F842Z",
        "pivot": { "quota": "1/2", "titolarita": "Proprieta'" }
      }
    ]
  },
  "events": [
    {
      "date": "2025-12-02",
      "differences": {
        "added":    [ { "quota": "1/2", "denominazione": "BERNES ANGELO" } ],
        "removed":  [ { "quota": "",    "denominazione": "AMATEIS DAVIDE" } ],
        "modified": []
      }
    }
  ],
  "proprietari_history": [
    {
      "date": "2025-11-02",
      "read_at": "2025-11-02 00:00:00",
      "scanner_request_id": 2071196,
      "proprietari": [
        { "denominazione": "AMATEIS DAVIDE",     "codice_fiscale": "MTSDVD82H03D208H", "quota": "",    "titolarita": "" },
        { "denominazione": "COLELLA ANNA MARIA", "codice_fiscale": "CLLNMR35P44F842Z", "quota": "1/2", "titolarita": "Proprieta'" }
      ]
    },
    {
      "date": "2025-12-02",
      "read_at": "2025-12-02 00:00:00",
      "scanner_request_id": 2241215,
      "proprietari": [
        { "denominazione": "BERNES ANGELO",      "quota": "1/2", "titolarita": "Proprieta'" },
        { "denominazione": "COLELLA ANNA MARIA", "quota": "1/2", "titolarita": "Proprieta'" }
      ]
    }
  ]
}
CampoCosa contiene
modelDettaglio dell'immobile monitorato (comune, foglio/particella/sub, categoria, indirizzo…)
model.soggettiProprietari attuali (con quota e titolarità nel pivot)
eventsVariazioni proprietari: diff per data (added / removed / modified)
proprietari_historySnapshot dei proprietari a ogni lettura (una entry per scanner_request, con date/read_at): la storia grezza, non deduplicata, di com'erano i proprietari ad ogni controllo

Caso d'uso: ricerca da indirizzo

Hai un indirizzo e vuoi gli immobili al catasto associati. Flusso in 2 fasi: risoluzione indirizzo → immobili.

  1. list-addresses (risoluzione indirizzo)POST /api/scanner/list-addresses. Attendi via socket/webhook/polling. La lista degli indirizzi normalizzati è già nel campo results della risposta di GET /api/scanner-request/{id} — non serve chiamare /results.
  2. Immobili da indirizzo — usa un indirizzo del passo 1 come address_value nella chiamata di scanner immobili (endpoint preciso su Swagger, sezione Scanner).
  3. Recupero risultati — quando arriva SUCCESS, chiama GET /api/scanner-request/{id}/results per la lista immobili.

Caso d'uso: visure, ispezioni, note, mappe (PDF)

Tutte le richieste che producono un PDF (visura catastale, ispezione, nota, mappa) seguono lo stesso pattern.

  1. Crei la richiestaPOST /api/scanner/{type} (il tipo dipende dal documento, vedi Swagger).
  2. Attendi il completamento — via WebSocket o webhook: lo stato passa per ENQUEUEDSCANNING fino a SUCCESS o ERROR.
  3. Retry automatico su errori transient — se durante l'elaborazione c'è un errore temporaneo (linea con i servizi esterni), il sistema riprova da solo ogni ~5 minuti. Non devi reinviare nulla. Il credito viene scalato solo al primo esito positivo.
  4. Ottieni il PDF — il file è restituito direttamente nella risposta di GET /api/scanner-request/{id} (non serve /results). In più il PDF viene inviato automaticamente via email all'indirizzo associato al token, quindi l'utente finale lo riceve comunque anche senza integrare il download lato client.

Polling — solo come ultima opzione

Sconsigliato

Hai due meccanismi push (WebSocket e webhook) che eliminano la necessità di polling. Valutali prima: il polling aumenta latenza e carico server senza vantaggi reali.

Se proprio devi pollare, interroga periodicamente:

http
GET /api/scanner-request/{id}

Linee guida pratiche:

  • Intervallo minimo consigliato: 3 secondi tra una chiamata e l'altra.
  • Backoff esponenziale dopo qualche tentativo (es. 3s → 6s → 12s).
  • Limite di tentativi sensato (es. 5 minuti totali), poi timeout lato tuo.
  • Interrompi appena lo stato è SUCCESS o ERROR.

Risposta tipica:

json
{ "id": 12345, "status": "SCANNING", "percentage": 42 }

Note operative

  • Conserva sempre l'id (scanner_request_id) restituito alla creazione: serve per ogni operazione successiva.
  • Gestisci esplicitamente lo stato ERROR: leggi il messaggio per capire la causa.
  • Non condividere mai il token API: trattalo come una password.
  • In produzione, preferisci webhook o socket al polling.