Skip to content

Form — Server-to-server ​

Crea e gestisci pratiche direttamente dal tuo backend, autenticandoti con la api_token (Bearer). Base URL: https://app.dokicasa.it.

Endpoint principali ​

MethodPathCosa fa
GET/api/v3/catalog-formsTutti i form disponibili (servizi, contratti, bundle) con slug e form_url — pubblico
GET/api/list-form/canone-concordato-{citta}Lista degli step del flusso per una città
GET/api/v3/form/{slug}Schema (campi) di un form
POST/api/v3/form/{slug}Submit del form → crea una pratica
PUT/api/v3/form/{bundleUserServiceId}Aggiorna un form già inviato
GET/api/v3/user/{id}/servicesLista pratiche (filtrabile per external_id)
GET/api/v3/practices/{practice}Dettaglio + stato di una pratica
GET/api/v3/practices/{practice}/tasksTask collegati alla pratica
POST/api/v3/tasks/{task}/commentsRispondi a un task (commento + allegati)
GET/api/v3/contract/{id}/pdfScarica il PDF di una pratica-contratto (anche step di bundle)
GET/api/v3/user-bundle/{userBundle}/canone-valuesValori del calcolo del canone (step 2 dei bundle canone concordato): fasce per tipo di contratto e immobile intero o porzione
GET/api/v3/contract/{id}/docScarica il Word (.docx) di una pratica-contratto (richiede il download Word abilitato sull'account)
POST/api/v3/practices/{practice}/duplicate-partnerDuplica una pratica (cambio inquilino): clona gli step duplicabili in una nuova pratica

0. Scopri i form disponibili ​

Un'unica chiamata pubblica ti dà tutti i form, ordinati per nome:

bash
curl https://app.dokicasa.it/api/v3/catalog-forms -H "Accept: application/json"

Ogni voce ha kind (service / contract / bundle), name, slug e form_url. I bundle includono steps (un form per step):

json
[
  { "kind": "service", "name": "Visura catastale", "slug": "visura-catastale",
    "form_url": "/api/v3/form/visura-catastale", "steps": [] },
  { "kind": "bundle", "name": "Canone Concordato", "slug": "canone-concordato",
    "form_url": null, "steps": [
      { "step": 1, "name": "Contratto", "type": "Contract",
        "slug": "contratto-locazione", "form_url": "/api/v3/form/contratto-locazione" }
    ] }
]

La lista interattiva (con copia-slug) è anche in SDK JavaScript → Form disponibili.

1. Crea una pratica ​

php
$res = Http::withToken(env('DOKICASA_TOKEN'))
    ->acceptJson()
    ->post('https://app.dokicasa.it/api/v3/form/locazione-ad-uso-abitativo-4-4', [
        'form' => [ /* ...risposte dei campi... */ ],
        'metadata' => [
            'external_id' => 'ordine_8842', // il TUO id, per ritrovarla dopo
        ],
    ])
    ->json();

// $res['id'], $res['external_id']
js
const res = await fetch(
  'https://app.dokicasa.it/api/v3/form/locazione-ad-uso-abitativo-4-4',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.DOKICASA_TOKEN}`,
      'Content-Type': 'application/json',
      Accept: 'application/json',
    },
    body: JSON.stringify({
      form: { /* ...risposte... */ },
      metadata: { external_id: 'ordine_8842' },
    }),
  }
).then((r) => r.json())
bash
curl -X POST \
  https://app.dokicasa.it/api/v3/form/locazione-ad-uso-abitativo-4-4 \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"form":{},"metadata":{"external_id":"ordine_8842"}}'

WARNING

POST /api/v3/form/{slug} può scalare credito dal wallet del partner. Verifica il saldo prima di automatizzare creazioni massive.

Come si compila form ​

form è un oggetto indicizzato per slug del campo; ogni valore è { "type": "<tipo>", "value": <valore> }. Recupera i campi (con type, is_required, depends_from, selectable_options) da GET /api/v3/form/{slug}.

jsonc
{
  "form": {
    // selectable: basta la label come stringa
    "tipologia_ape_5": { "type": "selectable", "value": "Locazione" },
    "numero_di_telefono_per_contatto_5": { "type": "varchar", "value": "3331234567" },
    "dati_immobile_6_6": { "type": "address", "value": "Via Cavour 1, 50100 Firenze (FI)" }
  },
  "metadata": { "external_id": "ordine_8842" }
}

Attenzione ad alcuni tipi:

  • selectable — invia la label dell'opzione come stringa (viene normalizzata in automatico): { "type": "selectable", "value": "Locazione" }. Se il campo è a scelta multipla (is_multiple: true nello schema), invia un array di label: { "type": "selectable", "value": ["Opzione A", "Opzione B"] }. In ogni caso value deve contenere stringhe (la label): non inviare un oggetto annidato né un array dentro value (es. { "value": ["..."] } come oggetto singolo non è valido).
  • customer_data — array di oggetti, uno per persona/entità: [{ "type": "Persona Fisica", "name": "...", "address": "...", "fiscal_code": "...", ... }]. I campi richiesti dipendono dal type (vedi fields nello schema). Questi blocchi vanno sempre compilati anche quando lo schema riporta is_required: 0.
  • immobile (es. blocco_immobile) — lista di oggetti immobile, ognuno con indirizzo, categoria_catastale, foglio_immobile, parcella_immobile, subalterno_immobile, rendita_immobile. Un singolo oggetto (non in lista) non è valido.
  • file — value è una lista di oggetti, uno per allegato, ognuno con filename e content. Non si usa multipart/form-data e non esiste un endpoint di upload separato: il contenuto viaggia dentro il campo (vedi Allegare un file). Molti campi file sono richiesti solo su determinati rami di un selectable (depends_from): scegliendo l'altro ramo non servono.
  • Un campo è obbligatorio solo se attivo: se la sua depends_from non è soddisfatta viene ignorato.

Campi che si possono fornire più tardi ​

Alcuni campi obbligatori si possono rimandare: nello schema hanno has_skip_button: true e skip_button_text con il testo che vede l'utente (per esempio l'APE dell'attestazione: "Ne sono in possesso, lo fornirò successivamente").

Per rimandarli basta non mandarli: ometti la chiave dal form, oppure inviala con valore vuoto (null, "", []). Non serve nessun campo aggiuntivo e non c'è un valore speciale da usare.

jsonc
{
  "form": {
    // ape_1_1_7 semplicemente non c'è: verrà richiesto dopo
    "tipologia_ape_5": { "type": "selectable", "value": "Locazione" }
  }
}

La pratica viene accettata e il campo risulta rimandato, esattamente come se l'utente avesse premuto il pulsante dall'interfaccia: il nostro backoffice vede che il documento non era disponibile e continua a richiederlo. Il documento va poi fornito con i canali abituali, non c'è un endpoint dedicato.

WARNING

Vale solo per i campi con has_skip_button: true. Tutti gli altri campi obbligatori attivi, se mancanti, restituiscono 422 con "Campo obbligatorio mancante.".

Allegare un file (campi di tipo file) ​

I campi di tipo file si inviano nello stesso JSON del form, non come multipart/form-data. value è una lista di oggetti, uno per allegato:

jsonc
{
  "form": {
    "documenti_roma_1_1_7": {
      "type": "file",
      "value": [
        {
          "filename": "documenti-firmati.pdf",
          "content": "JVBERi0xLjQKJeLjz9MK..."      // base64 del file
        }
      ]
    }
  },
  "metadata": { "external_id": "ordine_8842" }
}

content accetta due formati, riconosciuti in automatico:

FormatoEsempioComportamento
base64"JVBERi0xLjQK..."il contenuto viene decodificato e salvato
URL pubblica"https://tuo-dominio.it/doc.pdf"il file viene scaricato dal nostro server

filename è facoltativo ma consigliato: da lì ricaviamo l'estensione. Se manca, proviamo a dedurla dall'URL. content invece è obbligatorio: senza, la chiamata risponde 400 con File "<nome>" has no content.

Esempio completo con curl (base64 di un PDF locale):

bash
CONTENT=$(base64 -w0 documenti-firmati.pdf)   # su macOS: base64 -i documenti-firmati.pdf

curl -X POST "https://app.dokicasa.it/api/v3/form/attestazione-contratto-locazione-roma" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"form\": {
      \"documenti_roma_1_1_7\": {
        \"type\": \"file\",
        \"value\": [{ \"filename\": \"documenti-firmati.pdf\", \"content\": \"$CONTENT\" }]
      }
    },
    \"metadata\": { \"external_id\": \"ordine_8842\" }
  }"

WARNING

value deve essere una lista di oggetti. Passare il solo nome del file come stringa ("value": "contratto.pdf") fa rispondere 201 ma non allega nulla: la pratica viene creata con attachments: []. Allo stesso modo non funzionano multipart/form-data, né la parte attachments[] — che serve solo per gli allegati delle risposte ai task (vedi Rispondi a un task).

NOTE

Se il campo è obbligatorio e attivo, inviarlo vuoto ("", [], [""]) fa rispondere 422 Campo obbligatorio mancante. Un campo file obbligatorio resta tale anche quando nel form web mostra il pulsante "salta".

Pratica singola vs bundle ​

L'external_id è il tuo id (della tua piattaforma) e determina anche il raggruppamento:

  • Primo invio con un external_id nuovo → crea la pratica. Se lo slug è un servizio/contratto singolo nasce una pratica autonoma; se è lo step 1 di un bundle viene creato il bundle raccoglitore.
  • Invio successivo con lo stesso external_id → lo step viene agganciato al bundle già avviato (step 2, 3, …).

L'external_id resta legato alla pratica e la rende ritrovabile con filter[external_id].

I bundle vanno sempre avviati dallo step 1

Se il primo invio con un external_id nuovo usa lo slug di uno step interno (step 2, 3, …), il raccoglitore non può essere creato e la pratica nascerebbe isolata, senza gli step precedenti. In questo caso la risposta è 422:

json
{
  "error": "BUNDLE_NOT_STARTED",
  "message": "The service \"Creazione Documenti Canone Concordato Roma\" is step 4 of the bundle \"Canone Concordato Roma\" and cannot be the first call for a new external_id. Start the bundle by calling the first step (\"foglio-caratteristiche-immobile-roma\") with the same external_id, then call the remaining steps.",
  "first_step_slug": "foglio-caratteristiche-immobile-roma"
}

Il campo first_step_slug contiene lo slug da cui partire. Gli step del bundle si scoprono con GET /api/v3/catalog-forms (vedi Scopri i form disponibili).

Creare uno step come pratica singola: metadata.standalone ​

Alcuni servizi fanno parte di un bundle ma hanno senso anche da soli. Se vuoi crearne uno senza bundle, dichiaralo esplicitamente:

json
{
  "form": { },
  "metadata": {
    "external_id": "ordine_8842",
    "standalone": true
  }
}

Con standalone: true la pratica nasce autonoma: non viene cercato né creato nessun raccoglitore, e il controllo sopra non si applica. Ometti il parametro quando vuoi il comportamento normale a bundle.

Un external_id non si riusa fra pratica singola e bundle ​

Se un external_id è già stato usato per una pratica singola, non può poi fare da raccoglitore per gli step di un bundle. In quel caso la risposta è 409:

json
{
  "error": "EXTERNAL_ID_NOT_A_BUNDLE",
  "message": "The external_id \"ordine_8842\" is already used by a standalone practice and cannot be used to add bundle steps. Use a different external_id for the bundle."
}

Usa un external_id diverso per il bundle.

2. Ritrova le tue pratiche ​

php
$practices = Http::withToken(env('DOKICASA_TOKEN'))
    ->acceptJson()
    ->get('https://app.dokicasa.it/api/v3/user/me/services', [
        'filter[external_id]' => 'ordine_8842',
    ])
    ->json();

3. Stato e task di una pratica ​

php
$id = 84213; // id pratica oppure uuid pubblico

$practice = Http::withToken(env('DOKICASA_TOKEN'))->acceptJson()
    ->get("https://app.dokicasa.it/api/v3/practices/{$id}")->json();
// $practice['status'] → es. WAITING / DOING / ...

$tasks = Http::withToken(env('DOKICASA_TOKEN'))->acceptJson()
    ->get("https://app.dokicasa.it/api/v3/practices/{$id}/tasks")->json();
// $tasks['tasks'] → Activity (type=TASK) con status TO_DO / DOING / DONE

TIP

Con l'uuid al posto dell'id numerico, GET /practices/{practice} e .../tasks sono pubbliche (senza Bearer): comode per un link in sola lettura. Con l'id numerico servono Bearer e proprietà della pratica.

4. Rispondi a un task ​

Quando una pratica è in lavorazione il backoffice può assegnarti dei task (Activity con type = TASK, leggibili con GET /practices/{practice}/tasks): ad esempio caricare un documento o fornire un'informazione mancante.

Per rispondere invii un commento (testo e/o allegati) al task. La chiamata:

  • richiede sempre il Bearer api_token dell'utente;
  • accetta solo task assegnati a te (assigned_to) e agganciati a una pratica di tua proprietà; altrimenti 403;
  • riporta il task in stato DOING e lo riassegna al backoffice, che riceve la notifica.

WARNING

{task} è l'id del singolo task, cioè il campo id di un elemento dell'array tasks[] restituito da GET /practices/{practice}/tasks — non l'id della pratica (practice_id / id del raccoglitore). Passando l'id della pratica ottieni 404 (o 403 se quell'id coincide per caso con un task non tuo).

jsonc
// GET /api/v3/practices/84213/tasks
{
  "practice_id": 84213,   // ❌ NON questo
  "tasks": [
    { "id": 99812, ... }  // ✅ questo: {task} = 99812
  ]
}
php
$taskId = 99812;

// Solo testo
$comment = Http::withToken(env('DOKICASA_TOKEN'))->acceptJson()
    ->post("https://app.dokicasa.it/api/v3/tasks/{$taskId}/comments", [
        'comment' => 'Ho caricato il documento richiesto.',
    ])
    ->json();

// Con allegati (multipart)
$comment = Http::withToken(env('DOKICASA_TOKEN'))->acceptJson()
    ->attach('attachments[]', file_get_contents('/path/ci.pdf'), 'ci.pdf')
    ->post("https://app.dokicasa.it/api/v3/tasks/{$taskId}/comments", [
        'comment' => 'In allegato la carta d\'identità.',
    ])
    ->json();
js
const taskId = 99812

const form = new FormData()
form.append('comment', "In allegato la carta d'identità.")
form.append('attachments[]', fileBlob, 'ci.pdf') // opzionale

const comment = await fetch(
  `https://app.dokicasa.it/api/v3/tasks/${taskId}/comments`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.DOKICASA_TOKEN}`,
      Accept: 'application/json',
    },
    body: form, // niente Content-Type manuale: lo imposta FormData
  }
).then((r) => r.json())
bash
curl -X POST \
  https://app.dokicasa.it/api/v3/tasks/99812/comments \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -H "Accept: application/json" \
  -F "comment=In allegato la carta d'identità." \
  -F "attachments[]=@/path/ci.pdf"

NOTE

Devi fornire almeno uno tra comment e attachments[]. La risposta 201 contiene il commento creato; il task passa a DOING in attesa della verifica del backoffice.

5. Scarica il PDF di un contratto ​

Le pratiche di tipo contratto (type: CONTRACT) espongono il PDF generato, scaricabile on-demand con il Bearer api_token:

GET /api/v3/contract/{id}/pdf
  • {id} è l'id della pratica-contratto: lo stesso id che ti restituisce POST /api/v3/form/{slug} quando lo slug è un contratto, oppure — se il contratto è uno step di un bundle — l'id di quello step (user_service_id). Vale quindi sia per i contratti singoli sia per gli step-contratto di un bundle.
  • Richiede il Bearer e la proprietà della pratica (quelle create via API appartengono al partner). Un id che non è tuo → 401.
  • Di default risponde con il PDF inline (Content-Type: application/pdf); con ?download_mode=1 forza il download come allegato (Content-Disposition: attachment; filename="<nome-contratto>.pdf").
php
$id = 84213; // id pratica-contratto (dal POST form, o user_service_id dello step)

$pdf = Http::withToken(env('DOKICASA_TOKEN'))
    ->get("https://app.dokicasa.it/api/v3/contract/{$id}/pdf", [
        'download_mode' => 1, // forza attachment; ometti per il PDF inline
    ])
    ->body();

file_put_contents("contratto_{$id}.pdf", $pdf);
bash
curl -L -X GET \
  "https://app.dokicasa.it/api/v3/contract/84213/pdf?download_mode=1" \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -o contratto_84213.pdf

TIP

Il PDF del contratto ti arriva anche come allegato del webhook CLOSED (campo attachments[].content_base64, vedi sotto). Questa GET serve quando lo vuoi on-demand — ad esempio scaricarlo o rigenerarlo prima della chiusura.

NOTE

Parametri opzionali: download_mode=1 (attachment), send_mail=1 (invia il PDF via email al proprietario della pratica), preview (anteprima).

Versione Word (.docx) ​

Lo stesso contratto è scaricabile anche nella versione editabile Word, con lo stesso Bearer api_token:

GET /api/v3/contract/{id}/doc
  • {id} è lo stesso id usato per il PDF: l'id della pratica-contratto (dal POST /api/v3/form/{slug}) oppure l'user_service_id dello step-contratto di un bundle.
  • Richiede il Bearer e la proprietà della pratica.
  • Gating aggiuntivo: il download Word dev'essere abilitato sul tuo account partner. Se non lo è, risponde 403 con { "error": "..." } — scrivi al supporto per farlo attivare.
  • In caso di successo ritorna il file .docx; gli errori (permesso mancante, generazione non riuscita) sono restituiti in JSON con lo status HTTP appropriato (403, 502).
php
$id = 84213; // stesso id della pratica-contratto usato per il PDF

$docx = Http::withToken(env('DOKICASA_TOKEN'))
    ->get("https://app.dokicasa.it/api/v3/contract/{$id}/doc")
    ->body();

file_put_contents("contratto_{$id}.docx", $docx);
bash
curl -L -X GET \
  "https://app.dokicasa.it/api/v3/contract/84213/doc" \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -o contratto_84213.docx

6. Webhook (eventi verso il tuo backend) ​

Configura una webhook_url sul tuo account partner: Dokicasa vi POSTa un evento JSON ad ogni passaggio rilevante del ciclo di vita della pratica e dei task. Lo stesso webhook serve sia le pratiche create via API sia quelle create via SDK.

Configurare la webhook_url (in autonomia) ​

Puoi leggere e aggiornare la webhook_url del tuo account direttamente via API, senza passare dal supporto.

MethodPathDescrizione
GET/api/v3/user/meDettagli dell'account loggato (include la webhook_url attuale)
PATCH/api/v3/user/me/webhookImposta / aggiorna la webhook_url
bash
curl https://app.dokicasa.it/api/v3/user/me \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -H "Accept: application/json"
bash
curl -X PATCH \
  https://app.dokicasa.it/api/v3/user/me/webhook \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"webhook_url":"https://tuo-server.com/webhooks/dokicasa"}'

Il body accetta webhook_url (URL valido, obbligatorio come chiave). La risposta è { "webhook_url": "..." }. Per disattivare i webhook passa null o stringa vuota:

bash
curl -X PATCH https://app.dokicasa.it/api/v3/user/me/webhook \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"webhook_url":null}'

IMPORTANT

La webhook_url è per-ambiente: staging (testing.dokicasa.it) e produzione (app.dokicasa.it) hanno database separati, quindi il valore che imposti vale solo per l'ambiente su cui chiami l'API. Configura il receiver di staging chiamando la PATCH su testing.dokicasa.it e quello di produzione su app.dokicasa.it: così gli eventi di test raggiungono il tuo endpoint di staging e solo quelli reali arrivano in produzione.

Eventi ​

event_typeQuando
CREATEDPratica creata
UPDATEDPratica aggiornata
CLOSEDPratica conclusa (chiusa dal backoffice)
NEW_TASKIl backoffice ti apre un task sulla pratica
UPDATE_TASKUn task viene aggiornato o chiuso (task.status = "DONE")
SERVICE_NOT_AVAILABLEServizio non disponibile per quella pratica

NOTE

Non esiste un evento dedicato alla chiusura del task: chiudere un task è uno UPDATE_TASK con task.status = "DONE".

Payload ​

Tutti gli eventi condividono lo stesso involucro. Esempio per CLOSED:

jsonc
{
  "event_type": "CLOSED",
  "status": "CLOSED",
  "external_id": "ordine_8842",      // il TUO id (null se non impostato)
  "name": "Consulenza ed assistenza legale",
  "service_name": "Consulenza ed assistenza legale",
  "user_service_id": 99076,
  "type": "SERVICE",                  // SERVICE | CONTRACT
  "attachments": [
    { "name": "documento.pdf", "mime_type": "application/pdf",
      "url": null, "content_base64": "JVBERi0xLjQK..." }
  ],
  "notes": null,
  "details": null,                    // valorizzato per lo step 2 canone concordato, vedi sotto
  "metadata": {},
  // presenti solo se la pratica è uno step di un bundle:
  "bundle_id": 146025,
  "bundle_name": "Canone Concordato Roma",
  "step": "2"
}

Gli allegati possono arrivare inline (content_base64) — es. il PDF del contratto — oppure come url (file pubblico): controlla quale dei due è valorizzato.

details è null per quasi tutte le pratiche. Fa eccezione lo step 2 dei bundle canone concordato, dove porta i valori del calcolo: vedi Valori del calcolo del canone.

Per gli eventi sui task (NEW_TASK / UPDATE_TASK) l'involucro include i campi task e notify:

jsonc
{
  "event_type": "UPDATE_TASK",
  "status": "UPDATE_TASK",
  "external_id": "ordine_8842",
  "user_service_id": 99078,
  "type": "SERVICE",
  "task": {
    "id": 41852,
    "type": "TASK",
    "status": "DONE",               // DONE = task chiuso
    "description": "Carica la carta d'identità",
    "assigned_to": 1
  },
  "notify": null                     // valorizzato su NEW_TASK, null sugli update
}

Consegna e idempotenza ​

  • Consegna asincrona (con retry), metodo POST, body JSON.
  • Il webhook in uscita non è firmato: proteggi l'endpoint ricevente con una whitelist degli IP di Dokicasa e/o una URL con path segreto.
  • Il payload non contiene un id di consegna dedicato: deduplica sulla coppia user_service_id + event_type (e task.id per gli eventi sui task) e rispondi sempre 2xx, anche ai duplicati.

7. Valori del calcolo del canone ​

Nei bundle canone concordato lo step 2 è il Calcolo Canone Concordato: si chiude da solo e produce due PDF, uno per l'immobile intero e uno per la porzione. Dentro quei PDF ci sono le fasce di canone applicabili — che ora ti arrivano anche come dati, senza doverle rileggere dal documento.

Li ottieni in due modi, con lo stesso identico contenuto.

Nel webhook di chiusura ​

Quando lo step 2 si chiude, il webhook CLOSED porta i PDF in attachments e i valori nel campo details:

jsonc
{
  "event_type": "CLOSED",
  "user_service_id": 119599,
  "bundle_id": 146025,
  "bundle_name": "Canone Concordato Roma",
  "step": "2",
  "attachments": [
    { "name": "Calcolo Canone - Intero Immobile", "mime_type": "application/pdf",
      "url": "https://app.dokicasa.it/storage/...", "content_base64": null },
    { "name": "Calcolo Canone - Stanze o Porzione", "mime_type": "application/pdf",
      "url": "https://app.dokicasa.it/storage/...", "content_base64": null }
  ],
  "details": {
    "immobile": {
      "intero_o_porzione": "Porzione di Immobile: Stanza 1 mq. 20",
      "is_porzione": true,
      "indirizzo": "PORZIONE: VIA ESEMPIO 22, 00100 ROMA (RM)"
    },
    "canoni": {
      "Contratto 3+2":        { "min": "308,00", "max": "730,00" },
      "Contratto 4+2":        { "min": "308,00", "max": "782,00" },
      "Contratto 5+2":        { "min": "308,00", "max": "789,00" },
      "Contratto 6+2":        { "min": "308,00", "max": "796,00" },
      "Contratto Studenti":   { "min": "308,00", "max": "730,00" },
      "Contratto Transitorio":{ "min": "308,00", "max": "804,00" }
    },
    "altri_valori": {
      "Ordinario Libero Irpef": "1.995,00",
      "Ordinario Condordato Irpef": "1.396,50"
    }
  }
}

A richiesta ​

http
GET /api/v3/user-bundle/{userBundle}/canone-values
Authorization: Bearer <api_token>

{userBundle} è il bundle_id che trovi nella pratica e nel webhook.

bash
curl -H "Authorization: Bearer $TOKEN" \
  https://app.dokicasa.it/api/v3/user-bundle/146025/canone-values
json
{
  "ready": true,
  "immobile": { "intero_o_porzione": "Intero Immobile", "is_porzione": false,
                "indirizzo": "VIA ESEMPIO 22, 00100 ROMA (RM)" },
  "canoni": { "Contratto 3+2": { "min": "308,00", "max": "730,00" } },
  "altri_valori": {}
}
json
{
  "ready": false,
  "message": "Il calcolo del canone non e' ancora disponibile per questa pratica."
}

I campi ​

CampoCosa contiene
immobile.intero_o_porzioneCome lo scrive il calcolo: "Intero Immobile" oppure la descrizione della porzione (es. "Porzione di Immobile: Stanza 1 mq. 20")
immobile.is_porzionetrue se il calcolo riguarda una porzione, false se l'immobile intero. Usa questo invece di confrontare la stringa
immobile.indirizzoL'indirizzo come compare sul report
canoniUna voce per tipo di contratto, ognuna con min e max
altri_valoriIl resto del report (analisi di convenienza fiscale: Irpef, cedolare, IMU, reddito stimato)

IMPORTANT

Su una porzione le fasce non si applicano così come sono. Quando is_porzione è true, i valori di canoni si riferiscono alla sola porzione calcolata: leggi intero_o_porzione per sapere di quale si tratta, e usa il PDF "Calcolo Canone - Stanze o Porzione" come documento di riferimento.

NOTE

I tipi di contratto presenti dipendono dall'accordo territoriale della città. Le coppie non calcolate, o calcolate a zero, non compaiono: se un tipo non c'è, per quella pratica non è applicabile.

TIP

Lo step 2 gira in asincrono: se chiami l'endpoint troppo presto ricevi ready: false. Se preferisci non fare polling, aspetta il webhook CLOSED — arriva quando il calcolo è finito e porta gli stessi valori in details.

8. Ambiente di staging (sandbox) ​

Per integrare e testare senza toccare la produzione è disponibile un ambiente di staging con un database isolato:

  • Base URL: https://testing.dokicasa.it
  • Stesse API, stesso Bearer api_token (rilasciato sullo staging).

Per provare i webhook in autonomia lo staging espone endpoint che simulano le azioni che normalmente fa il backoffice. Sono disponibili solo in staging (in produzione rispondono 403), richiedono il Bearer e che la pratica/task sia di tua proprietà.

MethodPathWebhook generato
POST/api/v3/sandbox/practices/{userService}/simulate-closeCLOSED (con un PDF di esempio in allegato)
POST/api/v3/sandbox/practices/{userService}/simulate-taskNEW_TASK (apre un task assegnato all'utente della pratica)
POST/api/v3/sandbox/tasks/{task}/simulate-updateUPDATE_TASK (aggiorna stato/assegnatario/descrizione)
POST/api/v3/sandbox/tasks/{task}/simulate-closeUPDATE_TASK con task.status = "DONE"

{userService} è l'id della pratica (user service); {task} è l'id del singolo task (tasks[].id da GET /practices/{practice}/tasks).

bash
curl -X POST \
  https://testing.dokicasa.it/api/v3/sandbox/practices/99076/simulate-close \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -H "Accept: application/json"
bash
curl -X POST \
  https://testing.dokicasa.it/api/v3/sandbox/practices/99078/simulate-task \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"description":"Carica la carta d'\''identità"}'
bash
curl -X POST \
  https://testing.dokicasa.it/api/v3/sandbox/tasks/41852/simulate-close \
  -H "Authorization: Bearer $DOKICASA_TOKEN" \
  -H "Accept: application/json"

Parametri opzionali (JSON body): simulate-task accetta description e status (default TO_DO); simulate-update accetta status, assigned_to, description e user_service_status. La risposta include webhook_sent (false se non hai una webhook_url configurata).

9. Duplica una pratica (cambio inquilino) ​

Quando su un immobile con una pratica già conclusa cambia l'inquilino, invece di ri-eseguire il Calcolo a mano puoi duplicare la pratica: tutti gli step duplicabili (immobile e calcolo) vengono clonati in una nuova pratica con un nuovo external_id, pronta per registrare il nuovo inquilino.

POST /api/v3/practices/{practice}/duplicate-partner
Authorization: Bearer <token>

{practice} è l'id pratica (bundle) che ottieni da GET /api/v3/user/{id}/services (campo practice_id).

Vengono clonati sempre tutti gli step duplicabili della pratica (immobile e calcolo): non si passa l'elenco degli step.

Body (tutti opzionali):

CampoTipoDefaultDescrizione
external_idstring—Nuovo external_id della pratica duplicata (per i PUT successivi)
bundle_namestring"<nome> (copia)"Nome della nuova pratica

Billing: per ogni step clonato vale la stessa logica del submit (§1): se lo step è gratuito per il tuo account non viene addebitato nulla, altrimenti prima si consuma un credito compatibile e in mancanza si scala il castelletto (wallet). Se su uno step a pagamento manca sia il credito sia la capienza wallet → 401 con il numero di step, e niente viene creato (operazione atomica: o si duplica tutto o niente).

bash
curl -X POST "https://app.dokicasa.it/api/v3/practices/45210/duplicate-partner" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"external_id":"pratica-2026-00987"}'

Risposta 201:

json
{
  "practice_id": 45999,
  "external_id": "pratica-2026-00987",
  "steps": [
    { "step": 1, "bundle_user_service_id": 88123, "name": "Creazione Documenti Canone Concordato del 15/07/2026" },
    { "step": 2, "bundle_user_service_id": 88124, "name": "Certificazione Contratto Locazione del 15/07/2026" }
  ]
}

Con i bundle_user_service_id restituiti aggiorni poi i dati del nuovo inquilino via PUT /api/v3/form/{bundle_user_service_id} (vedi §1).

Errori: 403 la pratica non è tua · 422 pratica non duplicabile o nessuno step duplicabile · 401 castelletto insufficiente (con lo step) · 403 piano API non abilitato per quel servizio.

Riferimento OpenAPI ​

La specifica completa (parametri, schemi, risposte) è qui sotto, generata dalla stessa spec pubblicata anche su api.dokicasa.it/api/documentation.