Docs

How to send signals

Everything arrives through one front door: POST /api/public/ingest. The snippet, Stripe, CSV import and your own backend all write through it.

1. The front door

Authenticate with your tenant API key. Accounts and end users are created on first sight.

POST /api/public/ingest
{
  "api_key": "lh_…",
  "external_user_id": "user_123",
  "external_account_id": "acct_456",
  "event_name": "module_completed",
  "properties": { "module": "wine-service" },
  "occurred_at": "2026-01-01T12:00:00Z",
  "identity": { "name": "Dana Ruiz", "email": "dana@example.com", "phone": "+15555550123" }
}

Returns 200 with the stored event id. Malformed payloads return 400 with a list of what was wrong. Requests are rate limited per tenant.

2. The tracking snippet

Served from this app. It sends page views, and clicks on any element carrying data-lh-event. Extra properties come from data-lh-prop-* attributes.

<script src="/api/public/lh.js"></script>
<script>
  LongHaul.init({ key: "lh_…" });
  LongHaul.identify({ id: "user_123", email: "dana@example.com", name: "Dana Ruiz", accountId: "acct_456" });
</script>

<button data-lh-event="module_started" data-lh-prop-module="wine-service">Start</button>

3. Stripe

Point a Stripe webhook at /api/public/stripe-webhook?tenant=your-slug and save the signing secret in Setup. We listen for invoice.payment_failed and customer.subscription.created / updated / deleted, map the Stripe customer email to an end user, and write each one through the front door.

4. CSV import

Upload a CSV in Setup and map columns to email, name, account and external id. Rows become accounts and end users immediately; their signals start flowing once the snippet or API is live.

Event naming conventions

  • object_verb, lowercase, underscores: module_completed, quiz_passed.
  • Past tense — the event already happened.
  • Keep the name stable; put the variation in properties.
  • Billing events arrive namespaced: stripe.invoice.payment_failed.

Developer handoff

Every tenant has a Developer handoff page with all of the above already filled in with that tenant's real key, the event names its health model expects, the Stripe webhook URL, and a live panel showing the last ten signals received so a developer can confirm the install without asking anyone.

Sign in and open the tenant, then Developer handoff in the tenant nav.

Sign in to open it

Merge fields

Available in every email, SMS and call script template:

{{first_name}}  {{name}}  {{email}}
{{account_name}}  {{tenant_name}}
{{sender_name}}  {{event_date}}