# Integrating Tidings

Tidings is a lightweight product-analytics service. Instrument this codebase to
send it events. The whole integration is plain HTTP — **there is no SDK to
install**. Drop this file at your repo root (or into your agent's rules) and
follow it.

## The one endpoint

- `POST $TIDINGS_API/api/v1/events/`
- Headers: `Content-Type: application/json` and `X-API-Key: $TIDINGS_KEY`.
- The API key is per-project, prefixed `td_`, and managed in the Tidings app
  under **Settings → API keys**. Read it from configuration/environment; never
  hard-code or commit it.

### Single event

```json
{
  "name": "signup_completed",
  "distinct_id": "user_123",
  "properties": { "plan": "pro" },
  "timestamp": "2026-08-13T10:00:00Z"
}
```

- `name` (required) — snake_case description of what happened.
- `distinct_id` (required) — a stable user or session identifier.
- `properties` (optional) — arbitrary JSON context; defaults to `{}`.
- `timestamp` (optional, ISO 8601) — defaults to server arrival time. Send a
  past timestamp to backfill history.

### Batch (up to 500 events)

```json
{ "events": [ { "name": "page_view", "distinct_id": "sess_abc" }, { "name": "button_click", "distinct_id": "sess_abc" } ] }
```

### Responses

- `202 {"accepted": n}` — success.
- `400` — invalid payload (missing field, malformed timestamp, batch > 500).
- `401` — missing, unknown, or revoked key.
- `429` — rate limited (600 requests/min per project; a batch counts as one).

## House rules for the integration

1. Create a small tracking helper in this project's language and house style,
   exposing `track(name, properties)`.
2. Read `TIDINGS_API` and `TIDINGS_KEY` from configuration/environment. If the
   key is unset, every call must **silently no-op**.
3. Analytics must never break the product: swallow all errors, never throw, and
   never block the caller — fire-and-forget (use `keepalive` / background
   delivery where the platform offers it).
4. **Identity.** Get this right — it decides whether the Users list, funnels and
   retention mean anything.
   - **Browser or web app?** Prefer the hosted snippet over hand-written HTTP.
     One tag, no build step:
     `<script defer src="https://tidingshq.com/js/v1/tidings.js" data-key="$TIDINGS_KEY"></script>`
     It mints and persists the anonymous id, sends page views, and does the
     sign-in link below for you; instrumenting is then just
     `tidings.track(name, properties)`. Skip the rest of this point.
   - **Wiring HTTP by hand?** Generate **one** anonymous `distinct_id` per
     device (e.g. `anon_<uuid>`) and persist it — localStorage, or a
     long-lived cookie, where consent allows. Never mint a fresh id per page
     load or per visit: that files every return visit under a new person and
     makes the Users list and retention meaningless.
   - **On sign-in**, POST once to `$TIDINGS_API/api/v1/alias/` with the same
     `X-API-Key` and
     `{"distinct_id": "<your user id>", "anonymous_id": "<the persisted anonymous id>"}`.
     That rewrites everything the anonymous half of the visit did onto the
     account. Treat `409` as "already linked, carry on". From then on send
     every event under the user id; on sign-out, generate a fresh anonymous id.
   - A per-session id is acceptable **only** when the product has no sign-in and
     no consent for persistent storage.
5. Instrument this app's 3–5 most meaningful events (signup, core action
   completed, purchase, …) plus page or screen views where relevant. Name events
   in snake_case.
6. Keep property payloads small and free of secrets or personal data beyond what
   is needed.

When done, list the events you instrumented and where the calls live.

---

The machine-readable version of these docs lives at `/llms.txt`. Full
human docs: /docs.
