Skip to content

Market Scanner integration

Integrate with an AI assistant
Download or copy the integration prompt for Market Scanner and paste it into Claude Code, Cursor or Copilot: it contains auth, end-to-end examples and a checklist.
Open

Dokicasa's Scanner APIs let you obtain deeds (visure), inspections, notes, cadastral maps and address searches. They are asynchronous: this guide shows how to receive results efficiently, avoiding polling when possible.

🎮 Try the API live

Open the interactive Playground: paste your token, fire each call in sandbox and watch socket and polling arrive in real time, test webhooks, and copy ready-to-use code in cURL, Python, Node, JavaScript and PHP.

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

Documentation and authentication

Interactive docs for all endpoints are on Swagger, Scanner section: api.dokicasa.it/api/documentation#/Scanner

Every call must be authenticated with a Bearer token:

http
Authorization: Bearer YOUR_API_TOKEN

WARNING

The token must never end up in public repos or the frontend: treat it like a password.

Main endpoints

Summary table — the full schema is on Swagger.

MethodPathWhat it does
POST/api/scanner/{type}Create a scanner request (visura / inspection / map / list-addresses / …). Returns scanner_request_id.
GET/api/scanner-request/{id}Request status + result. For PDFs you get the link result.pdf_path here (the file is NOT in the JSON: download it from the link); for list-addresses the data is already in the JSON.
GET/api/scanner-request/{id}/resultsStructured results for "normal" searches (e.g. properties from an address).
WSprivate-scanner_request.{id}Private WebSocket channel for realtime updates.

Request states

ENQUEUED  →  SCANNING  →  SUCCESS
                       ╲→  ERROR

ENQUEUED and SCANNING are transient; SUCCESS and ERROR are final.

Query types

Every query is a POST /api/scanner/{type}. Below are the available type values (the parameter schema of each is on Swagger, Scanner section).

Data lookups — the result is JSON in the result field:

typeWhat it doesField in result
search-fiscal-codeFiscal code from first name, last name and provinceanagrafiche
tax-dataProperties owned by a fiscal/VAT codeimmobili
catastoProperties from sheet / parcelimmobili
intestatari-catastoOwners of a parcelintestatari
list-addressesNormalize an address → list of streetsaddresses
immobili-by-nameNationwide properties from first/last name (2 steps)immobili

Documents — no API call returns the PDF: the result.pdf_path field holds the link to download it (see note below):

typeDocument
visura-catasto / visura-tax-dataCadastral survey (from cadastral data / from fiscal-VAT code)
ispezione-catasto / ispezione-tax-dataMortgage inspection
nota-catasto / nota-tax-dataTranscription note
mappa-catastoCadastral map
elaborato-planimetricoFloor-plan document
elenco-immobiliProperty list

IMPORTANT

catasto ≠ cadastral survey. catasto is the property lookup by sheet/parcel and returns the immobili list (JSON data). The cadastral survey PDF is instead visura-catasto (from cadastral data) or visura-tax-data (from fiscal/VAT code). Likewise the mortgage inspection is ispezione-catasto / ispezione-tax-data.

TIP

Inspections and notes require the comune in the body: it is used to resolve the competent land registry office (conservatoria).

IMPORTANT

API calls never return the PDF: they only return the link result.pdf_path. The file is downloaded with a separate GET on that link, passing your Bearer token (authenticated endpoint, not a public anonymous link). The link is stable and permanent (it does not expire): you can store it and reuse it as a reference in your own workflow.

⚠️ With curl the response is binary (Content-Type: application/pdf): without -o curl prints nothing to the terminal (it looks like an empty "200"), but the file is there. Save it:

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

immobili-by-name — two-phase search

immobili-by-name finds a person's properties nationwide starting from first and last name. Since there can be several namesakes, it works in two phases.

Phase 1 — list of namesakes

POST /api/scanner/immobili-by-name with only first and last name:

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

When it completes (via socket/polling, like every async call) the result holds the list of namesakes found:

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"
    }
    // … other namesakes
  ]
}
  • needs_selection: true → you must pick one person from the list.
  • If there is only one namesake the selection is automatic: you get the Phase 2 result directly.

Phase 2 — properties of the chosen person

Call the same endpoint adding selected_value = the value field of the namesake chosen in Phase 1 (copy it verbatim):

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

When it completes the result holds:

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

The properties are persisted: the full list (with owners) is retrieved paginated from GET /api/scanner-request/{id}/results.

NOTE

Both phases are regular async calls: the POST returns ENQUEUED, then via socket or polling you reach SUCCESS and read the result. Very common names may exceed Phase 1's result limit: narrow it down with more specific data.

How it works

All scanner calls are asynchronous. The flow is always the same, only how you receive the status update changes.

  1. Create the requestPOST /api/scanner/{type} → you get a scanner_request_id and the initial state ENQUEUED.
  2. Wait for completion — pick one of these (in order of preference):
    • WebSocket — instant push, no polling.
    • Webhook callback — our server calls yours when it finishes.
    • Polling (fallback) — only if you can't use the two above.
  3. Get the result — when the state is SUCCESS:
    • PDF (visure, inspections, maps, notes): the file is already in the GET /api/scanner-request/{id} response.
    • Structured searches: call GET /api/scanner-request/{id}/results.

Realtime updates via WebSocket

⭐ Recommended for frontend apps

Recommended approach for web/mobile: no polling, instant updates, progress bars and push notifications.

Each request publishes events on a dedicated private channel:

private-scanner_request.{id}

where {id} is the scanner_request_id returned by the initial POST.

Socket — host and key per environment

The socket is separate per environment: use the right host and key for where you call.

EnvironmentAPI base URLwsHost (socket)keyauthEndpoint
Sandbox / testhttps://api-dev.dokicasa.itapi-dev.dokicasa.itstaginghttps://api-dev.dokicasa.it/broadcasting/auth
Productionhttps://api.dokicasa.itsocket.dokicasa.itra9jcihuz0sx7vtw8yuthttps://api.dokicasa.it/broadcasting/auth

WARNING

Don't mix environments: connecting to the production socket while calling in sandbox (or vice versa) subscribes the channel but you receive no events (it's a different Reverb). The example below uses production values; for sandbox swap key, wsHost and authEndpoint with the Sandbox row.

Full example (JavaScript + Pusher/Reverb)

js
import Pusher from 'pusher-js'

const apiToken = 'YOUR_API_TOKEN'

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 } },
})

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())

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('Final result:', data)
}

Webhook & signature

⭐ Recommended for backend integrations

Pass a URL when creating the request: when it finishes, Dokicasa POSTs the result to you. Delivery is async with retries — no setup in the panel.

There are two fields you can add to the initial POST body (either or both):

Field (in POST body)Payload you receiveWhen it fires
callbackcompact: { id, type, status, percentage, result, created_at, updated_at, finished_at }when the scan finishes
webhookfull: { scanner_request: { …whole request object, with paramsandresult… } }on the completion event
json
{
  "type": "catasto",
  "comune": "Roma", "foglio": "123", "particella": "45", "search_catasto_type": "F",
  "callback": "https://your-server.com/dokicasa/scanner-callback",
  "webhook":  "https://your-server.com/dokicasa/scanner-webhook"
}

Municipalities split into cadastral sections

In some municipalities — Rome, Naples, Genoa — sheet and parcel alone are not enough to identify a property: the cadastral section is needed too. Add sezione_comune to the request in those cases.

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

It is optional and takes part in identifying the property together with foglio, particella, subalterno and comune: two properties sharing a parcel but sitting in different sections stay distinct. Omitting it in a municipality that uses sections may return the wrong property, or none.

The field is named sezione_comune: a sezione key is ignored without error.

The result you receive has a type-dependent shape — see Result format by type.

callback vs webhook — what's the difference

Same mechanism (a signed POST to your URL when processing finishes, with retries): only the payload shape differs.

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
Payloadcompact, flat fieldsfull scanner_request object
Includes input paramsNoYes
Signature header (HMAC)YesYes
When it fireswhen the scan finisheswhen the scan finishes

Which to use

  • callback → lightweight payload: you just react to the outcome (id, status, result). The most common choice.
  • webhook → the whole record, including the original request params: handy to reconcile/log without keeping state on your side.

They're independent: pass one, the other, or both (you then receive two separate POSTs). Both signed with the Signature header.

Signature — verify authenticity

Every webhook includes a Signature header: the HMAC-SHA256 of the raw body, signed with your api_token (the same one you use as Bearer). So you don't manage any new key — you verify with your own api key.

Signature = hash_hmac('sha256', <raw_body>, <your_api_token>)
php
$raw = file_get_contents('php://input');                 // RAW body, not re-serialized
$expected = hash_hmac('sha256', $raw, $MY_API_TOKEN);
if (!hash_equals($expected, $_SERVER['HTTP_SIGNATURE'] ?? '')) {
    http_response_code(401); exit;                        // invalid signature
}
js
const crypto = require('crypto')
const raw = req.rawBody                                   // RAW body (Buffer/string)
const expected = crypto.createHmac('sha256', MY_API_TOKEN).update(raw).digest('hex')
if (expected !== req.get('Signature')) return res.sendStatus(401)

Sign the **raw** body

Compute the HMAC over the exact bytes you received, before parsing/re-serializing the JSON: re-encoding changes whitespace and key order, and the signature won't match.

Idempotency

On network errors delivery is retried: identify the request by its id in the body and always respond 2xx, even on duplicates, to avoid useless retries.

Result format by type

The result field (found in GET /api/scanner-request/{id}, and the same one delivered via socket and webhook) changes shape depending on the type of request. The main shapes are below, with real examples.

`result` vs `/results`

  • result = the full scan payload (properties, personal data, PDF…), ready to use.
  • GET /api/scanner-request/{id}/results = a paginated view of the persisted properties only (with owners), handy when there are many. It applies to the types that list properties (catasto, tax-data, list-addresses, intestatari-catasto) and returns a Laravel paginator { 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/1980", "luogo_di_nascita": "ROMA (RM)",
      "sesso": "M", "codice_fiscale": "RSSMRA80E17H501U"
    }
  ]
}
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
}

Where to find each individual call's schema

The input parameters and the exact type of each query (foglio/particella, tax_code, tipo_visura, etc.) are on Swagger, Scanner section: api.dokicasa.it/api/documentation#/Scanner. This page instead covers how to receive and interpret the results.

How to test the integration

There is a sandbox environmenthttps://api-dev.dokicasa.it — that mirrors the production API but makes no real queries: every call returns realistic sample data, consumes no credit and produces no real documents. Compared to production, only the base URL changes: endpoints, parameters, response format, WebSocket and webhooks are identical.

🔑 Test API key (sandbox)

Use this token: it's a user with every call enabled and free, made specifically for testing. In sandbox the origin whitelist is disabled, so you can call from localhost or any test domain.

HfBfYShns3E02no3kkwCRWQugkVNcJCtrRN2BPoI5NxhqsiXaxxnSPpIHY2O

Send it as the Authorization: Bearer <token> header on every call.

Already loaded in the Playground

In the interactive Playground this token is pre-filled (and copyable): open it, hit Run and watch socket + polling live, with zero setup.

Endpoints to test

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

WhatEndpoint
Start a searchPOST /api/scanner/{type}{ scanner_request_id, status }
Status + resultGET /api/scanner-request/{id} · …/{id}/status
Paginated resultsGET /api/scanner-request/{id}/results
Create monitoringPOST /api/monitoring/from-catasto
Simulate webhook (sandbox only)POST /api/user-monitorings/{id}/simulate-webhook

Socket — sandbox (see also Socket environments)

ParameterValue
wsHostapi-dev.dokicasa.it
keystaging
authEndpointhttps://api-dev.dokicasa.it/broadcasting/auth
channel · eventprivate-scanner_request.{id} · ScannerRequestNotification

Quick try (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" }
# then:  GET /api/scanner-request/12345/status   until  status = SUCCESS

What to verify:

  1. Async flow — the POST returns ENQUEUED, then via socket (instant) or polling you reach SUCCESS.
  2. Response shape — map the result fields (see Result format by type).
  3. Webhook — point webhook_url at an inspector (webhook.site); in sandbox the monitoring sends a sample event immediately on creation (or re-trigger it with /simulate-webhook).
  4. Signature — validate the Signature header (see Signature section).

NOTE

In sandbox the data is representative and fixed (it does not vary with the input parameters): it shows you the result structure. When ready, switch to the production base URL and production socket (see the Socket environments table).

TIP

For your first tests use list-addresses or search-fiscal-code: they are fast and immediately show you the shape of result without needing specific cadastral data.

Property monitoring

Beyond one-off searches, you can monitor a property over time: Dokicasa periodically re-checks the cadastral owners and, when they change, sends you a webhook with the variation.

Start a monitoring

POST /api/monitoring/from-catasto — start straight from the cadastral data (no property id needed). If the property isn't in the system yet, it's first resolved with a cadastral search (owners included) and saved, then monitoring is activated.

json
{
  "foglio": "123",
  "particella": "45",
  "subalterno": "7",
  "comune": "Roma",
  "provincia": "RM",
  "webhook_url": "https://your-server.com/dokicasa/monitoring-changed"
}
FieldRequiredNotes
foglio, particellathe cadastral "triplet"
subalternonumeric, optional
comunecadastral code or town name
provinciaprovince code (no TN/BZ)
search_catasto_typeF buildings (default) / T land
sezione_comunefor towns with sections (Naples/Rome/Genoa)
webhook_urlURL to receive changes (see below)
name, notefree labels

Response: { immobile_id, user_monitoring_id, created, scanned, last_monitoring, next_monitoring } (last_monitoring = last run, null if never; next_monitoring = next run).

Owners-change webhook

If you set webhook_url, on every owners change detected by the monitoring Dokicasa makes an async POST to that URL with the full change JSON:

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 } ],
        "removed":  [ { "quota": "1/2", "nome": "Luigi", "cognome": "Verdi", "denominazione": null } ],
        "modified": [ { "id": "CF:RSSMRA80A01H501U", "before": { "quota": "1/3" }, "after": { "quota": "1/2" } } ]
      }
    }
  ]
}

NOTE

The webhook fires automatically from the monitoring cron, with retries on network errors. Respond 2xx to acknowledge receipt.

TIP

webhook_url is optional at creation: if you don't pass it, nothing is sent. You can add or change it later (see below).

Test the webhook in **sandbox** (staging)

On the test environment (https://api-dev.dokicasa.it) you don't have to wait for a real owners change:

  • On creation, if you pass a webhook_url, we send a sample immobile.owners_changed event to that URL right away (the create response includes simulated_webhook_sent: true).
  • You can re-send it any time with POST /api/user-monitorings/{id}/simulate-webhook.

The payload has the exact same format as the real event: point webhook_url to an inspector (webhook.site) to see it arrive. In production these helpers are disabled — the webhook only fires on a real owners change.

Listing your monitorings

GET /api/users/me/monitorings — returns your monitorings (paginated). Filters via query string:

ParameterValuesNotes
filter.typeIMMOBILE (default) / SOGGETTOmonitoring type
filter.statusACTIVE, EXPIRED, CANCELLEDmultiple allowed, comma-separated
filter.sourceAPI / CENSIMENTOmonitoring origin
filter.change_owner1only those with owner changes
filter.textfree textsearches notes + address/cadastral data
sort_ends_atASC / DESCsort by expiry (default: id DESC)
pagenumberpage (paginated response)

Need only the ids? GET /api/users/me/monitorings-ids.

NOTE

This is a read-only endpoint: it always works with your Bearer token, even without a configured origin whitelist. The default filter.type=IMMOBILE returns only property monitorings — for subjects pass filter.type=SOGGETTO.

Managing a monitoring

MethodPathWhat it does
GET/api/user-monitorings/{id}Detail of the monitoring: monitoring data, linked property (model), current owners (model.soggetti), change events (events) and the owner reads history (proprietari_history). See example below.
PATCH/api/user-monitorings/{id}/webhookSet or remove (pass null) the webhook_url. Body: { "webhook_url": "https://…" }.
PATCH/api/user-monitorings/{id}Change status: ACTIVE, DISACTIVE (paused), CANCELED.
POST/api/user-monitorings/{id}/renewRenew the monitoring.
POST/api/user-monitorings/{id}/simulate-webhookSandbox only: immediately sends a sample immobile.owners_changed event to the webhook_url, to test delivery without waiting for a real change. In production it returns 404.
DELETE/api/user-monitorings/{id}Delete the monitoring.

Example response — monitoring detail

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'" }
      ]
    }
  ]
}
FieldContents
modelDetail of the monitored property (municipality, sheet/parcel/sub, category, address…)
model.soggettiCurrent owners (with quota and titolarità in the pivot)
eventsOwner changes: diff per date (added / removed / modified)
proprietari_historyOwner snapshot at every read (one entry per scanner_request, with date/read_at): the raw, non-deduplicated history of who the owners were at each check

You have an address and want the associated cadastral properties. Two phases: address resolution → properties.

  1. list-addresses (address resolution)POST /api/scanner/list-addresses. Wait via socket/webhook/polling. The normalized address list is already in the results field of GET /api/scanner-request/{id} — no need to call /results.
  2. Properties from an address — use an address from step 1 as address_value in the properties scanner call (exact endpoint on Swagger, Scanner section).
  3. Fetch results — when SUCCESS arrives, call GET /api/scanner-request/{id}/results for the property list.

Use case: visure, inspections, notes, maps (PDF)

All requests producing a PDF (cadastral visura, inspection, note, map) follow the same pattern.

  1. Create the requestPOST /api/scanner/{type} (type depends on the document, see Swagger).
  2. Wait for completion — via WebSocket or webhook: state goes ENQUEUEDSCANNING up to SUCCESS or ERROR.
  3. Automatic retry on transient errors — if a temporary error occurs (line issues with external services), the system retries by itself every ~5 minutes. You don't need to resend anything. Credit is charged only on the first success.
  4. Get the PDF — the file is returned directly in the GET /api/scanner-request/{id} response (no /results). The PDF is also sent automatically by email to the address bound to the token, so the end user gets it even without integrating the client-side download.

Polling — last resort only

Not recommended

You have two push mechanisms (WebSocket and webhook) that remove the need for polling. Consider them first: polling increases latency and server load with no real benefit.

If you really must poll, query periodically:

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

Practical guidelines:

  • Recommended minimum interval: 3 seconds between calls.
  • Exponential backoff after a few attempts (e.g. 3s → 6s → 12s).
  • A sensible attempt limit (e.g. 5 minutes total), then time out on your side.
  • Stop as soon as the state is SUCCESS or ERROR.

Typical response:

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

Operational notes

  • Always keep the id (scanner_request_id) returned at creation: you need it for every later operation.
  • Handle the ERROR state explicitly: read the message to understand the cause.
  • Never share the API token: treat it like a password.
  • In production, prefer webhook or socket over polling.