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

# Getting Started with the Statisfy SDK

> Install @statisfy/digital-workers-react, mint a session token, wrap your app in StatisfyProvider, and render your first Digital Worker chat.

This guide takes you from an empty React app to a working Digital Worker chat embedded in your product.

## Prerequisites

* A React 18 or 19 application.
* A **publishable key** for your tenant (`pk_live_…` or `pk_test_…`). Create one in **Portals → Developer keys** (see [Portals overview](/portals/overview)).
* A **published Portal** in your tenant — its `portal_id` scopes the SDK session.
* Your customers sign in to your app through your tenant's identity provider (Clerk), so you can obtain a signed JWT for the current user.

## 1. Install

```bash theme={null}
npm install @statisfy/digital-workers-react
```

Import the stylesheet once, near your app's entry point:

```tsx theme={null}
import '@statisfy/digital-workers-react/styles.css'
```

## 2. Mint a session token

Before the SDK can talk to Statisfy, your app exchanges the signed-in user's identity for a Statisfy **session token**. Do this against `POST /sdk/auth`, sending the user's JWT as the bearer, your publishable key as a header, and the target `portal_id` in the body:

```ts theme={null}
async function mintStatisfyToken(userJwt: string): Promise<string> {
  const res = await fetch('https://api.statisfy.app/sdk/auth', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${userJwt}`,
      'X-Statisfy-Publishable-Key': 'pk_live_your_key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ portal_id: 'your-portal-uuid' })
  })
  if (!res.ok) throw new Error(`Statisfy auth failed: ${res.status}`)
  const { token } = await res.json() // { token, token_type, expires_in, portal? }
  return token
}
```

<Note>
  The returned token is short-lived (default **1 hour**, see `expires_in`). Cache it and re-mint before it expires — `getToken` (next step) is the natural place to do that.
</Note>

The user's email must belong to a **customer** (a person on an account) of your tenant — that's how the gateway binds the session to a `customer_id`. If the email isn't a customer, `/sdk/auth` returns `403 not_a_customer`.

## 3. Wrap your app in `StatisfyProvider`

`StatisfyProvider` supplies the gateway URL and authentication to every SDK component via React context. Set it up once, high in your tree:

```tsx theme={null}
import { StatisfyProvider } from '@statisfy/digital-workers-react'
import { useCallback } from 'react'

function Root() {
  // Return the current session token; re-mint when needed. Memoize so the
  // provider doesn't re-render its subtree on every render.
  const getToken = useCallback(async () => {
    return await getCachedOrFreshStatisfyToken() // your caching around mintStatisfyToken
  }, [])

  return (
    <StatisfyProvider
      baseUrl="https://api.statisfy.app"
      publishableKey="pk_live_your_key"
      getToken={getToken}
    >
      <YourApp />
    </StatisfyProvider>
  )
}
```

| Prop             | Type                            | Required | Description                                                                   |
| ---------------- | ------------------------------- | -------- | ----------------------------------------------------------------------------- |
| `baseUrl`        | `string`                        | yes      | Statisfy gateway base URL, e.g. `https://api.statisfy.app`.                   |
| `getToken`       | `() => Promise<string \| null>` | yes      | Returns the current session token (from `/sdk/auth`). Called before requests. |
| `publishableKey` | `string`                        | no       | Your `pk_live_…` / `pk_test_…`. Required for the chat routes.                 |

<Warning>
  Pass a **stable** `getToken` (wrap it in `useCallback`). The provider memoizes its context value on `getToken` identity, so an inline arrow function would re-render every consumer on each render.
</Warning>

## 4. Render your first component

Now any SDK component works anywhere beneath the provider — no connection props needed.

```tsx theme={null}
import { DigitalWorkerChat } from '@statisfy/digital-workers-react'

function SupportPanel() {
  return (
    <DigitalWorkerChat
      workerId="your-digital-worker-uuid"
      workerName="Onboarding Assistant"
      greeting="Hi! How can I help with your onboarding?"
      className="dw-h-[600px]"
    />
  )
}
```

That's a fully working chat: streaming replies, conversation history, unread tracking, and inline forms. See [Digital Worker Chat](/sdk/digital_worker_chat) for every prop and the headless client.

To show a customer's onboarding project instead, render the [Project Module](/sdk/projects):

```tsx theme={null}
import { ProjectModule } from '@statisfy/digital-workers-react'

// Omit projectId to auto-resolve the signed-in customer's onboarding project.
<ProjectModule />
```

## 5. Theming (optional)

The SDK's styles are self-contained and prefixed (`dw:`) so they won't collide with your app's CSS. Override the look with `--dw-*` CSS variables on any ancestor of the SDK components:

```css theme={null}
:root {
  --dw-primary: #4f46e5;        /* accent / buttons */
  --dw-radius: 12px;            /* corner rounding */
  /* …see the rendered widget for the full set of --dw-* variables */
}
```

## Troubleshooting

<Accordion title="401 user_token_invalid">
  The bearer token's signature didn't verify. Make sure `getToken` returns the **session token from `/sdk/auth`** (not the raw Clerk JWT), that it hasn't expired, and that you minted it against the same tenant/environment your gateway points at.
</Accordion>

<Accordion title="403 not_a_customer">
  The signed-in user's email isn't a person on any account in your tenant. Add them as a contact, or sign in as a known customer.
</Accordion>

<Accordion title="Components throw 'must be rendered inside <StatisfyProvider>'">
  An SDK component is mounted outside the provider. Move it beneath `<StatisfyProvider>`.
</Accordion>
