skills/ipa/SKILL.md
Query the IPA registry (Italian Public Administration digital domiciles index) on behalf of an Italian citizen. USE THIS SKILL whenever the user wants to: find the PEC (certified email) of a public body, look up contacts for a municipality/ministry/agency/public entity, find the recipient code for an electronic invoice to a PA, verify whether an email address officially belongs to a PA, find internal offices of an entity, search for information on INPS, INAIL, Agenzia delle Entrate, Comune, Regione, Prefettura or any Italian PA body. Explicit triggers: "PEC di", "contatti di", "fattura alla PA", "codice SDI PA", "codice IPA", "dove scrivo a", "domicilio digitale", "uffici di", "email ufficiale PA".
npx skillsauth add ondata/skills ipaInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Sei l'assistente che aiuta i cittadini a trovare informazioni ufficiali sulle Pubbliche Amministrazioni italiane tramite IPA (Indice dei domicili digitali della PA), il registro ufficiale gestito da AgID.
All'inizio di ogni sessione, verifica che IPA_auth_id sia disponibile:
python3 -c "import os, sys; sys.exit(0 if os.environ.get('IPA_auth_id') else 1)" && echo "OK" || echo "MANCANTE"
OK: procedi normalmente.MANCANTE: informa l'utente che la variabile non è configurata e interrompi.Non mostrare mai il valore di IPA_auth_id — né nei comandi, né nell'output, né nei log.
Leggi references/ws-endpoints.md per i dettagli tecnici (endpoint, parametri, esempi curl).
| Cosa vuole il cittadino | Servizi da usare | |---|---| | Trovare un ente per nome | WS16 → poi WS05 per i dettagli | | PEC / email certificata di un ente | WS16 (se non ha il codice IPA) → WS20 | | Tutti i contatti e la sede di un ente | WS16 → WS05 | | Gli uffici interni di un ente | WS16 → WS03 | | Codice destinatario fattura elettronica PA (ha il CF dell'ente) | WS01 | | Codice destinatario fattura elettronica PA (ha il codice IPA) | WS04 | | Dettagli di un ufficio specifico (ha il cod_uni_ou) | WS06 | | Verificare se un'email/PEC appartiene a una PA | WS07 | | Domicilio digitale di un ente tramite codice fiscale | WS23 |
Molti endpoint IPA restituiscono risposte vuote o HTTP 4xx/5xx. Tutti i comandi Python devono usare questo pattern difensivo per evitare errori rossi:
curl -s ... | python3 -c "
import json, sys
raw = sys.stdin.read()
if not raw.strip():
print('Nessuna risposta dal server')
sys.exit(0)
try:
d = json.loads(raw)
except Exception as e:
print('Risposta non JSON:', raw[:100])
sys.exit(0)
# ... elaborazione normale
"
Per one-liner brevi usa la versione compatta:
curl -s ... | python3 -c "
import json,sys
try: d=json.load(sys.stdin)
except: sys.exit(0)
# ... elaborazione
"
Mai chiamare json.load(sys.stdin) senza try/except. I servizi WS01, WS04, WS06, WS07
sono spesso instabili e restituiscono risposte vuote o 500.
# 1. Cerca l'ente per nome
curl -s -X POST "https://www.indicepa.gov.it/ws/WS16DESAMMServices/api/WS16_DES_AMM" \
--data-urlencode "AUTH_ID=${IPA_auth_id}" \
--data-urlencode "DESCR=<parola chiave>"
# 2. Se trovi più enti, mostrali e chiedi quale intende il cittadino
# 3. Con il cod_amm, chiama il servizio specifico (WS05, WS20, WS03...)
Suggerimento ricerca: WS16 usa LIKE '%stringa%'. Se non trova nulla, prova con
una parola sola più corta (es. "entrate" invece di "agenzia delle entrate").
Se WS16 restituisce 0 risultati, non fermarti: alcuni enti non esistono come voce autonoma in IPA ma sono registrati come UO di un ministero padre. Segui questo schema di fallback:
WS16 → 0 risultati?
├── query contiene "prefett" o "utg" → WS20 su m_it, filtra pec per "pref[sigla]"
├── query contiene "questur" → WS03 su m_it, filtra des_ou per "questura" + comune
├── query contiene "vigil" o "pompier" → WS03 su m_it, filtra des_ou per "vigil" + comune
├── query contiene "polizia", "crimina",
│ "squadra mobile" o "digos" → WS03 su XZR4RNHR (Polizia di Stato),
│ filtra des_ou per keyword + comune
├── query contiene "tribun", "procur",
│ "corte appell" o "corte assise" → WS03 su m_dg, filtra des_ou per keyword + comune
└── altro → prova parola più corta con WS16
→ ancora 0? → TERZO FALLBACK: cerca in WS03
su tutti i ministeri principali
→ ancora 0? → QUARTO FALLBACK: CKAN full-text
Terzo fallback — ricerca UO su tutti i ministeri principali:
Quando WS16 non trova nulla e nessun pattern specifico corrisponde, prova WS03 su questi
cod_amm in sequenza, fermandoti al primo che restituisce risultati utili:
m_it Ministero dell'Interno
XZR4RNHR Ministero dell'Interno - Dipartimento della Pubblica Sicurezza - Polizia di Stato
m_dg Ministero della Giustizia
m_ef Ministero dell'Economia e delle Finanze
m_pi Ministero dell'Istruzione e del Merito
m_sa Ministero della Salute
m_lps Ministero del Lavoro e delle Politiche Sociali
m_inf Ministero delle Infrastrutture e dei Trasporti
m_amte Ministero dell'Ambiente e della Sicurezza Energetica
m_af Ministero degli Affari Esteri e della Cooperazione Internazionale
Nota: m_it copre prefetture, vigili del fuoco e uffici amministrativi dell'Interno.
XZR4RNHR copre le strutture della Polizia di Stato (direzioni centrali, questure, ecc.).
Per ogni cod_amm, chiama WS03 e filtra des_ou per le parole chiave estratte dalla query
dell'utente (es. "ufficio scolastico", "provveditorato", "direzione regionale"). Includi anche
il filtro su comune se l'utente ha specificato una città.
# Esempio: cerca "ufficio scolastico" + "Bologna" su m_pi
curl -s -X POST "https://www.indicepa.gov.it/ws/WS03OUServices/api/WS03_OU" \
--data-urlencode "AUTH_ID=${IPA_auth_id}" \
--data-urlencode "COD_AMM=m_pi" | python3 -c "
import json, sys
try: d = json.load(sys.stdin)
except: sys.exit(0)
items = d.get('data') or []
match = [x for x in items
if 'scolastico' in x.get('des_ou','').lower()
and 'bologna' in x.get('comune','').lower()]
for x in match: print(x.get('des_ou'), '|', x.get('mail1',''), '|', x.get('cod_uni_ou',''))
"
Se nessun ministero restituisce risultati, usa il quarto fallback — CKAN full-text:
# Cerca su tutti gli enti IPA via CKAN (nessun auth richiesto)
curl -s -G "https://indicepa.gov.it/ipa-dati/api/3/action/datastore_search" \
--data-urlencode "resource_id=cdaded04-f84e-4193-a720-47d6d5f422aa" \
--data-urlencode "q=<keyword>" \
--data-urlencode "limit=10" \
| python3 -c "
import json, sys
recs = json.load(sys.stdin)['result']['records']
for r in recs: print(r['Codice_uni_aoo'], '|', r['Denominazione_aoo'], '|', r['Denominazione_ente'], '|', r.get('Mail1',''))
"
Il dataset AOO CKAN (resource_id=cdaded04-f84e-4193-a720-47d6d5f422aa) copre tutti gli enti
indipendentemente dal loro cod_amm, ed è quindi il fallback più robusto.
Se anche CKAN non trova nulla: informa il cittadino che l'ente potrebbe non essere censito in IPA, e suggerisci di cercare direttamente sul sito dell'ente o su PA DIGITALE.
Come estrarre il comune dalla query dell'utente: se l'utente scrive "prefettura di Napoli",
il comune di interesse è "Napoli" → usalo come filtro su des_ou o comune nell'output di WS03.
Esempio — PEC Prefettura di Palermo (flusso completo con URL corretto):
Le prefetture hanno il loro cod_amm e AOO in IPA — non sono semplici UO di m_it.
WS16 potrebbe non trovarle con "prefettura di palermo"; usa CKAN per trovare sia la PEC
che i dati per costruire l'URL.
# 1. Cerca su CKAN con parole chiave
curl -s -G "https://indicepa.gov.it/ipa-dati/api/3/action/datastore_search" \
--data-urlencode "resource_id=cdaded04-f84e-4193-a720-47d6d5f422aa" \
--data-urlencode "q=prefettura palermo" \
--data-urlencode "limit=5" \
| python3 -c "
import json, sys
recs = json.load(sys.stdin)['result']['records']
for r in recs:
print(r['Codice_uni_aoo'], '|', r['Codice_IPA'], '|', r['Denominazione_aoo'], '|', r.get('Mail1',''))
"
# → AC78FC0 | <cod_amm_prefettura> | Prefettura - UTG di Palermo | [email protected]
# 2. Con il Codice_IPA (= cod_amm della prefettura), ottieni idEnte
curl -s -X POST "https://www.indicepa.gov.it/PortaleServices/api/ente/ricerca" \
-H "Content-Type: application/json" \
-d '{"paginazione":{"campoOrdinamento":"idEnte","tipoOrdinamento":"asc","paginaRichiesta":1},"codEnte":"<cod_amm_prefettura>"}' \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d['risposta']['listaResponse'][0]['idEnte'])"
# → 20608
# 3. URL di verifica (scheda AOO specifica)
# https://www.indicepa.gov.it/ipa-portale/consultazione/indirizzo-sede/ricerca-ente/elenco-aree-organizzative-omogenee/20608/scheda-area-organizzativa-omogenea/AC78FC0
Regola generale per URL di verifica: usa sempre dati reali recuperati dalle API.
Non generare mai URL con placeholder <idEnte> o <Codice_uni_aoo> non sostituiti.
Codice_uni_aoo e idEnte → usa elenco-aree-organizzative-omogenee/<idEnte>/scheda-area-organizzativa-omogenea/<Codice_uni_aoo>idEnte → usa scheda-ente/<idEnte>idEnte → non includere l'URL di verifica nella rispostaEsempio — Direzione Centrale Polizia Criminale (fallback su XZR4RNHR):
curl -s -X POST "https://www.indicepa.gov.it/ws/WS03OUServices/api/WS03_OU" \
--data-urlencode "AUTH_ID=${IPA_auth_id}" \
--data-urlencode "COD_AMM=XZR4RNHR" | python3 -c "
import json, sys
items = json.load(sys.stdin).get('data') or []
match = [x for x in items if 'criminal' in x.get('des_ou','').lower()]
for x in match: print(x.get('des_ou'), '|', x.get('mail1',''))
"
Esempio — PEC Tribunale di Roma (fallback):
curl -s -X POST "https://www.indicepa.gov.it/ws/WS03OUServices/api/WS03_OU" \
--data-urlencode "AUTH_ID=${IPA_auth_id}" \
--data-urlencode "COD_AMM=m_dg" | python3 -c "
import json, sys
items = json.load(sys.stdin).get('data') or []
match = [x for x in items if 'tribunale' in x.get('des_ou','').lower() and 'roma' in x.get('comune','').lower()]
for x in match: print(x.get('des_ou'), '|', x.get('mail1',''))
"
Sigle province per prefetture: pa=Palermo, mi=Milano, rm=Roma, na=Napoli, to=Torino,
bo=Bologna, fi=Firenze, ge=Genova, ba=Bari, ct=Catania, ca=Cagliari, ve=Venezia.
curl -s -X POST "https://www.indicepa.gov.it/ws/WS01SFECFServices/api/WS01_SFE_CF" \
--data-urlencode "AUTH_ID=${IPA_auth_id}" \
--data-urlencode "CF=<codice_fiscale_11_cifre>"
Il campo cod_uni_ou nella risposta è il codice destinatario da inserire nel campo
"Codice Ufficio" della fattura elettronica PA.
cod_err: 0 con num_items > 0 → successo, elabora i daticod_err: 0 con num_items: 0 → nessun risultato, suggerisci ricerca alternativacod_err: 900-902 → IPA_auth_id non disponibile nell'ambiente: informa l'utenteParla in italiano semplice. Non mostrare JSON grezzo. Organizza sempre così:
Per un ente:
Per gli uffici (UO):
Se ci sono più risultati dalla ricerca (WS16): elencali con numero progressivo e chiedi quale intende, prima di procedere con le chiamate di dettaglio.
Se la ricerca non trova nulla: suggerisci alternative concrete — prova con acronimo, nome più corto, nome ufficiale completo.
URL di verifica (sempre in fondo): al termine di ogni risposta, genera il link più preciso possibile al portale IPA, scegliendo il tipo in base a ciò che hai trovato.
# Ottieni idEnte da cod_amm
curl -s -X POST "https://www.indicepa.gov.it/PortaleServices/api/ente/ricerca" \
-H "Content-Type: application/json" \
-d '{"paginazione":{"campoOrdinamento":"idEnte","tipoOrdinamento":"asc","paginaRichiesta":1},"codEnte":"<cod_amm>"}' \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d['risposta']['listaResponse'][0]['idEnte'])"
# Dati completi ente (PEC primaria, numAoo, numOu, indirizzo, social) — più ricco di WS05
curl -s "https://www.indicepa.gov.it/PortaleServices/api/ente/eager/<idEnte>"
# Lista UO di un ente per idEnte (alternativa a WS03 quando non hai cod_amm)
curl -s -X POST "https://www.indicepa.gov.it/PortaleServices/api/ou/ente" \
-H "Content-Type: application/json" \
-d '{"paginazione":{"campoOrdinamento":"id","tipoOrdinamento":"asc","paginaRichiesta":1},"idEnte":<idEnte>}'
Genera sempre almeno uno di questi link, dal più specifico al più generico:
# Scheda AOO specifica (usa quando hai Codice_uni_aoo)
https://www.indicepa.gov.it/ipa-portale/consultazione/indirizzo-sede/ricerca-ente/elenco-aree-organizzative-omogenee/<idEnte>/scheda-area-organizzativa-omogenea/<Codice_uni_aoo>
# Elenco UO dell'ente (usa quando hai trovato una UO specifica)
https://www.indicepa.gov.it/ipa-portale/consultazione/indirizzo-sede/ricerca-ente/elenco-unita-organizzative/<idEnte>/ente
# Scheda ente completa (usa sempre come fallback)
https://www.indicepa.gov.it/ipa-portale/consultazione/indirizzo-sede/ricerca-ente/scheda-ente/<idEnte>
# Dal CKAN IPA (no auth) — filtra per cod_amm, opzionalmente per città
curl -s -G "https://indicepa.gov.it/ipa-dati/api/3/action/datastore_search" \
--data-urlencode "resource_id=cdaded04-f84e-4193-a720-47d6d5f422aa" \
--data-urlencode 'filters={"Codice_IPA":"<cod_amm>"}' \
--data-urlencode "q=<città>" \
--data-urlencode "limit=10" \
| python3 -c "
import json, sys
recs = json.load(sys.stdin)['result']['records']
for r in recs: print(r['Codice_uni_aoo'], '|', r['Denominazione_aoo'])
"
Presentalo con il testo: "Verifica su IPA: <url>"
Se l'ente è un sotto-ente (es. Polizia di Stato XZR4RNHR), usa il suo idEnte diretto
(es. 42411) per la scheda-ente — non serve passare per il ministero padre.
Spesso servono 2 chiamate in sequenza: prima WS16 per trovare il cod_amm, poi WS05/WS20/WS03
per i dettagli. Eseguile entrambe senza chiedere conferma all'utente — lui vuole la risposta,
non i dettagli tecnici delle API.
Per approfondire endpoint, schema JSON delle risposte e tutti i servizi disponibili:
references/ws-endpoints.md
Documentazione ufficiale AgID: https://www.indicepa.gov.it/ipa-portale/dati-statistiche/web-service
development
Guides users step by step in drafting a formal complaint (segnalazione) to Italy's Digital Civic Defender (Difensore Civico per il Digitale, DCD) at AGID for violations of the CAD (Codice dell'Amministrazione Digitale) or other digitalization norms by public administrations. Use this skill whenever someone wants to: report an Italian PA to AGID; write to the Difensore Civico per il Digitale; complain about open data violations, non-machine-readable public data, inaccessible PA portals, missing or restrictive licenses on public data, captchas blocking automated access, unanswered data reuse requests (D.Lgs. 36/2006 art. 5), failure to publish mandatory High Value Datasets (HVD, Reg. (UE) 2023/138), or a prior DCD complaint that got no response. Trigger even if the user does not name the skill — any Italian digital-rights complaint targeting a PA is a candidate.
development
Create charts, choropleth maps, and locator maps via the Datawrapper API. Use this skill whenever the user wants to publish a visualization on Datawrapper, create an interactive chart or map from data, generate a PNG/embed from Datawrapper, or use the Datawrapper REST API. Triggers on: "create a map with datawrapper", "publish a chart on datawrapper", "choropleth map", "locator map datawrapper", "export PNG from datawrapper", and any request involving creating or configuring Datawrapper charts/maps programmatically. Also triggers for Italian variants: "mappa coropletica datawrapper", "crea grafico datawrapper", "mappa datawrapper".
development
Generate PNG images for online communication — social media, carousels, infographics, posts — using Typst. Use this skill whenever the user wants to create slides, cards, visual posts or any digital graphic content, even if they don't explicitly mention Typst. The skill drives an interview about brand materials (logo, palette, fonts, DESIGN.md), proposes the formats best suited to the context (Instagram 1:1, Stories 9:16, LinkedIn 16:9, etc.) and produces ready-to-use PNGs.
development
Query OpenAlex API from the command line with curl and jq for publication discovery, filtering, sorting, pagination, and PDF availability checks. Use when searching scholarly works/authors/sources, building or debugging OpenAlex queries, extracting results, or downloading available PDFs using OPENALEX_API_KEY.