Skip to main content
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.

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.

Seamless Integration

Users stay on your platform while completing ActionFi tasks

User Attribution

Link completions to your user accounts for rewards tracking

Custom Theming

Match the widget to your brand with full color customization
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.

Quick Start

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

Add the Container

Create a div element where you want the widget to appear. Give it enough height for the task list.
<div id="actionmodel-widget" style="min-height: 400px;"></div>
2

Load the SDK

Add the SDK script to your page. It exposes the global ActionModel object.
<script src="https://cdn.actionmodel.com/sdk/actionmodel-sdk.min.js"></script>
3

Initialize the Widget

Call ActionModel.init() with your Partner ID to render the widget.
<script>
  const widget = ActionModel.init({
    container: '#actionmodel-widget',
    partnerId: 'your-partner-uuid',
  });
</script>
Partner ID: Your Partner ID is a UUID provided when you sign up as an ActionFi partner. Contact [email protected] if you don’t have one.

Complete Quick Start Example

<!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://cdn.actionmodel.com/sdk/actionmodel-sdk.min.js"></script>

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

Installation


Configuration Options

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

Required Options

OptionTypeDescription
containerstring | HTMLElementCSS selector (e.g., '#widget') or DOM element reference
partnerIdstringYour Partner Integration UUID

Optional Options

OptionTypeDefaultDescription
domainstringwindow.location.hostnameDomain to load tasks for
bountyIdstringFilter tasks to a specific bounty campaign
userExternalUserDataIdentify your user for task attribution
themeWidgetThemeDark themeCustomize widget appearance
onEventfunctionCallback for widget events
Minimal configuration with just the required options:
const widget = ActionModel.init({
  container: '#actionmodel-widget',
  partnerId: 'abc123-def456-...',
});
Complete configuration using all available options:
const widget = ActionModel.init({
  container: document.getElementById('widget'),
  partnerId: 'abc123-def456-...',
  domain: 'mysite.com',
  bountyId: 'bounty-uuid-optional',
  user: {
    externalId: 'user-123',
    email: '[email protected]',
    metadata: { plan: 'premium', tier: 'gold' }
  },
  theme: {
    colors: {
      primary: '#6366f1',
      background: '#ffffff',
    }
  },
  onEvent: (event) => {
    analytics.track(event.type, event);
  }
});
By default, the widget shows tasks for your current domain. You can override this:
// 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
Filter to show only tasks from a specific bounty campaign:
ActionModel.init({
  container: '#widget',
  partnerId: 'your-partner-uuid',
  bountyId: 'specific-bounty-campaign-uuid',
});
Use this when running limited-time campaigns or themed task sets.

TypeScript Interfaces

For TypeScript projects, here are the complete type definitions for the SDK:
interface ActionModelConfig {
  /** CSS selector or DOM element where the widget renders */
  container: string | HTMLElement;

  /** Your Partner Integration UUID */
  partnerId: string;

  /** Domain to load tasks for (defaults to current hostname) */
  domain?: string;

  /** Filter tasks to a specific bounty campaign */
  bountyId?: string;

  /** User identification for attribution */
  user?: ExternalUserData;

  /** Widget theming options */
  theme?: WidgetTheme;

  /** Callback for widget events */
  onEvent?: (event: WidgetEvent) => void;
}

interface ExternalUserData {
  /** Your internal user ID (required, must be unique) */
  externalId: string;

  /** User's email address */
  email?: string;

  /** Additional user attributes for analytics */
  metadata?: Record<string, string | number | boolean>;
}

interface WidgetTheme {
  colors?: {
    primary?: string;
    primaryHover?: string;
    background?: string;
    surface?: string;
    border?: string;
    text?: string;
    textSecondary?: string;
    textLight?: string;
    textMuted?: string;
    success?: string;
    warning?: string;
    error?: string;
  };
  borderRadius?: {
    sm?: string;
    md?: string;
  };
  fontFamily?: string;
}

interface WidgetEvent {
  type:
    | 'widget_initialized'
    | 'extension_discovered'
    | 'extension_unavailable'
    | 'tasks_loaded'
    | 'tasks_load_failed'
    | 'training_started'
    | 'training_failed';
  partnerId: string;
  domain: string;
  taskId?: string;
  taskCount?: number;
  error?: string;
}

interface WidgetInstance {
  /** Reload tasks from the server */
  refresh(): void;

  /** Remove widget and clean up event listeners */
  destroy(): void;
}

interface ActionModelSDK {
  /** Initialize a new widget instance */
  init(config: ActionModelConfig): WidgetInstance;

  /** Destroy all widget instances on the page */
  destroyAll(): void;
}

declare global {
  interface Window {
    ActionModel: ActionModelSDK;
  }
}
Using with TypeScript: Copy these interfaces into a actionmodel.d.ts file in your project to get full type checking and autocompletion.

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.
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).

User Object Properties

PropertyTypeRequiredDescription
externalIdstringYesYour internal user ID (must be unique per user)
emailstringNoUser’s email address
metadataobjectNoAdditional user attributes for analytics

Basic User Identification

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

Full User Identification

ActionModel.init({
  container: '#widget',
  partnerId: 'your-partner-uuid',
  user: {
    externalId: 'user_12345',
    email: '[email protected]',
    metadata: {
      plan: 'enterprise',
      accountAge: 365,
      region: 'US',
      referralSource: 'organic'
    }
  }
});
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.

Dynamic User Identification

If your user logs in after the widget is initialized, destroy and reinitialize:
// 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 SDK supports comprehensive theming including colors, typography, and border radius.

Available Color Properties

PropertyDescriptionDefault
primaryPrimary brand color (buttons, links, accents)#9000ff
primaryHoverHover state for primary elements#7406c3
backgroundWidget background#1a1a1a
surfaceCard/surface background#292929
borderBorder color#3d3d3d
textPrimary text color#ffffff
textSecondarySecondary text color#bdbdbd
textLightLight text color#dcdcdc
textMutedMuted text (timestamps, hints)#7c7c7c
successSuccess state color#4ade80
warningWarning state color#f97316
errorError state color#ef4444

Custom Brand Example

Match the widget to a custom brand:
ActionModel.init({
  container: '#widget',
  partnerId: 'your-partner-uuid',
  theme: {
    colors: {
      primary: '#0066FF',       // Brand blue
      primaryHover: '#0052CC',
      background: '#FAFBFC',    // Light gray background
      surface: '#FFFFFF',
      border: '#DFE1E6',
      text: '#172B4D',
      textSecondary: '#5E6C84',
      textMuted: '#97A0AF',
    },
    borderRadius: {
      sm: '4px',
      md: '8px',
    },
    fontFamily: "'Roboto', 'Helvetica Neue', sans-serif",
  }
});

Events

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

Event Handler Setup

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

EventDescriptionAdditional Properties
widget_initializedWidget has been initialized and is readypartnerId, domain
extension_discoveredBrowser extension found and validatedpartnerId, domain
extension_unavailableExtension not installed or not detectedpartnerId, domain
tasks_loadedTasks successfully loaded from serverpartnerId, domain, taskCount
tasks_load_failedFailed to load tasks (network error, etc.)partnerId, domain, error
training_startedUser started a training taskpartnerId, domain, taskId
training_failedTraining failed to startpartnerId, domain, taskId, error

Event Handling Examples

Send events to your analytics platform:
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);
  }
});
Show a custom prompt when the extension isn’t installed:
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';
}
Handle errors gracefully:
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;
    }
  }
});
Track when users engage with tasks:
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'
      });
    }
  }
});

Configuring Webhooks

Coming Soon: Webhook configuration is currently being updated. This section will be expanded with full documentation shortly.
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

Task Completion Webhooks

Receive POST requests when users complete training tasks, including task ID, user data, and completion metadata.

User Activity Events

Get notified when users start training sessions, reach milestones, or achieve rewards.

Secure Signatures

All webhook payloads will be signed with HMAC-SHA256 for verification.

Retry Logic

Failed deliveries will be retried with exponential backoff to ensure reliability.

Interested in Early Access?

Contact [email protected] 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.
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.
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.
// Destroy all widgets
ActionModel.destroyAll();

Framework Integration

React Integration

Create a reusable component that handles lifecycle properly:
import { useEffect, useRef } from 'react';

function ActionModelWidget({ partnerId, userId, userEmail }) {
  const containerRef = useRef(null);
  const widgetRef = useRef(null);

  useEffect(() => {
    // Load SDK script dynamically
    const script = document.createElement('script');
    script.src = 'https://cdn.actionmodel.com/sdk/actionmodel-sdk.min.js';
    script.async = true;

    script.onload = () => {
      // Initialize widget after script loads
      if (containerRef.current && window.ActionModel) {
        widgetRef.current = window.ActionModel.init({
          container: containerRef.current,
          partnerId,
          user: userId ? {
            externalId: userId,
            email: userEmail,
          } : undefined,
          onEvent: (event) => {
            console.log('ActionModel event:', event);
          }
        });
      }
    };

    document.body.appendChild(script);

    // Cleanup on unmount
    return () => {
      if (widgetRef.current) {
        widgetRef.current.destroy();
      }
      script.remove();
    };
  }, [partnerId, userId, userEmail]);

  return (
    <div
      ref={containerRef}
      style={{ minHeight: 400, width: '100%' }}
    />
  );
}

export default ActionModelWidget;
Usage:
function App() {
  const { user } = useAuth(); // Your auth hook

  return (
    <div>
      <h1>Training Tasks</h1>
      <ActionModelWidget
        partnerId="your-partner-uuid"
        userId={user?.id}
        userEmail={user?.email}
      />
    </div>
  );
}

Requirements

Before integrating the Partner SDK, ensure you have:

Partner Integration

Partner ID RequiredYour partnerId must be configured in the ActionModel system. Contact [email protected] to get started.

Domain Whitelisting

Domain Must Be AllowedYour domain must be whitelisted for your partner integration. This is configured when you sign up as a partner.

Browser Extension

Users Need the ExtensionUsers must have the ActionModel browser extension installed to complete training tasks. The widget will show an install prompt if not detected.

HTTPS Required

Secure ConnectionThe SDK requires HTTPS in production. Development on localhost is supported.

Supported Browsers

The ActionModel browser extension supports:
BrowserSupportedNotes
ChromeFull support
EdgeChromium-based
BraveChromium-based
OperaChromium-based
FirefoxComing soon
SafariComing soon

Troubleshooting

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, Edge, Brave, and other Chromium browsers
  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
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: Email [email protected] with your Partner ID and domain
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
ActionModel.init({
  container: '#widget',
  partnerId: 'your-partner-uuid',
  onEvent: (event) => {
    if (event.type === 'tasks_load_failed') {
      console.error('Failed to load tasks:', event.error);
    }
  }
});
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:
const Widget = dynamic(() => import('./Widget'), { ssr: false });
Nuxt.js:
<client-only>
  <ActionModelWidget />
</client-only>
General approach:
// Only initialize in browser
if (typeof window !== 'undefined') {
  // Load and initialize SDK
}
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)
// Wait for DOM and script
document.addEventListener('DOMContentLoaded', () => {
  const script = document.createElement('script');
  script.src = 'https://cdn.actionmodel.com/sdk/actionmodel-sdk.min.js';
  script.onload = () => {
    if (document.getElementById('widget')) {
      ActionModel.init({
        container: '#widget',
        partnerId: 'your-partner-uuid',
      });
    }
  };
  document.body.appendChild(script);
});
Problem: Need more information about what’s happening.Solution: Enable debug logging:
// 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

Frequently Asked Questions

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.
Use the user parameter to identify your users:
ActionModel.init({
  container: '#widget',
  partnerId: 'your-partner-uuid',
  user: {
    externalId: 'your-user-id',
    email: '[email protected]'
  }
});
Completions are then attributed to this user. You can access completion data through the Partner Dashboard or via webhook integrations.
Yes, you can initialize multiple widgets with different configurations:
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.
Yes, we provide a sandbox environment for testing integrations. Contact your partner manager to get sandbox credentials and test domains configured.
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 for details.

Support

Getting Help



Ready to integrate? Contact [email protected] to get your Partner ID and start embedding ActionFi into your platform.