{
  "name": "Tinylytics \u2014 Receive Webhooks",
  "nodes": [
    {
      "parameters": {
        "content": "## Tinylytics \u2192 n8n webhooks\n\n1. Activate this workflow and copy the **Production URL** from the Webhook node.\n2. In Tinylytics: Account Settings \u2192 Webhooks \u2192 create a **Generic** webhook with that URL.\n3. Select the events you want (`monitor_down`, `monitor_up`, `new_hit`, `new_kudo`, `new_event`, `new_insight`, `content_issue`).\n4. Leave **Wrap content** off.\n5. Paste the webhook signing secret into the Verify Signature Code node.\n6. Use **Send Test** in Tinylytics to confirm delivery.\n\nTinylytics requires a **public** HTTP(S) URL (localhost is blocked). Paid plan required for webhooks.",
        "height": 360,
        "width": 520,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -700,
        -200
      ],
      "id": "9670fffc-499d-49d0-8276-ff27185add7a",
      "name": "Setup"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "tinylytics",
        "options": {
          "rawBody": true
        }
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -420,
        80
      ],
      "id": "88e27e54-22dc-4d4b-a534-2574dd3faca2",
      "name": "Tinylytics Webhook",
      "webhookId": "8ffc878d-e1ed-4fa8-8022-c6318fbcfbfe"
    },
    {
      "parameters": {
        "jsCode": "const crypto = require('crypto');\n\n// Paste the signing secret from Tinylytics Account Settings \u2192 Webhooks.\nconst SIGNING_SECRET = 'REPLACE_WITH_WEBHOOK_SIGNING_SECRET';\n\nif (!SIGNING_SECRET || SIGNING_SECRET.startsWith('REPLACE_')) {\n  throw new Error('Set SIGNING_SECRET to your Tinylytics webhook signing secret');\n}\n\nconst item = $input.first();\nconst headers = item.json.headers || {};\nconst signature = String(headers['x-signature'] || headers['X-Signature'] || '');\nif (!signature) {\n  throw new Error('Missing X-Signature header');\n}\n\nconst rawBody = await this.helpers.getBinaryDataBuffer(0, 'data');\nconst expected = 'sha256=' + crypto.createHmac('sha256', SIGNING_SECRET).update(rawBody).digest('hex');\n\nconst sigBuf = Buffer.from(signature);\nconst expBuf = Buffer.from(expected);\nif (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) {\n  throw new Error('Invalid Tinylytics webhook signature');\n}\n\nconst body = item.json.body;\nreturn [{\n  json: {\n    event: body.event || headers['x-tinylytics-event'] || headers['X-Tinylytics-Event'],\n    delivery_id: headers['x-tinylytics-delivery'] || headers['X-Tinylytics-Delivery'],\n    payload: body,\n  },\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -180,
        80
      ],
      "id": "ea0efe43-75c7-466e-90f0-425284bcd1e7",
      "name": "Verify Signature"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "bf733bf3-5f0f-4144-83b5-398bf6c66086",
                    "leftValue": "={{ $json.event }}",
                    "rightValue": "monitor_down",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "monitor_down"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "d18b48ca-8f03-4f7b-935e-4188979bb6dc",
                    "leftValue": "={{ $json.event }}",
                    "rightValue": "monitor_up",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "monitor_up"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "d053f87c-4bb8-4c86-baaa-726d2c079ae4",
                    "leftValue": "={{ $json.event }}",
                    "rightValue": "new_hit",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "new_hit"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "7180133f-f166-4434-a65a-b45d53b42762",
                    "leftValue": "={{ $json.event }}",
                    "rightValue": "new_kudo",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "new_kudo"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "f4272d26-b805-45fb-b63e-4754fef628cc",
                    "leftValue": "={{ $json.event }}",
                    "rightValue": "new_event",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "new_event"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "65df035b-42df-4f1a-bf8d-613556d89e98",
                    "leftValue": "={{ $json.event }}",
                    "rightValue": "new_insight",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "new_insight"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "b99d0c61-186e-4fb9-9db4-7359cfae826b",
                    "leftValue": "={{ $json.event }}",
                    "rightValue": "content_issue",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "content_issue"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        60,
        80
      ],
      "id": "5b0187b4-dde3-4c38-a99c-198c961de9d6",
      "name": "Route Event"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "5e526e62-85a2-4f66-a096-4f7abfc1db0a",
              "name": "message",
              "value": "=\ud83d\udd34 {{ $json.payload.site.name }} is down\\n{{ $json.payload.site.url }}\\nError: {{ $json.payload.downtime.error }}\\nDashboard: {{ $json.payload.site.dashboard_url }}",
              "type": "string"
            },
            {
              "id": "69306b19-8735-4f37-952b-a621caca1270",
              "name": "event",
              "value": "={{ $json.event }}",
              "type": "string"
            },
            {
              "id": "8e30f416-740a-4e3b-b2e1-4a0689b9cba0",
              "name": "delivery_id",
              "value": "={{ $json.delivery_id }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        340,
        -120
      ],
      "id": "a2503c41-2118-4588-88b4-e651569edbde",
      "name": "Format Monitor Down"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "0ac4284c-7d39-4de1-a6e4-1d8af5ecb21d",
              "name": "message",
              "value": "=\ud83d\udfe2 {{ $json.payload.site.name }} is back up\\nDowntime: {{ $json.payload.downtime.duration_formatted }}\\nDashboard: {{ $json.payload.site.dashboard_url }}",
              "type": "string"
            },
            {
              "id": "636baa9b-87b2-40e3-9f35-f51c7bb7d38a",
              "name": "event",
              "value": "={{ $json.event }}",
              "type": "string"
            },
            {
              "id": "ac2c148b-c3f9-4400-9be4-15486e6197aa",
              "name": "delivery_id",
              "value": "={{ $json.delivery_id }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        340,
        40
      ],
      "id": "d1fabaad-62e3-41fe-94f6-21b89b8e7241",
      "name": "Format Monitor Up"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "2619599a-4768-4ec8-a573-ec85fa939a72",
              "name": "message",
              "value": "=Hit on {{ $json.payload.site.name }}: {{ $json.payload.hit.path }}\\nReferrer: {{ $json.payload.hit.referrer || 'direct' }}\\nCountry: {{ $json.payload.hit.country || 'unknown' }}",
              "type": "string"
            },
            {
              "id": "6a18ddc1-ebf1-474f-b3c6-ceeae94f28d3",
              "name": "event",
              "value": "={{ $json.event }}",
              "type": "string"
            },
            {
              "id": "db3ca580-97ce-4fee-8cf3-0e8d5257b9de",
              "name": "hit_id",
              "value": "={{ $json.payload.hit.id }}",
              "type": "number"
            },
            {
              "id": "137b2357-a333-4c21-acce-4ad82f755cca",
              "name": "delivery_id",
              "value": "={{ $json.delivery_id }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        340,
        200
      ],
      "id": "28714c0f-d0a6-487d-8773-e016e9c1cd1a",
      "name": "Format New Hit"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "34813740-83f4-451b-b251-28b9627c7ca6",
              "name": "message",
              "value": "=\ud83d\udc9c New kudo on {{ $json.payload.site.name }}\\nPath: {{ $json.payload.kudo.path }}\\nUID: {{ $json.payload.kudo.uid }}",
              "type": "string"
            },
            {
              "id": "cb176173-49b7-4758-ab98-879bbedc93e0",
              "name": "event",
              "value": "={{ $json.event }}",
              "type": "string"
            },
            {
              "id": "ed2a49b3-758d-44cc-861c-ff4e0552fac0",
              "name": "kudo_uid",
              "value": "={{ $json.payload.kudo.uid }}",
              "type": "string"
            },
            {
              "id": "0a34566b-dfe1-40bf-bcdd-37eda9c8d440",
              "name": "delivery_id",
              "value": "={{ $json.delivery_id }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        340,
        360
      ],
      "id": "c0fe1f5b-acc7-436d-a8e8-4762294f6e58",
      "name": "Format New Kudo"
    },
    {
      "parameters": {
        "content": "## Next step\\n\\nConnect Slack, email, Discord, Linear, etc. to the Format nodes.\\n\\nUse `delivery_id` (or `hit.id` / `kudo.uid`) for idempotency \u2014 Tinylytics deliveries are at-least-once.",
        "height": 180,
        "width": 360,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        580,
        40
      ],
      "id": "458bfd3c-4899-4fa6-b0c7-e2e2436c8a21",
      "name": "Next Step"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "de8c6ebd-3cf7-44d6-83df-8c4df32b37da",
              "name": "message",
              "value": "=Custom event on {{ $json.payload.site.name }}: {{ $json.payload.analytics_event.event }}\\nPath: {{ $json.payload.analytics_event.path }}\\nValue: {{ $json.payload.analytics_event.value || 'none' }}",
              "type": "string"
            },
            {
              "id": "cd70d302-dddf-4bbb-bee4-7e4f0158e6bc",
              "name": "event",
              "value": "={{ $json.event }}",
              "type": "string"
            },
            {
              "id": "7361c3a2-c0fd-4d68-975d-ba926d3a195b",
              "name": "analytics_event_id",
              "value": "={{ $json.payload.analytics_event.id }}",
              "type": "number"
            },
            {
              "id": "dd548687-915c-49d2-b62a-14866732c1ee",
              "name": "delivery_id",
              "value": "={{ $json.delivery_id }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        340,
        520
      ],
      "id": "5a242773-5bf0-4cb1-9b2c-362d59d5f1ee",
      "name": "Format New Event"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "d7cc2449-8e3c-4ee6-b895-b449be01c637",
              "name": "message",
              "value": "=New insight for {{ $json.payload.site.name }}\\n{{ $json.payload.insight.summary }}\\nDashboard: {{ $json.payload.site.dashboard_url }}",
              "type": "string"
            },
            {
              "id": "02ef0204-2e6f-4d79-b291-e2586ae0680d",
              "name": "event",
              "value": "={{ $json.event }}",
              "type": "string"
            },
            {
              "id": "f6da631e-decf-4341-a66c-4cd5dd911f1c",
              "name": "insight_id",
              "value": "={{ $json.payload.insight.id }}",
              "type": "number"
            },
            {
              "id": "e83ecbfb-ae31-4b97-a3f0-fc51ce2cb859",
              "name": "delivery_id",
              "value": "={{ $json.delivery_id }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        340,
        680
      ],
      "id": "e977a346-c215-493e-abc0-49e369f0208e",
      "name": "Format New Insight"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "5359ccbc-7bfb-4cf3-b0f9-c0467eb473db",
              "name": "message",
              "value": "=Content issue on {{ $json.payload.site.name }}\\n{{ $json.payload.check.issue_type }}: {{ $json.payload.check.url }}\\n{{ $json.payload.check.error_message }}",
              "type": "string"
            },
            {
              "id": "05eefc1a-23da-4930-9df1-9a2dbdd96498",
              "name": "event",
              "value": "={{ $json.event }}",
              "type": "string"
            },
            {
              "id": "555a629c-ce2e-46a0-8393-9fdd051157f3",
              "name": "check_id",
              "value": "={{ $json.payload.check.id }}",
              "type": "number"
            },
            {
              "id": "72f80314-281a-4770-be53-26a511fce708",
              "name": "delivery_id",
              "value": "={{ $json.delivery_id }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        340,
        840
      ],
      "id": "905cff08-df98-4458-9898-f71a7c155f59",
      "name": "Format Content Issue"
    }
  ],
  "pinData": {},
  "connections": {
    "Tinylytics Webhook": {
      "main": [
        [
          {
            "node": "Verify Signature",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify Signature": {
      "main": [
        [
          {
            "node": "Route Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route Event": {
      "main": [
        [
          {
            "node": "Format Monitor Down",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format Monitor Up",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format New Hit",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format New Kudo",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format New Event",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format New Insight",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format Content Issue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "tags": []
}
