# Installing Hinto's browser SDK

Hinto investigates support tickets by reading evidence. The browser SDK is where most of that evidence
comes from: it records what your users actually did in your app, so an investigation can cite a real
session instead of guessing.

This guide is the manual path. If you use a coding agent (Claude Code, Cursor, Codex, or any MCP client),
Hinto's onboarding hands you a prompt that does all of this for you, and the agent-driven setup path can
perform the install itself. See https://gethinto.com/agent-onboarding.md

## What it captures

- Page views and navigation
- Clicks and other interactions
- Console messages
- Network request outcomes: method, URL, status, and duration. Never request or response bodies, headers, or cookies
- The signed-in user you attach with `identify`, so a session can be matched to a customer

## What you need first

1. An **ingest key**, minted in Hinto under Settings, API keys, or handed to you by the onboarding step.
   An ingest key is a publishable browser key: it can only write session data, it cannot read tickets,
   customers, or investigations, and you can revoke it at any time.
2. Your app's **origin** registered in Hinto (Settings, API keys, "Allowed app origins"). The browser
   blocks requests from an unregistered origin before they ever reach Hinto, so this is not optional. Add
   `http://localhost:3000` (or whichever port you use) while you develop.

## Install

```
npm i @gethinto/browser-sdk
```

Use whichever package manager the project already uses.

## Initialize

Initialize once at app startup, and keep the returned client so you can call `identify` later.

```js
import { initHinto } from "@gethinto/browser-sdk";

const hinto = initHinto({
  ingestUrl: "https://your-hinto-api-host/v1/ingest",
  apiKey: import.meta.env.VITE_HINTO_INGEST_KEY, // or your framework's public env var
});
```

Put the key in an environment file rather than hard-coding it. It ends up in your frontend bundle either
way, which is fine for this key class, but an env var is what lets you rotate it without a code change.

## Identify your users

This is the step that makes the whole thing worth doing.

```js
// Wherever the signed-in user becomes known:
hinto.identify({ kind: "email", value: user.email });
```

Without `identify`, sessions still arrive, but nobody can tell whose they are. An investigation into
"Priya cannot export her report" needs Priya's session, and `identify` is how Hinto finds it. You can also
identify by your own user id with `{ kind: "external_id", value: user.id }`.

## Verify it works

Run your app, sign in, and load a page. In Hinto, the onboarding SDK step flips to "first session
received" on its own, and the Evidence page starts reporting session coverage.

## Troubleshooting

**No sessions arriving.** Almost always an origin mismatch. Open your browser's network tab and look for a
failed request to `/v1/ingest`; a CORS error means the origin your app is served from is not in Hinto's
allowed list. Add it under Settings, API keys. Remember that `http://localhost:3000` and
`http://127.0.0.1:3000` are different origins, as are different ports.

**Sessions arriving but not attached to anyone.** `identify` is missing, or it runs before your user object
is loaded. Call it after sign-in completes, and again after a user switch.

**A 401 from the ingest endpoint.** The key is wrong, was revoked, or belongs to a different environment.
Mint a fresh one under Settings, API keys.

**A 403 from the ingest endpoint.** The key exists but lacks session-ingest permission. Mint a key with the
session capture purpose rather than the MCP handoff purpose.

## Privacy

The SDK records what your app did, not what your users typed. Its defaults are deny-first: it keeps as
little as possible, and scrubs what it does keep before anything leaves the browser.

### What is never captured

- Form values and keystrokes
- Request and response bodies
- Headers and cookies
- Full query strings, which are stripped by default from every captured URL

### What redaction covers

Every captured URL, including its path segments, and every payload string passes through redaction
patterns before it leaves the browser. The built-in patterns scrub email addresses, bearer tokens,
sensitive query parameters such as `token`, `secret`, `password`, API keys and suffixed forms like
`reset_token`, OAuth `code` values, and long opaque strings such as hex ids and API tokens.

### Tuning

Two config knobs let you adjust this:

```js
initHinto({
  ingestUrl: "https://your-hinto-api-host/v1/ingest",
  apiKey: import.meta.env.VITE_HINTO_INGEST_KEY,
  // Keep named query parameters in captured URLs. The default keeps none.
  allowUrlSearchParams: ["page"],
  // Replace the built-in redaction patterns with your own. This applies to
  // both URLs and payloads.
  redactPatterns: [/\/reset\/[A-Za-z0-9]+/g],
});
```

`allowUrlSearchParams` is an allowlist of query parameter names. The redaction patterns still run over
whatever survives, so a parameter named like a secret is scrubbed even when allowlisted. Allowlist a
parameter only if you are comfortable capturing its values.

`redactPatterns` replaces the default list rather than extending it, so include the built-in shapes you
still want alongside your own (for example, a custom pattern for short path tokens like `/reset/AbC123`
that the defaults do not catch).

Before production, review what your app prints to the console, and treat captured sessions with the same
care as your own logs.
