Skip to content
Indiana
Dashboard
Docs
Browse docs / Quickstart

Quickstart

NEXT.JS APP ROUTER · TYPESCRIPT · SANDBOX

Identify a Customer, issue a session and read holdings in three HTTP requests.

Download HTTP integration example
API v1Updated

Before you start

Use HTTP to complete Customer → session → holdings, without installing an Indiana SDK. Add purchases, balances or UI after the connection works.

  • Select App/Sandbox in the Console, open SDK setup and copy the API URL and App ID.
  • Create a Server Key with customers:write, sessions:create and entitlements:read. Prepare Node.js 22+ and a test user ID verified by your login system.
  • A paid Product, Offering, Paywall or Funnel is not required for this first connection.

Related:Keys, scope and permissions

1. Configure Sandbox

Save configuration in an uncommitted .env.local. Add INDIANA_VERIFIED_USER_ID using your verified test user ID. Browser configuration is used in the later Web integration.

.env.localShell
# Copy these values from your App → Sandbox → SDK setupINDIANA_BASE_URL=https://your-indiana-host.exampleINDIANA_PROJECT_ID=your-project-idINDIANA_SECRET_KEY=your-sandbox-server-key # Public browser configuration; no secret keyNEXT_PUBLIC_INDIANA_BASE_URL=https://your-indiana-host.exampleNEXT_PUBLIC_INDIANA_PROJECT_ID=your-project-id

2. Identify and read holdings

Save the complete script as quickstart.mjs and run it. All three requests use the same App/Sandbox. The final request uses the Customer Token to read its own data, without customerId.

Expected result

The script prints customerId and balances. Repeating an externalUserId in the same scope returns the same Customer. Empty balances still mean the connection worked; there is no eligible holding yet.

quickstart.mjsJavaScript
// Node.js 22+. Run on your trusted backend. Never log keys or Customer Tokens.const required = (name) => {  const value = process.env[name];  if (!value) throw new Error(`Missing ${name}`);  return value;};const baseUrl = required("INDIANA_BASE_URL");const projectId = required("INDIANA_PROJECT_ID");const serverKey = required("INDIANA_SECRET_KEY");async function post(path, body, credential = serverKey) {  const response = await fetch(new URL(path, baseUrl), {    method: "POST", redirect: "error", signal: AbortSignal.timeout(10000),    headers: {      Authorization: `Bearer ${credential}`, "Content-Type": "application/json",      "X-Indiana-Project": projectId, "X-Indiana-Environment": "sandbox",    },    body: JSON.stringify(body),  });  const result = await response.json();  if (!response.ok) throw new Error(`${response.status} ${result.error?.code ?? "HTTP_ERROR"} requestId=${result.requestId ?? "unknown"}`);  return result;}// Use an ID verified by your own login system.const { customer } = await post("/api/v1/customers", {  externalUserId: required("INDIANA_VERIFIED_USER_ID"),});const { customerToken } = await post("/api/v1/customer-sessions", { customerId: customer.id });const { holdings } = await post("/api/v1/customer-holdings", {}, customerToken);console.log({ customerId: customer.id, balances: holdings.balances });

3. Connect your login system

In your app, put the first two requests in your authenticated backend: verify login → identify by stable user.id → return only customerToken/expiresAt. Return 401 anonymously and never trust a browser-supplied Customer ID.

Related:Copy the Next.js HTTP integration files

4. Connect your Web app

Add the exact website Origin in SDK setup, including scheme, host and port. The browser gets a token from your session endpoint and reads customer-holdings. Keep the token in memory; cancel requests and clear prior state on sign-out or account changes.

Completion checklist

Anonymous session calls return 401; signed-in reads return the current Customer's holdings; no Server Key reaches the browser; account changes clear old data. Reads are for display; protected business work still requires backend authorization and atomic consumption.

Troubleshooting and next steps

What to check
ResultAction
UNAUTHENTICATEDDistinguish host login from Indiana credential failure. Check the Key or issue a fresh expired session.
FORBIDDENCheck the operation permission and browser Origin.
SCOPE_MISMATCHMatch the Key, App ID, environment and server/browser configuration.
balances: []The connection worked. Configure a Feature and free default Product, or purchase independently; no Funnel is required.

What to check

  • UNAUTHENTICATED

    Action
    Distinguish host login from Indiana credential failure. Check the Key or issue a fresh expired session.
  • FORBIDDEN

    Action
    Check the operation permission and browser Origin.
  • SCOPE_MISMATCH

    Action
    Match the Key, App ID, environment and server/browser configuration.
  • balances: []

    Action
    The connection worked. Configure a Feature and free default Product, or purchase independently; no Funnel is required.

Related:Understand Product, Feature and Entitlement

Related:Add metering and consumption

Related:Troubleshoot by error code