diff --git a/docs/docs/configuration/advanced/analytics.md b/docs/docs/configuration/advanced/analytics.md
new file mode 100644
index 0000000000..0171a05810
--- /dev/null
+++ b/docs/docs/configuration/advanced/analytics.md
@@ -0,0 +1,41 @@
+---
+id: analytics
+title: Anonymous Analytics
+---
+
+import AnalyticsFields from "@site/src/components/AnalyticsFields";
+import NavPath from "@site/src/components/NavPath";
+
+Frigate can send one anonymous usage report a day. The reports show the maintainers which hardware to support, which features people use, and how releases perform. Sharing is off until you turn it on.
+
+## Turning it on
+
+Enable **Share anonymous analytics** at , or set it in your config:
+
+```yaml
+telemetry:
+ analytics: true
+```
+
+The same page has a **Preview the report** button that shows exactly what the next report contains.
+
+## How it's sent
+
+- Once a day, as a JSON POST to `https://analytics.frigate.video/report`
+- The server looks up your country and region from your IP address and never stores the address
+- Raw reports are kept for 60 days; only aggregate totals are published
+- A random install ID, stored in `/config/.analytics.json`, keeps your install from being counted twice. Turning sharing off deletes it
+
+## What's never sent
+
+- Camera, zone, group, profile, or user names
+- Object labels, face names, or license plate text
+- IP addresses, hostnames, URLs, or stream paths
+- Credentials or API keys
+- Events, recordings, or anything from them
+
+## Every field
+
+Fields marked public appear in the published totals. The machine-readable schema is [frigate-analytics-schema.json](pathname:///frigate-analytics-schema.json).
+
+
diff --git a/docs/docs/configuration/advanced/reference.md b/docs/docs/configuration/advanced/reference.md
index e41eb9cfa2..9d709816da 100644
--- a/docs/docs/configuration/advanced/reference.md
+++ b/docs/docs/configuration/advanced/reference.md
@@ -1194,6 +1194,9 @@ ui:
# Optional: Telemetry configuration
telemetry:
+ # Optional: Share one anonymous usage report a day (default: shown below)
+ # NOTE: See https://docs.frigate.video/configuration/advanced/analytics for what is sent
+ analytics: False
# Optional: Enabled network interfaces for bandwidth stats monitoring (default: empty list, let nethogs search all)
network_interfaces:
- eth
diff --git a/docs/sidebars.ts b/docs/sidebars.ts
index 9a1ec1ffe2..41c4d59ae2 100644
--- a/docs/sidebars.ts
+++ b/docs/sidebars.ts
@@ -130,6 +130,7 @@ const sidebars: SidebarsConfig = {
label: "Advanced Configuration",
items: [
"configuration/advanced/system",
+ "configuration/advanced/analytics",
"configuration/advanced/reference",
{
type: "link",
diff --git a/docs/src/components/AnalyticsFields/index.jsx b/docs/src/components/AnalyticsFields/index.jsx
new file mode 100644
index 0000000000..b14b088619
--- /dev/null
+++ b/docs/src/components/AnalyticsFields/index.jsx
@@ -0,0 +1,60 @@
+import React from "react";
+import schema from "@site/static/frigate-analytics-schema.json";
+
+function resolve(node) {
+ if (!node) return node;
+ if (node.$ref) return schema.$defs[node.$ref.split("/").pop()];
+ if (node.anyOf) {
+ const inner = node.anyOf.find((option) => option.type !== "null");
+ return inner ? resolve(inner) : node;
+ }
+ return node;
+}
+
+function rows(properties, prefix = "") {
+ return Object.entries(properties).flatMap(([name, field]) => {
+ const path = prefix ? `${prefix}.${name}` : name;
+ const row = {
+ path,
+ description: field.description,
+ isPublic: field["x-public"],
+ };
+ const target = resolve(field);
+
+ if (target?.properties) return [row, ...rows(target.properties, path)];
+
+ const values = resolve(target?.additionalProperties);
+ if (values?.properties)
+ return [row, ...rows(values.properties, `${path}.`)];
+
+ const items = resolve(target?.items);
+ if (items?.properties) return [row, ...rows(items.properties, `${path}[]`)];
+
+ return [row];
+ });
+}
+
+export default function AnalyticsFields() {
+ return (
+