Sviluppatori

Riferimento API

v1

API HTTP in sola lettura per esportare tutte le segnalazioni di un progetto: commenti, screenshot, session replay, file, contesto browser e conversazione.

I nomi dei campi, gli endpoint e i valori di stato restano quelli dell’API (es. aperto). Questa pagina è pubblica; le chiavi API e i token del player no.

Ultimo aggiornamento 3 ottobre 2026

Panoramica

L’API v1 di FixNoty è un’interfaccia HTTP versionata, in sola lettura, per esportare tutte le segnalazioni di un singolo progetto. È pensata per script, CI, tool interni e automazioni successive — anche un agent AI che ha bisogno dello stesso contesto di risoluzione che vede una persona in dashboard.

L’API non cambia stato, non scrive commenti, non elimina oggetti e non restituisce path di storage grezzi né materiale segreto. I media arrivano come URL firmati a tempo. Il session replay arriva come chunk rrweb oppure come link firmato di 24 ore allo stesso player interattivo della tab Replay in dashboard — non come un nuovo MP4.

  • Protocollo: HTTPS JSON. Non si usano body di richiesta (tutti i metodi sono GET).
  • Prefisso: /api/v1. I prefissi di lingua come /it o /en valgono solo per il sito; non fanno parte del path API.
  • Scope: un progetto per chiave. Un id di un altro progetto risponde 404 con code not_found.
  • Compatibilità: gli enum di stato, priorità e categoria sono le stringhe italiane del database (es. aperto). Nei client non vanno tradotti.

URL di base e versioning

Host di produzione: https://www.fixnoty.com. In sviluppo usa l’origine del tuo deploy. Concatena il prefisso senza slash finale:

http

https://www.fixnoty.com/api/v1/feedback

I breaking change escono sotto un nuovo prefisso (/api/v2). In v1 possono comparire campi JSON aggiuntivi senza cambio di versione; i client devono ignorare le chiavi sconosciute. In v1 non c’è un documento OpenAPI.

Autenticazione

Autenticati con una chiave API di progetto. La chiave vale per un solo progetto e dà accesso in sola lettura. Autentica il progetto, non un utente: chiunque abbia il secret (script, collaboratore, integrazione) può leggere tutte le segnalazioni di quel progetto.

Invia il secret con esattamente uno di questi header:

http

Authorization: Bearer fn_live_…
X-Api-Key: fn_live_…

Se sono presenti entrambi, vince Authorization: Bearer. Chiavi in query string, cookie e body vengono ignorate.

CampoTipoDescrizione
Formatstringfn_live_ + 64 caratteri esadecimali minuscoli in produzione, oppure fn_test_ + 64 hex in sviluppo. Regex: ^fn_(live|test)_[a-f0-9]{64}$.
Storagesha256In database si salva solo l’hash SHA-256 del secret. Il valore in chiaro si vede una sola volta in Impostazioni progetto → API e non è recuperabile.
PrefixstringIn dashboard compare fn_live_ / fn_test_ più i primi 8 caratteri hex, così riconosci la chiave senza il secret.
LifecycleownerLe chiavi API sono incluse nel piano Agency (e negli account demo). Prova gratuita e Pro non possono crearle; le chiavi già esistenti rispondono 403 plan_required. Solo il proprietario del progetto crea e revoca le chiavi. Al massimo 20 chiavi attive per progetto. La revoca è immediata: le richieste successive rispondono 401 invalid_api_key e i link del player 24h legati a quella chiave smettono di funzionare.
last_used_atdatetimeAggiornato al massimo una volta al minuto dopo una richiesta riuscita. Se manca, la chiave non è mai stata usata con successo.
Tratta il secret come una password. Non committarlo, non incollarlo in chat, non metterlo nel JavaScript del browser. Per ruotarlo: crea una chiave nuova e revoca la vecchia.

Errori

Le risposte di errore usano un JSON stabile. code è sempre in inglese ed è il campo su cui fare lo switch. error è un messaggio leggibile e può essere localizzato da Accept-Language (it, oppure fallback en).

json

{
  "error": "Invalid or revoked API key.",
  "code": "invalid_api_key"
}
CampoTipoDescrizione
401 missing_api_keyauthManca sia Authorization: Bearer sia X-Api-Key.
401 invalid_api_keyauthChiave malformata, sconosciuta o revocata. Stesso code nei tre casi, così non si può sondare se una chiave esiste.
400 invalid_requestqueryQuery non valida (status, feedback_type, updated_since, limit o cursor).
404 not_foundscopeIl progetto o la segnalazione non esiste, l’id non è un UUID, oppure appartiene a un altro progetto. Le sonde cross-project sono indistinguibili da un id assente.
429 rate_limitedthrottleTroppe richieste. Retry-After indica i secondi finché la finestra scorrevole ha di nuovo capienza.
403 plan_requiredplanIl piano del proprietario non include l’API. C’è su Agency (e in demo); non su prova gratuita né su Pro.
503 database_unavailableinfraL’API non raggiunge il database configurato.

Un token del player malformato, scaduto o legato a una chiave revocata non è un errore v1: la pagina watch e lo stream eventi rispondono 404 o 410 e non fanno parte del contratto versionato.

Rate limit

Finestre scorrevoli di 60 secondi, contate in memoria per istanza del server (MVP; non è una quota Redis globale). Valgono entrambi i limiti:

  • 60 richieste / minuto / IP client, valutate prima di parsare la chiave. Anche chi non è autenticato consuma questo budget.
  • 120 richieste / minuto / chiave autenticata, valutate dopo il lookup riuscito della chiave.

Superato uno dei due limiti si ottiene 429 con code rate_limited e header Retry-After (secondi, minimo 1). L’accesso API è incluso in Agency, non è un piano a parte.

Header e cache

Ogni risposta JSON v1 include:

http

Cache-Control: private, no-store

Gli URL dei media firmati e i link del player non devono finire in cache CDN o cache HTTP condivise. Il client può tenere la risposta in memoria locale fino a expires_at, poi deve richiamare GET /feedback/:id/assets (o /replay) per un URL nuovo.

I secret non vengono mai scritti nei log applicativi. Non loggare Authorization, X-Api-Key, token del player o le query string degli URL firmati.

Paginazione a cursore

GET /feedback usa keyset pagination su (updated_at DESC, id DESC). Non usa SQL OFFSET.

  • Dimensione pagina di default 50; massimo 100. I valori sopra il massimo vengono tagliati, non rifiutati.
  • Se esiste un’altra pagina, has_more è true e next_cursor è un blob opaco in base64url. Rimandalo come cursor.
  • Un cursore malformato dà 400 invalid_request. Non inventare i cursori: non sono un linguaggio di query pubblico.

File firmati

I binari non sono mai inline nel JSON e l’API non restituisce uno zip del progetto. Ogni file è un oggetto:

json

{
  "url": "https://…/object/sign/…?token=…",
  "expires_in": 1800,
  "expires_at": "2026-10-02T10:30:00.000Z"
}
CampoTipoDescrizione
screenshot, widget attachments, debug attachmentsTTL 1800s30 minuti dal momento in cui la risposta viene emessa.
rrweb manifest + chunks, 24h player watch URLTTL 86400s24 ore. Riusa gli oggetti già salvati per il player in dashboard; nessuno step di encode extra.

Fai GET direttamente sull’url. Dopo la scadenza lo storage rifiuta il token; richiama assets o replay. Le righe allegato includono anche id, file_name, mime_type, file_size e created_at.

Session replay

La cattura nativa è eventi DOM rrweb, non una registrazione schermo. La v1 espone due formati di prima classe più un video legacy opzionale:

  1. formats.player — pagina HTML firmata che monta lo stesso player rrweb della tab Replay in dashboard. Valida 24 ore. Zero storage extra.
  2. formats.rrweb — manifest JSON firmato + file chunk in ordine (24 ore). Concatena gli array di eventi dei chunk e passali a un player rrweb.
  3. formats.video — presente solo se sulla riga esiste già un MP4 storico. La v1 non transcodifica rrweb in MP4. Stato tipico: not_generated.

formats.still è lo screenshot della segnalazione (TTL 30 minuti). timeline è il log azioni del widget (click, navigazione, input mascherati, errori JS) — un array JSON più piccolo, distinto da rrweb.

Le segnalazioni senza registrazione rispondono comunque HTTP 200 con has_replay: false. HTTP 404 significa che la segnalazione non appartiene al progetto della chiave.

Sicurezza del player 24 ore

L’URL watch è un capability link, non una pagina di catalogo. Viene emesso solo dopo l’autenticazione con una chiave API valida di quel progetto, è legato all’id di quella chiave e firmato crittograficamente. La documentazione di questa pagina è pubblica; i token no.
  • Emesso solo da GET /api/v1/feedback/:id e …/replay autenticati, per una segnalazione dello stesso progetto della chiave.
  • HMAC-SHA256 su un payload con scadenza. I token sono opachi: i client non devono parsarli.
  • Le claim includono id progetto, id segnalazione, id chiave API e scadenza Unix. Revocare la chiave invalida tutti i link player già emessi, anche prima delle 24 ore.
  • Token scaduti, falsi o revocati → 404/410. La pagina watch non richiede login in dashboard (così un browser o un agent con browser può aprirla). Avere l’URL è la credenziale, fino a scadenza o revoca.
  • Header di risposta: Cache-Control: private, no-store, Referrer-Policy: no-referrer, X-Frame-Options: DENY, Content-Security-Policy: frame-ancestors 'none', X-Robots-Tag: noindex, nofollow, X-Content-Type-Options: nosniff. Il player non si può mettere in iframe su un altro sito e non è offerto ai motori di ricerca.

Il player HTML carica gli eventi da uno stream interno GET /api/replay-watch/:token (limitato per IP, stessi header di sicurezza). Quella rotta non fa parte del contratto v1; integra su formats.player.watch.url o formats.rrweb.

GET /api/v1/project

GET/api/v1/project

Conferma la chiave e restituisce il progetto a cui è legata. Usala come verifica di connettività prima di elencare le segnalazioni.

json

{
  "project": {
    "id": "11111111-2222-4333-8444-555555555555",
    "name": "Client site",
    "site_url": "https://staging.example.com"
  }
}
CampoTipoDescrizione
project.iduuidProgetto legato alla chiave.
project.namestringNome visibile.
project.site_urlstringURL del sito configurato.

GET /api/v1/feedback

GET/api/v1/feedback

Indice leggero delle segnalazioni del progetto. Non include mai recording_events, console_logs, conversation, activity né URL firmati dei file. Per quelli usa dettaglio / assets / replay.

Parametri query

CampoTipoDescrizione
statusstringDefault open. open o aperte = aperto + in_lavorazione + in_attesa_cliente. all o tutti = tutti gli stati. Altrimenti una lista di stati database separati da virgola.
feedback_typeenumvisual | debug_replay | text_content | request
updated_sinceISO 8601Limite inferiore incluso su updated_at. Timestamp non validi → 400.
limitint1–100, default 50. Non interi e valori < 1 → 400.
cursoropaquenext_cursor della pagina precedente.
includeignoredViene parsato, ma i JSONB pesanti (recording_events) non vengono mai dumpati sull’endpoint lista.

json

{
  "data": [
    {
      "id": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee",
      "pin_number": 4,
      "status": "aperto",
      "priority": "bloccante",
      "category": "bug",
      "feedback_type": "debug_replay",
      "title": "Il checkout non invia l’ordine",
      "comment": "Clicco Paga e non succede nulla.",
      "client_title": null,
      "page_url": "https://staging.example.com/checkout",
      "page_path": "/checkout",
      "created_at": "2026-10-01T14:12:00.000Z",
      "updated_at": "2026-10-01T15:40:00.000Z",
      "last_activity_at": "2026-10-01T15:40:00.000Z",
      "author_name": "Anna",
      "assigned_to": null,
      "has_screenshot": true,
      "has_replay": true,
      "has_attachments": true,
      "has_console_errors": true,
      "attachment_count": 1
    }
  ],
  "next_cursor": null,
  "has_more": false
}
CampoTipoDescrizione
iduuidId della segnalazione.
pin_numbernumberNumero pin sulla pagina.
statusenumaperto | in_lavorazione | in_attesa_cliente | risolto | archiviato
priorityenumnormale | importante | bloccante
categoryenumtesto | design | immagine | bug | mobile | contenuto | altro
feedback_typeenumvisual | debug_replay | text_content | request
titlestring | nullTitolo in dashboard.
commentstringCommento del cliente.
client_titlestring | nullTitolo del brief «nuova richiesta».
page_urlstringURL completo della pagina al momento della cattura.
page_pathstringPathname ricavato da page_url (query e hash rimossi).
created_at / updated_atdatetimeISO 8601 UTC. updated_at guida il cursore.
last_activity_atdatetime | nullUltima attività in dashboard o portale.
author_namestringNome visibile del cliente.
assigned_toobject | null{ user_id, name, email } se assegnata.
has_screenshot / has_replay / has_attachments / has_console_errorsbooleanFlag leggeri per decidere se chiamare dettaglio/replay.
attachment_countnumberNumero di allegati widget (non i file debug).
next_cursorstring | nullCursore opaco, oppure null sull’ultima pagina.
has_morebooleanSe esiste un’altra pagina.

GET /api/v1/feedback/:id

GET/api/v1/feedback/{id}

Contesto completo per capire (e un giorno risolvere) una segnalazione del progetto della chiave. La query opzionale include=recording_events aggiunge la timeline del widget. Gli eventi rrweb non sono mai inline: usa gli URL firmati in replay.

http

GET /api/v1/feedback/aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee?include=recording_events
Authorization: Bearer $FIXNOTY_API_KEY

Identità

CampoTipoDescrizione
iduuidId della segnalazione.
project_iduuidSempre il progetto legato alla chiave.
pin_numbernumberNumero pin.
status / priority / category / feedback_typeenumStesse enumerazioni dell’endpoint lista.
created_at / updated_at / last_activity_atdatetimeTimestamp UTC.
resolved_at / archived_atdatetime | nullImpostati quando la segnalazione esce dal set aperto.

Testo / brief

CampoTipoDescrizione
title / commentstringIl titolo può essere null; il commento è il testo principale del cliente.
user_goal / actual_result / expected_resultstring | nullCampi bug strutturati, se il cliente li ha compilati.
reproducibility / impactstring | nullContesto opzionale di gravità.
affected_devicesstring[] | nullDispositivi citati dal cliente.
client_title / client_descriptionstring | nullBrief «nuova richiesta».
waiting_for_customer_reasonstring | nullPerché la segnalazione è in attesa del cliente.

Dove

CampoTipoDescrizione
page_url / page_pathstringDove è stato messo il pin.
positionobject{ x, y, x_percent, y_percent } coordinate click/pin.
selectionobject{ type: point | area | none, rect_*_percent } rettangolo selezionato.
scrollobject{ x, y } offset di scroll.
viewportobject{ width, height } viewport di layout al momento della cattura.

Ambiente

CampoTipoDescrizione
environment.user_agentstring | nullStringa UA grezza.
environment.browser / devicestring | nullBrowser e classe dispositivo rilevati.
environment.recording_metadataobject | nullviewport_width/height, visual_viewport_*, device_pixel_ratio, orientation, screen_*, touch_support, pointer_events, device_class, platform, pages_visited, event_count, replay_segment_count, session_id. I secret sono rimossi.

Persone

CampoTipoDescrizione
authorobject{ name, email }. L’email può essere null.
assigned_toobject | null{ user_id, name, email }.
visible_to_clientbooleanSe il portale cliente mostra questa segnalazione.
client_confirmed_at / client_reopened_atdatetime | nullConferma / riapertura dal portale.

Media

CampoTipoDescrizione
screenshotsigned | nullJPEG/PNG/WebP firmato 30 minuti. screenshot_captured_at è quando è stato scattato.
attachments[]arrayFile widget con URL firmati (30 min).
debug_attachments[]arrayFile catturati durante Registra problema (30 min).
replayobject | nullVedi il pacchetto replay. playback è sempre “rrweb”. video è un alias di formats.video.file se esiste un MP4 legacy.

Debug, conversazione, flag

CampoTipoDescrizione
console_logsarray{ type: error | unhandledrejection, message, stack?, url?, timestamp_ms }.
recording_eventsarray | nullTimeline widget. null se non passi include=recording_events.
recording_warningsarray{ code, message } dalla cattura.
debug_summary / captured_valuesobject | array | nullPayload debug avanzato sanitizzato. Le chiavi sensibili sono rimosse in ricorsione.
conversation[]arrayNote team + commenti cliente, in ordine di data. { id, body, created_at, visibility: internal | client, source: team | client, author_name }.
activity[]array{ id, action, old_value, new_value, created_at }. action è una delle enumerazioni attività sotto. created, status_changed, priority_changed, category_changed, internal_notes_updated, customer_update_added, waiting_reason_updated, feedback_resolved, feedback_archived, client_comment_added, client_confirmed_resolution, client_reopened_task, visibility_changed, screenshot_captured, replay_attached, file_attached, client_context_added, title_updated, assignee_changed.
flagsobjecthas_screenshot, has_replay, has_attachments, has_console_errors, has_advanced_data, has_debug_files.
Mai restituiti: pin_hash, cookie, materiale service-role, key_hash, access_token_hash, path storage non firmati, valori Authorization. Gli oggetti nidificati vengono ripuliti in ricorsione da quelle chiavi prima della serializzazione.

GET /api/v1/feedback/:id/assets

GET/api/v1/feedback/{id}/assets

Riemette gli URL firmati senza riscaricare commenti, conversazione o attività. Il expires_in in cima è il TTL di screenshot/allegati (1800s). replay_expires_in è il TTL di player/rrweb (86400s).

json

{
  "data": {
    "screenshot": {
      "url": "https://….supabase.co/storage/v1/object/sign/feedback-screenshots/…?token=redacted",
      "expires_in": 1800,
      "expires_at": "2026-10-02T10:30:00.000Z"
    },
    "attachments": [],
    "debug_attachments": [],
    "replay": null
  },
  "expires_in": 1800,
  "replay_expires_in": 86400
}

GET /api/v1/feedback/:id/replay

GET/api/v1/feedback/{id}/replay

Busta dedicata al replay. La timeline del widget è inclusa di default. Apri formats.player.watch.url nel browser per vedere la sessione come in dashboard.

json

{
  "data": {
    "feedback_id": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee",
    "has_replay": true,
    "duration_seconds": 42,
    "recording_session_id": null,
    "debug_session_id": "bbbbbbbb-cccc-4ddd-8eee-ffffffffffff",
    "expires_in": 86400,
    "formats": {
      "rrweb": {
        "available": true,
        "playback": "rrweb",
        "expires_in": 86400,
        "manifest": {
          "url": "https://….supabase.co/storage/v1/object/sign/debug-replays/…?token=redacted",
          "expires_in": 86400,
          "expires_at": "2026-10-03T10:00:00.000Z"
        },
        "chunks": [
          {
            "url": "https://…/chunk-000.json?token=redacted",
            "expires_in": 86400,
            "expires_at": "2026-10-03T10:00:00.000Z"
          }
        ]
      },
      "player": {
        "available": true,
        "playback": "player",
        "expires_in": 86400,
        "watch": {
          "url": "https://www.fixnoty.com/it/replay/<signed-token>",
          "expires_in": 86400,
          "expires_at": "2026-10-03T10:00:00.000Z"
        }
      },
      "video": {
        "available": false,
        "playback": "mp4",
        "status": "not_generated",
        "mime_type": "video/mp4",
        "expires_in": 86400,
        "file": null
      },
      "still": {
        "url": "https://….supabase.co/storage/v1/object/sign/feedback-screenshots/…?token=redacted",
        "expires_in": 1800,
        "expires_at": "2026-10-02T10:30:00.000Z"
      }
    },
    "timeline": [
      {
        "t": 1200,
        "at": "2026-10-01T14:12:01.200Z",
        "type": "click",
        "page_url": "https://staging.example.com/checkout"
      }
    ],
    "warnings": []
  },
  "expires_in": 86400
}

Come usare i chunk rrweb

  1. GET dell’URL firmato del manifest.
  2. GET di ogni URL chunk, in ordine di array.
  3. Concatena gli array di eventi.
  4. Passa il risultato a un player rrweb. Non è un MP4.

Enumerazioni

status

aperto | in_lavorazione | in_attesa_cliente | risolto | archiviato

priority

normale | importante | bloccante

category

testo | design | immagine | bug | mobile | contenuto | altro

feedback_type

visual | debug_replay | text_content | request

recording event type

widget_loaded · widget_reconnected · click · tap · pointer_down · pointer_up · scroll · navigation · input_focus · input_change_masked · password_field_interaction_masked · keydown_masked · file_selection_masked · console_error · js_error · unhandled_rejection · visibility_change · pagehide · beforeunload · debug_started · debug_stopped · replay_started · replay_stopped · replay_snapshot · input_value_captured · input_value_masked · dropdown_open · dropdown_selection · picker_open · picker_selection · file_selected · file_attached · file_attachment_skipped · orientation_change · viewport_resize · visual_viewport_resize · tab_open

Elementi timeline: { t, at, type, page_url?, from?, to?, x?, y?, … }. Password e input sensibili sono mascherati in cattura (tipi *_masked).

Consumo da un sistema AI

Questa API è un canale dati. Non chiama un modello e non sistema le segnalazioni da sola.

  • LLM solo testo. Legge lista + dettaglio JSON, console, timeline e conversazione. Ottiene azioni, errori, copy e un fotogramma. Non ottiene il movimento.
  • Agent con browser. Apre formats.player.watch.url, preme play e guarda o screenshotta il DOM ricostruito. Un GET HTTP nudo su quell’URL restituisce l’HTML/JS del player, non i fotogrammi.
  • Player proprio. Scarica i chunk formats.rrweb. Sono gli stessi eventi della dashboard.

Non generiamo MP4 da rrweb: duplicerebbe storage già tenuto come chunk di eventi. Il player 24 ore riusa quei chunk.

Modello di sicurezza

  • Questo URL di documentazione è pubblico di proposito. Le docs pubbliche descrivono il contratto. Non danno accesso.
  • Il controllo accessi è la chiave API di progetto (hashata a riposo) più i filtri server-side su project_id. Indovinare l’UUID di una segnalazione di un altro progetto non la fa trapelare.
  • RLS in dashboard: solo il proprietario può leggere/inserire/aggiornare le righe di project_api_keys.
  • I link del player sono unlisted, non indicizzati, non embeddabili, a tempo e revocabili con la chiave. Non inoltrarli.
  • Non mettere le chiavi API nei bundle frontend né in pagine che leakano il Referer. Preferisci caller server-side.

Esempi curl

bash

export FIXNOTY_API_KEY='fn_live_…'
export APP_URL='https://www.fixnoty.com'

# Verifica la chiave
curl -sS "$APP_URL/api/v1/project" \
  -H "Authorization: Bearer $FIXNOTY_API_KEY"

# Segnalazioni aperte (default)
curl -sS "$APP_URL/api/v1/feedback?status=open&limit=50" \
  -H "Authorization: Bearer $FIXNOTY_API_KEY"

# Tutti gli stati dal 1° ottobre 2026
curl -sS "$APP_URL/api/v1/feedback?status=all&updated_since=2026-10-01T00:00:00.000Z" \
  -H "Authorization: Bearer $FIXNOTY_API_KEY"

# Dettaglio completo + timeline widget
curl -sS "$APP_URL/api/v1/feedback/FEEDBACK_ID?include=recording_events" \
  -H "Authorization: Bearer $FIXNOTY_API_KEY"

# Solo media firmati (refresh)
curl -sS "$APP_URL/api/v1/feedback/FEEDBACK_ID/assets" \
  -H "X-Api-Key: $FIXNOTY_API_KEY"

# Pacchetto replay: player 24h + rrweb + still + timeline
curl -sS "$APP_URL/api/v1/feedback/FEEDBACK_ID/replay" \
  -H "Authorization: Bearer $FIXNOTY_API_KEY"

Versioning e fuori scope

Versione attuale: v1. Possono comparire campi aggiuntivi. Restano esplicitamente fuori dalla v1:

  • POST / PATCH / DELETE (stato, commenti, assegnazione)
  • Zip sincrono di tutto il progetto
  • Billing, piani a pagamento, webhook
  • SDK ufficiali e OpenAPI
  • Inferenza di modelli, auto-fix o webhook AI in uscita
  • Transcodifica MP4 da rrweb

Riferimento pubblico canonico: questa pagina. Una nota tecnica allineata sta nel repository in docs/api-v1.md. Stampa dal browser («Stampa → Salva come PDF») è supportata; non spediamo un PDF versionato a parte che può divergere dall’implementazione.

Cookie di misurazione

I cookie tecnici di accesso restano sempre attivi. Analytics e annunci partono solo se li accetti. Non mandiamo a Google email, nomi o testi dei feedback. Informativa cookie