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.
Step 0: Configure npm Access (Private Package)
The npm package is private. You need a token from ActionModel to install via npm.
Request a token: contact the partner onboarding team and ask for an npm deploy token with read_package_registry scope for the Partner SDK.
Add .npmrc (project root) or ~/.npmrc (global):
# 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}
Export the token (local dev):
export ACTIONMODEL_NPM_TOKEN = "YOUR_TOKEN"
Do not commit tokens. Prefer env var substitution as shown above.
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 >
Load the SDK
Load the SDK. CDN/UMD (no bundler): < script src = "https://sdk.actionmodel.com/sdk/latest/actionmodel-sdk.umd.js" ></ script >
npm (bundlers / React / Vue): npm install @actionmodel/partner-sdk
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 ActionModel partner. Contact the partner onboarding team 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://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
Script Tag (Recommended)
npm (Recommended)
CDN Installation The simplest way to add the SDK is via our CDN. Add this script tag to your HTML: < 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. 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.
<!-- 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 >
Package Manager Installation Install via npm (includes TypeScript types and framework wrappers): npm install @actionmodel/partner-sdk
Import and initialize: import ActionModel from '@actionmodel/partner-sdk' ;
const instance = ActionModel . init ({
container: '#actionmodel-widget' ,
partnerId: 'your-partner-uuid' ,
});
This package is private. If install fails, complete Quick Start Step 0 (.npmrc + token).
Configuration Options
The ActionModel.init() function accepts a configuration object with the following options:
Required Options
Option Type Description containerstring | HTMLElementCSS selector (e.g., '#widget') or DOM element reference partnerIdstringYour Partner Integration UUID
Optional Options
Option Type Default Description domainstringwindow.location.hostnameDomain to load tasks for bountyIdstring— Filter tasks to a specific bounty campaign userExternalUserData— Identify your user for task attribution featuresWidgetFeatures— Feature flags (e.g. enableQuery) themeWidgetThemeDark mode Theming (mode, tokens, radius, font) onEvent(event) => void— Callback for widget events
Basic Configuration Example
Minimal configuration with just the required options: const widget = ActionModel . init ({
container: '#actionmodel-widget' ,
partnerId: 'abc123-def456-...' ,
});
Full Configuration Example
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: 'user@example.com' ,
metadata: { plan: 'premium' , tier: 'gold' }
},
features: { enableQuery: true },
theme: { mode: 'light' },
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, these are the public types exported by the SDK (authoritative for npm installs):
View TypeScript Interfaces
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 ;
}
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
Property Type Required Description externalIdstringYes Your internal user ID (must be unique per user) emailstringNo User’s email address metadataobjectNo Additional 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: 'user@example.com' ,
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 theme option supports mode, semantic tokens (sys), component tokens (comp), border radius, and font family.
Presets import ActionModel , { themePresets } from '@actionmodel/partner-sdk' ;
ActionModel . init ({
container: '#widget' ,
partnerId: 'your-partner-uuid' ,
theme: themePresets . light , // or themePresets.dark
});
Helpers (createLightTheme, createDarkTheme) import ActionModel , { createLightTheme } from '@actionmodel/partner-sdk' ;
ActionModel . init ({
container: '#widget' ,
partnerId: 'your-partner-uuid' ,
theme: createLightTheme ({
sys: { accent: '#0066ff' , accentHover: '#0052cc' },
}),
});
Common Token Overrides 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' ,
},
});
Component-Level Overrides ActionModel . init ({
container: '#widget' ,
partnerId: 'your-partner-uuid' ,
theme: {
comp: {
taskRowHoverBg: 'rgba(0, 0, 0, 0.04)' ,
taskRowSelectedBg: '#e5e7eb' ,
rewardBadgeBg: '#eef2ff' ,
},
},
});
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
Event Description Notes widget_initializedinit() called and instance createdEmitted immediately extension_discoveredExtension discovered and validated Can re-fire on install/login events extension_unavailableExtension missing or user not authenticated Prompt install/account creation tasks_loadedInitial tasks page loaded Includes taskCount tasks_load_failedInitial tasks load failed Includes error training_startedTraining started Includes taskId training_finishedTraining completed taskId may be missingtraining_failedTraining failed May include needsUpdate view_history_clickedUser clicked “view history” Includes recordingSessionId query_submittedUser submitted a query for a failed task Includes taskCompletionId and optional queryId query_failedQuery submission failed Includes error tasks_updatingTask list is refreshing Reason: training_started | training_finished | query_submitted | manual_refresh tasks_updatedTask list updated Includes updatedTasks task IDs tasks_update_failedTask refresh failed Includes willRetry recording_in_progressRecording in progress Optional elapsedSeconds
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 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.
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
Next.js
Vue 3
Angular
React Integration Use the React wrapper from @actionmodel/partner-sdk/react. 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: 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 >
</>
);
Next.js Integration Use dynamic imports to prevent SSR issues: '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: // 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 >
);
}
Vue 3 Integration Use the Vue 3 wrapper from @actionmodel/partner-sdk/vue. < 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 <ClientOnly> to avoid SSR issues. Angular Integration Angular should use the vanilla npm API (@actionmodel/partner-sdk) or the CDN script tag (UMD). Example using npm: 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 ();
Requirements
Before integrating the Partner SDK, ensure you have:
Partner Integration Partner ID Required Your partnerId must be configured in the ActionModel system. Contact the partner onboarding team to get started.
Domain Whitelisting Domain Must Be Allowed Your domain must be whitelisted for your partner integration. This is configured when you sign up as a partner.
Browser Extension 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.
HTTPS Required Secure Connection The SDK requires HTTPS in production. Development on localhost is supported.
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
npm install fails (401/403/404)
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.@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: export ACTIONMODEL_NPM_TOKEN = "YOUR_TOKEN"
If you don’t have a token, contact the partner onboarding team .
Widget shows 'Extension Not Found'
Widget shows 'Access Denied'
No tasks appear in the widget
SSR Errors (Next.js, Nuxt, etc.)
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: 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
For a deeper mapping of extension/backend failures to SDK-friendly error codes, see docs/ERROR_CODES.md in the SDK repo.
Frequently Asked Questions
Can I customize which tasks appear?
Yes, you have several options:
Domain filtering : Set the domain parameter to show tasks for a specific domain
Bounty filtering : Set the bountyId parameter to show only tasks from a specific campaign
Custom task configuration : Work with our team to define custom tasks for your integration
Contact your partner manager to discuss task customization options.
How do I track user completions?
Use the user parameter to identify your users: 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.
Can I use multiple widgets on one page?
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.
Is there a sandbox/test environment?
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
Documentation Full ActionModel documentation including API reference and guides
Partner Support Contact your partner representative for integration support
Getting Help
Documentation : docs.actionmodel.com
Partner Support : Partner onboarding team
Technical Issues : Include your Partner ID, domain, and browser console logs when reporting issues
ActionFi Overview Learn about ActionFi, the bounty layer of Action Model
For Platforms Become an ActionFi partner and get verified users
Browser Extension Learn about the training browser extension
Ready to integrate? Contact the partner onboarding team to get your Partner ID and start embedding the widget into your platform.