0.0
The project is in a healthy, maintained state
Tracks click rates, signups, logins, and daily app interactions via REST API. Targets automatically move from cold to warm or warm to hot as they engage — increasing Crusader send frequency in real time.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

 Project Readme

Helios Tracker

A Rails engine that exposes your app's user, visit, and email-suppression data as JSON API endpoints for Helios Crusader to consume.

Helios pulls from these endpoints daily to track signups, logins, click-throughs, and blocked emails — automatically moving targets from cold to warm to hot based on engagement.

Requirements

Installation

Add to your Gemfile:

gem "helios-tracker"

Then run:

bundle install
rails generate helios_tracker:install
rails db:migrate

The install generator will:

  1. Install and configure Universal Track Manager (visit tracking)
  2. Add include UniversalTrackManagerConcern to your ApplicationController
  3. Create a blocked_emails migration
  4. Generate config/initializers/helios_tracker.rb
  5. Mount the engine in your routes

If Universal Track Manager is already installed, the generator will detect it and skip that step.

Generator Options

# Specify your User model class (default: "User")
rails generate helios_tracker:install --user-class=Account

# Skip UTM installation if already set up
rails generate helios_tracker:install --skip-utm

Configuration

Set your API key in your environment (.env, credentials, or server config):

HELIOS_TRACKER_API_KEY=your_secret_key_here

Then edit config/initializers/helios_tracker.rb:

HeliosTracker.configure do |config|
  # ---- Users ----

  config.user_class_name = "User"

  # Return users updated since query_start.
  # Receives (query_start, params). Must return an ActiveRecord relation.
  config.user_scope = ->(query_start, params) {
    User.where.not(email: "")
        .where("updated_at > ?", query_start)
  }

  # Map API field names to model attributes or lambdas.
  # :email is required. All others are optional.
  config.user_fields = {
    email:                      :email,
    created_at:                 :created_at,
    source_ip:                  :source_ip,
    login_count:                :login_count,
    accounts_owned_count:       ->(user) { user.accounts_owned_count },
    free_accounts_count:        ->(user) { user.free_accounts_count },
    unsubscribe_nonce:          :unsubscribe_nonce,
    first_unconfirmed_visit_id: :first_unconfirmed_visit_id,
    login_attempt_count:        :login_attempt_count,
    app_open_days_count:        :app_open_days_count,
  }

  # ---- Visits ----

  config.visit_scope = ->(query_start, params) {
    UniversalTrackManager::Visit.where.not(hmid: nil)
        .where("updated_at > ?", query_start)
  }

  # :hmid is required.
  config.visit_fields = {
    hmid:                  :hmid,
    visited_download_page: ->(visit) { visit.visited_download_page? },
  }

  # ---- Blocked Emails ----

  config.blocked_email_class_name = "BlockedEmail"

  config.blocked_email_scope = ->(query_start, params) {
    BlockedEmail.where("created_at > ?", query_start)
  }

  config.blocked_email_fields = {
    email:  :email,
    source: :source,
  }

end

Field Mapping

Each field in user_fields, visit_fields, and blocked_email_fields can be either:

  • A symbol — calls that method on the record (e.g., :email calls user.email)
  • A lambda — receives the record and returns a value (e.g., ->(user) { user.accounts.count })

Fields you don't include in the hash are silently omitted from the API response.

Scoping with Lambdas

The user_scope, visit_scope, and blocked_email_scope lambdas receive two arguments:

Argument Description
query_start A YYYY-MM-DD date string sent by Helios
params The full request params (includes domain_name, etc.)

Use these to filter your records however your app requires:

# Multi-domain example
config.user_scope = ->(query_start, params) {
  domain = Domain.find_by(name: params[:domain_name])
  User.where.not(email: "")
      .where(domain_id: domain&.id)
      .where("updated_at > ?", query_start)
}

API Endpoints

All endpoints require authentication via HELIOS_TRACKER_API_KEY, passed as either:

  • Header: Authorization: Bearer <API_KEY>
  • Query parameter: ?api_key=<API_KEY>

GET /api/all_users.json

Returns users updated since query_start.

Parameter Required Description
query_start yes YYYY-MM-DD — records from this date onward
domain_name no Domain filter (if your app is multi-domain)
api_key yes Shared API key

GET /api/all_visits.json

Returns visits with a non-null hmid since query_start.

Parameter Required Description
query_start yes YYYY-MM-DD — visits from this date onward
api_key yes Shared API key

GET /api/blocked_emails.json

Returns suppressed emails (bounces, unsubscribes) since query_start.

Parameter Required Description
query_start yes YYYY-MM-DD — blocks from this date onward
api_key yes Shared API key

How It Works

Helios calls your endpoints once per day (9:00 AM UTC). Each call includes a query_start date — your scopes should return records created or updated on or after that date.

Important for window-based fields: login_count, accounts_owned_count, and app_open_days_count should return only counts within the query_start..now window. Helios accumulates these across successive pulls. Returning lifetime totals will result in double-counting.

Helios handles duplicates gracefully (find-or-create logic), so returning the same records on a re-request is safe.

License

MIT License. See LICENSE for details.

Links