{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://snipzr.com/schemas/v1/link-stats.schema.json",
  "title": "snipzr /v1 link stats response",
  "description": "Response body of GET /v1/urls/{slug}/stats (and the identical /x/ web-app route). Versioned contract: additive fields may be introduced in a backward-compatible way; removals/renames are a new major schema. All counts are non-negative integers; all dates are UTC. human = totals.requests - totals.bots.",
  "type": "object",
  "additionalProperties": false,
  "required": ["stats"],
  "properties": {
    "stats": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "slug", "zoneDomain", "range", "allTime", "totals", "series",
        "hourly", "geo", "referrers", "devices", "browsers", "os", "languages", "source", "freshness"
      ],
      "properties": {
        "slug": { "type": "string", "description": "The link slug." },
        "zoneDomain": { "type": "string", "description": "The link's domain (zone)." },
        "range": {
          "type": "object",
          "additionalProperties": false,
          "required": ["from", "to", "clampedTo", "retentionDays", "storedDays"],
          "properties": {
            "from": { "type": "string", "format": "date", "description": "Requested window start (UTC date, YYYY-MM-DD) as echoed back." },
            "to": { "type": "string", "format": "date", "description": "Effective window end (UTC date); never in the future." },
            "clampedTo": { "type": "string", "format": "date", "description": "Effective window start after the plan's retention floor. Equals `from` when nothing was clamped; later than `from` when the request exceeded the plan's readable window (the request is clamped, never rejected)." },
            "retentionDays": { "type": "integer", "minimum": 0, "description": "How many days of analytics the caller's plan can read (Free 30 / Basic 365 / Pro 730)." },
            "storedDays": { "type": "integer", "minimum": 0, "description": "Total days of data stored for the link (may exceed retentionDays — older data is retained but not readable on the current plan)." }
          }
        },
        "allTime": {
          "type": "object",
          "additionalProperties": false,
          "required": ["total", "preTracking", "trackingSince"],
          "properties": {
            "total": { "type": "integer", "minimum": 0, "description": "Lifetime hit counter incl. bots and pre-tracking clicks." },
            "preTracking": { "type": "integer", "minimum": 0, "description": "Hits accrued before per-day tracking began (a one-time snapshot). tracked-era hits ≈ total - preTracking." },
            "trackingSince": { "type": "string", "format": "date", "description": "First UTC day for which per-day data exists." }
          }
        },
        "totals": {
          "type": "object",
          "additionalProperties": false,
          "required": ["human", "bots", "requests", "qr", "click"],
          "properties": {
            "human": { "type": "integer", "minimum": 0, "description": "Human clicks over the window (requests - bots). The headline number." },
            "bots": { "type": "integer", "minimum": 0, "description": "Bot / HEAD / prefetch / link-preview requests over the window." },
            "requests": { "type": "integer", "minimum": 0, "description": "All requests over the window (human + bots). Not adversarial-grade; do not bill on it." },
            "qr": { "type": "integer", "minimum": 0, "description": "Human clicks that came from a QR scan." },
            "click": { "type": "integer", "minimum": 0, "description": "Human clicks that came from a direct short-link click." }
          }
        },
        "series": {
          "type": "array",
          "description": "Per-UTC-day points across the (clamped) window, ascending by date. Days with no data are omitted.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["date", "n", "b"],
            "properties": {
              "date": { "type": "string", "format": "date", "description": "UTC day." },
              "n": { "type": "integer", "minimum": 0, "description": "All requests that day (human + bots)." },
              "b": { "type": "integer", "minimum": 0, "description": "Bot requests that day (human = n - b)." }
            }
          }
        },
        "hourly": {
          "type": "object",
          "additionalProperties": false,
          "description": "Rolling last-24-hours histogram of human clicks by UTC hour, independent of the requested window. Populated when the window includes the recent days; empty for a purely-historical window.",
          "required": ["fromUtc", "buckets"],
          "properties": {
            "fromUtc": { "type": "string", "format": "date-time", "description": "UTC top-of-hour at which buckets[0] starts (the hour 23 hours ago). buckets[23] is the current partial hour." },
            "buckets": { "type": "array", "minItems": 24, "maxItems": 24, "items": { "type": "integer", "minimum": 0 } }
          }
        },
        "geo": { "$ref": "#/$defs/kvList", "description": "Human clicks by country. Key is an ISO-3166 alpha-2 code, or \"unknown\" when the country could not be determined." },
        "referrers": { "$ref": "#/$defs/kvList", "description": "Human clicks by referring host (host only, never a full URL). Sentinels: \"~direct\" (no referrer sent), \"~other\" (folded tail / over the per-day host budget). Real hosts are unescaped." },
        "devices": { "$ref": "#/$defs/kvList", "description": "Human clicks by device family (e.g. desktop, mobile, tablet)." },
        "browsers": { "$ref": "#/$defs/kvList", "description": "Human clicks by browser family." },
        "os": { "$ref": "#/$defs/kvList", "description": "Human clicks by operating-system family." },
        "languages": { "$ref": "#/$defs/kvList", "description": "Human clicks by Accept-Language primary subtag (allowlisted), or \"~other\"." },
        "source": {
          "type": "object",
          "additionalProperties": false,
          "required": ["click", "qr"],
          "properties": {
            "click": { "type": "integer", "minimum": 0 },
            "qr": { "type": "integer", "minimum": 0 }
          }
        },
        "freshness": {
          "type": "string",
          "enum": ["todayLive", "final", "degraded"],
          "description": "todayLive = window includes today and is merged with live data; final = window entirely in the past (fully consolidated); degraded = analytics store was unreachable and only recent live data is shown."
        }
      }
    }
  },
  "$defs": {
    "kvList": {
      "type": "array",
      "description": "Dimension breakdown, sorted by value descending. k = bucket key, v = count.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["k", "v"],
        "properties": {
          "k": { "type": "string" },
          "v": { "type": "integer", "minimum": 0 }
        }
      }
    }
  }
}