Skip to main content

Support State

Configure typed feature flags and onboarding state with support.ts.

Use support.ts when your app needs typed feature flags or onboarding progress backed by Cossistant.

The config gives TypeScript the allowed flags, step IDs, and step metadata. The API stores the actual visitor or contact state.

Use this when

  • you want useFeatureFlag("new-message") to autocomplete valid flags
  • you want onboarding steps typed from one config file
  • you need onboarding metadata between steps
  • you want server-side feature flag management with realtime browser updates

Smallest working change

Create support.ts and register it for inference.

tssupport.ts
import {
  createSupport,
  type SupportOnboardingMetadata,
} from "@cossistant/core";
 
export const support = createSupport({
  featureFlags: ["new-message", "billing-v2"],
  onboarding: {
    steps: [
      { id: "workspace", isFirst: true },
      { id: "invite-team" },
      { id: "done", isLast: true },
    ],
  },
});
 
declare module "@cossistant/core/support-config" {
  interface SupportRegister {
    config: typeof support;
    onboardingMetadata: SupportOnboardingMetadata<
      typeof support,
      {
        workspace: { workspaceName?: string };
        "invite-team": { invitedEmails?: string[] };
      }
    >;
  }
}

Pass the config to the provider.

tsapp/providers.tsx
"use client";
 
import { SupportProvider } from "@cossistant/react";
import { support } from "@/support";
 
export function Providers({ children }: { children: React.ReactNode }) {
  return <SupportProvider support={support}>{children}</SupportProvider>;
}

Feature flags

Use useFeatureFlag() in React. The flag name is inferred from support.ts.

tscomponents/new-message-button.tsx
"use client";
 
import { useFeatureFlag } from "@cossistant/react";
 
export function NewMessageButton() {
  const isNewMessageActivated = useFeatureFlag("new-message");
 
  if (!isNewMessageActivated) {
    return null;
  }
 
  return <button>New message</button>;
}

Feature flag mutations require a private API key. Do this from your server, not the browser.

tsserver/feature-flags.ts
import { CossistantClient } from "@cossistant/core";
 
const client = new CossistantClient({
  apiUrl: "https://api.cossistant.com/v1",
  wsUrl: "wss://api.cossistant.com/ws",
  apiKey: process.env.COSSISTANT_PRIVATE_API_KEY,
});
 
await client.mutateSupportFeatureFlags({
  target: { type: "contact", id: contactId },
  operation: "add",
  flags: ["new-message"],
});

The same mutation works over HTTP.

curl -X PATCH "https://api.cossistant.com/v1/support/feature-flags" \
  -H "Authorization: Bearer $COSSISTANT_PRIVATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target": { "type": "contact", "id": "01K0000000000000000000000" },
    "operation": "add",
    "flags": ["new-message"]
  }'

Available targets are visitor, contact, and contactOrganization. A flag is enabled when any of those levels has it.

Onboarding

Use useOnboarding() to read the current step, complete steps, and store metadata for a step.

tscomponents/invite-step.tsx
"use client";
 
import { useOnboarding } from "@cossistant/react";
 
export function InviteStep() {
  const { currentStepId, setMetadata, completeStep } = useOnboarding();
 
  if (currentStepId !== "invite-team") {
    return null;
  }
 
  async function submit(invitedEmails: string[]) {
    await setMetadata("invite-team", { invitedEmails });
    await completeStep("invite-team");
  }
 
  return <InviteForm onSubmit={submit} />;
}

Each step has completed and metadata. setMetadata() replaces the metadata object for that step. Pass null to clear it.

Core usage in a browser

Support state also works without React. This public-key example must run in a browser whose Origin is allowed for the website; do not copy it into a Next.js server module. Server mutations use apiKey with a private key, as shown above.

tssupport-runtime.ts
import { CossistantClient } from "@cossistant/core";
import { support } from "./support";
 
const client = new CossistantClient({
  apiUrl: "https://api.cossistant.com/v1",
  wsUrl: "wss://api.cossistant.com/ws",
  publicKey: "pk_test_xxxx",
  support,
});
 
await client.fetchWebsite();
await client.fetchSupportState();
 
const isNewMessageEnabled = client.isFeatureFlagEnabled("new-message");
 
await client.setOnboardingMetadata("workspace", {
  workspaceName: "Acme",
});
 
if (isNewMessageEnabled) {
  await client.completeOnboardingStep("workspace");
}

Observable state model

  • Flags can be assigned at visitor, contact, or contact-organization level; a flag is enabled when any applicable level contains it.
  • Anonymous onboarding state belongs to the visitor. Once identified, contact onboarding state wins.
  • If an anonymous visitor has progress, Cossistant copies it to the contact only when that contact has no onboarding state.
  • Onboarding updates are optimistic. Feature flags and onboarding both update in realtime when Cossistant emits support state changes.

Next step

  1. Overview for the default widget setup.
  2. Hooks Reference for custom React UI.
  3. Types Reference for shared SDK types.

Was this page helpful?

Open a prefilled documentation issue so the team can act on your feedback.