> ## Documentation Index
> Fetch the complete documentation index at: https://docs.priceos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# useCustomer

> Read customer data and feature access, track usage, checkout, and open customer portal from one hook.

The `useCustomer` hook fetches customer profile data and feature access in one request. If the customer does not exist yet, PriceOS will create it automatically.

<Tip>
  Setup first: see the [React integration guide](/react-integration).
</Tip>

<Tabs>
  <Tab title="Typed">
    ```tsx theme={null}
    const result = useCustomer<MyFeatures>(options?)
    ```
  </Tab>

  <Tab title="Untyped">
    ```tsx theme={null}
    const result = useCustomer(options?)
    ```
  </Tab>
</Tabs>

## Parameters

<ParamField path="options.enabled" type="boolean">
  Enable or disable fetching. Defaults to `true`.
</ParamField>

<ParamField path="options.errorOnNotFound" type="boolean">
  When `true`, a `404` is surfaced as an error instead of returning `null`. Defaults to `false`.
</ParamField>

<ParamField path="options.swr" type="SWRConfiguration<PriceOSCustomer<MyFeatures> | null, Error>">
  Optional [SWR config](https://swr.vercel.app/docs/api#options) for caching/revalidation behavior.
</ParamField>

## Returns

<ParamField path="customer" type="PriceOSCustomer<MyFeatures> | null">
  Customer data including profile fields and `featureAccess`.
</ParamField>

<ParamField path="isLoading" type="boolean">
  Whether data is currently loading.
</ParamField>

<ParamField path="error" type="Error | null">
  Error from the latest request.
</ParamField>

<ParamField path="refetch" type="() => Promise<void>">
  Manually revalidate customer data.
</ParamField>

<ParamField path="trackUsage" type="(body) => Promise<TrackUsageResponse>">
  Tracks usage and revalidates customer and feature-access caches.
</ParamField>

<ParamField path="checkout" type="(body) => Promise<CreateCheckoutResponse>">
  Creates a checkout session and redirects to Stripe Checkout.
</ParamField>

<ParamField path="openCustomerPortal" type="(body?) => Promise<CreateCustomerPortalResponse>">
  Creates a customer portal session and redirects to Stripe Customer Portal.
</ParamField>

## `trackUsage` body

<ParamField path="featureKey" type="string" required>
  Feature key to track usage for (typed to your tracked limit feature keys when using generated types).
</ParamField>

<ParamField path="amount" type="number">
  Usage amount to record. Defaults to `1`.
</ParamField>

<ParamField path="eventKey" type="string">
  Stable event key for retries and deduplication.
</ParamField>

<ParamField path="occurredAt" type="number">
  Unix timestamp in milliseconds for when the event happened.
</ParamField>

<ParamField path="metadata" type="Record<string, string>">
  Optional string key/value metadata attached to the usage event.
</ParamField>

<Warning>
  When using built-in Next.js handlers (`priceos/next`), client hook calls must use a positive `amount`.
  For negative adjustments, track usage on your backend with the Node.js SDK or REST API.
</Warning>

## Examples

<Tabs>
  <Tab title="Typed Example">
    ```tsx theme={null}
    import { useCustomer } from "priceos/react";
    import type { MyFeatures } from "./priceos.types";

    export function CustomerCard() {
      const { customer, isLoading, error, refetch, trackUsage, checkout, openCustomerPortal } = useCustomer<MyFeatures>();

      const customerName = customer?.name ?? "Unknown";
      const hasBlogAccess = customer?.featureAccess?.blogs?.hasAccess;

      async function onAddSeat() {
        await trackUsage({
          featureKey: "team_seats",
          amount: 1,
          eventKey: `add_team_seat_${Date.now()}`,
        });
      }

      async function onUpgrade() {
        await checkout({
          productKey: "starter_monthly",
          successUrl: "https://app.acme.com/settings/billing?checkout=success",
          cancelUrl: "https://app.acme.com/settings/billing?checkout=canceled",
        });
      }

      async function onManageBilling() {
        await openCustomerPortal();
      }

      if (isLoading) return <div>Loading...</div>;
      if (error) return <button onClick={refetch}>Retry</button>;

      return (
        <div>
          <h3>{customerName}</h3>
          <p>Blog access: {hasBlogAccess ? "Yes" : "No"}</p>
          <button onClick={onAddSeat}>Add teammate</button>
          <button onClick={onUpgrade}>Upgrade</button>
          <button onClick={onManageBilling}>Manage billing</button>
        </div>
      );
    }
    ```
  </Tab>

  <Tab title="Untyped Example">
    ```tsx theme={null}
    import { useCustomer } from "priceos/react";

    export function CustomerCard() {
      const { customer, isLoading, error, refetch, trackUsage, checkout, openCustomerPortal } = useCustomer();

      const customerName = customer?.name ?? "Unknown";
      const hasBlogAccess = customer?.featureAccess?.blogs?.hasAccess;

      async function onAddSeat() {
        await trackUsage({
          featureKey: "team_seats",
          amount: 1,
          eventKey: `add_team_seat_${Date.now()}`,
        });
      }

      async function onUpgrade() {
        await checkout({
          productKey: "starter_monthly",
          successUrl: "https://app.acme.com/settings/billing?checkout=success",
          cancelUrl: "https://app.acme.com/settings/billing?checkout=canceled",
        });
      }

      async function onManageBilling() {
        await openCustomerPortal();
      }

      if (isLoading) return <div>Loading...</div>;
      if (error) return <button onClick={refetch}>Retry</button>;

      return (
        <div>
          <h3>{customerName}</h3>
          <p>Blog access: {hasBlogAccess ? "Yes" : "No"}</p>
          <button onClick={onAddSeat}>Add teammate</button>
          <button onClick={onUpgrade}>Upgrade</button>
          <button onClick={onManageBilling}>Manage billing</button>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>
