Integrazione Market Scanner
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 URL | https://api.dokicasa.it |
| Auth | Authorization: Bearer <api_token> |
| Content-Type | application/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:
Authorization: Bearer YOUR_API_TOKENWARNING
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.
| Metodo | Path | Cosa 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}/results | Risultati strutturati per le ricerche "normali" (es. immobili da indirizzo). |
| WS | private-scanner_request.{id} | Canale WebSocket privato per gli aggiornamenti realtime. |
GET | /api/scanner/health | Stato del servizio: Sister funziona, è lento, è fermo o in manutenzione programmata, anche per tipo di scansione. Dettagli |
Stati della richiesta
ENQUEUED → SCANNING → SUCCESS
╲→ ERRORENQUEUED 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:
type | Cosa fa | Campo in result |
|---|---|---|
search-fiscal-code | Nominativi con codice fiscale da nome, cognome e provincia facoltativa (gratuita) | anagrafiche |
search-company | Società (persone giuridiche) con codice fiscale da ragione sociale e provincia facoltativa (a listino) | anagrafiche |
tax-data | Immobili posseduti da un CF / P.IVA (provincia e comune facoltativi) | immobili |
catasto | Immobili da foglio / particella | immobili |
intestatari-catasto | Proprietari di una particella | intestatari |
list-addresses | Normalizza un indirizzo → lista di vie | addresses |
immobili-by-name | Immobili 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):
type | Documento |
|---|---|
visura-catasto / visura-tax-data | Visura catastale (da dati catastali / da CF-P.IVA) |
ispezione-catasto / ispezione-tax-data | Ispezione ipotecaria |
nota-catasto / nota-tax-data | Nota di trascrizione |
mappa-catasto | Mappa catastale |
elaborato-planimetrico | Elaborato planimetrico |
elenco-immobili | Elenco 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.
Visura attuale o storica
Le due visure non si comportano allo stesso modo con tipo_visura:
| Chiamata | tipo_visura | Visura che ricevi |
|---|---|---|
visura-catasto (per immobile) | ANALITICA | storica analitica |
SINTETICA | storica sintetica | |
COMPLETA | attuale | |
visura-tax-data (per soggetto) | ANALITICA | SINTETICA | COMPLETA | attuale per soggetto |
visura-tax-data + "storica": true | ANALITICA | storica analitica per soggetto |
SINTETICA | storica sintetica per soggetto |
WARNING
Per la visura per immobile COMPLETA non vuol dire "con più dati": è l'unica senza storico. Per la visura per soggetto, invece, la storica si chiede solo con "storica": true: senza, anche ANALITICA e SINTETICA restituiscono la visura attuale.
Visura per soggetto: attuale o storica
- Attuale (default, senza
storica): gli immobili di cui il soggetto è intestatario oggi nella provincia indicata. Se oggi in quella provincia non ha niente, Sister non produce la visura. - Storica (
"storica": true): tutti gli immobili di cui il soggetto è stato intestatario nella provincia, anche se oggi non li possiede più, con i passaggi di intestazione. È la "Visura storica per soggetto" di Sister: serve per esempio per chi ha venduto, per un defunto o per ricostruire il patrimonio passato.
POST /api/scanner/visura-tax-data
{
"tax_code": "RSSMRA80A01H501U",
"provincia": "LT",
"tipo_visura": "ANALITICA",
"storica": true
}| Regola | Dettaglio |
|---|---|
| Persone fisiche e giuridiche | tax_code può essere un codice fiscale o una partita IVA (società) |
Solo ANALITICA o SINTETICA | con COMPLETA risponde 422 |
| Una provincia per chiamata | la visura copre tutta la provincia indicata in provincia; se il soggetto ha avuto immobili in più province serve una chiamata per ciascuna |
| Stesso prezzo | costa come una visura-tax-data normale |
| Tempi | la storica passa dal codice di sicurezza di Sister, risolto in automatico: può richiedere qualche secondo in più |
Visura a una data: situazione_al
Il modulo di Sister ha il campo "Situazione al": la visura per soggetto fotografa la situazione a quella data invece che a oggi. Con situazione_al (facoltativo, formato AAAA-MM-GG) la chiedi a una data passata. Senza il parametro la data è quella di oggi, come sempre.
POST /api/scanner/visura-tax-data
{
"tax_code": "RSSMRA80A01H501U",
"provincia": "LT",
"tipo_visura": "ANALITICA",
"storica": true,
"situazione_al": "2020-06-30"
}| Regola | Dettaglio |
|---|---|
Solo visura-tax-data | sulle altre chiamate il parametro non ha effetto; vale per codice fiscale e partita IVA |
| Formato | AAAA-MM-GG; una data nel futuro o in un altro formato risponde 422 |
Con storica | è il caso tipico: chi era intestatario a quella data, anche se oggi non possiede più niente |
| Stesso prezzo | costa come una visura-tax-data normale |
| Tempi | con una data diversa da oggi Sister chiede il codice di sicurezza, risolto in automatico: qualche secondo in più |
| Data troppo vecchia | se a quella data negli atti informatizzati non c'è niente, la risposta è Nessun documento trovato. |
| Documenti già scaricati | la data torna in situazione_al nell'elenco dei documenti del soggetto (null = situazione al giorno della richiesta) |
Solo terreni o solo fabbricati: search_catasto_type
Come il menu "Catasto" della ricerca per soggetto di Sister, search_catasto_type sceglie quale catasto interrogare. Facoltativo: senza, la ricerca copre terreni e fabbricati, come sempre.
| Valore | Cosa restituisce |
|---|---|
E (default) | terreni e fabbricati |
T | solo i terreni |
F | solo i fabbricati |
POST /api/scanner/tax-data
{
"tax_code": "RSSMRA80A01H501U",
"search_catasto_type": "F"
}| Regola | Dettaglio |
|---|---|
| Dove vale | tax-data, visura-tax-data, immobili-by-name (in entrambe le fasi); codice fiscale e partita IVA |
| Valori | E, T o F; altro valore → 422 |
| Formato | le righe di immobili sono le stesse: cambia solo quali ci sono (catasto vale T o F su ogni riga) |
| Stesso prezzo | costa come la stessa ricerca senza parametro |
| Documenti già scaricati | nell'elenco dei documenti del soggetto la visura riporta catasto (E, T o F) |
NOTE
Nelle ricerche per immobile (catasto, intestatari-catasto, visura-catasto, nota-catasto…) lo stesso parametro indica il catasto dell'immobile cercato e accetta solo F o T.
Provincia e comune (facoltativi). In tax-data si possono restringere gli immobili a una provincia e, dentro la provincia, a un comune. Senza, la ricerca resta su tutta Italia come sempre.
POST /api/scanner/tax-data
{
"tax_code": "RSSMRA80A01H501U",
"provincia": "RM",
"comune": "Colleferro"
}| Regola | Dettaglio |
|---|---|
provincia | sigla (RM); Trento e Bolzano non sono su Sister → 422 |
comune | nome del comune, con provincia (lo stesso nome esiste in più province); in alternativa codice ISTAT o catastale (H501), e allora la provincia non serve |
| Confronto | accenti, apostrofi e maiuscole non contano (Lurago d Erba = LURAGO D'ERBA) |
| Nessun immobile | elenco vuoto, come una ricerca senza risultati |
| Stesso prezzo | costa come la stessa ricerca senza filtri |
Elenco o documento? Le ricerche dati tax-data e immobili-by-name restituiscono solo gli immobili attuali del soggetto, come elenco JSON. Un elenco degli immobili posseduti in passato Sister non lo fornisce: il passato si ottiene soltanto come documento, con la visura storica per soggetto qui sopra.
La mappa catastale ha un suo parametro esplicito, tipo_richiesta (ATTUALITA | STORICA).
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:
curl -o visura.pdf "<pdf_path>" -H "Authorization: Bearer <TOKEN>"search-fiscal-code — elenco nominativi (gratuita)
Restituisce tutti i nominativi registrati in Catasto con quel nome e cognome nella provincia indicata (o in tutta Italia se la provincia non viene passata), ognuno con codice fiscale, data e luogo di nascita. La chiamata è gratuita: non scala il wallet e non serve una voce di listino.
POST /api/scanner/search-fiscal-code:
{ "first_name": "MARIO", "last_name": "ROSSI", "provincia": "MI" }| Campo | Obbligatorio | Note |
|---|---|---|
first_name | ✅ | nome (basta una delle parole: VERONICA trova anche MARIA VERONICA) |
last_name | ✅ | cognome, confrontato in modo esatto (anche composto: DE LUCA) |
provincia | — | sigla di 2 lettere dell'ufficio catastale in cui cercare. Facoltativa: senza, la ricerca è nazionale. TN e BZ non sono ammesse (catasto tavolare, escluse anche dalla nazionale) |
birth_date | — | data di nascita AAAA-MM-GG. Facoltativa: restringe gli omonimi. Restano nell'elenco anche i soggetti che in Catasto non hanno la data di nascita |
NOTE
Nazionale o per provincia? Senza provincia la ricerca copre tutta Italia: è la scelta giusta quando non sai dove sono gli immobili. Con cognomi molto diffusi i risultati sono tanti: se la ricerca va in errore per troppi risultati, ripetila indicando la provincia.
Troppi risultati. Oltre 400 soggetti Sister non restituisce l'elenco (capita con nomi molto comuni anche in una sola provincia, es. MARIA COLOMBO a MI). La richiesta finisce subito in ERROR con details: "TOO_MANY_RESULTS", senza tentativi a vuoto: ripetila con birth_date (e con la provincia, se non l'avevi indicata). Con la data di nascita MARIA COLOMBO a MI passa da oltre 400 a pochi soggetti.
Al termine (socket o polling) il result contiene la lista:
{
"immobili": [],
"anagrafiche": [
{
"cognome": "ROSSI", "nome": "MARIO",
"data_di_nascita": "17/05/1958", "luogo_di_nascita": "MILANO (MI)",
"sesso": "M", "codice_fiscale": "RSSMRA58E17F205X"
},
{
"cognome": "ROSSI", "nome": "MARIO",
"data_di_nascita": "02/11/1971", "luogo_di_nascita": "MONZA (MB)",
"sesso": "M", "codice_fiscale": "RSSMRA71S02F704Y"
}
]
}Serve a scegliere il soggetto giusto tra gli omonimi confrontando data e luogo di nascita, e a ricavarne il codice fiscale da usare poi con tax-data.
TIP
Errori di battitura in Catasto. La ricerca per nominativo è esatta: se una persona è registrata con il cognome scritto male (es. RISSI invece di ROSSI), cercando ROSSI non esce. Accenti e apostrofi invece vengono normalizzati. La ricerca per codice fiscale (tax-data) confronta solo il codice fiscale, quindi trova il soggetto anche se in Catasto il nome è scritto male. Flusso consigliato: search-fiscal-code per individuare la persona, poi tax-data con il suo codice fiscale.
search-company — società da ragione sociale
Il gemello di search-fiscal-code per le persone giuridiche: restituisce le società registrate in Catasto la cui denominazione contiene le parole cercate, ognuna con sede e codice fiscale. Non richiede visure.
WARNING
A pagamento. A differenza di search-fiscal-code, questa ricerca richiede una voce SEARCH-COMPANY nel tuo listino. Senza, la chiamata risponde 403 PARTNER_PRICING_REQUIRED: contatta Dokicasa per attivarla.
POST /api/scanner/search-company:
{ "company_name": "ESEMPIO IMMOBILIARE", "provincia": "MI" }| Campo | Obbligatorio | Note |
|---|---|---|
company_name | ✅ | ragione sociale, 2-150 caratteri. Accenti e apostrofi vengono normalizzati |
provincia | — | sigla di 2 lettere. Facoltativa: senza, la ricerca è nazionale. No TN/BZ |
Al termine (socket o polling) il result contiene la lista:
{
"immobili": [],
"anagrafiche": [
{ "denominazione": "ESEMPIO IMMOBILIARE SRL", "sede": "MILANO (MI)", "codice_fiscale": "01234567890" },
{ "denominazione": "ESEMPIO IMMOBILIARE S.R.L. IN LIQUIDAZIONE", "sede": "ROMA (RM)", "codice_fiscale": "09876543210" },
{ "denominazione": "ESEMPIO IMMOBILIARE SRL CON SEDE IN MILANO VIA ESEMPIO 1", "sede": "", "codice_fiscale": "" }
]
}Da sapere:
- La stessa società compare più volte. Negli atti la denominazione è stata scritta in modi diversi (
C.I.M.E.P.,C I M E P, con o senza la sede): righe diverse con lo stessocodice_fiscale. Raggruppa per codice fiscale prima di mostrarle. sedeecodice_fiscalepossono essere vuoti sulle registrazioni più vecchie.- Una sola parola generica non basta. Con
IMMOBILIAREda sola il Catasto non cerca e la richiesta termina inERRORcon "troppo generica: aggiungere un'altra parola della ragione sociale". Anche oltre il limite di risultati del Catasto la richiesta va inERROR: aggiungi una parola o indica la provincia. - Trovato il codice fiscale, usa
tax-dataper avere gli immobili della società.
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:
{ "first_name": "CHRISTIAN", "last_name": "CANNATA" }Al termine (via socket/polling, come ogni chiamata async) il result contiene la lista degli omonimi trovati:
{
"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.
- La Fase 1 è gratuita: si paga una volta sola, alla Fase 2 (o alla Fase 1 quando c'è un solo omonimo e arrivano subito gli immobili). Anche gli omonimi senza codice fiscale hanno il loro
valuee si possono scegliere.
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):
{
"first_name": "CHRISTIAN",
"last_name": "CANNATA",
"selected_value": "9800054753#0#CANNATA#CHRISTIAN#CNNCRS91D08E625S#LIVORNO#08/04/1991#LI"
}Al termine il result contiene:
{
"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.
- Crei la richiesta —
POST /api/scanner/{type}→ ricevi unoscanner_request_ide lo stato inizialeENQUEUED. - 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.
- 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.
- PDF (visure, ispezioni, mappe, note): il file è già nella risposta di
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.
| Ambiente | Base URL API | wsHost (socket) | key | authEndpoint |
|---|---|---|---|---|
| Sandbox / test | https://api-dev.dokicasa.it | api-dev.dokicasa.it | staging | https://api-dev.dokicasa.it/broadcasting/auth |
| Produzione | https://api.dokicasa.it | socket.dokicasa.it | ra9jcihuz0sx7vtw8yut | https://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)
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 ricevi | Quando parte |
|---|---|---|
callback | compatto: { id, type, status, percentage, result, created_at, updated_at, finished_at } | a fine scansione |
webhook | completo: { scanner_request: { …oggetto richiesta intero, con paramseresult… } } | sull'evento di completamento |
{
"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.
{
"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.
{
"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"
}{
"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"
}
}callback | webhook | |
|---|---|---|
| Payload | compatto, campi flat | oggetto scanner_request intero |
Include i params d'input | No | Sì |
Header Signature (HMAC) | Sì | Sì |
| Quando parte | a fine scansione | a fine scansione |
Quale usare
callback→ payload leggero: ti basta reagire all'esito (id,status,result). È la scelta più comune.webhook→ l'intero record, inclusi iparamsoriginali 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>)$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
}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, ... }.
{
"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
}
]
}{
"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
}
]
}{
"immobili": [],
"anagrafiche": [
{
"cognome": "ROSSI", "nome": "MARIO",
"data_di_nascita": "17/05/1938", "luogo_di_nascita": "MILANO (MI)",
"sesso": "M", "codice_fiscale": "RSSMRA38E17F205T"
}
]
}{
"immobili": [],
"anagrafiche": [
{ "denominazione": "ESEMPIO IMMOBILIARE SRL", "sede": "MILANO (MI)", "codice_fiscale": "01234567890" }
]
}{
"addresses": {
"123494##VIA ROMA": " VIA ROMA ",
"337696##VIA ROMAGNOSI": " VIA ROMAGNOSI ",
"12428##VIALE ROMAGNA": " VIALE ROMAGNA "
}
}{
"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 sandbox — https://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.
HfBfYShns3E02no3kkwCRWQugkVNcJCtrRN2BPoI5NxhqsiXaxxnSPpIHY2OPassalo 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
| Cosa | Endpoint |
|---|---|
| Avvia una ricerca | POST /api/scanner/{type} → { scanner_request_id, status } |
| Stato + risultato | GET /api/scanner-request/{id} · …/{id}/status |
| Risultati paginati | GET /api/scanner-request/{id}/results |
| Crea monitoraggio | POST /api/monitoring/from-catasto |
| Simula webhook (solo sandbox) | POST /api/user-monitorings/{id}/simulate-webhook |
Socket — sandbox (vedi anche Ambienti del socket)
| Parametro | Valore |
|---|---|
wsHost | api-dev.dokicasa.it |
key | staging |
authEndpoint | https://api-dev.dokicasa.it/broadcasting/auth |
| canale · evento | private-scanner_request.{id} · ScannerRequestNotification |
Prova rapida (curl)
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 = SUCCESSCosa verificare:
- Flusso async — la POST torna
ENQUEUED, poi via socket (istantaneo) o polling arrivi aSUCCESS. - Forma delle risposte — mappa i campi del
result(vedi Formato risultati per tipo). - Webhook — punta
webhook_urla un inspector (webhook.site); in sandbox il monitoraggio invia subito un evento d'esempio alla creazione (o richiamalo con/simulate-webhook). - 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.
{
"foglio": "123",
"particella": "45",
"subalterno": "7",
"comune": "Roma",
"provincia": "RM",
"webhook_url": "https://tuo-server.com/dokicasa/monitoring-changed"
}| Campo | Obbligatorio | Note |
|---|---|---|
foglio, particella | ✅ | la "tripletta" catastale |
subalterno | — | numerico, opzionale |
comune | ✅ | codice catastale o nome comune |
provincia | ✅ | sigla (no TN/BZ) |
search_catasto_type | — | F fabbricati (default) / T terreni |
sezione_comune | — | per comuni con sezioni (Napoli/Roma/Genova) |
webhook_url | — | URL a cui inviare le variazioni (vedi sotto) |
name, note | — | etichette 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:
{
"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 eventoimmobile.owners_changedd'esempio a quell'URL (la risposta della create includesimulated_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:
| Parametro | Valori | Note |
|---|---|---|
filter.type | IMMOBILE (default) / SOGGETTO | tipo di monitoraggio |
filter.status | ACTIVE, EXPIRED, CANCELLED | anche multipli, separati da virgola |
filter.source | API / CENSIMENTO | origine del monitoraggio |
filter.change_owner | 1 | solo quelli con variazioni proprietari |
filter.text | testo libero | cerca in note + indirizzo/dati catastali |
sort_ends_at | ASC / DESC | ordina per scadenza (default: id DESC) |
page | numero | pagina (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
| Metodo | Path | Cosa 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}/webhook | Imposta 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}/renew | Rinnova il monitoraggio. |
POST | /api/user-monitorings/{id}/simulate-webhook | Solo 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
{
"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'" }
]
}
]
}| Campo | Cosa contiene |
|---|---|
model | Dettaglio dell'immobile monitorato (comune, foglio/particella/sub, categoria, indirizzo…) |
model.soggetti | Proprietari attuali (con quota e titolarità nel pivot) |
events | Variazioni proprietari: diff per data (added / removed / modified) |
proprietari_history | Snapshot 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.
list-addresses(risoluzione indirizzo) —POST /api/scanner/list-addresses. Attendi via socket/webhook/polling. La lista degli indirizzi normalizzati è già nel camporesultsdella risposta diGET /api/scanner-request/{id}— non serve chiamare/results.- Immobili da indirizzo — usa un indirizzo del passo 1 come
address_valuenella chiamata di scanner immobili (endpoint preciso su Swagger, sezione Scanner). - Recupero risultati — quando arriva
SUCCESS, chiamaGET /api/scanner-request/{id}/resultsper 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.
- Crei la richiesta —
POST /api/scanner/{type}(il tipo dipende dal documento, vedi Swagger). - Attendi il completamento — via WebSocket o webhook: lo stato passa per
ENQUEUED→SCANNINGfino aSUCCESSoERROR. - 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.
- 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.
Stato del servizio (Sister)
Le interrogazioni catastali passano da Sister, il portale dell'Agenzia delle Entrate, che ogni tanto rallenta, si ferma o va in manutenzione programmata (di solito annunciata con un banner tipo "il servizio è indisponibile dalle 17.00 alle 18:00 circa"). Noi lo controlliamo ogni 10 minuti in base ai tempi reali delle scansioni e leggiamo gli avvisi di manutenzione pubblicati da Sister; puoi usare l'esito per avvisare i tuoi utenti o, se il servizio è fermo, passare le richieste al manuale invece di accodarle.
GET /api/scanner/health
Authorization: Bearer <token>Campi della risposta
| Campo | Tipo | Descrizione |
|---|---|---|
status | string | ok | slow | down | unknown (vedi tabella sotto). |
accepting_requests | boolean | false solo con status = down. È il campo su cui decidere. |
message | string | Messaggio in italiano pronto per l'utente. Può cambiare: non usarlo per decidere. |
since | string (ISO 8601) | null | Da quando dura il problema. Durante una manutenzione è l'orario di inizio. null con ok. |
sister_checked_at | string (ISO 8601) | null | Ultimo controllo di Sister. |
maintenance | object | null | Manutenzione programmata in corso o annunciata (la prossima). null se non ce ne sono. |
maintenance.in_progress | boolean | true se adesso siamo dentro la finestra starts_at → ends_at. |
maintenance.starts_at | string (ISO 8601) | Inizio della manutenzione. |
maintenance.ends_at | string (ISO 8601) | Fine prevista: a quest'ora il Market Scanner riparte da solo. |
maintenance.scanner_blocked | boolean | true se la manutenzione è in corso e il Market Scanner è sospeso. |
maintenance.message | string | Messaggio in italiano pronto per l'utente ("sarà in manutenzione dalle 17:00 alle 18:00" / "è in manutenzione fino alle 18:00"). Se la finestra finisce il giorno dopo l'orario riporta anche la data ("fino alle 02:00 del 18/09"). |
maintenance.sister_message | string | null | Testo originale del banner di Sister, così com'è. |
queue.enqueued | integer | Scansioni in attesa (ultime 2 ore). |
queue.scanning | integer | Scansioni in lavorazione. |
queue.max_wait_seconds | integer | Attesa della richiesta in coda da più tempo. |
recommended_poll_seconds | integer | Ogni quanto richiederlo (300). |
generated_at | string (ISO 8601) | Quando è stata calcolata la risposta. |
Gli orari sono in ora italiana (+02:00 / +01:00).
status | Significato | Cosa conviene fare |
|---|---|---|
ok | Servizio operativo. | Niente. Se maintenance non è null, avvisare della manutenzione in arrivo. |
slow | Sister risponde ma più lento del solito: le scansioni arrivano, in più tempo. | Avvisare che i tempi sono più lunghi. |
down | Sister non risponde, è in manutenzione programmata, oppure il servizio è sospeso da noi. accepting_requests = false. | Bloccare le nuove richieste e passarle al manuale. Con maintenance.in_progress = true puoi dire all'utente a che ora riparte (ends_at). |
unknown | Stato non disponibile (controllo non aggiornato, o troppe poche scansioni per giudicare). Non è un guasto. | Trattarlo come ok. |
Stato per tipo di scansione: types
Sister a volte ferma solo alcuni servizi (per esempio le ispezioni ipotecarie) mentre il resto funziona, e l'avviso non sempre arriva. Per questo types dà lo stato di ogni tipo di scansione, ricavato dagli esiti reali delle richieste dell'ultima ora. La chiave è il type che usi in POST /api/scanner/{type}.
"types": {
"intestatari-catasto": { "status": "ok", "reason": null, "since": null, "requests": { "total": 57, "with_errors": 0 }, "last_success_at": "2026-09-26T10:41:12+02:00" },
"ispezione-catasto": { "status": "down", "reason": "errors", "since": "2026-09-26T10:12:03+02:00", "requests": { "total": 4, "with_errors": 3 }, "last_success_at": "2026-09-26T10:05:40+02:00" },
"mappa-catasto": { "status": "unknown", "reason": null, "since": null, "requests": { "total": 0, "with_errors": 0 }, "last_success_at": "2026-09-25T17:32:32+02:00" }
}types.<type>.status | Significato |
|---|---|
ok | Le richieste di questo tipo vanno a buon fine (anche quando serve un secondo tentativo, normale per le visure col codice di sicurezza). |
degraded | Almeno il 30% delle ricerche dell'ultima ora ha dato errore anche dopo i tentativi, su più server. |
down | Dopo l'ultima richiesta andata bene sono fallite almeno 3 ricerche diverse su almeno 2 server, oppure c'è una manutenzione Sister in corso (reason: "maintenance"). since = primo errore. |
unknown | Nessuna richiesta di questo tipo nell'ultima ora: non sappiamo. last_success_at dice quando ha funzionato l'ultima volta (ultime 24 ore). Non è un guasto. |
- È solo informativo: le richieste non vengono bloccate. Decidi tu se avvisare l'utente o passare quel tipo al manuale.
- Gli errori del dato cercato (comune inesistente, particella che non esiste, troppi risultati) non contano: dicono che la ricerca era sbagliata, non che Sister è fermo.
- La stessa ricerca ripetuta più volte conta una volta sola, e un errore su un solo server non basta: gli errori di un singolo server li gestiamo noi, spostando la richiesta su un altro.
requests.totalerequests.with_errorscontano le ricerche distinte dell'ultima ora.
Esempi di risposta
{
"status": "ok",
"accepting_requests": true,
"message": "Servizio operativo.",
"since": null,
"sister_checked_at": "2026-09-17T10:40:07+02:00",
"maintenance": null,
"queue": { "enqueued": 3, "scanning": 2, "max_wait_seconds": 14 },
"recommended_poll_seconds": 300,
"generated_at": "2026-09-17T10:41:00+02:00"
}{
"status": "ok",
"accepting_requests": true,
"message": "Servizio operativo.",
"since": null,
"sister_checked_at": "2026-09-17T14:30:05+02:00",
"maintenance": {
"in_progress": false,
"starts_at": "2026-09-17T17:00:00+02:00",
"ends_at": "2026-09-17T18:00:00+02:00",
"scanner_blocked": false,
"message": "Sister (Agenzia delle Entrate) sara' in manutenzione programmata dalle 17:00 alle 18:00.",
"sister_message": "A causa di un intervento programmato, il servizio e' indisponibile dalle 17.00 alle 18:00 circa"
},
"queue": { "enqueued": 5, "scanning": 3, "max_wait_seconds": 22 },
"recommended_poll_seconds": 300,
"generated_at": "2026-09-17T14:31:00+02:00"
}{
"status": "down",
"accepting_requests": false,
"message": "Sister (Agenzia delle Entrate) e' in manutenzione programmata fino alle 18:00: le scansioni riprenderanno al termine.",
"since": "2026-09-17T17:00:00+02:00",
"sister_checked_at": "2026-09-17T17:12:00+02:00",
"maintenance": {
"in_progress": true,
"starts_at": "2026-09-17T17:00:00+02:00",
"ends_at": "2026-09-17T18:00:00+02:00",
"scanner_blocked": true,
"message": "Sister (Agenzia delle Entrate) e' in manutenzione programmata fino alle 18:00: le scansioni riprenderanno al termine.",
"sister_message": "A causa di un intervento programmato, il servizio e' indisponibile dalle 17.00 alle 18:00 circa"
},
"queue": { "enqueued": 0, "scanning": 0, "max_wait_seconds": 0 },
"recommended_poll_seconds": 300,
"generated_at": "2026-09-17T17:12:00+02:00"
}{
"status": "slow",
"accepting_requests": true,
"message": "Sister (Agenzia delle Entrate) e' piu' lento del solito: le scansioni vanno avanti ma impiegano piu' tempo.",
"since": "2026-09-17T12:20:00+02:00",
"sister_checked_at": "2026-09-17T12:40:07+02:00",
"maintenance": null,
"queue": { "enqueued": 12, "scanning": 4, "max_wait_seconds": 95 },
"recommended_poll_seconds": 300,
"generated_at": "2026-09-17T12:41:00+02:00"
}{
"status": "down",
"accepting_requests": false,
"message": "Sister (Agenzia delle Entrate) non risponde: le scansioni restano in attesa.",
"since": "2026-09-17T09:50:00+02:00",
"sister_checked_at": "2026-09-17T10:10:04+02:00",
"maintenance": null,
"queue": { "enqueued": 27, "scanning": 6, "max_wait_seconds": 1260 },
"recommended_poll_seconds": 300,
"generated_at": "2026-09-17T10:11:00+02:00"
}Da sapere
- Decidi su
accepting_requests(ostatus=down), mai sul testo dimessage. - Una manutenzione annunciata non blocca niente:
statusresta quello del momento e le scansioni vanno avanti fino astarts_at. Usamaintenanceper avvisare l'utente in anticipo. - Durante la manutenzione
statusèdowne il Market Scanner è sospeso:POST /api/scanner/{type}risponde400conSISTER_FORCE_BLOCKe la stessa manutenzione nel campomaintenance(vedi sotto). Allo scadere diends_atlo scanner riparte da solo, senza bisogno di altro. - Una manutenzione viene rilevata quando le nostre scansioni incontrano l'avviso di Sister: può comparire con poco anticipo, oppure direttamente già in corso.
- Se
ends_atè passato,maintenancetornanull(o mostra la manutenzione successiva, se ce n'è un'altra annunciata). - Lo stato
slow/downmisurato sui tempi cambia solo se il problema dura almeno due controlli di fila (10-20 minuti): una scansione lenta da sola non lo accende, e tornaokdopo due controlli normali. La manutenzione invece si applica subito. queueriguarda le scansioni delle ultime 2 ore:max_wait_secondsè l'attesa della richiesta in coda da più tempo.- La risposta resta in cache 60 secondi: interrogalo ogni
recommended_poll_seconds(5 minuti), non più spesso, dal tuo backend. Se la chiamata fallisce (rete, 5xx) tieni l'ultimo stato noto. - Con
downdovuto a Sister che non risponde (senza manutenzione) le richieste inviate vengono comunque accettate e restano in coda finché Sister non torna: il blocco è una scelta tua. - In sandbox lo stato di Sister è normalmente
unknownemaintenanceènull.
Richieste inviate durante una manutenzione
Creazione rifiutata — se il Market Scanner è sospeso, POST /api/scanner/{type} non crea la richiesta e non scala crediti:
// HTTP 400
{
"error": "SISTER_FORCE_BLOCK",
"maintenance": {
"in_progress": true,
"starts_at": "2026-09-17T17:00:00+02:00",
"ends_at": "2026-09-17T18:00:00+02:00",
"scanner_blocked": true,
"message": "Sister (Agenzia delle Entrate) e' in manutenzione programmata fino alle 18:00: le scansioni riprenderanno al termine.",
"sister_message": "A causa di un intervento programmato, il servizio e' indisponibile dalle 17.00 alle 18:00 circa"
}
}maintenance è null se la sospensione non dipende da una manutenzione programmata (blocco manuale nostro): in quel caso non c'è un orario di ripartenza, controlla GET /api/scanner/health.
Richiesta già partita — se la manutenzione inizia mentre una richiesta è in lavorazione, la richiesta passa a ERROR e il suo result indica il motivo con code:
{
"id": 12345,
"status": "ERROR",
"result": {
"error": "Sister (Agenzia delle Entrate) e' in manutenzione programmata fino alle 18:00: le scansioni riprenderanno al termine.",
"code": "SISTER_MAINTENANCE",
"maintenance": {
"in_progress": true,
"starts_at": "2026-09-17T17:00:00+02:00",
"ends_at": "2026-09-17T18:00:00+02:00",
"scanner_blocked": true,
"message": "Sister (Agenzia delle Entrate) e' in manutenzione programmata fino alle 18:00: le scansioni riprenderanno al termine.",
"sister_message": "A causa di un intervento programmato, il servizio e' indisponibile dalle 17.00 alle 18:00 circa"
}
}
}result.code | Significato |
|---|---|
SISTER_MAINTENANCE | Sister in manutenzione programmata; maintenance indica fino a quando (può essere null se l'avviso non riportava gli orari). |
SISTER_UNAVAILABLE | Sister ha mostrato un avviso di indisponibilità senza orario; maintenance è null. |
Le richieste che producono un PDF (visure, ispezioni, note, mappe) vengono rilanciate da noi dopo la fine della manutenzione, come per gli altri errori temporanei (vedi Caso d'uso: visure, ispezioni, note, mappe). Le altre vanno reinviate dopo ends_at.
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:
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 è
SUCCESSoERROR.
Risposta tipica:
{ "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.