Custodo per sviluppatori

Ogni funzione del prodotto è chiamabile. Sono le stesse che usa l’assistente AI.

Non esiste una seconda API con regole diverse: quello che una persona può fare dall’applicazione, un programma lo può fare con una chiave — e un assistente collegato via MCP vede esattamente lo stesso elenco, con gli stessi limiti.

136
operazioni, una per ogni cosa che il prodotto sa fare
1
endpoint: POST /api/v1/tools/<nome>
MCP
lo stesso catalogo, sul protocollo che gli assistenti parlano
0
chiamate per leggere gli schemi: l’OpenAPI è pubblico

1Una chiave

Un amministratore la crea in Impostazioni → Integrazioni. Compare una volta sola: conserviamo solo la sua impronta, quindi non possiamo mostrarla di nuovo nemmeno noi.

cst_live_xxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

Alla creazione si sceglie anche quanto può fare, e i valori predefiniti sono i più stretti:

  • Permessi — spuntati uno per uno. Una chiave nuova nasce in sola lettura.
  • Moduli raggiungibili — vuoto significa tutti. Restringerli accorcia l’elenco di strumenti che l’integrazione vede.
  • Azioni distruttive — spenta. Senza quella spunta la chiave non vede nemmeno gli strumenti che eliminano.
  • Chiamate al minuto e tetto giornaliero, indirizzi ammessi (singoli o blocchi CIDR), scadenza facoltativa.

2Un endpoint

Ogni operazione si chiama nello stesso modo: POST /api/v1/tools/<nome>, con gli argomenti nel corpo.

Chiamare uno strumento
curl -X POST https://api-custodo.xdevapp.it/api/v1/tools/documents_search \
  -H "Authorization: Bearer cst_live_…" \
  -H "Content-Type: application/json" \
  -d '{"query":"fattura acme","limit":10}'

Per sapere cosa quella chiave può chiamare:

Il catalogo, filtrato per la credenziale
curl https://api-custodo.xdevapp.it/api/v1/tools \
  -H "Authorization: Bearer cst_live_…"

Il riferimento completo — un’operazione per strumento, con lo schema esatto degli argomenti — è pubblicato in OpenAPI 3.1 e non richiede autenticazione, perché va letto prima di avere una chiave:

Gli schemi, senza chiave
GET https://api-custodo.xdevapp.it/api/v1/openapi.json

La stessa cosa da leggere, generata da quel documento — oppure scaricala come collection già pronta per Postman o Insomnia.

3Come leggere la risposta

Ogni chiamata torna con un codice HTTP e, quando è 200, con un corpo che dice se lo strumento ha fatto o ha rifiutato. Le due cose sono diverse e vanno gestite diversamente.

EsitoSignificaCosa fare
200 is_error: falseFatto. La risposta è in data.result.
200 is_error: trueLo strumento ha girato e ha rifiutato: documento sotto blocco legale, offerta già chiusa, campo non valido.Correggi gli argomenti. Ritentare identico non serve.
401La chiave non è valida, è stata revocata o è scaduta.Controlla la chiave.
403La chiamata non è avvenuta: modulo non attivo, permesso mancante, livello insufficiente, oppure strumento distruttivo e chiave senza quel consenso. Il campo error.code dice quale.Serve un amministratore.
404Quel nome di strumento non esiste in questa versione.Controlla l’elenco.
429Minuto o giornata esauriti per quella chiave.Attendi Retry-After secondi.

I codici in error.code sono stabili: fai i controlli su quelli, non sul messaggio.

4Oppure MCP, se dall’altra parte c’è un assistente

Le stesse operazioni sono raggiungibili sul protocollo MCP — quello che Claude e ChatGPT già parlano — allo stesso indirizzo per tutti:

POST https://api-custodo.xdevapp.it/mcp

Funziona con la stessa chiave API come Bearer, oppure con OAuth 2.1 (registrazione dinamica del client, PKCE obbligatorio, schermata di consenso in cui la persona spunta cosa concedere). I metadati sono dove il protocollo li cerca: /.well-known/oauth-protected-resource/mcp.

Un assistente non vede più di chi lo ha collegato Il catalogo che riceve è già filtrato: moduli dell’organizzazione, permessi della persona, livello commerciale, e i limiti della credenziale. Uno strumento che verrebbe rifiutato non gli viene nemmeno elencato.

5I webhook, per sapere quando succede qualcosa

Il contrario delle chiamate: quando in Custodo succede qualcosa, chiamiamo noi il tuo sistema. Si configurano in Integrazioni → Webhook: un indirizzo https e gli eventi che vuoi (nessuna spunta = tutti).

Ogni consegna porta la firma:

POST /il-tuo-hook HTTP/1.1
X-Custodo-Event: documents.document.ready
X-Custodo-Delivery: 6f1e…
X-Custodo-Signature: t=1788280000,v1=<hmac-sha256 esadecimale>
Content-Type: application/json

{"id":"6f1e…","event":"documents.document.ready","tenant_id":"…","created_at":"…","data":{…}}

Verificarla

// Node
import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(secret, rawBody, header) {
  const parts = new Map(header.split(',').map((p) => p.split('=')));
  const t = Number(parts.get('t'));
  // Rifiuta quello che ha più di cinque minuti: il momento è dentro la firma
  // proprio perché una consegna intercettata non sia riutilizzabile domani.
  if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.get('v1') ?? '');
  return a.length === b.length && timingSafeEqual(a, b);
}

Firma il corpo grezzo, prima di qualunque parsing: un JSON riserializzato non produce gli stessi byte.

Se il tuo server non risponde

Riproviamo con attese crescenti — 3 secondi, 9, 27, 81, 243 — e poi ci fermiamo. Ogni tentativo lascia una riga nel registro delle consegne, con il codice di risposta e le prime righe di quello che è tornato indietro, e da lì puoi riprovare a mano.

Consegniamo solo su indirizzi pubblici Solo https, e mai verso reti private o indirizzi di servizio interni. Il controllo si ripete a ogni consegna e non solo quando salvi l’indirizzo: un nome che stamattina risolveva a un indirizzo pubblico può risolvere altrove nel pomeriggio.

6Scaricare la collection

Tre file, tutti generati dallo stesso documento OpenAPI che il server pubblica — con le operazioni disponibili al momento della generazione. Imposta apiKey con la tua chiave e sostituisci gli argomenti di esempio con i valori del tuo ambiente prima di inviare una richiesta.

La copia scaricata è ferma al deploy Se stai generando un client in una pipeline, puntala all’originale — https://api-custodo.xdevapp.it/api/v1/openapi.json, pubblico e senza chiave — e non a questi file: la copia qui esiste perché il browser possa salvarla e perché la CORS dell’API non nomina questo sito.

7I limiti, detti prima

  • Una chiave non supera la persona per cui è stata creata: se domani le togli un permesso, la chiave lo perde alla chiamata successiva, senza rotazioni.
  • Firmare non è chiamabile. Firmare è un atto di volontà di una persona fisica; non esiste un’operazione che firmi al posto suo.
  • Creare chiavi non è chiamabile da una chiave: una credenziale che può allargarsi da sola è esattamente la cosa che questo modello di permessi esiste per impedire.
  • Un interruttore generale per fermare tutto, per organizzazione, e il registro di ogni chiamata — con esito, durata e credenziale — che nessuno può modificare.

Parliamone

Se stai valutando un’integrazione e vuoi capire se regge il tuo caso, scrivici da questa pagina: ti risponde chi l’ha costruito.