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

# Whitelabel Integration Docs

> Embed ActionFi training tasks directly into your platform with the Partner SDK. Full customization, user attribution, and real-time events.

<Note>
  **Partner SDK**: Embed ActionFi directly into your platform. Your users complete training tasks without leaving your site, you get attribution for every completion, and the LAM gets valuable training data. Everyone wins.
</Note>

## What is the Partner SDK?

The ActionModel Partner SDK lets you **embed the ActionFi training widget** directly into your website or application. Instead of sending users to the ActionFi dashboard, they can discover and complete training tasks right inside your platform.

<CardGroup cols={3}>
  <Card title="Seamless Integration" icon="puzzle-piece" color="#9333ea">
    Users stay on your platform while completing ActionFi tasks
  </Card>

  <Card title="User Attribution" icon="user-check" color="#3b82f6">
    Link completions to your user accounts for rewards tracking
  </Card>

  <Card title="Custom Theming" icon="palette" color="#10b981">
    Match the widget to your brand with full color customization
  </Card>
</CardGroup>

<Info>
  **Who is this for?** The Partner SDK is for platforms that have partnered with ActionFi and want to embed training tasks directly into their product. If you're not yet a partner, [apply here](/actionfi/for-platforms).
</Info>

***

## Quick Start

Get the widget running in under 5 minutes with this minimal setup.

<Steps>
  <Step title="Step 0: Configure npm Access (Private Package)">
    The npm package is private. You need a token from ActionModel to install via npm.

    1. Request a token: contact the **partner onboarding team** and ask for an npm deploy token with `read_package_registry` scope for the Partner SDK.

    2. Add `.npmrc` (project root) or `~/.npmrc` (global):

    ```ini theme={null}
    # ActionModel Partner SDK (private registry)
    @actionmodel:registry=https://gitlab.com/api/v4/projects/76313235/packages/npm/
    //gitlab.com/api/v4/projects/76313235/packages/npm/:_authToken=${ACTIONMODEL_NPM_TOKEN}
    ```

    3. Export the token (local dev):

    ```bash theme={null}
    export ACTIONMODEL_NPM_TOKEN="YOUR_TOKEN"
    ```

    <Warning>
      **Do not commit tokens.** Prefer env var substitution as shown above.
    </Warning>
  </Step>

  <Step title="Add the Container">
    Create a `div` element where you want the widget to appear. Give it enough height for the task list.

    ```html theme={null}
    <div id="actionmodel-widget" style="min-height: 400px;"></div>
    ```
  </Step>

  <Step title="Load the SDK">
    Load the SDK.

    **CDN/UMD (no bundler):**

    ```html theme={null}
    <script src="https://sdk.actionmodel.com/sdk/latest/actionmodel-sdk.umd.js"></script>
    ```

    **npm (bundlers / React / Vue):**

    ```bash theme={null}
    npm install @actionmodel/partner-sdk
    ```
  </Step>

  <Step title="Initialize the Widget">
    Call `ActionModel.init()` with your Partner ID to render the widget.

    ```html theme={null}
    <script>
      const widget = ActionModel.init({
        container: '#actionmodel-widget',
        partnerId: 'your-partner-uuid',
      });
    </script>
    ```
  </Step>
</Steps>

<Tip>
  **Partner ID**: Your Partner ID is a UUID provided when you sign up as an ActionModel partner. Contact the **partner onboarding team** if you don't have one.
</Tip>

### Complete Quick Start Example

```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <title>ActionModel Training Tasks</title>
</head>
<body>
  <!-- Container for the widget -->
  <div id="actionmodel-widget" style="min-height: 400px;"></div>

  <!-- Load SDK -->
  <script src="https://sdk.actionmodel.com/sdk/latest/actionmodel-sdk.umd.js"></script>

  <script>
    const widget = ActionModel.init({
      container: '#actionmodel-widget',
      partnerId: 'your-partner-uuid',
    });
  </script>
</body>
</html>
```

***

## Installation

<Tabs>
  <Tab title="Script Tag (Recommended)">
    ### CDN Installation

    The simplest way to add the SDK is via our CDN. Add this script tag to your HTML:

    ```html theme={null}
    <script src="https://sdk.actionmodel.com/sdk/latest/actionmodel-sdk.umd.js"></script>
    ```

    The SDK exposes a global `ActionModel` object on `window` that you can use to initialize widgets.

    <Note>
      **Best Practice**: Place the script tag at the end of your `<body>` or use the `defer` attribute to ensure the DOM is ready before initialization.
    </Note>

    ```html theme={null}
    <!-- Option 1: At end of body -->
    <body>
      <div id="actionmodel-widget"></div>
      <script src="https://sdk.actionmodel.com/sdk/latest/actionmodel-sdk.umd.js"></script>
      <script>
        ActionModel.init({ /* options */ });
      </script>
    </body>

    <!-- Option 2: With defer in head -->
    <head>
      <script defer src="https://sdk.actionmodel.com/sdk/latest/actionmodel-sdk.umd.js"></script>
    </head>
    ```
  </Tab>

  <Tab title="npm (Recommended)">
    ### Package Manager Installation

    Install via npm (includes TypeScript types and framework wrappers):

    ```bash theme={null}
    npm install @actionmodel/partner-sdk
    ```

    Import and initialize:

    ```typescript theme={null}
    import ActionModel from '@actionmodel/partner-sdk';

    const instance = ActionModel.init({
      container: '#actionmodel-widget',
      partnerId: 'your-partner-uuid',
    });
    ```

    <Info>
      This package is private. If install fails, complete Quick Start Step 0 (`.npmrc` + token).
    </Info>
  </Tab>
</Tabs>

***

## Configuration Options

The `ActionModel.init()` function accepts a configuration object with the following options:

### Required Options

| Option      | Type                    | Description                                               |
| ----------- | ----------------------- | --------------------------------------------------------- |
| `container` | `string \| HTMLElement` | CSS selector (e.g., `'#widget'`) or DOM element reference |
| `partnerId` | `string`                | Your Partner Integration UUID                             |

### Optional Options

| Option     | Type               | Default                    | Description                                |
| ---------- | ------------------ | -------------------------- | ------------------------------------------ |
| `domain`   | `string`           | `window.location.hostname` | Domain to load tasks for                   |
| `bountyId` | `string`           | —                          | Filter tasks to a specific bounty campaign |
| `user`     | `ExternalUserData` | —                          | Identify your user for task attribution    |
| `features` | `WidgetFeatures`   | —                          | Feature flags (e.g. `enableQuery`)         |
| `theme`    | `WidgetTheme`      | Dark mode                  | Theming (`mode`, tokens, radius, font)     |
| `onEvent`  | `(event) => void`  | —                          | Callback for widget events                 |

<AccordionGroup>
  <Accordion title="Basic Configuration Example" icon="code">
    Minimal configuration with just the required options:

    ```javascript theme={null}
    const widget = ActionModel.init({
      container: '#actionmodel-widget',
      partnerId: 'abc123-def456-...',
    });
    ```
  </Accordion>

  <Accordion title="Full Configuration Example" icon="sliders">
    Complete configuration using all available options:

    ```javascript theme={null}
    const widget = ActionModel.init({
      container: document.getElementById('widget'),
      partnerId: 'abc123-def456-...',
      domain: 'mysite.com',
      bountyId: 'bounty-uuid-optional',
      user: {
        externalId: 'user-123',
        email: 'user@example.com',
        metadata: { plan: 'premium', tier: 'gold' }
      },
      features: { enableQuery: true },
      theme: { mode: 'light' },
      onEvent: (event) => {
        analytics.track(event.type, event);
      }
    });
    ```
  </Accordion>

  <Accordion title="Domain Filtering" icon="filter">
    By default, the widget shows tasks for your current domain. You can override this:

    ```javascript theme={null}
    // Show tasks for a specific domain
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      domain: 'app.yourplatform.com',
    });
    ```

    This is useful if:

    * Your widget is on a different subdomain than where tasks are completed
    * You want to preview tasks from another domain during development
  </Accordion>

  <Accordion title="Bounty Filtering" icon="bullseye">
    Filter to show only tasks from a specific bounty campaign:

    ```javascript theme={null}
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      bountyId: 'specific-bounty-campaign-uuid',
    });
    ```

    Use this when running limited-time campaigns or themed task sets.
  </Accordion>
</AccordionGroup>

***

## TypeScript Interfaces

For TypeScript projects, these are the public types exported by the SDK (authoritative for npm installs):

<Accordion title="View TypeScript Interfaces" icon="code">
  ```typescript theme={null}
  export interface WidgetFeatures {
    enableQuery?: boolean;
  }

  export interface ExternalUserData {
    externalId: string;
    email?: string;
    metadata?: Record<string, unknown>;
  }

  export interface WidgetInitOptions {
    container: string | HTMLElement;
    partnerId: string;
    domain?: string;
    bountyId?: string;
    theme?: WidgetTheme;
    onEvent?: (event: PartnerWidgetEvent) => void;
    user?: ExternalUserData;
    features?: WidgetFeatures;
  }

  export type PartnerWidgetEvent =
    | { type: 'widget_initialized'; partnerId: string; domain: string }
    | { type: 'extension_discovered'; partnerId: string; domain: string }
    | { type: 'extension_unavailable'; partnerId: string; domain: string }
    | { type: 'tasks_loaded'; partnerId: string; domain: string; taskCount: number }
    | { type: 'tasks_load_failed'; partnerId: string; domain: string; error: string }
    | { type: 'training_started'; partnerId: string; domain: string; taskId: string }
    | { type: 'training_finished'; partnerId: string; domain: string; taskId?: string }
    | { type: 'training_failed'; partnerId: string; domain: string; taskId: string; error: string; needsUpdate?: boolean }
    | { type: 'view_history_clicked'; partnerId: string; domain: string; recordingSessionId: string }
    | { type: 'query_submitted'; partnerId: string; domain: string; taskId: string; taskCompletionId: string; queryId?: string }
    | { type: 'query_failed'; partnerId: string; domain: string; taskId: string; error: string }
    | { type: 'tasks_updating'; partnerId: string; domain: string; reason: 'training_started' | 'training_finished' | 'query_submitted' | 'manual_refresh' }
    | { type: 'tasks_updated'; partnerId: string; domain: string; taskCount: number; updatedTasks: string[] }
    | { type: 'tasks_update_failed'; partnerId: string; domain: string; error: string; willRetry: boolean }
    | { type: 'recording_in_progress'; partnerId: string; domain: string; taskId: string; elapsedSeconds?: number };

  export interface ActionModelInstance {
    refresh(): void;
    destroy(): void;
  }
  ```
</Accordion>

<Tip>
  **Using with TypeScript**: Copy these interfaces into a `actionmodel.d.ts` file in your project to get full type checking and autocompletion.
</Tip>

***

## User Identification

Link task completions to your users for attribution and analytics. When you identify users, their completions are permanently mapped to your internal user ID.

<Warning>
  **Important**: The `externalId` creates a permanent mapping between your user and ActionModel. Use a stable identifier that won't change (e.g., database user ID, not email address).
</Warning>

### User Object Properties

| Property     | Type     | Required | Description                                     |
| ------------ | -------- | -------- | ----------------------------------------------- |
| `externalId` | `string` | Yes      | Your internal user ID (must be unique per user) |
| `email`      | `string` | No       | User's email address                            |
| `metadata`   | `object` | No       | Additional user attributes for analytics        |

### Basic User Identification

```javascript theme={null}
ActionModel.init({
  container: '#widget',
  partnerId: 'your-partner-uuid',
  user: {
    externalId: 'user_12345',  // Your database user ID
  }
});
```

### Full User Identification

```javascript theme={null}
ActionModel.init({
  container: '#widget',
  partnerId: 'your-partner-uuid',
  user: {
    externalId: 'user_12345',
    email: 'user@example.com',
    metadata: {
      plan: 'enterprise',
      accountAge: 365,
      region: 'US',
      referralSource: 'organic'
    }
  }
});
```

<Info>
  **Metadata Use Cases**: The `metadata` object can contain any JSON-serializable data. Use it to segment users in your analytics, personalize experiences, or track attribution across your systems.
</Info>

### Dynamic User Identification

If your user logs in after the widget is initialized, destroy and reinitialize:

```javascript theme={null}
// Initial anonymous widget
let widget = ActionModel.init({
  container: '#widget',
  partnerId: 'your-partner-uuid',
});

// After user logs in
function onUserLogin(user) {
  widget.destroy();
  widget = ActionModel.init({
    container: '#widget',
    partnerId: 'your-partner-uuid',
    user: {
      externalId: user.id,
      email: user.email,
    }
  });
}
```

***

## Theming

Customize the widget to match your brand. The `theme` option supports `mode`, semantic tokens (`sys`), component tokens (`comp`), border radius, and font family.

<Tabs>
  <Tab title="Presets">
    ### Presets

    ```typescript theme={null}
    import ActionModel, { themePresets } from '@actionmodel/partner-sdk';

    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      theme: themePresets.light, // or themePresets.dark
    });
    ```
  </Tab>

  <Tab title="Helpers">
    ### Helpers (`createLightTheme`, `createDarkTheme`)

    ```typescript theme={null}
    import ActionModel, { createLightTheme } from '@actionmodel/partner-sdk';

    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      theme: createLightTheme({
        sys: { accent: '#0066ff', accentHover: '#0052cc' },
      }),
    });
    ```
  </Tab>

  <Tab title="Common Overrides (sys)">
    ### Common Token Overrides

    ```typescript theme={null}
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      theme: {
        mode: 'light',
        sys: {
          accent: '#0066ff',
          accentHover: '#0052cc',
          bg: '#ffffff',
          surface1: '#f5f5f5',
          surface2: '#ffffff',
          border: '#e5e7eb',
          fg: '#111827',
          fgMuted: '#6b7280',
        },
        borderRadius: { sm: '6px', md: '10px' },
        fontFamily: 'ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif',
      },
    });
    ```
  </Tab>

  <Tab title="Component Overrides (comp)">
    ### Component-Level Overrides

    ```typescript theme={null}
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      theme: {
        comp: {
          taskRowHoverBg: 'rgba(0, 0, 0, 0.04)',
          taskRowSelectedBg: '#e5e7eb',
          rewardBadgeBg: '#eef2ff',
        },
      },
    });
    ```
  </Tab>
</Tabs>

***

## Events

Subscribe to widget events for analytics, custom behavior, or debugging.

### Event Handler Setup

```javascript theme={null}
ActionModel.init({
  container: '#widget',
  partnerId: 'your-partner-uuid',
  onEvent: (event) => {
    console.log('Widget event:', event.type, event);

    // Send to your analytics
    analytics.track(event.type, event);
  }
});
```

### Event Types Reference

| Event                   | Description                                 | Notes                                                                                      |
| ----------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `widget_initialized`    | `init()` called and instance created        | Emitted immediately                                                                        |
| `extension_discovered`  | Extension discovered and validated          | Can re-fire on install/login events                                                        |
| `extension_unavailable` | Extension missing or user not authenticated | Prompt install/account creation                                                            |
| `tasks_loaded`          | Initial tasks page loaded                   | Includes `taskCount`                                                                       |
| `tasks_load_failed`     | Initial tasks load failed                   | Includes `error`                                                                           |
| `training_started`      | Training started                            | Includes `taskId`                                                                          |
| `training_finished`     | Training completed                          | `taskId` may be missing                                                                    |
| `training_failed`       | Training failed                             | May include `needsUpdate`                                                                  |
| `view_history_clicked`  | User clicked "view history"                 | Includes `recordingSessionId`                                                              |
| `query_submitted`       | User submitted a query for a failed task    | Includes `taskCompletionId` and optional `queryId`                                         |
| `query_failed`          | Query submission failed                     | Includes `error`                                                                           |
| `tasks_updating`        | Task list is refreshing                     | Reason: `training_started` \| `training_finished` \| `query_submitted` \| `manual_refresh` |
| `tasks_updated`         | Task list updated                           | Includes `updatedTasks` task IDs                                                           |
| `tasks_update_failed`   | Task refresh failed                         | Includes `willRetry`                                                                       |
| `recording_in_progress` | Recording in progress                       | Optional `elapsedSeconds`                                                                  |

### Event Handling Examples

<AccordionGroup>
  <Accordion title="Analytics Integration" icon="chart-line">
    Send events to your analytics platform:

    ```javascript theme={null}
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      onEvent: (event) => {
        // Google Analytics 4
        gtag('event', event.type, {
          partner_id: event.partnerId,
          domain: event.domain,
          task_id: event.taskId,
          task_count: event.taskCount,
        });

        // Mixpanel
        mixpanel.track(event.type, event);

        // Amplitude
        amplitude.track(event.type, event);
      }
    });
    ```
  </Accordion>

  <Accordion title="Extension Install Prompt" icon="download">
    Show a custom prompt when the extension isn't installed:

    ```javascript theme={null}
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      onEvent: (event) => {
        if (event.type === 'extension_unavailable') {
          showExtensionInstallModal();
        }
      }
    });

    function showExtensionInstallModal() {
      // Your custom modal logic
      document.getElementById('install-prompt').style.display = 'block';
    }
    ```
  </Accordion>

  <Accordion title="Error Handling" icon="triangle-exclamation">
    Handle errors gracefully:

    ```javascript theme={null}
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      onEvent: (event) => {
        switch (event.type) {
          case 'tasks_load_failed':
            console.error('Failed to load tasks:', event.error);
            showErrorMessage('Unable to load training tasks. Please try again.');
            break;
          case 'training_failed':
            console.error('Training failed:', event.error);
            showErrorMessage('Unable to start training. Please check your extension.');
            break;
        }
      }
    });
    ```
  </Accordion>

  <Accordion title="Task Completion Tracking" icon="check">
    Track when users engage with tasks:

    ```javascript theme={null}
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      onEvent: (event) => {
        if (event.type === 'training_started') {
          // User started a task
          console.log(`User started task: ${event.taskId}`);

          // Track engagement
          analytics.track('task_engagement', {
            taskId: event.taskId,
            action: 'started'
          });
        }
      }
    });
    ```
  </Accordion>
</AccordionGroup>

***

## Configuring Webhooks

<Info>
  **Coming Soon**: Webhook configuration is currently being updated. This section will be expanded with full documentation shortly.
</Info>

Webhooks allow you to receive real-time notifications on your server when events occur in the ActionModel widget, such as task completions, user registrations, and training sessions.

### What's Coming

<CardGroup cols={2}>
  <Card title="Task Completion Webhooks" icon="check-circle" color="#10b981">
    Receive POST requests when users complete training tasks, including task ID, user data, and completion metadata.
  </Card>

  <Card title="User Activity Events" icon="user" color="#3b82f6">
    Get notified when users start training sessions, reach milestones, or achieve rewards.
  </Card>

  <Card title="Secure Signatures" icon="shield-check" color="#9333ea">
    All webhook payloads will be signed with HMAC-SHA256 for verification.
  </Card>

  <Card title="Retry Logic" icon="rotate" color="#f59e0b">
    Failed deliveries will be retried with exponential backoff to ensure reliability.
  </Card>
</CardGroup>

### Interested in Early Access?

Contact the **partner onboarding team** to discuss webhook integration for your platform.

***

## Instance Methods

The `init()` function returns a widget instance with methods for controlling the widget.

### `refresh()`

Reload tasks from the server. Useful after user actions that might affect available tasks.

```javascript theme={null}
const widget = ActionModel.init({ /* options */ });

// Refresh tasks manually
document.getElementById('refresh-btn').onclick = () => {
  widget.refresh();
};
```

### `destroy()`

Remove the widget and clean up all event listeners. Always call this before reinitializing or when the widget is no longer needed.

```javascript theme={null}
const widget = ActionModel.init({ /* options */ });

// Clean up when navigating away
window.addEventListener('beforeunload', () => {
  widget.destroy();
});
```

### `ActionModel.destroyAll()`

Static method to destroy all widget instances on the page. Useful in single-page applications.

```javascript theme={null}
// Destroy all widgets
ActionModel.destroyAll();
```

***

## Framework Integration

<Tabs>
  <Tab title="React">
    ### React Integration

    Use the React wrapper from `@actionmodel/partner-sdk/react`.

    ```tsx theme={null}
    import { ActionModelWidget } from '@actionmodel/partner-sdk/react';

    export function TrainingTasks({ partnerId, user }) {
      return (
        <ActionModelWidget
          partnerId={partnerId}
          user={user ? { externalId: user.id, email: user.email } : undefined}
          features={{ enableQuery: true }}
          style={{ minHeight: 400, width: '100%' }}
          onEvent={(e) => console.log(e.type, e)}
        />
      );
    }
    ```

    **Usage:**

    For imperative control, use the hook:

    ```tsx theme={null}
    import { useActionModel } from '@actionmodel/partner-sdk/react';

    const { containerRef, instanceRef, refresh, destroy } = useActionModel({
      partnerId: 'your-partner-uuid',
      onEvent: (e) => console.log(e),
    });

    return (
      <>
        <div ref={containerRef} style={{ minHeight: 400 }} />
        <button onClick={refresh}>Refresh</button>
        <button onClick={destroy}>Destroy</button>
      </>
    );
    ```
  </Tab>

  <Tab title="Next.js">
    ### Next.js Integration

    Use dynamic imports to prevent SSR issues:

    ```tsx theme={null}
    'use client';

    import dynamic from 'next/dynamic';

    const ActionModelWidget = dynamic(
      () => import('@actionmodel/partner-sdk/react').then((m) => m.ActionModelWidget),
      { ssr: false }
    );

    export default function TrainingPage() {
      return <ActionModelWidget partnerId="your-partner-uuid" style={{ minHeight: 400 }} />;
    }
    ```

    **Usage in pages:**

    ```jsx theme={null}
    // app/training/page.tsx
    import ActionModelWidget from '@/components/ActionModelWidget';

    export default function TrainingPage() {
      return (
        <main>
          <h1>Complete Training Tasks</h1>
          <ActionModelWidget partnerId="your-partner-uuid" />
        </main>
      );
    }
    ```
  </Tab>

  <Tab title="Vue 3">
    ### Vue 3 Integration

    Use the Vue 3 wrapper from `@actionmodel/partner-sdk/vue`.

    ```vue theme={null}
    <template>
      <ActionModelWidget
        partner-id="your-partner-uuid"
        :user="{ externalId: 'user-123', email: 'user@example.com' }"
        :features="{ enableQuery: true }"
        :theme="{ mode: 'light' }"
        :style="{ minHeight: '400px' }"
        @event="handleEvent"
      />
    </template>

    <script setup>
    import { ActionModelWidget } from '@actionmodel/partner-sdk/vue';

    const handleEvent = (e) => console.log(e.type, e);
    </script>
    ```

    For Nuxt 3, wrap in `&lt;ClientOnly&gt;` to avoid SSR issues.
  </Tab>

  <Tab title="Angular">
    ### Angular Integration

    Angular should use the vanilla npm API (`@actionmodel/partner-sdk`) or the CDN script tag (UMD). Example using npm:

    ```typescript theme={null}
    import ActionModel, { type ActionModelInstance } from '@actionmodel/partner-sdk';

    let instance: ActionModelInstance | null = null;

    // after view init
    instance = ActionModel.init({
      container: document.getElementById('widget')!,
      partnerId: 'your-partner-uuid',
      onEvent: (e) => console.log(e),
    });

    // on destroy
    instance?.destroy();
    ```
  </Tab>
</Tabs>

***

## Requirements

Before integrating the Partner SDK, ensure you have:

<CardGroup cols={2}>
  <Card title="Partner Integration" icon="handshake" color="#9333ea">
    **Partner ID Required**

    Your `partnerId` must be configured in the ActionModel system. Contact the **partner onboarding team** to get started.
  </Card>

  <Card title="Domain Whitelisting" icon="shield-check" color="#3b82f6">
    **Domain Must Be Allowed**

    Your domain must be whitelisted for your partner integration. This is configured when you sign up as a partner.
  </Card>

  <Card title="Browser Extension" icon="puzzle-piece" color="#10b981">
    **Users Need the Extension**

    Users must have the ActionModel browser extension installed to complete training tasks. The widget will show an install prompt if not detected.
  </Card>

  <Card title="HTTPS Required" icon="lock" color="#f59e0b">
    **Secure Connection**

    The SDK requires HTTPS in production. Development on `localhost` is supported.
  </Card>
</CardGroup>

### Supported Browsers

The ActionModel browser extension supports:

| Browser | Supported | Notes                                |
| ------- | --------- | ------------------------------------ |
| Chrome  | ✅         | Full support                         |
| Edge    | ✅         | Chromium-based                       |
| Brave   | ✅         | Chromium-based                       |
| Arc     | ✅         | Chromium-based                       |
| Opera   | ✅         | Chromium-based                       |
| Mises   | ✅         | Chromium-based, also works on mobile |
| Firefox | 🚧        | Coming soon                          |
| Safari  | 🚧        | Coming soon                          |

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="npm install fails (401/403/404)" icon="box">
    **Problem:** `npm install @actionmodel/partner-sdk` fails with authentication or not found errors.

    **Fix:** Configure `.npmrc` for the private registry and provide a token from ActionModel.

    ```ini theme={null}
    @actionmodel:registry=https://gitlab.com/api/v4/projects/76313235/packages/npm/
    //gitlab.com/api/v4/projects/76313235/packages/npm/:_authToken=${ACTIONMODEL_NPM_TOKEN}
    ```

    Then export the token:

    ```bash theme={null}
    export ACTIONMODEL_NPM_TOKEN="YOUR_TOKEN"
    ```

    If you don't have a token, contact the **partner onboarding team**.
  </Accordion>

  <Accordion title="Widget shows 'Extension Not Found'" icon="puzzle-piece">
    **Problem:** The widget displays a message that the extension is not found.

    **Solutions:**

    1. **Ensure extension is installed**: Users must install the ActionModel browser extension from the Chrome Web Store

    2. **Check browser compatibility**: The extension supports Chrome-family browsers (Chrome, Edge, Brave, Arc, Opera, Mises)

    3. **Verify extension is enabled**: Check `chrome://extensions` to ensure it's not disabled

    4. **Try refreshing**: Sometimes the extension takes a moment to register. Call `widget.refresh()` or reload the page
  </Accordion>

  <Accordion title="Widget shows 'Access Denied'" icon="ban">
    **Problem:** The widget shows an access denied error.

    **Solutions:**

    1. **Verify your partnerId**: Ensure the Partner ID is correct and matches what was provided

    2. **Check domain whitelisting**: Your domain must be allowed in your partner configuration

    3. **Confirm partner status**: Ensure your partner integration is active and enabled

    4. **Contact support**: Contact the **partner onboarding team** with your Partner ID and domain
  </Accordion>

  <Accordion title="No tasks appear in the widget" icon="list">
    **Problem:** The widget loads but shows no tasks.

    **Solutions:**

    1. **Verify domain configuration**: Ensure tasks are configured for your domain or the specified `domain` parameter

    2. **Check bountyId**: If filtering by bounty, verify the bounty ID is correct

    3. **Listen for errors**: Add an `onEvent` handler and check for `tasks_load_failed` events

    4. **Network issues**: Check browser console for network errors

    ```javascript theme={null}
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      onEvent: (event) => {
        if (event.type === 'tasks_load_failed') {
          console.error('Failed to load tasks:', event.error);
        }
      }
    });
    ```
  </Accordion>

  <Accordion title="SSR Errors (Next.js, Nuxt, etc.)" icon="server">
    **Problem:** Server-side rendering errors like "window is not defined".

    **Solutions:**

    The SDK requires browser APIs and cannot run on the server. Use one of these approaches:

    **Next.js:**

    ```javascript theme={null}
    const Widget = dynamic(() => import('./Widget'), { ssr: false });
    ```

    **Nuxt.js:**

    ```vue theme={null}
    <client-only>
      <ActionModelWidget />
    </client-only>
    ```

    **General approach:**

    ```javascript theme={null}
    // Only initialize in browser
    if (typeof window !== 'undefined') {
      // Load and initialize SDK
    }
    ```
  </Accordion>

  <Accordion title="Widget not rendering" icon="eye-slash">
    **Problem:** The widget container exists but nothing renders.

    **Solutions:**

    1. **Check container exists**: Ensure the container element exists in the DOM before calling `init()`

    2. **Script loaded**: Verify the SDK script has loaded before initialization

    3. **Console errors**: Check browser console for JavaScript errors

    4. **Container size**: Ensure the container has dimensions (min-height, width)

    ```javascript theme={null}
    // Wait for DOM and script
    document.addEventListener('DOMContentLoaded', () => {
      const script = document.createElement('script');
      script.src = 'https://sdk.actionmodel.com/sdk/latest/actionmodel-sdk.umd.js';
      script.onload = () => {
        if (document.getElementById('widget')) {
          ActionModel.init({
            container: '#widget',
            partnerId: 'your-partner-uuid',
          });
        }
      };
      document.body.appendChild(script);
    });
    ```
  </Accordion>

  <Accordion title="Enable Debug Mode" icon="bug">
    **Problem:** Need more information about what's happening.

    **Solution:** Enable debug logging:

    ```javascript theme={null}
    // Enable before initializing
    window.__ACTIONMODEL_DEBUG__ = true;

    // Then initialize
    ActionModel.init({ /* options */ });
    ```

    This will log detailed information to the browser console about:

    * SDK initialization
    * Extension detection
    * API requests and responses
    * Event emissions
  </Accordion>
</AccordionGroup>

<Info>
  For a deeper mapping of extension/backend failures to SDK-friendly error codes, see `docs/ERROR_CODES.md` in the SDK repo.
</Info>

***

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="Can I customize which tasks appear?" icon="filter">
    Yes, you have several options:

    1. **Domain filtering**: Set the `domain` parameter to show tasks for a specific domain
    2. **Bounty filtering**: Set the `bountyId` parameter to show only tasks from a specific campaign
    3. **Custom task configuration**: Work with our team to define custom tasks for your integration

    Contact your partner manager to discuss task customization options.
  </Accordion>

  <Accordion title="How do I track user completions?" icon="chart-line">
    Use the `user` parameter to identify your users:

    ```javascript theme={null}
    ActionModel.init({
      container: '#widget',
      partnerId: 'your-partner-uuid',
      user: {
        externalId: 'your-user-id',
        email: 'user@example.com'
      }
    });
    ```

    Completions are then attributed to this user. You can access completion data through the Partner Dashboard or via webhook integrations.
  </Accordion>

  <Accordion title="Can I use multiple widgets on one page?" icon="clone">
    Yes, you can initialize multiple widgets with different configurations:

    ```javascript theme={null}
    const widget1 = ActionModel.init({
      container: '#widget-1',
      partnerId: 'your-partner-uuid',
      domain: 'domain1.com'
    });

    const widget2 = ActionModel.init({
      container: '#widget-2',
      partnerId: 'your-partner-uuid',
      domain: 'domain2.com'
    });
    ```

    Use `ActionModel.destroyAll()` to clean up all instances at once.
  </Accordion>

  <Accordion title="Is there a sandbox/test environment?" icon="flask">
    Yes, we provide a sandbox environment for testing integrations. Contact your partner manager to get sandbox credentials and test domains configured.
  </Accordion>

  <Accordion title="What data is collected?" icon="database">
    The widget itself collects minimal data:

    * Widget initialization events
    * Task interaction events (start, complete, fail)
    * User identification data you provide

    The browser extension (when users complete tasks) collects path data needed to train the LAM. See our [Privacy Policy](/privacy) for details.
  </Accordion>
</AccordionGroup>

***

## Support

<CardGroup cols={2}>
  <Card title="Documentation" icon="book" href="https://docs.actionmodel.com" color="#3b82f6">
    Full ActionModel documentation including API reference and guides
  </Card>

  <Card title="Partner Support" icon="headset" color="#9333ea">
    Contact your partner representative for integration support
  </Card>
</CardGroup>

### Getting Help

* **Documentation**: [docs.actionmodel.com](https://docs.actionmodel.com)
* **Partner Support**: Partner onboarding team
* **Technical Issues**: Include your Partner ID, domain, and browser console logs when reporting issues

***

## Related Resources

<CardGroup cols={3}>
  <Card title="ActionFi Overview" icon="book-open" href="/actionfi/actionfi-overview" color="#9333ea">
    Learn about ActionFi, the bounty layer of Action Model
  </Card>

  <Card title="For Platforms" icon="building" href="/actionfi/for-platforms" color="#3b82f6">
    Become an ActionFi partner and get verified users
  </Card>

  <Card title="Browser Extension" icon="puzzle-piece" href="/the-large-action-model-lam/browser-extension-overview" color="#10b981">
    Learn about the training browser extension
  </Card>
</CardGroup>

***

**Ready to integrate?** Contact the **partner onboarding team** to get your Partner ID and start embedding the widget into your platform.
