orchard
mirrorEvery site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.
blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics
1{{ define "extra_head" }}{{ template "social" . }}{{ end }}
2{{ define "extra_css" }}{{ range .PageStyles }}<link href="{{ . }}" rel="stylesheet">{{ end }}{{ end }}
3{{ define "breadcrumbs" }}
4<nav aria-label="breadcrumb">
5 <ol class="breadcrumb mb-0">
6 <li class="breadcrumb-item"><a href="/">Home</a></li>
7 <li class="breadcrumb-item active" aria-current="page">{{ .Title }}</li>
8 </ol>
9</nav>
10{{ end }}
11{{ define "main" }}
12<div class="container my-5">
13 <div class="row">
14 <div class="col-lg-8 offset-lg-2">
15 <div class="section-label mb-2">collector ยท recipes</div>
16 <h1 class="fw-bolder text-white" style="letter-spacing: -0.01em;">{{ .Title }}</h1>
17 <p class="docs-lead">
18 All events are sent as JSON to a single <code>POST /collect/</code>
19 endpoint. The site tag pushes page_view, click, scroll, and page_leave
20 automatically, and everything else is a one-liner on
21 <code>collectorQueue</code>, or a raw HTTP call from any language.
22 </p>
23 </div>
24 </div>
25
26 <div class="row mt-4">
27 <div class="col-lg-8 offset-lg-2">
28 <div class="accordion docs-accordion" id="docsAccordion">
29
30 <div class="accordion-item">
31 <h2 class="accordion-header" id="headingOne">
32 <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
33 Collect custom clicks
34 </button>
35 </h2>
36 <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#docsAccordion">
37 <div class="accordion-body">
38 <p>Attach a handler to any interactive element. The event name is
39 whatever you want to measure later.</p>
40 <textarea class="codebox" rows="3" readonly>onclick="collectorQueue.push({event: 'signup_cta_click'})"</textarea>
41 </div>
42 </div>
43 </div>
44
45 <div class="accordion-item">
46 <h2 class="accordion-header" id="headingTwo">
47 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
48 Collect custom page views
49 </button>
50 </h2>
51 <div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#docsAccordion">
52 <div class="accordion-body">
53 <p>Use this for SPA route changes, funnel checkpoints
54 ("invoice paid"), or any meaningful page-level moment. Push
55 once per visit:</p>
56 <textarea class="codebox" rows="3" readonly><script>collectorQueue.push({event: 'checkout_success'});</script></textarea>
57 </div>
58 </div>
59 </div>
60
61 <div class="accordion-item">
62 <h2 class="accordion-header" id="headingThree">
63 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
64 Attach custom data to any event
65 </button>
66 </h2>
67 <div id="collapseThree" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#docsAccordion">
68 <div class="accordion-body">
69 <p>Every event is stored as JSON in an <code>extra</code>
70 column. Add whatever keys you need, no migration required,
71 query with SQLite JSON1
72 <code>json_extract(extra, '$.user_segment')</code> later.</p>
73 <textarea class="codebox" rows="3" readonly><script>collectorQueue.push({event: 'admin_page_view', data: {user_segment: 'admins'}});</script></textarea>
74 </div>
75 </div>
76 </div>
77
78 <div class="accordion-item">
79 <h2 class="accordion-header" id="headingFour">
80 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
81 Send events over raw HTTP
82 </button>
83 </h2>
84 <div id="collapseFour" class="accordion-collapse collapse" aria-labelledby="headingFour" data-bs-parent="#docsAccordion">
85 <div class="accordion-body">
86 <p>The collector endpoint is plain JSON, so any HTTP client
87 will do. Include a valid <code>collector_id</code>,
88 <code>event</code>, and optional <code>data</code>.</p>
89 <textarea class="codebox" rows="4" readonly>curl -X POST -H 'Content-Type: application/json' \
90 -d '{"collector_id": "PROPERTY_COLLECTOR_ID", "event": "custom_app_event", "data": {"app_id": 4}}' \
91 YOUR_COLLECTOR_SERVER/collect/</textarea>
92 </div>
93 </div>
94 </div>
95
96 <div class="accordion-item">
97 <h2 class="accordion-header" id="headingFive">
98 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFive" aria-expanded="false" aria-controls="collapseFive">
99 Server-side events (Python)
100 </button>
101 </h2>
102 <div id="collapseFive" class="accordion-collapse collapse" aria-labelledby="headingFive" data-bs-parent="#docsAccordion">
103 <div class="accordion-body">
104 <p>Any backend, any library, any runtime. Here's a Python
105 example using <code>requests</code>:</p>
106 <textarea class="codebox" rows="5" readonly>import requests
107
108requests.post(
109 'YOUR_COLLECTOR_SERVER/collect/',
110 json={'collector_id': 'PROPERTY_COLLECTOR_ID',
111 'event': 'subscription_renewed',
112 'data': {'plan': 'pro', 'mrr': 49}}
113)</textarea>
114 </div>
115 </div>
116 </div>
117
118 </div>
119 </div>
120 </div>
121</div>
122{{ end }}