Skip to main content
Recurso can report a tiny, anonymous signal so the maintainers can answer one question they otherwise cannot: how many self-hosted instances reach their first real invoice. For an open-source project without a hosted service, that single activation metric is how you see whether the software is useful — and where to put effort.
Telemetry is off by default. With the default configuration Recurso makes zero network calls for telemetry and writes zero telemetry rows. Nothing is sent unless you explicitly set TELEMETRY_OPTIN=true.

Enabling and disabling

Telemetry is controlled by a single environment variable. To turn it off after enabling, remove TELEMETRY_OPTIN=true (or set it to anything other than true) and restart.

What is sent

When — and only when — telemetry is enabled, Recurso POSTs small JSON events to TELEMETRY_ENDPOINT. Each event is a single fire-and-forget POST with a 5-second timeout and no retries; if it fails, Recurso logs one line at debug level and moves on. Telemetry never runs on a request path, so it can never slow down or block an API call. Every event carries an anonymous instance_id — a random UUID generated once and stored in your database (telemetry_instance table). It is not derived from your hostname, MAC address, license key, domain, or anything else that could identify you or your customers.

instance_started

Sent once per process start.
deployment is a coarse hint only: docker (a container marker file exists) or binary.

Milestones

Each milestone is sent at most once, ever, per instance — a flag is persisted on the telemetry_instance row, so a milestone never fires twice, not across restarts and not across replicas sharing one database.
The four milestone events are:
  • milestone_first_plan
  • milestone_first_customer
  • milestone_first_invoicethe activation metric
  • milestone_first_payment

heartbeat

Sent at startup, then every 24 hours.
tenants and subscriptions are bucketed ranges, never exact numbers. The only possible values are "0", "1-9", "10-99", and "100+". The exact counts are read locally and collapsed into a range before anything leaves the process.

What is never sent

  • No amounts, prices, currencies, revenue, or MRR.
  • No names, emails, phone numbers, addresses, or tax IDs.
  • No API keys, secrets, tokens, or connection strings.
  • No customer, plan, invoice, subscription, or tenant IDs.
  • No hostnames, IP addresses, domains, or MAC addresses.
  • No exact counts of anything — only the four bucket ranges above.
The complete list of fields ever transmitted is exactly the keys shown in the JSON examples above. There is no free-text field and no catch-all payload.

Verify it yourself

Don’t take our word for it — point the endpoint at a server you control and watch every byte:
Any request-logging endpoint works (nc -l 9999, a webhook.site URL, an ngrok tunnel, etc.). Create a plan, a customer, a subscription, and mark an invoice paid, and you’ll see exactly the events documented here — and nothing else. You can also read the source: internal/adapter/telemetry/telemetry.go is the only code that builds and sends payloads.
https://telemetry.recurso.dev (the hosted collector) is being stood up separately. Until it exists, an enabled instance simply POSTs into the void and logs the failure at debug level — which changes nothing about your instance’s behavior.

Next steps

Going to production

The rest of the production checklist