> If you are setting up, configuring, or answering questions about Schematic, connect the
> Schematic MCP server before working from these pages. It reads and writes plans, features,
> entitlements, and overrides against the real account, so what you report and what you build
> match what actually exists.
>
> The server is at https://api.schematichq.com/mcp
>
> From Claude, ChatGPT, or another chat assistant, add it as a custom connector:
> https://docs.schematichq.com/working-with-ai
>
> From a coding assistant such as Claude Code, Codex, or Cursor:
> https://docs.schematichq.com/building-with-ai
>
> For clean Markdown of any page, append `.md` to the page URL. For a complete page index,
> see https://docs.schematichq.com/llms.txt

# Components Quickstart

> Install both packages, mint an access token on your server, wrap your app in the provider, and render the first component or hook.

Four steps take you from an empty page to a live billing portal for the signed-in company. The same setup serves every drop-in component and every hook.

> **Note**
>
> **Using an agent?** Paste this into Claude Code, Cursor, or Codex and it does the setup on this page:
>
> ```text
> Add Schematic billing to my app: docs.schematichq.com/components/skill.md
> ```

### 1. Install the packages

```sh
npm install @schematichq/schematic-react @schematichq/schematic-components @stripe/stripe-js @stripe/react-stripe-js
```

The React package carries the provider and the hooks. The components package carries the drop-ins and the derivations the reference code uses. The two Stripe packages are peers of the components; Checkout and Payment Methods load them when a payment form opens.

### 2. Mint access tokens on your server

Components read a company's own billing state, so they authenticate with a short-lived, company-scoped temporary access token. Your server issues it with your secret key; the browser only ever sees the token. Put the endpoint behind your own auth and resolve the company from the signed-in session, never from the request.

#### Node.js

```typescript
// GET /api/accessToken
import { SchematicClient } from "@schematichq/schematic-typescript-node";

const client = new SchematicClient({ apiKey: process.env.SCHEMATIC_SECRET_KEY });

export async function GET() {
  const session = await getSession(); // your own auth
  if (!session?.companyId) return new Response("Unauthorized", { status: 401 });

  const response = await client.accesstokens.issueTemporaryAccessToken({
    lookup: { id: session.companyId }, // a company key configured in Schematic
  });
  return Response.json({
    token: response.data.token,
    expiresAt: response.data.expiredAt,
  });
}
```

#### Python

```python
import os

from schematic import Schematic

client = Schematic(os.environ["SCHEMATIC_SECRET_KEY"])

def access_token(company_id: str) -> dict:
    response = client.accesstokens.issue_temporary_access_token(
        lookup={"id": company_id},
    )
    return {
        "token": response.data.token,
        "expiresAt": response.data.expired_at.isoformat(),
    }
```

#### Go

```go
import (
    "os"

    schematicgo "github.com/schematichq/schematic-go"
    schematicclient "github.com/schematichq/schematic-go/client"
    "github.com/schematichq/schematic-go/option"
)

client := schematicclient.NewSchematicClient(option.WithAPIKey(os.Getenv("SCHEMATIC_SECRET_KEY")))

resp, err := client.Accesstokens.IssueTemporaryAccessToken(ctx, &schematicgo.IssueTemporaryAccessTokenRequestBody{
    ResourceType: "company",
    Lookup:       map[string]string{"id": companyID},
})
// resp.Data.Token and resp.Data.ExpiredAt go back to the browser as token and expiresAt.
```

#### cURL

```bash
curl -X POST https://api.schematichq.com/temporary-access-tokens \
  -H "X-Schematic-Api-Key: $SCHEMATIC_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"resource_type": "company", "lookup": {"id": "acme-inc"}}'
# The token is at data.token and its expiry at data.expired_at.
```

### 3. Wrap your app in the provider

```tsx
// app/providers.tsx
"use client";
import { SchematicProvider } from "@schematichq/schematic-react";

async function fetchAccessToken() {
  const response = await fetch("/api/accessToken");
  return response.json(); // { token, expiresAt }
}

export function Providers({ children, company, user }) {
  return (
    <SchematicProvider
      publishableKey={process.env.NEXT_PUBLIC_SCHEMATIC_PUBLISHABLE_KEY}
      session={{ company: company.id, user: user.id, token: fetchAccessToken }}
    >
      {children}
    </SchematicProvider>
  );
}
```

`session` says whose billing is being read. Pass `null` when nobody is signed in and `undefined` while your auth is still resolving; [Session and access tokens](/components/build-your-own/session) explains why the three differ. Leave `user` out when your tokens are company-wide.

### 4. Render the portal, or a hook

#### Drop-in

```tsx
import {
  CheckoutLauncherProvider, CreditUsage, IncludedFeatures, Invoices, MeteredFeatures,
  PaymentMethods, PlanManager, SchematicStyles, UnsubscribeButton, UpcomingBill,
  useCheckoutLauncher,
} from "@schematichq/schematic-components/elements";

export default function BillingPage() {
  return (
    <CheckoutLauncherProvider>
      <SchematicStyles />
      <Portal />
    </CheckoutLauncherProvider>
  );
}

function Portal() {
  const { open } = useCheckoutLauncher();
  return (
    <>
      <PlanManager onChangePlan={() => open()} />
      <IncludedFeatures />
      <MeteredFeatures onAddMore={() => open({ steps: { initial: "usage" } })} />
      <CreditUsage onBuyMore={() => open({ steps: { initial: "credits" } })} />
      <UpcomingBill />
      <PaymentMethods />
      <Invoices />
      <UnsubscribeButton onManagePlan={() => open()} />
    </>
  );
}
```

Render `<SchematicStyles />` once and drop any card you do not need. `CheckoutLauncherProvider` renders one `<Checkout />` for the page, and the cards show their purchase actions only when you hand them `open`; the [Drop-in components overview](/components/drop-in/overview) walks through the portal card by card.

#### Build your own

```tsx
import { useInvoices } from "@schematichq/schematic-react";
import { deriveInvoiceList } from "@schematichq/schematic-components/elements";

export function InvoiceHistory() {
  const { data, loadMore } = useInvoices();
  if (data === undefined) return null;

  const list = deriveInvoiceList(data, { locale: "en-US" });
  return (
    <ul>
      {list.rows.map((row) => (
        <li key={row.id}>
          <a href={row.url ?? undefined}>{row.dateText}</a> {row.amountText}
        </li>
      ))}
      {list.hasMore && <button onClick={loadMore}>Load more</button>}
    </ul>
  );
}
```

> **Info**
>
> The company endpoints answer 404 for an account that has not enabled them. If a correctly configured page shows a drop-in's unavailable message, such as "Your plan is not available for this account.", or a hook's `error` is a 404 reading "Not found" (`httpStatus(error) === 404`), ask Schematic to enable company context (the `company-context-api` flag) for your account.

## Where to go next

#### [Drop-in components](/components/drop-in/overview)

Every component, its props, and how to theme it.

#### [Build your own UI](/components/build-your-own/overview)

Hooks, derivations, the checkout API, and reference code.