Non è un'API pubblica con una sua versione: sono gli stessi endpoint che le pagine usano per sé, aperti anche a chi non è un browser. Un 503 non è un guasto: è un prerequisito di configurazione non attivo, e il corpo dice quale.
Dove sono descritti
Tre rappresentazioni, tutte prodotte dallo stesso elenco (src/data/api-catalog.ts). Un catalogo, una specifica e una pagina che si contraddicono è il modo più rapido di non essere creduti da nessuno dei tre.
- Catalogo —
/.well-known/api-catalog, in application/linkset+json: una voce per endpoint con la sua anchor e le relazioni di servizio. È il formato che RFC 9727 definisce, scritto sopra il formato linkset di RFC 9264, ed è il target dell'header Link: rel="api-catalog" che ogni pagina di questo sito invia. - Specifica — openapi.json ↗: OpenAPI 3.1 generata dall'elenco, con i vincoli che il server applica davvero e gli stati che ogni handler può restituire. È il target
service-desc. - Stato — api/health ↗: cosa è attivo in questo momento e cosa no. È il target
status. - Questa pagina —
/api/ è il target service-doc: la descrizione per una persona.
Le note su prerequisiti e limiti non stanno nel catalogo: in un linkset gli attributi qualificano il target di un link, e i prerequisiti appartengono all'endpoint, che lì è l'anchor. Stanno nella specifica, operazione per operazione, e qui sotto.
Come si chiama uno di questi endpoint
Richiesta POST con Content-Type: application/json e corpo sotto 16 kB. L'Origin della richiesta deve essere questo sito: un browser su un altro dominio viene respinto con un 403 prima ancora di leggere il corpo. È la ragione per cui questi endpoint sono usati dalle pagine del sito e da un servizio che li chiama dal server, non da una pagina di un altro sito.
I controlli di struttura e di campo hanno un limite contato nel database: 5 richieste per client per ora, 150 complessive. L'invio di una richiesta: 8 per client ogni 10 minuti, 200 complessive. Oltre il limite la risposta è 429, non un silenzio.
Quando la verifica umana è attiva, il token del widget sta nel corpo (captchaToken) e il server lo verifica lui: un client che non lo manda riceve un 400 leggibile.
Gli endpoint
Invio di una richiesta (brief)
POST /api/leads — Registra una richiesta di progetto. È l’unico endpoint che scrive un dato personale.
Prerequisiti. LEAD_INTAKE_ENABLED=true; DATABASE_URL, oppure SUPABASE_URL con SUPABASE_SERVICE_ROLE_KEY; RATE_LIMIT_SECRET di almeno 32 caratteri; TURNSTILE_SECRET quando CAPTCHA_ENABLED=true. Senza, la risposta è 503 e dice cosa manca.
Limite. 8 richieste per client ogni 10 minuti, 200 complessive per ora.
Stati. 201: {saved:true, reference}: la referenza è la chiave idempotente della registrazione. · 400: Corpo non valido o verifica umana mancante. · 403: Origine della richiesta diversa da questo sito, o verifica umana non accettata. · 409: La stessa chiave idempotente con un corpo diverso. · 413: Corpo oltre 16 kB. · 415: Content-Type diverso da application/json. · 429: Limite di richieste raggiunto. · 503: Prerequisito non attivo: invio non abilitato, storage non configurato o verifica umana non configurata su questo server.
Cosa la risposta non misura.
- La referenza prova la registrazione. Non prova una notifica inviata, né una risposta umana.
- Una notificazione che non parte non annulla il 201: resta in attesa e viene ripresa da /api/retry-notifications.
- Non esiste un GET che legga le richieste: la lettura dei record sta nell’applicazione dell’operatore, non in questo sito.
Controllo strutturale di una pagina pubblica
POST /api/audit — Scarica l’HTML di un URL pubblico, lo legge senza eseguire gli script e aggiunge i segnali di robots.txt e sitemap del dominio.
Prerequisiti. URL_AUDIT_ENABLED=true; DATABASE_URL, oppure SUPABASE_URL con SUPABASE_SERVICE_ROLE_KEY; RATE_LIMIT_SECRET di almeno 32 caratteri. Senza, la risposta è 503 e dice cosa manca.
Limite. 5 richieste per client per ora, 150 complessive per ora. Il limite è nel database: senza storage il controllo non parte.
Stati. 200: Segnali osservati, cosa non è misurato e cosa resterebbe da verificare. · 400: URL non valido, non pubblico o con indirizzo non consentito. · 403: Origine della richiesta diversa da questo sito. · 422: La pagina non è leggibile: non HTML, oltre 1 MB, troppi redirect. · 429: Limite di richieste raggiunto. · 502: Impossibile leggere la pagina. · 503: Controllo non attivo su questo server o limite di richieste senza storage. · 504: Il sito ha impiegato troppo tempo a rispondere o a risolversi.
Cosa la risposta non misura.
- I campi
notMeasured e coverage fanno parte della risposta: indicizzazione effettiva, ranking, citazioni AI e accessibilità manuale richiedono fonti dedicate. - Un noindex è un segnale tecnico, non lo stato nell’indice.
- Nessun punteggio: un controllo strutturale non è un voto SEO o GEO.
Dati di campo Core Web Vitals (CrUX via PageSpeed Insights)
POST /api/audit-vitals — Chiede al dataset pubblico di Google i valori P75 osservati su un URL. Strategia mobile.
Prerequisiti. URL_AUDIT_ENABLED=true; DATABASE_URL, oppure SUPABASE_URL con SUPABASE_SERVICE_ROLE_KEY; RATE_LIMIT_SECRET di almeno 32 caratteri. Senza, la risposta è 503 e dice cosa manca.
Limite. 5 richieste per client per ora, 150 complessive per ora. Il limite è nel database: senza storage il controllo non parte.
Stati. 200: status: measured | no_field_data | unavailable, con la fonte e il perimetro. measured può convivere con no_field_data per un solo URL. · 400: URL non valido o non pubblico. · 403: Origine della richiesta diversa da questo sito. · 429: Limite di richieste raggiunto. · 503: Controllo non attivo su questo server.
Cosa la risposta non misura.
- Nessun valore è stimato quando il servizio pubblico non ne ha uno:
no_field_data non significa prestazioni buone. - Sono dati aggregati di visite reali (P75), non la misura del tuo dispositivo.
- Il campo CrUX e la misura di laboratorio di /api/audit-browser non sono confrontabili.
Audit nel browser con axe
POST /api/audit-browser — Apre la pagina in un browser headless sul server, blocca le risorse esterne e misura LCP/CLS di laboratorio e violazioni axe.
Prerequisiti. URL_AUDIT_ENABLED=true; DATABASE_URL, oppure SUPABASE_URL con SUPABASE_SERVICE_ROLE_KEY; RATE_LIMIT_SECRET di almeno 32 caratteri; un browser che il server può eseguire (AUDIT_BROWSER_EXECUTABLE); TURNSTILE_SECRET quando CAPTCHA_ENABLED=true. Senza, la risposta è 503 e dice cosa manca.
Limite. 5 richieste per client per ora, 150 complessive per ora. Il limite è nel database: senza storage il controllo non parte.
Stati. 200: Misure di laboratorio, violazioni axe con quelle non valutabili, risorse bloccate, perimetro dichiarato. · 400: URL non valido o verifica umana mancante. · 403: Origine non autorizzata o verifica umana non accettata. · 429: Limite di richieste raggiunto. · 503: Controllo non attivo, nessun browser disponibile sul server (nomina AUDIT_BROWSER_EXECUTABLE), o due audit già in corso.
Cosa la risposta non misura.
- Il browser è quello che il server può davvero eseguire: Chromium bundled su Linux, o un eseguibile nominato altrove. Un browser assente viene detto prima di consumare un limite.
- Le misure di questo browser non sono confrontabili con un laboratorio né con i dati di campo.
- Le risorse esterne della pagina sono bloccate: l’audit vede la pagina come la vede il server, non come la vede una rete reale.
Scansione delle richieste oltre la finestra di conservazione (operatore)
POST /api/prune-leads — Applica subito la finestra di conservazione dei record e risponde di quanti ne ha rimossi.
Prerequisiti. API_ADMIN_SECRET di almeno 32 caratteri; DATABASE_URL, oppure SUPABASE_URL con SUPABASE_SERVICE_ROLE_KEY. Senza, la risposta è 503 e dice cosa manca.
Accesso. Authorization: Bearer con il secret dell'operatore. Non è distribuito a chi usa il sito.
Stati. 200: {windowDays, removed} · 403: Secret dell’operatore mancante, corto o diverso. · 503: Storage non configurato.
Cosa la risposta non misura.
- Lo stesso lavoro il servizio lo fa da sé una volta al giorno: questo endpoint serve a leggerne il numero senza aspettare.
- La finestra è quella dichiarata nell’informativa; un valore di ambiente assurdo non diventa una cancellazione improvvisa.
Recupero delle notificazioni non partite (operatore)
POST /api/retry-notifications — Riprova l’invio delle notifiche di richieste salvate che non sono partite.
Prerequisiti. API_ADMIN_SECRET di almeno 32 caratteri; DATABASE_URL, oppure SUPABASE_URL con SUPABASE_SERVICE_ROLE_KEY; un canale di notificazione: RESEND_API_KEY con LEAD_NOTIFY_EMAIL e EMAIL_FROM, oppure CRM_WEBHOOK_URL con CRM_WEBHOOK_SECRET. Senza, la risposta è 503 e dice cosa manca.
Accesso. Authorization: Bearer con il secret dell'operatore. Non è distribuito a chi usa il sito.
Stati. 200: {processed} · 403: Secret dell’operatore mancante, corto o diverso. · 503: Storage non configurato.
Cosa la risposta non misura.
- Riprende solo invii degli ultimi 23 ore: le chiavi di idempotenza di Resend scadono dopo 24 ore e una ripresa oltre sarebbe un secondo invio.
- Elabora un record per chiamata, nell’ordine di creazione.
- Nessun canale configurato risponde {processed:0}: non è un errore, è che non c’è un canale da riprovare.
Stato del servizio
GET /api/health — Risponde dello stato del servizio e dei prerequisiti che lo abilitano. Nessuna autenticazione, nessuna scrittura.
0Stati. 200: Il servizio ha risposto, con l’elenco dei prerequisiti e il loro stato di configurazione.
Cosa la risposta non misura.
- Risponde 200 anche quando un prerequisito è spento: è la scelta dichiarata, perché un catalogo che punta a questo endpoint deve poter leggere una descrizione, non solo un codice.
- Lo stato dei prerequisiti è configurazione letta dal processo, non una prova di connessione al database o ai servizi esterni.
- Non è un monitor esterno e non misura disponibilità nel tempo.
Versione
Il numero che si legge nella specifica (1.1.0) è la versione del sito, non un contratto di versione di API. Un endpoint può cambiare senza che quel numero lo dichiari: la specifica è generata dal codice, quindi descrive il servizio che c'è adesso, non una promessa su una release.
Intorno a questi endpoint
Come sono trattati i dati che attraversano l'invio di una richiesta è descritto nella privacy ↗; i limiti di uso degli strumenti nelle note d'uso ↗. La preferenza dichiarata ai crawler — citazione concessa, addestramento negato — è nel robots.txt dello stesso dominio.
Il titolare è SmarthubTecnologies · [email protected] ↗.