Skip to main content
This page is written for an agent that writes client code. Every fact here is also on another page of this site. When a page and the TypeScript types of the published packages disagree, the types win. To load this site into an agent, use the menu at the top of any page. It offers Copy, ChatGPT, Claude, Cursor, VS Code, and MCP. Mintlify also serves the whole site as text at https://docs.grainql.com/llms.txt (an index) and https://docs.grainql.com/llms-full.txt (every page).
Machine-readable sources: dist/index.d.ts and dist/types/index.d.ts of @grainql/tag 4.5.1, and dist/index.d.ts plus dist/react/index.d.ts of @grainql/analytics-web 3.4.2. Read them from node_modules before you generate code.

Hard facts

  • The tenant identifier in every call is the tenant alias that the dashboard shows at https://grainql.com/dashboard, not the tenant UUID. The placeholder on this site is your-tenant-id.
  • Grain Tag is the browser script: https://tag.grainql.com/v4/your-tenant-id.js, or npm @grainql/tag. It collects page views, clicks, scroll depth, heatmaps, DOM snapshots, sessions, and attention without code. It has no remote configuration and no authentication strategy.
  • Analytics Web is the TypeScript SDK: npm @grainql/analytics-web. It sends events and user properties, reads remote configuration, runs in browsers and in Node.js, and ships React hooks under @grainql/analytics-web/react.
  • Both packages are cookieless by default. The visitor ID rotates every day. A persistent ID is stored only after a consent call.
  • Analytics Web batches events. The queue flushes every 5000 milliseconds or at 50 events, whichever comes first. flush() sends the queue now.
  • Analytics Web has three authStrategy values: NONE (the default), SERVER_SIDE with secretKey, and JWT with authProvider. SERVER_SIDE sends Authorization: Chase <secret>.
  • The Query API base URL is https://queryapis.grainql.com/v1/api/query. Every request carries an X-API-Key header.
  • The MCP server is at https://grainql.com/api/mcp. It uses OAuth 2.1 with the scopes mcp:read, mcp:query, and mcp:investigate. It is read-only.
  • Every remote configuration value is a string. getConfig returns string | undefined, and getAllConfigs returns Record<string, string>.
  • In Grain Tag, track and identify called before init are queued and replayed after init.
  • import { init, track, identify } from '@grainql/tag' has no side effects. Outside a browser, init returns a no-op stub, so server-side rendering needs no guard.

Which package

Read Install Grain for the full comparison.

Surface

Grain Tag, module exports of @grainql/tag. The script build exposes the same init, getInstance, track, identify, and GrainTag on window.GrainTag. Grain Tag, methods of GrainTagInstance. Analytics Web, the GrainAnalytics class of @grainql/analytics-web. createGrainAnalytics(config) returns the same class as new GrainAnalytics(config). SendEventOptions is { flush?: boolean }. RemoteConfigOptions is { immediateKeys?: string[], properties?: Record<string, string>, userId?: string, forceRefresh?: boolean, currentUrl?: string }. The types also export trackSystemEvent, getEphemeralSessionId, getCurrentPage, getEventCountSinceLastHeartbeat, resetEventCountSinceLastHeartbeat, getActivityDetector, getEffectiveUserIdPublic, and log as public methods. The tracking managers of the SDK call them. Application code does not need them. Analytics Web, exports of @grainql/analytics-web/react. UseConfigOptions and UseAllConfigsOptions are both { forceRefresh?: boolean, immediateKeys?: string[], properties?: Record<string, string> }. Read SDK reference and React hooks.

Configuration

GrainTagConfig, the argument of init in Grain Tag. The type file documents no defaults. GrainConfig, the argument of new GrainAnalytics and createGrainAnalytics in Analytics Web. Read Configuration options. A grant call moves the visitor from a daily rotating ID to a persistent ID. A revoke call clears the persistent ID. Read Privacy and consent.

Query API

Base URL https://queryapis.grainql.com/v1/api/query. Every request carries X-API-Key and, for POST, Content-Type: application/json. The path parameter is the tenant alias. Rate limits apply per API key. A response over the limit is 429 with a Retry-After header in seconds. Custom plans with higher limits exist. Read Query API for the error responses and Export and query data for a task guide.

Mistakes to avoid

Every SDK, the script URL, and the Query API path take the tenant alias that the dashboard shows. A UUID in tenantId sends events to no tenant. Replace your-tenant-id with the alias.
secretKey is a server credential. A bundle ships it to every visitor. In the browser, use Grain Tag, or Analytics Web with NONE or JWT. Keep SERVER_SIDE in Node.js and read the key from $GRAIN_SECRET_KEY.
GrainTagInstance has no getConfig. Remote configuration, feature flags, and A/B variants are Analytics Web features. Install @grainql/analytics-web for them.
Analytics Web collects no heatmap data by default. Set enableHeatmapTracking: true, or use Grain Tag, which collects heatmaps and DOM snapshots without an option.
Grain Tag has init, track, identify, and consent.grant. Analytics Web has createGrainAnalytics, grain.track, grain.login, and grain.grantConsent. A file that imports both sends every event two times. Pick one package per surface.
Analytics Web queues events and sends them on a timer. A function that returns before the timer fires loses the queue. Call await grain.flush() before the return, or pass { flush: true } to track.
Every configuration value is a string. getConfig('feature_enabled') returns 'true', not true. Compare with === 'true'.
The Query API allows 2 requests per minute on the Builder plan. A tight retry loop stays at 429. Read the Retry-After header and wait that many seconds before the next request.

Client checklist

1

Read the tenant alias from the dashboard

Put it in tenantId, in the script URL, or in the Query API path. Never the UUID.
2

Pick one package per surface

Grain Tag in the browser for automatic tracking. Analytics Web for remote configuration, React hooks, and servers.
3

Keep secrets on the server

secretKey and the Query API key come from environment variables in server code only.
4

Name events in snake_case

Use one noun and one past-tense verb, for example signup_completed. Read Name events.
5

Call identify or login after sign-in

Before that call, the visitor has a daily rotating ID. Read Identify users.
6

Flush before a short-lived process exits

await grain.flush() at the end of a serverless function, a script, or a test.
7

Compare configuration values as strings

Give every key a default in defaultConfigurations so the first render has a value.

Do not generate

  • Do not generate a consent banner that blocks tracking in the default mode. Both packages are cookieless by default and need no consent for that mode.
  • Do not generate code that reads window.grain. The script build exposes window.GrainTag. Analytics Web exposes nothing on window when it is imported from npm.
  • Do not generate an apiUrl other than the default unless the user has one.
  • Do not generate a call to grain.getConfig in Grain Tag code, or a call to consent.grant in Analytics Web code. Each package has its own API.
  • Do not generate a Query API call from browser code. The API key is a server credential.
  • Do not generate a method, an option, or an endpoint that is not in the tables on this page.