diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 4b462ffcb2a..ec84c16702e 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -2758,6 +2758,17 @@ export function TinybirdIcon(props: SVGProps) { ) } +export function ThriveIcon(props: SVGProps) { + return ( + + + + ) +} + export function ClayIcon(props: SVGProps) { return ( diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index 05866ee2fd6..d7fd5e43cd4 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -209,6 +209,7 @@ import { TelegramIcon, TemporalIcon, TextractIcon, + ThriveIcon, TinybirdIcon, TrelloIcon, TriggerDevIcon, @@ -466,6 +467,7 @@ export const blockTypeToIconMap: Record = { temporal: TemporalIcon, textract: TextractIcon, textract_v2: TextractIcon, + thrive: ThriveIcon, tinybird: TinybirdIcon, trello: TrelloIcon, trigger_dev: TriggerDevIcon, diff --git a/apps/docs/content/docs/en/integrations/meta.json b/apps/docs/content/docs/en/integrations/meta.json index de492727e78..f4db2777b6c 100644 --- a/apps/docs/content/docs/en/integrations/meta.json +++ b/apps/docs/content/docs/en/integrations/meta.json @@ -210,6 +210,7 @@ "telegram", "temporal", "textract", + "thrive", "tinybird", "trello", "trigger_dev", diff --git a/apps/docs/content/docs/en/integrations/thrive.mdx b/apps/docs/content/docs/en/integrations/thrive.mdx new file mode 100644 index 00000000000..d2987593bad --- /dev/null +++ b/apps/docs/content/docs/en/integrations/thrive.mdx @@ -0,0 +1,1492 @@ +--- +title: Thrive +description: Manage users, audiences, learning and CPD on Thrive +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +## Usage Instructions + +Integrate Thrive Learning into the workflow. Manage user lifecycle, audiences and their members and managers, content assignments and enrolments, learning completions, content and activity records, CPD, tags, and skills. + + + +## Actions + +### `thrive_create_user` + +Create a new user in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `ref` | string | Yes | Your organisation's unique identifier for this individual | +| `firstName` | string | Yes | The given name of the individual | +| `lastName` | string | Yes | The family name of the individual | +| `email` | string | No | The email address for the user \(required unless loginMethod is 'ref'\) | +| `loginMethod` | string | No | How the user logs in: 'email' or 'ref' \(defaults to 'email'\) | +| `role` | string | No | Role assigned: 'administrator', 'learneradmin', or 'learner' \(defaults to 'learner'\) | +| `jobTitle` | string | No | Name of this individual's role in your organisation | +| `managerRef` | string | No | Your organisation's unique identifier for this individual's line manager | +| `startDate` | string | No | Date this individual started with your organisation \(ISO 8601\) | +| `endDate` | string | No | Date this individual left your organisation \(ISO 8601\) | +| `timeZone` | string | No | The user's preferred timezone \(tenant default if omitted\) | +| `languageCode` | string | No | The user's preferred language \(e.g. 'en-gb'\) | +| `sso` | boolean | No | Whether the account is managed by an authentication provider | +| `domain` | string | No | Domain this individual is associated with | +| `additionalFields` | string | No | JSON object of custom field key-value pairs. Example: \{"department":"Sales"\} | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `user` | object | The created user | +| ↳ `id` | string | The user ID | +| ↳ `loginMethod` | string | How the user logs in | +| ↳ `ref` | string | Your organisation's unique identifier for the user | +| ↳ `email` | string | The email address for the user | +| ↳ `firstName` | string | The given name of the individual | +| ↳ `lastName` | string | The family name of the individual | +| ↳ `role` | string | Role assigned to this individual | +| ↳ `jobTitle` | string | Name of this individual's role | +| ↳ `managerRef` | string | The line manager's ref | +| ↳ `startDate` | string | Date started with the organisation | +| ↳ `endDate` | string | Date left the organisation | +| ↳ `timeZone` | string | The user's preferred timezone | +| ↳ `languageCode` | string | The user's preferred language | +| ↳ `active` | boolean | Whether the account is active or suspended | +| ↳ `createdAt` | string | Date/time the user was created | +| ↳ `updatedAt` | string | Date/time the user was last modified | +| ↳ `sso` | boolean | Whether the account is managed by an auth provider | +| ↳ `domain` | string | Domain this individual is associated with | +| ↳ `additionalFields` | json | Custom field values for this user | + +### `thrive_update_user` + +Update an existing user in Thrive by ref. Only the fields provided are changed. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `ref` | string | Yes | The user ref to update | +| `firstName` | string | No | The given name of the individual | +| `lastName` | string | No | The family name of the individual | +| `email` | string | No | The email address for the user | +| `loginMethod` | string | No | How the user logs in: 'email' or 'ref' | +| `role` | string | No | Role assigned: 'administrator', 'learneradmin', or 'learner' | +| `jobTitle` | string | No | Name of this individual's role in your organisation | +| `managerRef` | string | No | Your organisation's unique identifier for this individual's line manager | +| `startDate` | string | No | Date this individual started with your organisation \(ISO 8601\) | +| `endDate` | string | No | Date this individual left your organisation \(ISO 8601\) | +| `timeZone` | string | No | The user's preferred timezone | +| `languageCode` | string | No | The user's preferred language \(e.g. 'en-gb'\) | +| `sso` | boolean | No | Whether the account is managed by an authentication provider | +| `domain` | string | No | Domain this individual is associated with | +| `additionalFields` | string | No | JSON object of custom field key-value pairs. Example: \{"department":"Sales"\} | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `user` | object | The updated user | +| ↳ `id` | string | The user ID | +| ↳ `loginMethod` | string | How the user logs in | +| ↳ `ref` | string | Your organisation's unique identifier for the user | +| ↳ `email` | string | The email address for the user | +| ↳ `firstName` | string | The given name of the individual | +| ↳ `lastName` | string | The family name of the individual | +| ↳ `role` | string | Role assigned to this individual | +| ↳ `jobTitle` | string | Name of this individual's role | +| ↳ `managerRef` | string | The line manager's ref | +| ↳ `startDate` | string | Date started with the organisation | +| ↳ `endDate` | string | Date left the organisation | +| ↳ `timeZone` | string | The user's preferred timezone | +| ↳ `languageCode` | string | The user's preferred language | +| ↳ `active` | boolean | Whether the account is active or suspended | +| ↳ `createdAt` | string | Date/time the user was created | +| ↳ `updatedAt` | string | Date/time the user was last modified | +| ↳ `sso` | boolean | Whether the account is managed by an auth provider | +| ↳ `domain` | string | Domain this individual is associated with | +| ↳ `additionalFields` | json | Custom field values for this user | + +### `thrive_delete_user` + +Permanently delete (obfuscate) a user in Thrive by ref while retaining training history. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `ref` | string | Yes | The user ref to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Whether the user was deleted | + +### `thrive_suspend_user` + +Suspend a user in Thrive by ref, marking the account inactive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `ref` | string | Yes | The user ref to suspend | +| `endDate` | string | No | The date this individual left your organisation \(ISO 8601\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `user` | object | The suspended user | +| ↳ `id` | string | The user ID | +| ↳ `loginMethod` | string | How the user logs in | +| ↳ `ref` | string | Your organisation's unique identifier for the user | +| ↳ `email` | string | The email address for the user | +| ↳ `firstName` | string | The given name of the individual | +| ↳ `lastName` | string | The family name of the individual | +| ↳ `role` | string | Role assigned to this individual | +| ↳ `jobTitle` | string | Name of this individual's role | +| ↳ `managerRef` | string | The line manager's ref | +| ↳ `startDate` | string | Date started with the organisation | +| ↳ `endDate` | string | Date left the organisation | +| ↳ `timeZone` | string | The user's preferred timezone | +| ↳ `languageCode` | string | The user's preferred language | +| ↳ `active` | boolean | Whether the account is active or suspended | +| ↳ `createdAt` | string | Date/time the user was created | +| ↳ `updatedAt` | string | Date/time the user was last modified | +| ↳ `sso` | boolean | Whether the account is managed by an auth provider | +| ↳ `domain` | string | Domain this individual is associated with | +| ↳ `additionalFields` | json | Custom field values for this user | + +### `thrive_search_users` + +Search users in Thrive and return basic user information with pagination. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 100\) | +| `updatedSince` | string | No | Return only users updated on or after this date/time \(ISO 8601\) | +| `statuses` | string | No | Comma-separated statuses to include: active, inactive, expired, new | +| `omitStatuses` | string | No | Comma-separated statuses to exclude: active, inactive, expired, new | +| `status` | string | No | Filter by a single status: active, inactive, expired, or new | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The matching users | +| ↳ `id` | string | The user's ID | +| ↳ `ref` | string | The user's ref | +| ↳ `firstName` | string | The user's first name | +| ↳ `lastName` | string | The user's last name | +| ↳ `email` | string | The user's email | +| ↳ `role` | string | The user's role | +| ↳ `status` | string | The user's status | +| ↳ `positions` | array | The user's positions | +| ↳ `additionalFields` | json | Custom field values | +| ↳ `languageCode` | string | The user's language code | +| ↳ `deleted` | boolean | Whether the user has been deleted | +| ↳ `compliance` | number | The user's compliance score | +| ↳ `level` | number | The user's level | +| ↳ `firstLogin` | string | First login timestamp \(ISO 8601\) | +| ↳ `lastLogin` | string | Last login timestamp \(ISO 8601\) | +| ↳ `tags` | json | Tag membership \(e.g. skills\) | +| ↳ `usersFollowing` | array | IDs of users this user follows | +| ↳ `tagsFollowing` | array | Tags this user follows | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | +| ↳ `hasPicture` | boolean | Whether the user has a profile picture | +| ↳ `timeZone` | string | The user's time zone | +| ↳ `summary` | string | The user's summary | +| ↳ `relevancy` | number | The user's relevancy score | +| ↳ `rank` | json | The user's rank details | +| ↳ `agreedTerms` | boolean | Whether the user agreed to the terms | +| ↳ `onboarded` | boolean | Whether the user has been onboarded | +| ↳ `audiences` | array | Audience IDs the user belongs to | +| ↳ `singleSignOn` | boolean | Whether the user uses single sign-on | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_get_user_by_id` + +Get a single user in Thrive by their ID and return basic user information. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `id` | string | Yes | The user ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `user` | object | The user | +| ↳ `id` | string | The user's ID | +| ↳ `ref` | string | The user's ref | +| ↳ `firstName` | string | The user's first name | +| ↳ `lastName` | string | The user's last name | +| ↳ `email` | string | The user's email | +| ↳ `role` | string | The user's role | +| ↳ `status` | string | The user's status | +| ↳ `positions` | array | The user's positions | +| ↳ `additionalFields` | json | Custom field values | +| ↳ `languageCode` | string | The user's language code | +| ↳ `deleted` | boolean | Whether the user has been deleted | +| ↳ `compliance` | number | The user's compliance score | +| ↳ `level` | number | The user's level | +| ↳ `firstLogin` | string | First login timestamp \(ISO 8601\) | +| ↳ `lastLogin` | string | Last login timestamp \(ISO 8601\) | +| ↳ `tags` | json | Tag membership \(e.g. skills\) | +| ↳ `usersFollowing` | array | IDs of users this user follows | +| ↳ `tagsFollowing` | array | Tags this user follows | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | +| ↳ `hasPicture` | boolean | Whether the user has a profile picture | +| ↳ `timeZone` | string | The user's time zone | +| ↳ `summary` | string | The user's summary | +| ↳ `relevancy` | number | The user's relevancy score | +| ↳ `rank` | json | The user's rank details | +| ↳ `agreedTerms` | boolean | Whether the user agreed to the terms | +| ↳ `onboarded` | boolean | Whether the user has been onboarded | +| ↳ `audiences` | array | Audience IDs the user belongs to | +| ↳ `singleSignOn` | boolean | Whether the user uses single sign-on | + +### `thrive_get_user_by_ref` + +Get a single user in Thrive by their ref and return basic user information. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `ref` | string | Yes | The user ref | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `user` | object | The user \(basic information\) | +| ↳ `id` | string | The user's ID | +| ↳ `ref` | string | The user's ref | +| ↳ `firstName` | string | The user's first name | +| ↳ `lastName` | string | The user's last name | +| ↳ `email` | string | The user's email | +| ↳ `role` | string | The user's role | +| ↳ `status` | string | The user's status | +| ↳ `positions` | array | The user's positions | +| ↳ `additionalFields` | json | Custom field values | +| ↳ `languageCode` | string | The user's language code | + +### `thrive_list_audiences` + +List audiences and structures in Thrive with pagination. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `apiControlled` | boolean | No | Filter to only return audiences which are / are not API controlled | +| `updatedSince` | string | No | Return only audiences updated on or after this date/time \(ISO 8601\) | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 100\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The matching audiences | +| ↳ `id` | string | The id of the audience | +| ↳ `name` | string | The name of the audience | +| ↳ `reference` | string | The external reference for the audience | +| ↳ `apiControlled` | boolean | Whether the audience is API controlled | +| ↳ `category` | string | Either "audience" or "structure" | +| ↳ `type` | string | Either "manual" or "smart" | +| ↳ `parent` | object | Parent audience/structure information | +| ↳ `name` | string | The name of the parent audience | +| ↳ `reference` | string | The external reference for the parent | +| ↳ `id` | string | The id of the parent audience/structure | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_create_audience` + +Create a new audience or structure in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `name` | string | No | The name of the audience \(max 100 characters\) | +| `reference` | string | No | The external reference for the audience \(max 100 characters\) | +| `parentId` | string | No | The id or reference of the parent audience/structure; leave blank for a parent audience/structure | +| `category` | string | No | The audience category: 'audience' or 'structure' | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `audience` | object | The created audience | +| ↳ `id` | string | The id of the audience | +| ↳ `name` | string | The name of the audience | +| ↳ `reference` | string | The external reference for the audience | +| ↳ `apiControlled` | boolean | Whether the audience is API controlled | +| ↳ `category` | string | Either "audience" or "structure" | +| ↳ `type` | string | Either "manual" or "smart" | +| ↳ `parent` | object | Parent audience/structure information | +| ↳ `name` | string | The name of the parent audience | +| ↳ `reference` | string | The external reference for the parent | +| ↳ `id` | string | The id of the parent audience/structure | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | + +### `thrive_get_audience` + +Get a single audience or structure in Thrive by id or reference. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `audience` | object | The audience | +| ↳ `id` | string | The id of the audience | +| ↳ `name` | string | The name of the audience | +| ↳ `reference` | string | The external reference for the audience | +| ↳ `apiControlled` | boolean | Whether the audience is API controlled | +| ↳ `category` | string | Either "audience" or "structure" | +| ↳ `type` | string | Either "manual" or "smart" | +| ↳ `parent` | object | Parent audience/structure information | +| ↳ `name` | string | The name of the parent audience | +| ↳ `reference` | string | The external reference for the parent | +| ↳ `id` | string | The id of the parent audience/structure | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | + +### `thrive_update_audience` + +Update an audience in Thrive, optionally moving it to a new parent. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | +| `name` | string | No | The name of the audience \(max 100 characters\) | +| `reference` | string | No | The external reference for the audience \(max 100 characters\) | +| `parentId` | string | No | The id of the parent audience/structure to move the audience to | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `audience` | object | The updated audience | +| ↳ `id` | string | The id of the audience | +| ↳ `name` | string | The name of the audience | +| ↳ `reference` | string | The external reference for the audience | +| ↳ `apiControlled` | boolean | Whether the audience is API controlled | +| ↳ `category` | string | Either "audience" or "structure" | +| ↳ `type` | string | Either "manual" or "smart" | +| ↳ `parent` | object | Parent audience/structure information | +| ↳ `name` | string | The name of the parent audience | +| ↳ `reference` | string | The external reference for the parent | +| ↳ `id` | string | The id of the parent audience/structure | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | + +### `thrive_delete_audience` + +Delete an audience in Thrive (only if it has no child audiences). + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Whether the audience was deleted | + +### `thrive_list_audience_members` + +List the members of a Thrive audience with pagination. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 100\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The audience members | +| ↳ `userId` | string | The user's id | +| ↳ `reference` | string | The user's reference | +| ↳ `email` | string | The user's email | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_add_audience_members` + +Add members to a Thrive audience by email, ref, or id. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | +| `users` | string | Yes | JSON array of user emails/refs/ids to add \(1-100\). Example: \["user@example.com"\] | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `result` | object | The add/replace result, with successfully and unsuccessfully processed entities | +| ↳ `success` | object | Successfully processed entities | +| ↳ `count` | number | Number of successfully processed entities | +| ↳ `entities` | array | The successfully processed entities | +| ↳ `reference` | string | The entity reference | +| ↳ `failure` | object | Unsuccessfully processed entities | +| ↳ `count` | number | Number of unsuccessfully processed entities | +| ↳ `entities` | array | The unsuccessfully processed entities | +| ↳ `reason` | string | The reason for the failure | +| ↳ `reference` | string | The entity reference | + +### `thrive_replace_audience_members` + +Replace a Thrive audience's entire members list with the given users (does not support an empty array). + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | +| `users` | string | Yes | JSON array of user emails/refs/ids that replaces the whole members list \(1-100, no empty array\). Example: \["user@example.com"\] | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `result` | object | The add/replace result, with successfully and unsuccessfully processed entities | +| ↳ `success` | object | Successfully processed entities | +| ↳ `count` | number | Number of successfully processed entities | +| ↳ `entities` | array | The successfully processed entities | +| ↳ `reference` | string | The entity reference | +| ↳ `failure` | object | Unsuccessfully processed entities | +| ↳ `count` | number | Number of unsuccessfully processed entities | +| ↳ `entities` | array | The unsuccessfully processed entities | +| ↳ `reason` | string | The reason for the failure | +| ↳ `reference` | string | The entity reference | + +### `thrive_remove_audience_member` + +Remove a single member from a Thrive audience. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | +| `userRef` | string | Yes | The user email, ref, or id to remove | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Whether the audience member was removed | + +### `thrive_list_audience_managers` + +List the managers of a Thrive audience. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `managers` | array | The audience managers | +| ↳ `userId` | string | The user's id | +| ↳ `reference` | string | The user's reference | +| ↳ `email` | string | The user's email | +| ↳ `permissions` | object | The manager permissions | +| ↳ `audienceManager` | json | Audience manager permissions | +| ↳ `peopleManager` | json | People manager permissions | +| ↳ `administrator` | json | Administrator permissions \(structures only\) | + +### `thrive_add_audience_managers` + +Add managers to a Thrive audience with their permissions. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | +| `managers` | string | Yes | JSON array of manager objects \(1-100\). Each: \{"reference":"user@example.com","permissions":\{"audienceManager":\{"manageContent":true,"assignments":true\},"peopleManager":\{"canViewLearnPage":true,"insights":false,"manage":false\},"administrator":\{"canAddAudienceManagers":false\}\}\} | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `result` | object | The add/replace result, with successfully and unsuccessfully processed entities | +| ↳ `success` | object | Successfully processed entities | +| ↳ `count` | number | Number of successfully processed entities | +| ↳ `entities` | array | The successfully processed entities | +| ↳ `reference` | string | The entity reference | +| ↳ `failure` | object | Unsuccessfully processed entities | +| ↳ `count` | number | Number of unsuccessfully processed entities | +| ↳ `entities` | array | The unsuccessfully processed entities | +| ↳ `reason` | string | The reason for the failure | +| ↳ `reference` | string | The entity reference | + +### `thrive_replace_audience_managers` + +Replace a Thrive audience's entire manager list with the given managers (does not support an empty array). + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | +| `managers` | string | Yes | JSON array of manager objects that replaces the whole manager list \(1-100, no empty array\). Each: \{"reference":"user@example.com","permissions":\{"audienceManager":\{"manageContent":true,"assignments":true\},"peopleManager":\{"canViewLearnPage":true,"insights":false,"manage":false\},"administrator":\{"canAddAudienceManagers":false\}\}\} | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `result` | object | The add/replace result, with successfully and unsuccessfully processed entities | +| ↳ `success` | object | Successfully processed entities | +| ↳ `count` | number | Number of successfully processed entities | +| ↳ `entities` | array | The successfully processed entities | +| ↳ `reference` | string | The entity reference | +| ↳ `failure` | object | Unsuccessfully processed entities | +| ↳ `count` | number | Number of unsuccessfully processed entities | +| ↳ `entities` | array | The unsuccessfully processed entities | +| ↳ `reason` | string | The reason for the failure | +| ↳ `reference` | string | The entity reference | + +### `thrive_remove_audience_manager` + +Remove a single manager from a Thrive audience. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience id or audience reference | +| `userId` | string | Yes | The user email, ref, or id to remove as a manager | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Whether the audience manager was removed | + +### `thrive_list_assignments` + +List compliance assignments in Thrive, optionally filtered by audience. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | No | Filter by audience ID or audience reference | +| `updatedSince` | string | No | Return only items updated on or after this date/time \(ISO 8601\) | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-100, default 100\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assignments` | array | The matching assignments | +| ↳ `id` | string | The assignment ID | +| ↳ `audienceId` | string | The audience ID | +| ↳ `primaryContentId` | string | The content ID for the primary content | +| ↳ `alternativeContentIds` | array | Content IDs that can also complete the assignment | +| ↳ `hideAlternativeContent` | boolean | Whether to hide the alternative content | +| ↳ `completionPeriod` | number | Number of days required to complete the assignment | +| ↳ `recurrence` | number | Number of days until the assignment reoccurs | +| ↳ `isActive` | boolean | Whether the assignment is active | +| ↳ `isDeleted` | boolean | Whether the assignment is deleted | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `deletedAt` | string | Deletion timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | + +### `thrive_create_assignment` + +Create a compliance assignment in Thrive for an audience and content item. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceId` | string | Yes | The audience ID | +| `contentId` | string | Yes | The content ID for the primary content | +| `alternativeContentIds` | string | No | JSON array of content IDs that can also complete the assignment | +| `hideAlternativeContent` | boolean | No | Whether to hide the alternative content | +| `completionPeriod` | number | No | The number of days required to complete the assignment \(default 30\) | +| `recurrence` | number | No | The number of days until the assignment will reoccur | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assignment` | object | The created assignment | +| ↳ `id` | string | The assignment ID | +| ↳ `audienceId` | string | The audience ID | +| ↳ `primaryContentId` | string | The content ID for the primary content | +| ↳ `alternativeContentIds` | array | Content IDs that can also complete the assignment | +| ↳ `hideAlternativeContent` | boolean | Whether to hide the alternative content | +| ↳ `completionPeriod` | number | Number of days required to complete the assignment | +| ↳ `recurrence` | number | Number of days until the assignment reoccurs | +| ↳ `isActive` | boolean | Whether the assignment is active | +| ↳ `isDeleted` | boolean | Whether the assignment is deleted | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `deletedAt` | string | Deletion timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | + +### `thrive_get_assignment` + +Get a single compliance assignment in Thrive by its ID. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `assignmentId` | string | Yes | The assignment ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assignment` | object | The assignment | +| ↳ `id` | string | The assignment ID | +| ↳ `audienceId` | string | The audience ID | +| ↳ `primaryContentId` | string | The content ID for the primary content | +| ↳ `alternativeContentIds` | array | Content IDs that can also complete the assignment | +| ↳ `hideAlternativeContent` | boolean | Whether to hide the alternative content | +| ↳ `completionPeriod` | number | Number of days required to complete the assignment | +| ↳ `recurrence` | number | Number of days until the assignment reoccurs | +| ↳ `isActive` | boolean | Whether the assignment is active | +| ↳ `isDeleted` | boolean | Whether the assignment is deleted | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `deletedAt` | string | Deletion timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | + +### `thrive_update_assignment` + +Update a compliance assignment in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `assignmentId` | string | Yes | The assignment ID | +| `audienceId` | string | Yes | The audience ID | +| `contentId` | string | No | The content ID for the primary content | +| `completionPeriod` | number | No | The number of days required to complete the assignment | +| `recurrence` | number | No | The number of days until the assignment will reoccur | +| `alternativeContentIds` | string | No | JSON array of content IDs that can also complete the assignment | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assignment` | object | The updated assignment | +| ↳ `id` | string | The assignment ID | +| ↳ `audienceId` | string | The audience ID | +| ↳ `primaryContentId` | string | The content ID for the primary content | +| ↳ `alternativeContentIds` | array | Content IDs that can also complete the assignment | +| ↳ `hideAlternativeContent` | boolean | Whether to hide the alternative content | +| ↳ `completionPeriod` | number | Number of days required to complete the assignment | +| ↳ `recurrence` | number | Number of days until the assignment reoccurs | +| ↳ `isActive` | boolean | Whether the assignment is active | +| ↳ `isDeleted` | boolean | Whether the assignment is deleted | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `deletedAt` | string | Deletion timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | + +### `thrive_delete_assignment` + +Delete a compliance assignment in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `assignmentId` | string | Yes | The assignment ID | +| `audienceId` | string | Yes | The audience ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Whether the assignment was deleted | + +### `thrive_list_enrolments` + +List enrolments for a compliance assignment in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `assignmentId` | string | Yes | The assignment ID | +| `updatedAtFrom` | string | No | Start date to filter enrolments from \(ISO 8601\) | +| `updatedAtTo` | string | No | Date to filter enrolments up to \(ISO 8601\). Requires updatedAtFrom. | +| `status` | string | No | Filter by enrolment status: archived, complete, open, overdue, scheduled, or unassigned | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-100, default 100\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `enrolments` | array | The matching enrolments | +| ↳ `id` | string | The enrolment ID | +| ↳ `userId` | string | The assignee user ID | +| ↳ `assignmentId` | string | The assignment ID | +| ↳ `audienceId` | string | The audience ID | +| ↳ `primaryContentId` | string | The assigned content ID | +| ↳ `status` | string | Enrolment status | +| ↳ `availableDate` | string | Date a scheduled enrolment becomes open | +| ↳ `dueDate` | string | Date after which a scheduled enrolment is overdue | +| ↳ `lastCompletedAt` | string | Date a scheduled enrolment was last completed | +| ↳ `history` | array | Event-log history entries | +| ↳ `type` | string | The type of the logged event | +| ↳ `completionId` | string | The completion ID | +| ↳ `previousStatus` | string | The previous enrolment status | +| ↳ `nextStatus` | string | The next enrolment status | +| ↳ `createdAt` | string | Date the event was logged | +| ↳ `updatedAt` | string | Date the event was last modified | +| ↳ `updatedAt` | string | Date the enrolment was last updated | + +### `thrive_get_enrolment` + +Get a single enrolment for a compliance assignment in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `assignmentId` | string | Yes | The assignment ID | +| `enrolmentId` | string | Yes | The enrolment ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `enrolment` | object | The enrolment | +| ↳ `id` | string | The enrolment ID | +| ↳ `userId` | string | The assignee user ID | +| ↳ `assignmentId` | string | The assignment ID | +| ↳ `audienceId` | string | The audience ID | +| ↳ `primaryContentId` | string | The assigned content ID | +| ↳ `status` | string | Enrolment status | +| ↳ `availableDate` | string | Date a scheduled enrolment becomes open | +| ↳ `dueDate` | string | Date after which a scheduled enrolment is overdue | +| ↳ `lastCompletedAt` | string | Date a scheduled enrolment was last completed | +| ↳ `history` | array | Event-log history entries | +| ↳ `type` | string | The type of the logged event | +| ↳ `completionId` | string | The completion ID | +| ↳ `previousStatus` | string | The previous enrolment status | +| ↳ `nextStatus` | string | The next enrolment status | +| ↳ `createdAt` | string | Date the event was logged | +| ↳ `updatedAt` | string | Date the event was last modified | +| ↳ `updatedAt` | string | Date the enrolment was last updated | + +### `thrive_list_completions` + +List learning completion records in Thrive, optionally filtered by user or content. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `contentId` | string | No | Filter by content | +| `isRPL` | boolean | No | Filter by completions imported via Recognition of Prior Learning \(RPL\) | +| `userId` | string | No | Filter by user | +| `completedDateRangeStart` | string | No | Filter by completedDate \(completedDate >= this date/date-time\) | +| `completedDateRangeEnd` | string | No | Filter by completedDate \(completedDate <= this date/date-time\) | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 1000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `completions` | array | The matching completion records | +| ↳ `id` | string | The completion ID | +| ↳ `userId` | string | The user ID | +| ↳ `contentId` | string | The content ID for the content completed | +| ↳ `contentVersion` | number | The version of the content | +| ↳ `skills` | array | The skills acquired by completing this content | +| ↳ `completionType` | string | The type of completion record | +| ↳ `hadDueDate` | boolean | Whether the completion had a due date | +| ↳ `isRPL` | boolean | Whether the completion was imported via RPL | +| ↳ `completedAt` | string | Timestamp when the completion occurred \(ISO 8601\) | +| ↳ `activeUntil` | string | Timestamp the completion is valid until \(ISO 8601\) | + +### `thrive_get_completion` + +Get a single learning completion record in Thrive by its ID. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `id` | string | Yes | The completion ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `completion` | object | The completion record | +| ↳ `id` | string | The completion ID | +| ↳ `userId` | string | The user ID | +| ↳ `contentId` | string | The content ID for the content completed | +| ↳ `contentVersion` | number | The version of the content | +| ↳ `skills` | array | The skills acquired by completing this content | +| ↳ `completionType` | string | The type of completion record | +| ↳ `hadDueDate` | boolean | Whether the completion had a due date | +| ↳ `isRPL` | boolean | Whether the completion was imported via RPL | +| ↳ `completedAt` | string | Timestamp when the completion occurred \(ISO 8601\) | +| ↳ `activeUntil` | string | Timestamp the completion is valid until \(ISO 8601\) | + +### `thrive_create_completion` + +Record a learning completion in Thrive for a user and content item. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `userId` | string | Yes | The user ID | +| `contentId` | string | Yes | The content ID for the content completed | +| `completedAt` | string | Yes | ISO8601 timestamp when the completion occurred | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `statementId` | string | The completion statement ID | + +### `thrive_get_content` + +Get a single content record in Thrive by its ID. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `id` | string | Yes | Unique identifier of the content item | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | object | The content record | +| ↳ `id` | string | Unique identifier for the content | +| ↳ `title` | string | Title of the content | +| ↳ `description` | string | Detailed description \(may contain HTML\) | +| ↳ `tags` | array | Tags associated with this content | +| ↳ `type` | string | The kind of artifact associated with this content | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | +| ↳ `author` | string | User ID who authored the content | +| ↳ `isOfficial` | boolean | Whether the content is recognised as official | +| ↳ `duration` | object | Expected time to complete the content | +| ↳ `value` | number | Duration value | +| ↳ `unit` | string | The unit of the duration \(always 'minutes'\) | +| ↳ `contentHistory` | array | Chronological history of actions on this content | + +### `thrive_query_content` + +Query content records in Thrive with pagination and filtering options. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 20\) | +| `types` | string | No | Comma-separated content types \(article, assessment, broadcast, cmi5, elearning, event, file, pathway, question, quiz, scorm, url, video, mixed\). If both set, omitTypes is ignored. | +| `omitTypes` | string | No | Comma-separated content types \(article, assessment, broadcast, cmi5, elearning, event, file, pathway, question, quiz, scorm, url, video, mixed\). If both set, omitTypes is ignored. | +| `updatedSince` | string | No | Return only items updated on or after this date/time \(ISO 8601\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The matching content records | +| ↳ `id` | string | Unique identifier for the content | +| ↳ `title` | string | Title of the content | +| ↳ `description` | string | Detailed description \(may contain HTML\) | +| ↳ `tags` | array | Tags associated with this content | +| ↳ `type` | string | The kind of artifact associated with this content | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | +| ↳ `author` | string | User ID who authored the content | +| ↳ `isOfficial` | boolean | Whether the content is recognised as official | +| ↳ `duration` | object | Expected time to complete the content | +| ↳ `value` | number | Duration value | +| ↳ `unit` | string | The unit of the duration \(always 'minutes'\) | +| ↳ `contentHistory` | array | Chronological history of actions on this content | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_get_activity` + +Get a single activity record in Thrive by its ID. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `id` | string | Yes | Unique identifier of the activity | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `activity` | object | The activity record | +| ↳ `type` | string | The activity action type | +| ↳ `name` | string | The name of the activity | +| ↳ `id` | string | Unique ID for this activity record | +| ↳ `user` | string | User ID who triggered the activity | +| ↳ `date` | string | Timestamp when the activity occurred \(ISO 8601\) | +| ↳ `contextId` | string | Identifier for the context item | +| ↳ `contextType` | string | What this activity was in relation to | +| ↳ `data` | json | Unstructured activity data; shape varies by type | +| ↳ `with` | json | Additional context information | + +### `thrive_query_activities` + +Query activity records in Thrive with pagination and filtering options. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 20\) | +| `actions` | string | No | comma-separated activity types e.g. viewed,completed | +| `omitActions` | string | No | comma-separated activity types e.g. viewed,completed | +| `contentIds` | string | No | comma-separated content IDs | +| `contentType` | string | No | Filter by content type | +| `timestampFrom` | string | No | format YYYY-MM-DD hh:mm:ss | +| `timestampTo` | string | No | format YYYY-MM-DD hh:mm:ss | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The matching activity records | +| ↳ `type` | string | The activity action type | +| ↳ `name` | string | The name of the activity | +| ↳ `id` | string | Unique ID for this activity record | +| ↳ `user` | string | User ID who triggered the activity | +| ↳ `date` | string | Timestamp when the activity occurred \(ISO 8601\) | +| ↳ `contextId` | string | Identifier for the context item | +| ↳ `contextType` | string | What this activity was in relation to | +| ↳ `data` | json | Unstructured activity data; shape varies by type | +| ↳ `with` | json | Additional context information | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_get_cpd_category` + +Get a single CPD category in Thrive by its ID. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `categoryId` | string | Yes | The CPD category ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `category` | object | The CPD category | +| ↳ `categoryId` | string | Unique ID for this category record | +| ↳ `name` | string | Name of the category of CPD activity | + +### `thrive_query_cpd_categories` + +Query CPD categories in Thrive and return results with pagination. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 100\) | +| `updatedSince` | string | No | Return only items updated on or after this date/time \(ISO 8601\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The matching CPD categories | +| ↳ `categoryId` | string | Unique ID for this category record | +| ↳ `name` | string | Name of the category of CPD activity | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_get_cpd_entry` + +Get a single CPD log entry in Thrive by its ID. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `logEntryId` | string | Yes | The CPD log entry ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `entry` | object | The CPD entry | +| ↳ `logEntryId` | string | Unique ID for this activity record | +| ↳ `userId` | string | User ID who triggered this activity record | +| ↳ `activity` | object | The content item associated with the CPD log entry | +| ↳ `type` | string | The type of content \(e.g. file, article, video\) | +| ↳ `name` | string | The name of the content item | +| ↳ `category` | object | The CPD category | +| ↳ `categoryId` | string | Unique ID for this category record | +| ↳ `name` | string | Name of the category of CPD activity | +| ↳ `entryDate` | string | The date and time the CPD entry was logged \(ISO 8601\) | +| ↳ `durationMinutes` | number | Minutes logged as CPD from this activity | +| ↳ `description` | string | Summary or reflective statement | +| ↳ `isVerified` | boolean | Whether the activity was generated from verified system activity | + +### `thrive_query_cpd_entries` + +Query CPD log entries in Thrive and return results with pagination. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 100\) | +| `entryDateFrom` | string | No | Filter entries after this date \(format YYYY-MM-DD hh:mm:ss\) | +| `entryDateTo` | string | No | Filter entries before this date \(format YYYY-MM-DD hh:mm:ss\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The matching CPD entries | +| ↳ `logEntryId` | string | Unique ID for this activity record | +| ↳ `userId` | string | User ID who triggered this activity record | +| ↳ `activity` | object | The content item associated with the CPD log entry | +| ↳ `type` | string | The type of content \(e.g. file, article, video\) | +| ↳ `name` | string | The name of the content item | +| ↳ `category` | object | The CPD category | +| ↳ `categoryId` | string | Unique ID for this category record | +| ↳ `name` | string | Name of the category of CPD activity | +| ↳ `entryDate` | string | The date and time the CPD entry was logged \(ISO 8601\) | +| ↳ `durationMinutes` | number | Minutes logged as CPD from this activity | +| ↳ `description` | string | Summary or reflective statement | +| ↳ `isVerified` | boolean | Whether the activity was generated from verified system activity | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_get_cpd_requirement` + +Get a single CPD requirement summary in Thrive by its ID. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `audienceRequirementId` | string | Yes | The CPD requirement ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `requirement` | object | The CPD requirement | +| ↳ `audienceRequirementId` | string | Unique ID for this requirement record | +| ↳ `audienceId` | string | ID of the audience this requirement applies to | +| ↳ `requiredMinutes` | number | Number of minutes required for CPD completion | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | + +### `thrive_query_cpd_requirements` + +Query CPD requirement summaries in Thrive and return results with pagination. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 100\) | +| `updatedSince` | string | No | Return only items updated on or after this date/time \(ISO 8601\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The matching CPD requirements | +| ↳ `audienceRequirementId` | string | Unique ID for this requirement record | +| ↳ `audienceId` | string | ID of the audience this requirement applies to | +| ↳ `requiredMinutes` | number | Number of minutes required for CPD completion | +| ↳ `createdAt` | string | Creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Last-update timestamp \(ISO 8601\) | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_query_cpd_user_summaries` + +Query CPD user log summaries in Thrive and return results with pagination. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `entryDateFrom` | string | Yes | Filter entries after this date \(format YYYY-MM-DDThh:mm:ss\) | +| `entryDateTo` | string | Yes | Filter entries before this date \(format YYYY-MM-DDThh:mm:ss\) | +| `userIds` | string | No | Comma-separated user IDs to filter by | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 100\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The matching CPD user summaries | +| ↳ `userId` | string | ID of the user this summary is for | +| ↳ `durationMinutes` | number | Total CPD minutes logged by the user in the period | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_list_tags` + +List tags in Thrive and return tag information with pagination. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `page` | number | No | Page number for pagination \(default 1\) | +| `perPage` | number | No | Number of results per page \(1-1000, default 100\) | +| `updatedSince` | string | No | Return only tags updated on or after this date/time \(ISO 8601\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | array | The tags | +| ↳ `tag` | string | The name of the tag | +| ↳ `id` | string | The ID of the tag | +| ↳ `contents` | array | IDs of contents using this tag | +| ↳ `campaigns` | array | IDs of campaigns using this tag | +| ↳ `interests` | array | IDs of users interested in this tag | +| ↳ `skills` | array | IDs of users skilled in this tag | +| `pagination` | object | Pagination details | +| ↳ `totalResults` | number | Total number of results matching the query | +| ↳ `totalPages` | number | Total number of pages available | +| ↳ `page` | number | Current page number | +| ↳ `perPage` | number | Number of results per page | + +### `thrive_get_tag` + +Get a single tag in Thrive by its ID. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `tagId` | string | Yes | The tag ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tag` | object | The tag | +| ↳ `tag` | string | The name of the tag | +| ↳ `id` | string | The ID of the tag | +| ↳ `contents` | array | IDs of contents using this tag | +| ↳ `campaigns` | array | IDs of campaigns using this tag | +| ↳ `interests` | array | IDs of users interested in this tag | +| ↳ `skills` | array | IDs of users skilled in this tag | + +### `thrive_add_user_tags` + +Add one or more tags to a learner in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `userId` | string | Yes | The learner ID | +| `tags` | string | Yes | JSON array of tag names to add \(1-100\). Example: \["leadership"\] | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `status` | number | The HTTP status code of the operation | +| `message` | string | A human-readable result message | + +### `thrive_remove_user_tags` + +Remove one or more tags from a learner in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `userId` | string | Yes | The learner ID | +| `tags` | string | Yes | JSON array of tag names to remove \(1-100\). Example: \["leadership"\] | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `status` | number | The HTTP status code of the operation | +| `message` | string | A human-readable result message | + +### `thrive_update_user_skills` + +Update skills and levels for a learner in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | +| `userId` | string | Yes | The learner ID | +| `skills` | string | Yes | JSON array of skill objects \(1-100\). Each: \{"tagName":"leadership","level":1,"targetLevel":3\}. level/targetLevel optional \(min -1\). | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `status` | number | The HTTP status code of the operation | +| `message` | string | A human-readable result message | + +### `thrive_get_skill_levels` + +Get the available skill levels configured in Thrive. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `tenantId` | string | Yes | Thrive Tenant ID \(used as the Basic auth username\) | +| `apiKey` | string | Yes | Thrive API key \(used as the Basic auth password\) | +| `host` | string | No | Region-specific API host | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `levels` | array | The available skill levels | +| ↳ `name` | string | The name of the skill level | +| ↳ `isEnabled` | boolean | Whether the skill level is enabled | +| ↳ `value` | number | The numeric value of the skill level | + + diff --git a/apps/sim/blocks/blocks/thrive.ts b/apps/sim/blocks/blocks/thrive.ts new file mode 100644 index 00000000000..3f0062e3621 --- /dev/null +++ b/apps/sim/blocks/blocks/thrive.ts @@ -0,0 +1,1027 @@ +import { ThriveIcon } from '@/components/icons' +import { AuthMode, type BlockConfig, type BlockMeta, IntegrationType } from '@/blocks/types' + +const PAGINATED_OPS = [ + 'search_users', + 'list_audiences', + 'list_audience_members', + 'list_assignments', + 'list_enrolments', + 'list_completions', + 'query_content', + 'query_activities', + 'query_cpd_categories', + 'query_cpd_entries', + 'query_cpd_requirements', + 'query_cpd_user_summaries', + 'list_tags', +] + +const UPDATED_SINCE_OPS = [ + 'search_users', + 'list_audiences', + 'list_assignments', + 'query_content', + 'query_cpd_categories', + 'query_cpd_requirements', + 'list_tags', +] + +const REF_OPS = ['create_user', 'update_user', 'delete_user', 'suspend_user', 'get_user_by_ref'] +const ID_OPS = ['get_user_by_id', 'get_completion', 'get_content', 'get_activity'] +const USER_FIELD_OPS = ['create_user', 'update_user'] + +const AUDIENCE_ID_REQUIRED_OPS = [ + 'get_audience', + 'update_audience', + 'delete_audience', + 'list_audience_members', + 'add_audience_members', + 'replace_audience_members', + 'remove_audience_member', + 'list_audience_managers', + 'add_audience_managers', + 'replace_audience_managers', + 'remove_audience_manager', + 'create_assignment', + 'update_assignment', + 'delete_assignment', +] +const AUDIENCE_ID_OPS = [...AUDIENCE_ID_REQUIRED_OPS, 'list_assignments'] + +const ASSIGNMENT_ID_OPS = [ + 'get_assignment', + 'update_assignment', + 'delete_assignment', + 'list_enrolments', + 'get_enrolment', +] + +const USER_ID_REQUIRED_OPS = [ + 'remove_audience_manager', + 'add_user_tags', + 'remove_user_tags', + 'update_user_skills', + 'create_completion', +] +const USER_ID_OPS = [...USER_ID_REQUIRED_OPS, 'list_completions'] + +export const ThriveBlock: BlockConfig = { + type: 'thrive', + name: 'Thrive', + description: 'Manage users, audiences, learning and CPD on Thrive', + longDescription: + 'Integrate Thrive Learning into the workflow. Manage user lifecycle, audiences and their members and managers, content assignments and enrolments, learning completions, content and activity records, CPD, tags, and skills.', + docsLink: 'https://docs.sim.ai/tools/thrive', + category: 'tools', + integrationType: IntegrationType.HR, + bgColor: '#FFFFFF', + icon: ThriveIcon, + authMode: AuthMode.ApiKey, + + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Create User', id: 'create_user' }, + { label: 'Update User', id: 'update_user' }, + { label: 'Delete User', id: 'delete_user' }, + { label: 'Suspend User', id: 'suspend_user' }, + { label: 'Search Users', id: 'search_users' }, + { label: 'Get User by ID', id: 'get_user_by_id' }, + { label: 'Get User by Ref', id: 'get_user_by_ref' }, + { label: 'List Audiences', id: 'list_audiences' }, + { label: 'Create Audience', id: 'create_audience' }, + { label: 'Get Audience', id: 'get_audience' }, + { label: 'Update Audience', id: 'update_audience' }, + { label: 'Delete Audience', id: 'delete_audience' }, + { label: 'List Audience Members', id: 'list_audience_members' }, + { label: 'Add Audience Members', id: 'add_audience_members' }, + { label: 'Replace Audience Members', id: 'replace_audience_members' }, + { label: 'Remove Audience Member', id: 'remove_audience_member' }, + { label: 'List Audience Managers', id: 'list_audience_managers' }, + { label: 'Add Audience Managers', id: 'add_audience_managers' }, + { label: 'Replace Audience Managers', id: 'replace_audience_managers' }, + { label: 'Remove Audience Manager', id: 'remove_audience_manager' }, + { label: 'List Assignments', id: 'list_assignments' }, + { label: 'Create Assignment', id: 'create_assignment' }, + { label: 'Get Assignment', id: 'get_assignment' }, + { label: 'Update Assignment', id: 'update_assignment' }, + { label: 'Delete Assignment', id: 'delete_assignment' }, + { label: 'List Enrolments', id: 'list_enrolments' }, + { label: 'Get Enrolment', id: 'get_enrolment' }, + { label: 'List Completions', id: 'list_completions' }, + { label: 'Get Completion', id: 'get_completion' }, + { label: 'Create Completion', id: 'create_completion' }, + { label: 'Query Content', id: 'query_content' }, + { label: 'Get Content', id: 'get_content' }, + { label: 'Query Activities', id: 'query_activities' }, + { label: 'Get Activity', id: 'get_activity' }, + { label: 'Query CPD Categories', id: 'query_cpd_categories' }, + { label: 'Get CPD Category', id: 'get_cpd_category' }, + { label: 'Query CPD Entries', id: 'query_cpd_entries' }, + { label: 'Get CPD Entry', id: 'get_cpd_entry' }, + { label: 'Query CPD Requirements', id: 'query_cpd_requirements' }, + { label: 'Get CPD Requirement', id: 'get_cpd_requirement' }, + { label: 'Query CPD User Summaries', id: 'query_cpd_user_summaries' }, + { label: 'List Tags', id: 'list_tags' }, + { label: 'Get Tag', id: 'get_tag' }, + { label: 'Add User Tags', id: 'add_user_tags' }, + { label: 'Remove User Tags', id: 'remove_user_tags' }, + { label: 'Update User Skills', id: 'update_user_skills' }, + { label: 'Get Skill Levels', id: 'get_skill_levels' }, + ], + value: () => 'search_users', + }, + { + id: 'tenantId', + title: 'Tenant ID', + type: 'short-input', + placeholder: 'Enter your Thrive Tenant ID', + required: true, + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + placeholder: 'Enter your Thrive API key', + password: true, + required: true, + }, + { + id: 'host', + title: 'Region', + type: 'dropdown', + options: [ + { label: 'Production', id: 'public.api.learn.link' }, + { label: 'Production (MEA)', id: 'public.api.mea.learn.tech' }, + { label: 'Staging', id: 'public.api.learnstaging.link' }, + { label: 'Staging (MEA)', id: 'public.api.meastaging.learn.tech' }, + ], + value: () => 'public.api.learn.link', + }, + + { + id: 'ref', + title: 'User Ref', + type: 'short-input', + placeholder: "Your organisation's unique identifier for the user", + condition: { field: 'operation', value: REF_OPS }, + required: { field: 'operation', value: REF_OPS }, + }, + { + id: 'id', + title: 'Record ID', + type: 'short-input', + placeholder: 'The ID of the user, completion, content, or activity record', + condition: { field: 'operation', value: ID_OPS }, + required: { field: 'operation', value: ID_OPS }, + }, + { + id: 'firstName', + title: 'First Name', + type: 'short-input', + placeholder: 'The given name of the individual', + condition: { field: 'operation', value: USER_FIELD_OPS }, + required: { field: 'operation', value: 'create_user' }, + }, + { + id: 'lastName', + title: 'Last Name', + type: 'short-input', + placeholder: 'The family name of the individual', + condition: { field: 'operation', value: USER_FIELD_OPS }, + required: { field: 'operation', value: 'create_user' }, + }, + { + id: 'email', + title: 'Email', + type: 'short-input', + placeholder: "The user's email address (required unless login method is 'ref')", + condition: { field: 'operation', value: USER_FIELD_OPS }, + }, + { + id: 'loginMethod', + title: 'Login Method', + type: 'dropdown', + options: [ + { label: 'Email', id: 'email' }, + { label: 'Ref', id: 'ref' }, + ], + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + }, + { + id: 'role', + title: 'Role', + type: 'dropdown', + options: [ + { label: 'Learner', id: 'learner' }, + { label: 'Learner Admin', id: 'learneradmin' }, + { label: 'Administrator', id: 'administrator' }, + ], + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + }, + { + id: 'jobTitle', + title: 'Job Title', + type: 'short-input', + placeholder: "Name of this individual's role in your organisation", + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + }, + { + id: 'managerRef', + title: 'Manager Ref', + type: 'short-input', + placeholder: "The line manager's unique identifier", + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + }, + { + id: 'startDate', + title: 'Start Date', + type: 'short-input', + placeholder: 'Date the individual started (ISO 8601)', + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: 'Generate an ISO 8601 timestamp. Return ONLY the timestamp string.', + generationType: 'json-object', + }, + }, + { + id: 'endDate', + title: 'End Date', + type: 'short-input', + placeholder: 'Date the individual left (ISO 8601)', + condition: { field: 'operation', value: [...USER_FIELD_OPS, 'suspend_user'] }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: 'Generate an ISO 8601 timestamp. Return ONLY the timestamp string.', + generationType: 'json-object', + }, + }, + { + id: 'timeZone', + title: 'Time Zone', + type: 'short-input', + placeholder: "The user's preferred timezone", + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + }, + { + id: 'languageCode', + title: 'Language Code', + type: 'short-input', + placeholder: "The user's preferred language (e.g. en-gb)", + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + }, + { + id: 'sso', + title: 'SSO Managed', + type: 'switch', + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + }, + { + id: 'domain', + title: 'Domain', + type: 'short-input', + placeholder: 'Domain this individual is associated with', + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + }, + { + id: 'additionalFields', + title: 'Additional Fields', + type: 'long-input', + placeholder: '{"department":"Sales"}', + condition: { field: 'operation', value: USER_FIELD_OPS }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: + 'Generate a JSON object of custom field key-value pairs for a Thrive user. Return ONLY the JSON object.', + generationType: 'json-object', + }, + }, + + { + id: 'statuses', + title: 'Statuses', + type: 'short-input', + placeholder: 'Comma-separated: active, inactive, expired, new', + condition: { field: 'operation', value: 'search_users' }, + mode: 'advanced', + }, + { + id: 'omitStatuses', + title: 'Omit Statuses', + type: 'short-input', + placeholder: 'Comma-separated statuses to exclude', + condition: { field: 'operation', value: 'search_users' }, + mode: 'advanced', + }, + { + id: 'userStatus', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'Active', id: 'active' }, + { label: 'Inactive', id: 'inactive' }, + { label: 'Expired', id: 'expired' }, + { label: 'New', id: 'new' }, + ], + condition: { field: 'operation', value: 'search_users' }, + mode: 'advanced', + }, + { + id: 'enrolmentStatus', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'Archived', id: 'archived' }, + { label: 'Complete', id: 'complete' }, + { label: 'Open', id: 'open' }, + { label: 'Overdue', id: 'overdue' }, + { label: 'Scheduled', id: 'scheduled' }, + { label: 'Unassigned', id: 'unassigned' }, + ], + condition: { field: 'operation', value: 'list_enrolments' }, + mode: 'advanced', + }, + + { + id: 'audienceId', + title: 'Audience ID', + type: 'short-input', + placeholder: 'The audience ID or reference', + condition: { field: 'operation', value: AUDIENCE_ID_OPS }, + required: { field: 'operation', value: AUDIENCE_ID_REQUIRED_OPS }, + }, + { + id: 'name', + title: 'Audience Name', + type: 'short-input', + placeholder: 'The name of the audience', + condition: { field: 'operation', value: ['create_audience', 'update_audience'] }, + }, + { + id: 'reference', + title: 'Audience Reference', + type: 'short-input', + placeholder: 'The external reference for the audience', + condition: { field: 'operation', value: ['create_audience', 'update_audience'] }, + mode: 'advanced', + }, + { + id: 'parentId', + title: 'Parent ID', + type: 'short-input', + placeholder: 'The id/reference of the parent audience or structure', + condition: { field: 'operation', value: ['create_audience', 'update_audience'] }, + mode: 'advanced', + }, + { + id: 'category', + title: 'Category', + type: 'dropdown', + options: [ + { label: 'Audience', id: 'audience' }, + { label: 'Structure', id: 'structure' }, + ], + condition: { field: 'operation', value: 'create_audience' }, + mode: 'advanced', + }, + { + id: 'users', + title: 'Users', + type: 'long-input', + placeholder: '["user@example.com", "user-ref-2"]', + condition: { + field: 'operation', + value: ['add_audience_members', 'replace_audience_members'], + }, + required: { + field: 'operation', + value: ['add_audience_members', 'replace_audience_members'], + }, + wandConfig: { + enabled: true, + prompt: + 'Generate a JSON array of user emails, refs, or ids (1-100 items). Return ONLY the JSON array.', + generationType: 'json-object', + }, + }, + { + id: 'userRef', + title: 'User', + type: 'short-input', + placeholder: "The member's email, ref, or id to remove", + condition: { field: 'operation', value: 'remove_audience_member' }, + required: { field: 'operation', value: 'remove_audience_member' }, + }, + { + id: 'managers', + title: 'Managers', + type: 'long-input', + placeholder: + '[{"reference":"user@example.com","permissions":{"audienceManager":{"manageContent":true,"assignments":true},"peopleManager":{"canViewLearnPage":true,"insights":false,"manage":false}}}]', + condition: { + field: 'operation', + value: ['add_audience_managers', 'replace_audience_managers'], + }, + required: { + field: 'operation', + value: ['add_audience_managers', 'replace_audience_managers'], + }, + wandConfig: { + enabled: true, + prompt: + 'Generate a JSON array of Thrive audience manager objects (1-100). Each has "reference" (email or ref) and "permissions" with audienceManager {manageContent, assignments}, peopleManager {canViewLearnPage, insights, manage}, and optionally administrator {canAddAudienceManagers}. Return ONLY the JSON array.', + generationType: 'json-object', + }, + }, + + { + id: 'assignmentId', + title: 'Assignment ID', + type: 'short-input', + placeholder: 'The assignment ID', + condition: { field: 'operation', value: ASSIGNMENT_ID_OPS }, + required: { field: 'operation', value: ASSIGNMENT_ID_OPS }, + }, + { + id: 'enrolmentId', + title: 'Enrolment ID', + type: 'short-input', + placeholder: 'The enrolment ID', + condition: { field: 'operation', value: 'get_enrolment' }, + required: { field: 'operation', value: 'get_enrolment' }, + }, + { + id: 'contentId', + title: 'Content ID', + type: 'short-input', + placeholder: 'The content ID', + condition: { + field: 'operation', + value: ['create_assignment', 'update_assignment', 'list_completions', 'create_completion'], + }, + required: { field: 'operation', value: ['create_assignment', 'create_completion'] }, + }, + { + id: 'alternativeContentIds', + title: 'Alternative Content IDs', + type: 'long-input', + placeholder: '["63f57d805d8ecbf46f5b5278"]', + condition: { field: 'operation', value: ['create_assignment', 'update_assignment'] }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: + 'Generate a JSON array of content IDs that can also complete the assignment. Return ONLY the JSON array.', + generationType: 'json-object', + }, + }, + { + id: 'hideAlternativeContent', + title: 'Hide Alternative Content', + type: 'switch', + condition: { field: 'operation', value: 'create_assignment' }, + mode: 'advanced', + }, + { + id: 'completionPeriod', + title: 'Completion Period (days)', + type: 'short-input', + placeholder: 'Number of days required to complete (default 30)', + condition: { field: 'operation', value: ['create_assignment', 'update_assignment'] }, + mode: 'advanced', + }, + { + id: 'recurrence', + title: 'Recurrence (days)', + type: 'short-input', + placeholder: 'Number of days until the assignment reoccurs', + condition: { field: 'operation', value: ['create_assignment', 'update_assignment'] }, + mode: 'advanced', + }, + + { + id: 'userId', + title: 'User ID', + type: 'short-input', + placeholder: 'The user ID', + condition: { field: 'operation', value: USER_ID_OPS }, + required: { field: 'operation', value: USER_ID_REQUIRED_OPS }, + }, + { + id: 'isRPL', + title: 'Imported via RPL', + type: 'switch', + condition: { field: 'operation', value: 'list_completions' }, + mode: 'advanced', + }, + { + id: 'completedDateRangeStart', + title: 'Completed From', + type: 'short-input', + placeholder: 'Filter completions on/after this date (e.g. 2024-04-02)', + condition: { field: 'operation', value: 'list_completions' }, + mode: 'advanced', + }, + { + id: 'completedDateRangeEnd', + title: 'Completed To', + type: 'short-input', + placeholder: 'Filter completions on/before this date (e.g. 2024-04-30)', + condition: { field: 'operation', value: 'list_completions' }, + mode: 'advanced', + }, + { + id: 'completedAt', + title: 'Completed At', + type: 'short-input', + placeholder: 'ISO 8601 timestamp when the completion occurred', + condition: { field: 'operation', value: 'create_completion' }, + required: { field: 'operation', value: 'create_completion' }, + wandConfig: { + enabled: true, + prompt: 'Generate an ISO 8601 timestamp. Return ONLY the timestamp string.', + generationType: 'json-object', + }, + }, + + { + id: 'types', + title: 'Types', + type: 'short-input', + placeholder: 'Comma-separated content types (e.g. pathway,event)', + condition: { field: 'operation', value: 'query_content' }, + mode: 'advanced', + }, + { + id: 'omitTypes', + title: 'Omit Types', + type: 'short-input', + placeholder: 'Comma-separated content types to exclude', + condition: { field: 'operation', value: 'query_content' }, + mode: 'advanced', + }, + + { + id: 'actions', + title: 'Actions', + type: 'short-input', + placeholder: 'Comma-separated activity types (e.g. viewed,completed)', + condition: { field: 'operation', value: 'query_activities' }, + mode: 'advanced', + }, + { + id: 'omitActions', + title: 'Omit Actions', + type: 'short-input', + placeholder: 'Comma-separated activity types to exclude', + condition: { field: 'operation', value: 'query_activities' }, + mode: 'advanced', + }, + { + id: 'contentIds', + title: 'Content IDs', + type: 'short-input', + placeholder: 'Comma-separated content IDs', + condition: { field: 'operation', value: 'query_activities' }, + mode: 'advanced', + }, + { + id: 'contentType', + title: 'Content Type', + type: 'short-input', + placeholder: 'Filter by content type (e.g. content)', + condition: { field: 'operation', value: 'query_activities' }, + mode: 'advanced', + }, + { + id: 'timestampFrom', + title: 'Timestamp From', + type: 'short-input', + placeholder: 'YYYY-MM-DD hh:mm:ss', + condition: { field: 'operation', value: 'query_activities' }, + mode: 'advanced', + }, + { + id: 'timestampTo', + title: 'Timestamp To', + type: 'short-input', + placeholder: 'YYYY-MM-DD hh:mm:ss', + condition: { field: 'operation', value: 'query_activities' }, + mode: 'advanced', + }, + + { + id: 'categoryId', + title: 'CPD Category ID', + type: 'short-input', + placeholder: 'The CPD category ID', + condition: { field: 'operation', value: 'get_cpd_category' }, + required: { field: 'operation', value: 'get_cpd_category' }, + }, + { + id: 'logEntryId', + title: 'CPD Entry ID', + type: 'short-input', + placeholder: 'The CPD log entry ID', + condition: { field: 'operation', value: 'get_cpd_entry' }, + required: { field: 'operation', value: 'get_cpd_entry' }, + }, + { + id: 'audienceRequirementId', + title: 'CPD Requirement ID', + type: 'short-input', + placeholder: 'The CPD requirement ID', + condition: { field: 'operation', value: 'get_cpd_requirement' }, + required: { field: 'operation', value: 'get_cpd_requirement' }, + }, + { + id: 'entryDateFrom', + title: 'Entry Date From', + type: 'short-input', + placeholder: 'Entries: YYYY-MM-DD hh:mm:ss · Summaries: YYYY-MM-DDThh:mm:ss', + condition: { field: 'operation', value: ['query_cpd_entries', 'query_cpd_user_summaries'] }, + required: { field: 'operation', value: 'query_cpd_user_summaries' }, + }, + { + id: 'entryDateTo', + title: 'Entry Date To', + type: 'short-input', + placeholder: 'Entries: YYYY-MM-DD hh:mm:ss · Summaries: YYYY-MM-DDThh:mm:ss', + condition: { field: 'operation', value: ['query_cpd_entries', 'query_cpd_user_summaries'] }, + required: { field: 'operation', value: 'query_cpd_user_summaries' }, + }, + { + id: 'userIds', + title: 'User IDs', + type: 'short-input', + placeholder: 'Comma-separated user IDs to filter by', + condition: { field: 'operation', value: 'query_cpd_user_summaries' }, + mode: 'advanced', + }, + + { + id: 'tagId', + title: 'Tag ID', + type: 'short-input', + placeholder: 'The tag ID', + condition: { field: 'operation', value: 'get_tag' }, + required: { field: 'operation', value: 'get_tag' }, + }, + { + id: 'tags', + title: 'Tags', + type: 'long-input', + placeholder: '["leadership", "communication"]', + condition: { field: 'operation', value: ['add_user_tags', 'remove_user_tags'] }, + required: { field: 'operation', value: ['add_user_tags', 'remove_user_tags'] }, + wandConfig: { + enabled: true, + prompt: 'Generate a JSON array of tag names (1-100). Return ONLY the JSON array.', + generationType: 'json-object', + }, + }, + { + id: 'skills', + title: 'Skills', + type: 'long-input', + placeholder: '[{"tagName":"leadership","level":1,"targetLevel":3}]', + condition: { field: 'operation', value: 'update_user_skills' }, + required: { field: 'operation', value: 'update_user_skills' }, + wandConfig: { + enabled: true, + prompt: + 'Generate a JSON array of skill objects (1-100). Each has "tagName" and optionally "level" and "targetLevel" (numbers, min -1). Return ONLY the JSON array.', + generationType: 'json-object', + }, + }, + + { + id: 'apiControlled', + title: 'API Controlled Only', + type: 'switch', + condition: { field: 'operation', value: 'list_audiences' }, + mode: 'advanced', + }, + { + id: 'updatedAtFrom', + title: 'Updated From', + type: 'short-input', + placeholder: 'Filter enrolments updated on/after this date (ISO 8601)', + condition: { field: 'operation', value: 'list_enrolments' }, + mode: 'advanced', + }, + { + id: 'updatedAtTo', + title: 'Updated To', + type: 'short-input', + placeholder: 'Filter enrolments updated on/before this date (ISO 8601)', + condition: { field: 'operation', value: 'list_enrolments' }, + mode: 'advanced', + }, + { + id: 'updatedSince', + title: 'Updated Since', + type: 'short-input', + placeholder: 'Return only items updated on/after this date/time (ISO 8601)', + condition: { field: 'operation', value: UPDATED_SINCE_OPS }, + mode: 'advanced', + }, + { + id: 'page', + title: 'Page', + type: 'short-input', + placeholder: 'Page number (default 1)', + condition: { field: 'operation', value: PAGINATED_OPS }, + mode: 'advanced', + }, + { + id: 'perPage', + title: 'Per Page', + type: 'short-input', + placeholder: 'Results per page', + condition: { field: 'operation', value: PAGINATED_OPS }, + mode: 'advanced', + }, + ], + + tools: { + access: [ + 'thrive_create_user', + 'thrive_update_user', + 'thrive_delete_user', + 'thrive_suspend_user', + 'thrive_search_users', + 'thrive_get_user_by_id', + 'thrive_get_user_by_ref', + 'thrive_list_audiences', + 'thrive_create_audience', + 'thrive_get_audience', + 'thrive_update_audience', + 'thrive_delete_audience', + 'thrive_list_audience_members', + 'thrive_add_audience_members', + 'thrive_replace_audience_members', + 'thrive_remove_audience_member', + 'thrive_list_audience_managers', + 'thrive_add_audience_managers', + 'thrive_replace_audience_managers', + 'thrive_remove_audience_manager', + 'thrive_list_assignments', + 'thrive_create_assignment', + 'thrive_get_assignment', + 'thrive_update_assignment', + 'thrive_delete_assignment', + 'thrive_list_enrolments', + 'thrive_get_enrolment', + 'thrive_list_completions', + 'thrive_get_completion', + 'thrive_create_completion', + 'thrive_get_content', + 'thrive_query_content', + 'thrive_get_activity', + 'thrive_query_activities', + 'thrive_get_cpd_category', + 'thrive_query_cpd_categories', + 'thrive_get_cpd_entry', + 'thrive_query_cpd_entries', + 'thrive_get_cpd_requirement', + 'thrive_query_cpd_requirements', + 'thrive_query_cpd_user_summaries', + 'thrive_list_tags', + 'thrive_get_tag', + 'thrive_add_user_tags', + 'thrive_remove_user_tags', + 'thrive_update_user_skills', + 'thrive_get_skill_levels', + ], + config: { + tool: (params) => `thrive_${params.operation}`, + params: (params) => { + const result: Record = {} + const numberFields = ['page', 'perPage', 'completionPeriod', 'recurrence'] + for (const field of numberFields) { + const value = params[field] + if (value !== undefined && value !== '') result[field] = Number(value) + } + const booleanFields = ['sso', 'apiControlled', 'hideAlternativeContent', 'isRPL'] + for (const field of booleanFields) { + const value = params[field] + if (value !== undefined && value !== '') { + result[field] = value === true || value === 'true' + } + } + const status = params.userStatus || params.enrolmentStatus + if (status !== undefined && status !== '') result.status = status + return result + }, + }, + }, + + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + tenantId: { type: 'string', description: 'Thrive Tenant ID (Basic auth username)' }, + apiKey: { type: 'string', description: 'Thrive API key (Basic auth password)' }, + host: { type: 'string', description: 'Region-specific API host' }, + ref: { type: 'string', description: 'The user ref' }, + id: { type: 'string', description: 'The ID of the record' }, + firstName: { type: 'string', description: 'The given name of the individual' }, + lastName: { type: 'string', description: 'The family name of the individual' }, + email: { type: 'string', description: 'The email address for the user' }, + loginMethod: { type: 'string', description: 'How the user logs in (email or ref)' }, + role: { type: 'string', description: 'Role assigned to the user' }, + jobTitle: { type: 'string', description: "Name of the individual's role" }, + managerRef: { type: 'string', description: "The line manager's ref" }, + startDate: { type: 'string', description: 'Date the individual started (ISO 8601)' }, + endDate: { type: 'string', description: 'Date the individual left (ISO 8601)' }, + timeZone: { type: 'string', description: "The user's preferred timezone" }, + languageCode: { type: 'string', description: "The user's preferred language" }, + sso: { type: 'boolean', description: 'Whether the account is managed by an auth provider' }, + domain: { type: 'string', description: 'Domain the individual is associated with' }, + additionalFields: { + type: 'string', + description: 'JSON object of custom field key-value pairs', + }, + statuses: { type: 'string', description: 'Comma-separated user statuses to include' }, + omitStatuses: { type: 'string', description: 'Comma-separated user statuses to exclude' }, + userStatus: { type: 'string', description: 'Filter users by status' }, + enrolmentStatus: { type: 'string', description: 'Filter enrolments by status' }, + audienceId: { type: 'string', description: 'The audience ID or reference' }, + name: { type: 'string', description: 'The name of the audience' }, + reference: { type: 'string', description: 'The external reference for the audience' }, + parentId: { + type: 'string', + description: 'The id/reference of the parent audience or structure', + }, + category: { type: 'string', description: 'Audience category (audience or structure)' }, + users: { type: 'string', description: 'JSON array of user emails, refs, or ids' }, + userRef: { type: 'string', description: "The member's email, ref, or id" }, + managers: { type: 'string', description: 'JSON array of audience manager objects' }, + assignmentId: { type: 'string', description: 'The assignment ID' }, + enrolmentId: { type: 'string', description: 'The enrolment ID' }, + contentId: { type: 'string', description: 'The content ID' }, + alternativeContentIds: { type: 'string', description: 'JSON array of alternative content IDs' }, + hideAlternativeContent: { type: 'boolean', description: 'Whether to hide alternative content' }, + completionPeriod: { type: 'number', description: 'Days required to complete the assignment' }, + recurrence: { type: 'number', description: 'Days until the assignment reoccurs' }, + userId: { type: 'string', description: 'The user ID' }, + isRPL: { type: 'boolean', description: 'Filter by completions imported via RPL' }, + completedDateRangeStart: { + type: 'string', + description: 'Filter completions on/after this date', + }, + completedDateRangeEnd: { + type: 'string', + description: 'Filter completions on/before this date', + }, + completedAt: { type: 'string', description: 'ISO 8601 timestamp when the completion occurred' }, + types: { type: 'string', description: 'Comma-separated content types to include' }, + omitTypes: { type: 'string', description: 'Comma-separated content types to exclude' }, + actions: { type: 'string', description: 'Comma-separated activity types to include' }, + omitActions: { type: 'string', description: 'Comma-separated activity types to exclude' }, + contentIds: { type: 'string', description: 'Comma-separated content IDs' }, + contentType: { type: 'string', description: 'Filter by content type' }, + timestampFrom: { type: 'string', description: 'Filter activities after this timestamp' }, + timestampTo: { type: 'string', description: 'Filter activities before this timestamp' }, + categoryId: { type: 'string', description: 'The CPD category ID' }, + logEntryId: { type: 'string', description: 'The CPD log entry ID' }, + audienceRequirementId: { type: 'string', description: 'The CPD requirement ID' }, + entryDateFrom: { type: 'string', description: 'Filter CPD entries after this date' }, + entryDateTo: { type: 'string', description: 'Filter CPD entries before this date' }, + userIds: { type: 'string', description: 'Comma-separated user IDs to filter by' }, + tagId: { type: 'string', description: 'The tag ID' }, + tags: { type: 'string', description: 'JSON array of tag names' }, + skills: { type: 'string', description: 'JSON array of skill objects' }, + apiControlled: { type: 'boolean', description: 'Filter to API-controlled audiences only' }, + updatedAtFrom: { type: 'string', description: 'Filter enrolments updated on/after this date' }, + updatedAtTo: { type: 'string', description: 'Filter enrolments updated on/before this date' }, + updatedSince: { type: 'string', description: 'Return items updated on/after this date/time' }, + page: { type: 'number', description: 'Page number for pagination' }, + perPage: { type: 'number', description: 'Number of results per page' }, + }, + + outputs: { + user: { type: 'json', description: 'A user record' }, + results: { type: 'json', description: 'A page of results' }, + pagination: { type: 'json', description: 'Pagination details' }, + success: { type: 'boolean', description: 'Whether a delete operation succeeded' }, + result: { type: 'json', description: 'Bulk add/replace result with successes and failures' }, + audience: { type: 'json', description: 'An audience record' }, + managers: { type: 'json', description: 'A list of audience managers' }, + assignment: { type: 'json', description: 'An assignment record' }, + assignments: { type: 'json', description: 'A list of assignments' }, + enrolment: { type: 'json', description: 'An enrolment record' }, + enrolments: { type: 'json', description: 'A list of enrolments' }, + completion: { type: 'json', description: 'A completion record' }, + completions: { type: 'json', description: 'A list of completions' }, + statementId: { type: 'string', description: 'The completion statement ID' }, + content: { type: 'json', description: 'A content record' }, + activity: { type: 'json', description: 'An activity record' }, + category: { type: 'json', description: 'A CPD category record' }, + entry: { type: 'json', description: 'A CPD entry record' }, + requirement: { type: 'json', description: 'A CPD requirement record' }, + tag: { type: 'json', description: 'A tag record' }, + levels: { type: 'json', description: 'The available skill levels' }, + status: { type: 'number', description: 'The status code of a tag/skill mutation' }, + message: { type: 'string', description: 'A human-readable result message' }, + }, +} + +export const ThriveBlockMeta = { + tags: ['content-management', 'knowledge-base', 'automation'], + url: 'https://thrivelearning.com', + templates: [ + { + icon: ThriveIcon, + title: 'Onboard new hires into Thrive', + prompt: + 'Build a workflow that, when a new employee row is added to a Google Sheet, creates the user in Thrive with their ref, name, email, job title, and manager, then adds them to the relevant onboarding audience.', + modules: ['agent', 'workflows'], + category: 'operations', + tags: ['hr', 'onboarding', 'automation'], + alsoIntegrations: ['google_sheets'], + }, + { + icon: ThriveIcon, + title: 'Compliance completion digest', + prompt: + 'Create a scheduled weekly workflow that lists Thrive enrolments for a compliance assignment, filters those still open or overdue, and posts a Slack summary to the people-ops channel.', + modules: ['scheduled', 'agent', 'workflows'], + category: 'operations', + tags: ['compliance', 'reporting'], + alsoIntegrations: ['slack'], + }, + { + icon: ThriveIcon, + title: 'Sync leavers from HRIS', + prompt: + 'Build a workflow that reads terminated employees from an HRIS export and suspends each matching user in Thrive with their end date, then logs the result to a table.', + modules: ['tables', 'agent', 'workflows'], + category: 'operations', + tags: ['hr', 'lifecycle', 'automation'], + }, + { + icon: ThriveIcon, + title: 'Import historical completions', + prompt: + 'Create a workflow that reads a CSV of prior learning records and creates a completion in Thrive for each user and content item with the completion date.', + modules: ['files', 'agent', 'workflows'], + category: 'operations', + tags: ['migration', 'learning'], + }, + { + icon: ThriveIcon, + title: 'Assign mandatory training to an audience', + prompt: + 'Build a workflow that creates a Thrive content assignment for a chosen audience and primary content, sets a 30-day completion period, and reports how many learners were enrolled.', + modules: ['agent', 'workflows'], + category: 'operations', + tags: ['compliance', 'learning'], + }, + { + icon: ThriveIcon, + title: 'CPD shortfall report', + prompt: + 'Create a scheduled monthly workflow that queries Thrive CPD user summaries for a date range, compares logged minutes against each audience CPD requirement, and emails managers a list of learners below target.', + modules: ['scheduled', 'agent', 'workflows'], + category: 'operations', + tags: ['cpd', 'reporting'], + alsoIntegrations: ['gmail'], + }, + { + icon: ThriveIcon, + title: 'Tag learners by skill', + prompt: + 'Build a workflow that searches Thrive users by status, then adds skill tags and updates skill levels for each learner based on a mapping in a spreadsheet.', + modules: ['agent', 'workflows'], + category: 'operations', + tags: ['skills', 'automation'], + alsoIntegrations: ['google_sheets'], + }, + { + icon: ThriveIcon, + title: 'Trending content to Slack', + prompt: + 'Create a scheduled workflow that queries recently updated Thrive content and activity records, summarises the most engaged-with learning, and posts a weekly highlight to Slack.', + modules: ['scheduled', 'agent', 'workflows'], + category: 'marketing', + tags: ['content', 'engagement'], + alsoIntegrations: ['slack'], + }, + ], +} as const satisfies BlockMeta diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 8ee9b75e7d1..faf42594905 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -291,6 +291,7 @@ import { TelegramBlock, TelegramBlockMeta } from '@/blocks/blocks/telegram' import { TemporalBlock, TemporalBlockMeta } from '@/blocks/blocks/temporal' import { TextractBlock, TextractBlockMeta, TextractV2Block } from '@/blocks/blocks/textract' import { ThinkingBlock } from '@/blocks/blocks/thinking' +import { ThriveBlock, ThriveBlockMeta } from '@/blocks/blocks/thrive' import { TinybirdBlock, TinybirdBlockMeta } from '@/blocks/blocks/tinybird' import { TranslateBlock } from '@/blocks/blocks/translate' import { TrelloBlock, TrelloBlockMeta } from '@/blocks/blocks/trello' @@ -602,6 +603,7 @@ const BLOCK_REGISTRY: Record = { textract: TextractBlock, textract_v2: TextractV2Block, thinking: ThinkingBlock, + thrive: ThriveBlock, tinybird: TinybirdBlock, translate: TranslateBlock, trello: TrelloBlock, @@ -859,6 +861,7 @@ const BLOCK_META_REGISTRY: Record = { telegram: TelegramBlockMeta, temporal: TemporalBlockMeta, textract: TextractBlockMeta, + thrive: ThriveBlockMeta, tinybird: TinybirdBlockMeta, trello: TrelloBlockMeta, trigger_dev: TriggerDevBlockMeta, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 578b389d25f..3566c77add1 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -2758,6 +2758,17 @@ export function TinybirdIcon(props: SVGProps) { ) } +export function ThriveIcon(props: SVGProps) { + return ( + + + + ) +} + export function ClayIcon(props: SVGProps) { return ( diff --git a/apps/sim/lib/integrations/icon-mapping.ts b/apps/sim/lib/integrations/icon-mapping.ts index 8babc52cb36..48230469700 100644 --- a/apps/sim/lib/integrations/icon-mapping.ts +++ b/apps/sim/lib/integrations/icon-mapping.ts @@ -208,6 +208,7 @@ import { TelegramIcon, TemporalIcon, TextractIcon, + ThriveIcon, TinybirdIcon, TrelloIcon, TriggerDevIcon, @@ -440,6 +441,7 @@ export const blockTypeToIconMap: Record = { telegram: TelegramIcon, temporal: TemporalIcon, textract_v2: TextractIcon, + thrive: ThriveIcon, tinybird: TinybirdIcon, trello: TrelloIcon, trigger_dev: TriggerDevIcon, diff --git a/apps/sim/lib/integrations/integrations.json b/apps/sim/lib/integrations/integrations.json index 85ed86d61aa..dfaab88171c 100644 --- a/apps/sim/lib/integrations/integrations.json +++ b/apps/sim/lib/integrations/integrations.json @@ -16838,6 +16838,213 @@ "integrationType": "devops", "tags": ["automation"] }, + { + "type": "thrive", + "slug": "thrive", + "name": "Thrive", + "description": "Manage users, audiences, learning and CPD on Thrive", + "longDescription": "Integrate Thrive Learning into the workflow. Manage user lifecycle, audiences and their members and managers, content assignments and enrolments, learning completions, content and activity records, CPD, tags, and skills.", + "bgColor": "#FFFFFF", + "iconName": "ThriveIcon", + "docsUrl": "https://docs.sim.ai/tools/thrive", + "operations": [ + { + "name": "Create User", + "description": "Create a new user in Thrive." + }, + { + "name": "Update User", + "description": "Update an existing user in Thrive by ref. Only the fields provided are changed." + }, + { + "name": "Delete User", + "description": "Permanently delete (obfuscate) a user in Thrive by ref while retaining training history." + }, + { + "name": "Suspend User", + "description": "Suspend a user in Thrive by ref, marking the account inactive." + }, + { + "name": "Search Users", + "description": "Search users in Thrive and return basic user information with pagination." + }, + { + "name": "Get User by ID", + "description": "Get a single user in Thrive by their ID and return basic user information." + }, + { + "name": "Get User by Ref", + "description": "Get a single user in Thrive by their ref and return basic user information." + }, + { + "name": "List Audiences", + "description": "List audiences and structures in Thrive with pagination." + }, + { + "name": "Create Audience", + "description": "Create a new audience or structure in Thrive." + }, + { + "name": "Get Audience", + "description": "Get a single audience or structure in Thrive by id or reference." + }, + { + "name": "Update Audience", + "description": "Update an audience in Thrive, optionally moving it to a new parent." + }, + { + "name": "Delete Audience", + "description": "Delete an audience in Thrive (only if it has no child audiences)." + }, + { + "name": "List Audience Members", + "description": "List the members of a Thrive audience with pagination." + }, + { + "name": "Add Audience Members", + "description": "Add members to a Thrive audience by email, ref, or id." + }, + { + "name": "Replace Audience Members", + "description": "Replace a Thrive audience's entire members list with the given users (does not support an empty array)." + }, + { + "name": "Remove Audience Member", + "description": "Remove a single member from a Thrive audience." + }, + { + "name": "List Audience Managers", + "description": "List the managers of a Thrive audience." + }, + { + "name": "Add Audience Managers", + "description": "Add managers to a Thrive audience with their permissions." + }, + { + "name": "Replace Audience Managers", + "description": "Replace a Thrive audience's entire manager list with the given managers (does not support an empty array)." + }, + { + "name": "Remove Audience Manager", + "description": "Remove a single manager from a Thrive audience." + }, + { + "name": "List Assignments", + "description": "List compliance assignments in Thrive, optionally filtered by audience." + }, + { + "name": "Create Assignment", + "description": "Create a compliance assignment in Thrive for an audience and content item." + }, + { + "name": "Get Assignment", + "description": "Get a single compliance assignment in Thrive by its ID." + }, + { + "name": "Update Assignment", + "description": "Update a compliance assignment in Thrive." + }, + { + "name": "Delete Assignment", + "description": "Delete a compliance assignment in Thrive." + }, + { + "name": "List Enrolments", + "description": "List enrolments for a compliance assignment in Thrive." + }, + { + "name": "Get Enrolment", + "description": "Get a single enrolment for a compliance assignment in Thrive." + }, + { + "name": "List Completions", + "description": "List learning completion records in Thrive, optionally filtered by user or content." + }, + { + "name": "Get Completion", + "description": "Get a single learning completion record in Thrive by its ID." + }, + { + "name": "Create Completion", + "description": "Record a learning completion in Thrive for a user and content item." + }, + { + "name": "Query Content", + "description": "Query content records in Thrive with pagination and filtering options." + }, + { + "name": "Get Content", + "description": "Get a single content record in Thrive by its ID." + }, + { + "name": "Query Activities", + "description": "Query activity records in Thrive with pagination and filtering options." + }, + { + "name": "Get Activity", + "description": "Get a single activity record in Thrive by its ID." + }, + { + "name": "Query CPD Categories", + "description": "Query CPD categories in Thrive and return results with pagination." + }, + { + "name": "Get CPD Category", + "description": "Get a single CPD category in Thrive by its ID." + }, + { + "name": "Query CPD Entries", + "description": "Query CPD log entries in Thrive and return results with pagination." + }, + { + "name": "Get CPD Entry", + "description": "Get a single CPD log entry in Thrive by its ID." + }, + { + "name": "Query CPD Requirements", + "description": "Query CPD requirement summaries in Thrive and return results with pagination." + }, + { + "name": "Get CPD Requirement", + "description": "Get a single CPD requirement summary in Thrive by its ID." + }, + { + "name": "Query CPD User Summaries", + "description": "Query CPD user log summaries in Thrive and return results with pagination." + }, + { + "name": "List Tags", + "description": "List tags in Thrive and return tag information with pagination." + }, + { + "name": "Get Tag", + "description": "Get a single tag in Thrive by its ID." + }, + { + "name": "Add User Tags", + "description": "Add one or more tags to a learner in Thrive." + }, + { + "name": "Remove User Tags", + "description": "Remove one or more tags from a learner in Thrive." + }, + { + "name": "Update User Skills", + "description": "Update skills and levels for a learner in Thrive." + }, + { + "name": "Get Skill Levels", + "description": "Get the available skill levels configured in Thrive." + } + ], + "operationCount": 47, + "triggers": [], + "triggerCount": 0, + "authType": "api-key", + "category": "tools", + "integrationType": "hr", + "tags": ["content-management", "knowledge-base", "automation"] + }, { "type": "tinybird", "slug": "tinybird", diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 1fbcccb0789..717640bf6ab 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -3647,6 +3647,55 @@ import { } from '@/tools/temporal' import { textractParserTool, textractParserV2Tool } from '@/tools/textract' import { thinkingTool } from '@/tools/thinking' +import { + thriveAddAudienceManagersTool, + thriveAddAudienceMembersTool, + thriveAddUserTagsTool, + thriveCreateAssignmentTool, + thriveCreateAudienceTool, + thriveCreateCompletionTool, + thriveCreateUserTool, + thriveDeleteAssignmentTool, + thriveDeleteAudienceTool, + thriveDeleteUserTool, + thriveGetActivityTool, + thriveGetAssignmentTool, + thriveGetAudienceTool, + thriveGetCompletionTool, + thriveGetContentTool, + thriveGetCpdCategoryTool, + thriveGetCpdEntryTool, + thriveGetCpdRequirementTool, + thriveGetEnrolmentTool, + thriveGetSkillLevelsTool, + thriveGetTagTool, + thriveGetUserByIdTool, + thriveGetUserByRefTool, + thriveListAssignmentsTool, + thriveListAudienceManagersTool, + thriveListAudienceMembersTool, + thriveListAudiencesTool, + thriveListCompletionsTool, + thriveListEnrolmentsTool, + thriveListTagsTool, + thriveQueryActivitiesTool, + thriveQueryContentTool, + thriveQueryCpdCategoriesTool, + thriveQueryCpdEntriesTool, + thriveQueryCpdRequirementsTool, + thriveQueryCpdUserSummariesTool, + thriveRemoveAudienceManagerTool, + thriveRemoveAudienceMemberTool, + thriveRemoveUserTagsTool, + thriveReplaceAudienceManagersTool, + thriveReplaceAudienceMembersTool, + thriveSearchUsersTool, + thriveSuspendUserTool, + thriveUpdateAssignmentTool, + thriveUpdateAudienceTool, + thriveUpdateUserSkillsTool, + thriveUpdateUserTool, +} from '@/tools/thrive' import { tinybirdAppendDatasourceTool, tinybirdDeleteDatasourceRowsTool, @@ -6447,6 +6496,53 @@ export const tools: Record = { textract_parser: textractParserTool, textract_parser_v2: textractParserV2Tool, thinking_tool: thinkingTool, + thrive_create_user: thriveCreateUserTool, + thrive_update_user: thriveUpdateUserTool, + thrive_delete_user: thriveDeleteUserTool, + thrive_suspend_user: thriveSuspendUserTool, + thrive_search_users: thriveSearchUsersTool, + thrive_get_user_by_id: thriveGetUserByIdTool, + thrive_get_user_by_ref: thriveGetUserByRefTool, + thrive_list_audiences: thriveListAudiencesTool, + thrive_create_audience: thriveCreateAudienceTool, + thrive_get_audience: thriveGetAudienceTool, + thrive_update_audience: thriveUpdateAudienceTool, + thrive_delete_audience: thriveDeleteAudienceTool, + thrive_list_audience_members: thriveListAudienceMembersTool, + thrive_add_audience_members: thriveAddAudienceMembersTool, + thrive_replace_audience_members: thriveReplaceAudienceMembersTool, + thrive_remove_audience_member: thriveRemoveAudienceMemberTool, + thrive_list_audience_managers: thriveListAudienceManagersTool, + thrive_add_audience_managers: thriveAddAudienceManagersTool, + thrive_replace_audience_managers: thriveReplaceAudienceManagersTool, + thrive_remove_audience_manager: thriveRemoveAudienceManagerTool, + thrive_list_assignments: thriveListAssignmentsTool, + thrive_create_assignment: thriveCreateAssignmentTool, + thrive_get_assignment: thriveGetAssignmentTool, + thrive_update_assignment: thriveUpdateAssignmentTool, + thrive_delete_assignment: thriveDeleteAssignmentTool, + thrive_list_enrolments: thriveListEnrolmentsTool, + thrive_get_enrolment: thriveGetEnrolmentTool, + thrive_list_completions: thriveListCompletionsTool, + thrive_get_completion: thriveGetCompletionTool, + thrive_create_completion: thriveCreateCompletionTool, + thrive_get_content: thriveGetContentTool, + thrive_query_content: thriveQueryContentTool, + thrive_get_activity: thriveGetActivityTool, + thrive_query_activities: thriveQueryActivitiesTool, + thrive_get_cpd_category: thriveGetCpdCategoryTool, + thrive_query_cpd_categories: thriveQueryCpdCategoriesTool, + thrive_get_cpd_entry: thriveGetCpdEntryTool, + thrive_query_cpd_entries: thriveQueryCpdEntriesTool, + thrive_get_cpd_requirement: thriveGetCpdRequirementTool, + thrive_query_cpd_requirements: thriveQueryCpdRequirementsTool, + thrive_query_cpd_user_summaries: thriveQueryCpdUserSummariesTool, + thrive_list_tags: thriveListTagsTool, + thrive_get_tag: thriveGetTagTool, + thrive_add_user_tags: thriveAddUserTagsTool, + thrive_remove_user_tags: thriveRemoveUserTagsTool, + thrive_update_user_skills: thriveUpdateUserSkillsTool, + thrive_get_skill_levels: thriveGetSkillLevelsTool, tinybird_events: tinybirdEventsTool, tinybird_query: tinybirdQueryTool, tinybird_query_pipe: tinybirdQueryPipeTool, diff --git a/apps/sim/tools/thrive/add_audience_managers.ts b/apps/sim/tools/thrive/add_audience_managers.ts new file mode 100644 index 00000000000..6239989e8d4 --- /dev/null +++ b/apps/sim/tools/thrive/add_audience_managers.ts @@ -0,0 +1,78 @@ +import type { ThriveAddUsersResponse, ThriveAudienceManagersParams } from '@/tools/thrive/types' +import { THRIVE_ADD_USERS_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveArray, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const addAudienceManagersTool: ToolConfig< + ThriveAudienceManagersParams, + ThriveAddUsersResponse +> = { + id: 'thrive_add_audience_managers', + name: 'Thrive Add Audience Managers', + description: 'Add managers to a Thrive audience with their permissions.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + managers: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'JSON array of manager objects (1-100). Each: {"reference":"user@example.com","permissions":{"audienceManager":{"manageContent":true,"assignments":true},"peopleManager":{"canViewLearnPage":true,"insights":false,"manage":false},"administrator":{"canAddAudienceManagers":false}}}', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}/managers`, + method: 'POST', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => JSON.stringify(parseThriveArray(params.managers, 'managers')), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to add audience managers') + return { + success: true, + output: { result: { success: data?.success ?? null, failure: data?.failure } }, + } + }, + + outputs: { + result: { + type: 'object', + description: + 'The add/replace result, with successfully and unsuccessfully processed entities', + properties: THRIVE_ADD_USERS_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/add_audience_members.ts b/apps/sim/tools/thrive/add_audience_members.ts new file mode 100644 index 00000000000..7e5bb8c9b1c --- /dev/null +++ b/apps/sim/tools/thrive/add_audience_members.ts @@ -0,0 +1,76 @@ +import type { ThriveAddUsersResponse, ThriveAudienceUsersParams } from '@/tools/thrive/types' +import { THRIVE_ADD_USERS_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveArray, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const addAudienceMembersTool: ToolConfig = + { + id: 'thrive_add_audience_members', + name: 'Thrive Add Audience Members', + description: 'Add members to a Thrive audience by email, ref, or id.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + users: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'JSON array of user emails/refs/ids to add (1-100). Example: ["user@example.com"]', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}/members`, + method: 'POST', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => JSON.stringify(parseThriveArray(params.users, 'users')), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to add audience members') + return { + success: true, + output: { result: { success: data?.success ?? null, failure: data?.failure } }, + } + }, + + outputs: { + result: { + type: 'object', + description: + 'The add/replace result, with successfully and unsuccessfully processed entities', + properties: THRIVE_ADD_USERS_OUTPUT_PROPERTIES, + }, + }, + } diff --git a/apps/sim/tools/thrive/add_user_tags.ts b/apps/sim/tools/thrive/add_user_tags.ts new file mode 100644 index 00000000000..71b55550dc5 --- /dev/null +++ b/apps/sim/tools/thrive/add_user_tags.ts @@ -0,0 +1,69 @@ +import type { ThriveAddUserTagsParams, ThriveMessageResponse } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveArray, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const addUserTagsTool: ToolConfig = { + id: 'thrive_add_user_tags', + name: 'Thrive Add User Tags', + description: 'Add one or more tags to a learner in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The learner ID', + }, + tags: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'JSON array of tag names to add (1-100). Example: ["leadership"]', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/users/${encodeURIComponent(params.userId)}/tags`, + method: 'POST', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => ({ tags: parseThriveArray(params.tags, 'tags') }), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to add tags to learner') + return { + success: true, + output: { status: data?.status ?? null, message: data?.message ?? null }, + } + }, + + outputs: { + status: { type: 'number', description: 'The HTTP status code of the operation' }, + message: { type: 'string', description: 'A human-readable result message' }, + }, +} diff --git a/apps/sim/tools/thrive/create_assignment.ts b/apps/sim/tools/thrive/create_assignment.ts new file mode 100644 index 00000000000..d315b8b0505 --- /dev/null +++ b/apps/sim/tools/thrive/create_assignment.ts @@ -0,0 +1,113 @@ +import type { ThriveAssignmentResponse, ThriveCreateAssignmentParams } from '@/tools/thrive/types' +import { THRIVE_ASSIGNMENT_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveArray, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const createAssignmentTool: ToolConfig< + ThriveCreateAssignmentParams, + ThriveAssignmentResponse +> = { + id: 'thrive_create_assignment', + name: 'Thrive Create Assignment', + description: 'Create a compliance assignment in Thrive for an audience and content item.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience ID', + }, + contentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The content ID for the primary content', + }, + alternativeContentIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'JSON array of content IDs that can also complete the assignment', + }, + hideAlternativeContent: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether to hide the alternative content', + }, + completionPeriod: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'The number of days required to complete the assignment (default 30)', + }, + recurrence: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'The number of days until the assignment will reoccur', + }, + }, + + request: { + url: (params) => `${getThriveBaseUrl(params.host, 'v1')}/assignments`, + method: 'POST', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => { + const body: Record = { + audienceId: params.audienceId, + contentId: params.contentId, + } + if (params.alternativeContentIds) { + body.alternativeContentIds = parseThriveArray( + params.alternativeContentIds, + 'alternativeContentIds' + ) + } + if (params.hideAlternativeContent !== undefined) { + body.hideAlternativeContent = params.hideAlternativeContent + } + if (params.completionPeriod !== undefined) body.completionPeriod = params.completionPeriod + if (params.recurrence !== undefined) body.recurrence = params.recurrence + return body + }, + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to create assignment') + return { success: true, output: { assignment: data ?? null } } + }, + + outputs: { + assignment: { + type: 'object', + description: 'The created assignment', + properties: THRIVE_ASSIGNMENT_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/create_audience.ts b/apps/sim/tools/thrive/create_audience.ts new file mode 100644 index 00000000000..9d1553907dd --- /dev/null +++ b/apps/sim/tools/thrive/create_audience.ts @@ -0,0 +1,84 @@ +import type { ThriveAudienceResponse, ThriveCreateAudienceParams } from '@/tools/thrive/types' +import { THRIVE_AUDIENCE_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const createAudienceTool: ToolConfig = { + id: 'thrive_create_audience', + name: 'Thrive Create Audience', + description: 'Create a new audience or structure in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The name of the audience (max 100 characters)', + }, + reference: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The external reference for the audience (max 100 characters)', + }, + parentId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'The id or reference of the parent audience/structure; leave blank for a parent audience/structure', + }, + category: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "The audience category: 'audience' or 'structure'", + }, + }, + + request: { + url: (params) => `${getThriveBaseUrl(params.host, 'v1')}/audiences`, + method: 'POST', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => { + const body: Record = {} + if (params.name) body.name = params.name + if (params.reference) body.reference = params.reference + if (params.parentId) body.parentId = params.parentId + if (params.category) body.category = params.category + return body + }, + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to create audience') + return { success: true, output: { audience: data ?? null } } + }, + + outputs: { + audience: { + type: 'object', + description: 'The created audience', + properties: THRIVE_AUDIENCE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/create_completion.ts b/apps/sim/tools/thrive/create_completion.ts new file mode 100644 index 00000000000..892068040b6 --- /dev/null +++ b/apps/sim/tools/thrive/create_completion.ts @@ -0,0 +1,75 @@ +import type { + ThriveCreateCompletionParams, + ThriveCreateCompletionResponse, +} from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const createCompletionTool: ToolConfig< + ThriveCreateCompletionParams, + ThriveCreateCompletionResponse +> = { + id: 'thrive_create_completion', + name: 'Thrive Create Completion', + description: 'Record a learning completion in Thrive for a user and content item.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The user ID', + }, + contentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The content ID for the content completed', + }, + completedAt: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ISO8601 timestamp when the completion occurred', + }, + }, + + request: { + url: (params) => `${getThriveBaseUrl(params.host, 'v1')}/learning/completions`, + method: 'POST', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => ({ + userId: params.userId, + contentId: params.contentId, + completedAt: params.completedAt, + }), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to create completion') + return { success: true, output: { statementId: data?.statementId ?? null } } + }, + + outputs: { + statementId: { type: 'string', description: 'The completion statement ID' }, + }, +} diff --git a/apps/sim/tools/thrive/create_user.ts b/apps/sim/tools/thrive/create_user.ts new file mode 100644 index 00000000000..21ebd955c56 --- /dev/null +++ b/apps/sim/tools/thrive/create_user.ts @@ -0,0 +1,169 @@ +import type { ThriveCreateUserParams, ThriveUserResponse } from '@/tools/thrive/types' +import { THRIVE_USER_LIFECYCLE_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveJsonObject, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const createUserTool: ToolConfig = { + id: 'thrive_create_user', + name: 'Thrive Create User', + description: 'Create a new user in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + ref: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: "Your organisation's unique identifier for this individual", + }, + firstName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The given name of the individual', + }, + lastName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The family name of the individual', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "The email address for the user (required unless loginMethod is 'ref')", + }, + loginMethod: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "How the user logs in: 'email' or 'ref' (defaults to 'email')", + }, + role: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + "Role assigned: 'administrator', 'learneradmin', or 'learner' (defaults to 'learner')", + }, + jobTitle: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "Name of this individual's role in your organisation", + }, + managerRef: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "Your organisation's unique identifier for this individual's line manager", + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date this individual started with your organisation (ISO 8601)', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date this individual left your organisation (ISO 8601)', + }, + timeZone: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "The user's preferred timezone (tenant default if omitted)", + }, + languageCode: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "The user's preferred language (e.g. 'en-gb')", + }, + sso: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether the account is managed by an authentication provider', + }, + domain: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Domain this individual is associated with', + }, + additionalFields: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'JSON object of custom field key-value pairs. Example: {"department":"Sales"}', + }, + }, + + request: { + url: (params) => `${getThriveBaseUrl(params.host, 'v2')}/users`, + method: 'POST', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => { + const body: Record = { + ref: params.ref, + firstName: params.firstName, + lastName: params.lastName, + } + if (params.email) body.email = params.email + if (params.loginMethod) body.loginMethod = params.loginMethod + if (params.role) body.role = params.role + if (params.jobTitle) body.jobTitle = params.jobTitle + if (params.managerRef) body.managerRef = params.managerRef + if (params.startDate) body.startDate = params.startDate + if (params.endDate) body.endDate = params.endDate + if (params.timeZone) body.timeZone = params.timeZone + if (params.languageCode) body.languageCode = params.languageCode + if (params.sso !== undefined) body.sso = params.sso + if (params.domain) body.domain = params.domain + if (params.additionalFields) { + body.additionalFields = parseThriveJsonObject(params.additionalFields, 'additionalFields') + } + return body + }, + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to create user') + return { success: true, output: { user: data ?? null } } + }, + + outputs: { + user: { + type: 'object', + description: 'The created user', + properties: THRIVE_USER_LIFECYCLE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/delete_assignment.ts b/apps/sim/tools/thrive/delete_assignment.ts new file mode 100644 index 00000000000..719d8f70c98 --- /dev/null +++ b/apps/sim/tools/thrive/delete_assignment.ts @@ -0,0 +1,61 @@ +import type { ThriveDeleteAssignmentParams, ThriveDeleteResponse } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const deleteAssignmentTool: ToolConfig = + { + id: 'thrive_delete_assignment', + name: 'Thrive Delete Assignment', + description: 'Delete a compliance assignment in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + assignmentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The assignment ID', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience ID', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/assignments/${encodeURIComponent(params.assignmentId)}`, + method: 'DELETE', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => ({ audienceId: params.audienceId }), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to delete assignment') + return { success: true, output: { success: data?.success ?? true } } + }, + + outputs: { + success: { type: 'boolean', description: 'Whether the assignment was deleted' }, + }, + } diff --git a/apps/sim/tools/thrive/delete_audience.ts b/apps/sim/tools/thrive/delete_audience.ts new file mode 100644 index 00000000000..59eb4902563 --- /dev/null +++ b/apps/sim/tools/thrive/delete_audience.ts @@ -0,0 +1,53 @@ +import type { ThriveDeleteAudienceParams, ThriveDeleteResponse } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const deleteAudienceTool: ToolConfig = { + id: 'thrive_delete_audience', + name: 'Thrive Delete Audience', + description: 'Delete an audience in Thrive (only if it has no child audiences).', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}`, + method: 'DELETE', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to delete audience') + return { success: true, output: { success: data?.success ?? true } } + }, + + outputs: { + success: { type: 'boolean', description: 'Whether the audience was deleted' }, + }, +} diff --git a/apps/sim/tools/thrive/delete_user.ts b/apps/sim/tools/thrive/delete_user.ts new file mode 100644 index 00000000000..7cfd3610fc8 --- /dev/null +++ b/apps/sim/tools/thrive/delete_user.ts @@ -0,0 +1,54 @@ +import type { ThriveDeleteResponse, ThriveDeleteUserParams } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const deleteUserTool: ToolConfig = { + id: 'thrive_delete_user', + name: 'Thrive Delete User', + description: + 'Permanently delete (obfuscate) a user in Thrive by ref while retaining training history.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + ref: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The user ref to delete', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v2')}/users/ref/${encodeURIComponent(params.ref)}`, + method: 'DELETE', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to delete user') + return { success: true, output: { success: data?.success ?? true } } + }, + + outputs: { + success: { type: 'boolean', description: 'Whether the user was deleted' }, + }, +} diff --git a/apps/sim/tools/thrive/get_activity.ts b/apps/sim/tools/thrive/get_activity.ts new file mode 100644 index 00000000000..6ad78f2eaec --- /dev/null +++ b/apps/sim/tools/thrive/get_activity.ts @@ -0,0 +1,58 @@ +import type { ThriveActivityResponse, ThriveGetActivityParams } from '@/tools/thrive/types' +import { THRIVE_ACTIVITY_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getActivityTool: ToolConfig = { + id: 'thrive_get_activity', + name: 'Thrive Get Activity', + description: 'Get a single activity record in Thrive by its ID.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Unique identifier of the activity', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/activity/${encodeURIComponent(params.id)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get activity') + return { success: true, output: { activity: data ?? null } } + }, + + outputs: { + activity: { + type: 'object', + description: 'The activity record', + properties: THRIVE_ACTIVITY_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_assignment.ts b/apps/sim/tools/thrive/get_assignment.ts new file mode 100644 index 00000000000..81ba16bbd7a --- /dev/null +++ b/apps/sim/tools/thrive/get_assignment.ts @@ -0,0 +1,58 @@ +import type { ThriveAssignmentResponse, ThriveGetAssignmentParams } from '@/tools/thrive/types' +import { THRIVE_ASSIGNMENT_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getAssignmentTool: ToolConfig = { + id: 'thrive_get_assignment', + name: 'Thrive Get Assignment', + description: 'Get a single compliance assignment in Thrive by its ID.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + assignmentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The assignment ID', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/assignments/${encodeURIComponent(params.assignmentId)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get assignment') + return { success: true, output: { assignment: data ?? null } } + }, + + outputs: { + assignment: { + type: 'object', + description: 'The assignment', + properties: THRIVE_ASSIGNMENT_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_audience.ts b/apps/sim/tools/thrive/get_audience.ts new file mode 100644 index 00000000000..46318af9ce7 --- /dev/null +++ b/apps/sim/tools/thrive/get_audience.ts @@ -0,0 +1,58 @@ +import type { ThriveAudienceResponse, ThriveGetAudienceParams } from '@/tools/thrive/types' +import { THRIVE_AUDIENCE_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getAudienceTool: ToolConfig = { + id: 'thrive_get_audience', + name: 'Thrive Get Audience', + description: 'Get a single audience or structure in Thrive by id or reference.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get audience') + return { success: true, output: { audience: data ?? null } } + }, + + outputs: { + audience: { + type: 'object', + description: 'The audience', + properties: THRIVE_AUDIENCE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_completion.ts b/apps/sim/tools/thrive/get_completion.ts new file mode 100644 index 00000000000..68e50729fe5 --- /dev/null +++ b/apps/sim/tools/thrive/get_completion.ts @@ -0,0 +1,58 @@ +import type { ThriveCompletionResponse, ThriveGetCompletionParams } from '@/tools/thrive/types' +import { THRIVE_COMPLETION_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getCompletionTool: ToolConfig = { + id: 'thrive_get_completion', + name: 'Thrive Get Completion', + description: 'Get a single learning completion record in Thrive by its ID.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The completion ID', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/learning/completions/${encodeURIComponent(params.id)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get completion') + return { success: true, output: { completion: data ?? null } } + }, + + outputs: { + completion: { + type: 'object', + description: 'The completion record', + properties: THRIVE_COMPLETION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_content.ts b/apps/sim/tools/thrive/get_content.ts new file mode 100644 index 00000000000..a5e5e2e8d34 --- /dev/null +++ b/apps/sim/tools/thrive/get_content.ts @@ -0,0 +1,58 @@ +import type { ThriveContentResponse, ThriveGetContentParams } from '@/tools/thrive/types' +import { THRIVE_CONTENT_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getContentTool: ToolConfig = { + id: 'thrive_get_content', + name: 'Thrive Get Content', + description: 'Get a single content record in Thrive by its ID.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Unique identifier of the content item', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/contents/${encodeURIComponent(params.id)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get content') + return { success: true, output: { content: data ?? null } } + }, + + outputs: { + content: { + type: 'object', + description: 'The content record', + properties: THRIVE_CONTENT_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_cpd_category.ts b/apps/sim/tools/thrive/get_cpd_category.ts new file mode 100644 index 00000000000..6fa30f2e462 --- /dev/null +++ b/apps/sim/tools/thrive/get_cpd_category.ts @@ -0,0 +1,59 @@ +import type { ThriveCpdCategoryResponse, ThriveGetCpdCategoryParams } from '@/tools/thrive/types' +import { THRIVE_CPD_CATEGORY_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getCpdCategoryTool: ToolConfig = + { + id: 'thrive_get_cpd_category', + name: 'Thrive Get CPD Category', + description: 'Get a single CPD category in Thrive by its ID.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The CPD category ID', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/cpdCategories/${encodeURIComponent(params.categoryId)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get CPD category') + return { success: true, output: { category: data ?? null } } + }, + + outputs: { + category: { + type: 'object', + description: 'The CPD category', + properties: THRIVE_CPD_CATEGORY_OUTPUT_PROPERTIES, + }, + }, + } diff --git a/apps/sim/tools/thrive/get_cpd_entry.ts b/apps/sim/tools/thrive/get_cpd_entry.ts new file mode 100644 index 00000000000..321df964676 --- /dev/null +++ b/apps/sim/tools/thrive/get_cpd_entry.ts @@ -0,0 +1,58 @@ +import type { ThriveCpdEntryResponse, ThriveGetCpdEntryParams } from '@/tools/thrive/types' +import { THRIVE_CPD_ENTRY_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getCpdEntryTool: ToolConfig = { + id: 'thrive_get_cpd_entry', + name: 'Thrive Get CPD Entry', + description: 'Get a single CPD log entry in Thrive by its ID.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + logEntryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The CPD log entry ID', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/cpdEntries/${encodeURIComponent(params.logEntryId)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get CPD entry') + return { success: true, output: { entry: data ?? null } } + }, + + outputs: { + entry: { + type: 'object', + description: 'The CPD entry', + properties: THRIVE_CPD_ENTRY_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_cpd_requirement.ts b/apps/sim/tools/thrive/get_cpd_requirement.ts new file mode 100644 index 00000000000..71815239469 --- /dev/null +++ b/apps/sim/tools/thrive/get_cpd_requirement.ts @@ -0,0 +1,64 @@ +import type { + ThriveCpdRequirementResponse, + ThriveGetCpdRequirementParams, +} from '@/tools/thrive/types' +import { THRIVE_CPD_REQUIREMENT_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getCpdRequirementTool: ToolConfig< + ThriveGetCpdRequirementParams, + ThriveCpdRequirementResponse +> = { + id: 'thrive_get_cpd_requirement', + name: 'Thrive Get CPD Requirement', + description: 'Get a single CPD requirement summary in Thrive by its ID.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceRequirementId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The CPD requirement ID', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/cpdRequirementSummaries/${encodeURIComponent(params.audienceRequirementId)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get CPD requirement') + return { success: true, output: { requirement: data ?? null } } + }, + + outputs: { + requirement: { + type: 'object', + description: 'The CPD requirement', + properties: THRIVE_CPD_REQUIREMENT_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_enrolment.ts b/apps/sim/tools/thrive/get_enrolment.ts new file mode 100644 index 00000000000..f7d88e89679 --- /dev/null +++ b/apps/sim/tools/thrive/get_enrolment.ts @@ -0,0 +1,64 @@ +import type { ThriveEnrolmentResponse, ThriveGetEnrolmentParams } from '@/tools/thrive/types' +import { THRIVE_ENROLMENT_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getEnrolmentTool: ToolConfig = { + id: 'thrive_get_enrolment', + name: 'Thrive Get Enrolment', + description: 'Get a single enrolment for a compliance assignment in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + assignmentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The assignment ID', + }, + enrolmentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The enrolment ID', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/assignments/${encodeURIComponent(params.assignmentId)}/enrolments/${encodeURIComponent(params.enrolmentId)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get enrolment') + return { success: true, output: { enrolment: data ?? null } } + }, + + outputs: { + enrolment: { + type: 'object', + description: 'The enrolment', + properties: THRIVE_ENROLMENT_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_skill_levels.ts b/apps/sim/tools/thrive/get_skill_levels.ts new file mode 100644 index 00000000000..de6acd3d414 --- /dev/null +++ b/apps/sim/tools/thrive/get_skill_levels.ts @@ -0,0 +1,52 @@ +import type { ThriveGetSkillLevelsParams, ThriveSkillLevelsResponse } from '@/tools/thrive/types' +import { THRIVE_SKILL_LEVEL_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getSkillLevelsTool: ToolConfig = + { + id: 'thrive_get_skill_levels', + name: 'Thrive Get Skill Levels', + description: 'Get the available skill levels configured in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + }, + + request: { + url: (params) => `${getThriveBaseUrl(params.host, 'v1')}/skills/levels`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get skill levels') + return { success: true, output: { levels: Array.isArray(data) ? data : [] } } + }, + + outputs: { + levels: { + type: 'array', + description: 'The available skill levels', + items: { type: 'object', properties: THRIVE_SKILL_LEVEL_OUTPUT_PROPERTIES }, + }, + }, + } diff --git a/apps/sim/tools/thrive/get_tag.ts b/apps/sim/tools/thrive/get_tag.ts new file mode 100644 index 00000000000..98846c2db2f --- /dev/null +++ b/apps/sim/tools/thrive/get_tag.ts @@ -0,0 +1,58 @@ +import type { ThriveGetTagParams, ThriveTagResponse } from '@/tools/thrive/types' +import { THRIVE_TAG_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getTagTool: ToolConfig = { + id: 'thrive_get_tag', + name: 'Thrive Get Tag', + description: 'Get a single tag in Thrive by its ID.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + tagId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The tag ID', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/tags/${encodeURIComponent(params.tagId)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get tag') + return { success: true, output: { tag: data ?? null } } + }, + + outputs: { + tag: { + type: 'object', + description: 'The tag', + properties: THRIVE_TAG_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_user_by_id.ts b/apps/sim/tools/thrive/get_user_by_id.ts new file mode 100644 index 00000000000..876df5eda3e --- /dev/null +++ b/apps/sim/tools/thrive/get_user_by_id.ts @@ -0,0 +1,57 @@ +import type { ThriveGetUserByIdParams, ThriveUserResponse } from '@/tools/thrive/types' +import { THRIVE_USER_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getUserByIdTool: ToolConfig = { + id: 'thrive_get_user_by_id', + name: 'Thrive Get User by ID', + description: 'Get a single user in Thrive by their ID and return basic user information.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The user ID', + }, + }, + + request: { + url: (params) => `${getThriveBaseUrl(params.host, 'v1')}/user/${encodeURIComponent(params.id)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get user') + return { success: true, output: { user: data ?? null } } + }, + + outputs: { + user: { + type: 'object', + description: 'The user', + properties: THRIVE_USER_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/get_user_by_ref.ts b/apps/sim/tools/thrive/get_user_by_ref.ts new file mode 100644 index 00000000000..0831a7e94f9 --- /dev/null +++ b/apps/sim/tools/thrive/get_user_by_ref.ts @@ -0,0 +1,58 @@ +import type { ThriveGetUserByRefParams, ThriveUserResponse } from '@/tools/thrive/types' +import { THRIVE_BASIC_USER_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const getUserByRefTool: ToolConfig = { + id: 'thrive_get_user_by_ref', + name: 'Thrive Get User by Ref', + description: 'Get a single user in Thrive by their ref and return basic user information.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + ref: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The user ref', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/users/ref/${encodeURIComponent(params.ref)}`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to get user') + return { success: true, output: { user: data ?? null } } + }, + + outputs: { + user: { + type: 'object', + description: 'The user (basic information)', + properties: THRIVE_BASIC_USER_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/index.ts b/apps/sim/tools/thrive/index.ts new file mode 100644 index 00000000000..38cb79364c9 --- /dev/null +++ b/apps/sim/tools/thrive/index.ts @@ -0,0 +1,102 @@ +import { addAudienceManagersTool } from '@/tools/thrive/add_audience_managers' +import { addAudienceMembersTool } from '@/tools/thrive/add_audience_members' +import { addUserTagsTool } from '@/tools/thrive/add_user_tags' +import { createAssignmentTool } from '@/tools/thrive/create_assignment' +import { createAudienceTool } from '@/tools/thrive/create_audience' +import { createCompletionTool } from '@/tools/thrive/create_completion' +import { createUserTool } from '@/tools/thrive/create_user' +import { deleteAssignmentTool } from '@/tools/thrive/delete_assignment' +import { deleteAudienceTool } from '@/tools/thrive/delete_audience' +import { deleteUserTool } from '@/tools/thrive/delete_user' +import { getActivityTool } from '@/tools/thrive/get_activity' +import { getAssignmentTool } from '@/tools/thrive/get_assignment' +import { getAudienceTool } from '@/tools/thrive/get_audience' +import { getCompletionTool } from '@/tools/thrive/get_completion' +import { getContentTool } from '@/tools/thrive/get_content' +import { getCpdCategoryTool } from '@/tools/thrive/get_cpd_category' +import { getCpdEntryTool } from '@/tools/thrive/get_cpd_entry' +import { getCpdRequirementTool } from '@/tools/thrive/get_cpd_requirement' +import { getEnrolmentTool } from '@/tools/thrive/get_enrolment' +import { getSkillLevelsTool } from '@/tools/thrive/get_skill_levels' +import { getTagTool } from '@/tools/thrive/get_tag' +import { getUserByIdTool } from '@/tools/thrive/get_user_by_id' +import { getUserByRefTool } from '@/tools/thrive/get_user_by_ref' +import { listAssignmentsTool } from '@/tools/thrive/list_assignments' +import { listAudienceManagersTool } from '@/tools/thrive/list_audience_managers' +import { listAudienceMembersTool } from '@/tools/thrive/list_audience_members' +import { listAudiencesTool } from '@/tools/thrive/list_audiences' +import { listCompletionsTool } from '@/tools/thrive/list_completions' +import { listEnrolmentsTool } from '@/tools/thrive/list_enrolments' +import { listTagsTool } from '@/tools/thrive/list_tags' +import { queryActivitiesTool } from '@/tools/thrive/query_activities' +import { queryContentTool } from '@/tools/thrive/query_content' +import { queryCpdCategoriesTool } from '@/tools/thrive/query_cpd_categories' +import { queryCpdEntriesTool } from '@/tools/thrive/query_cpd_entries' +import { queryCpdRequirementsTool } from '@/tools/thrive/query_cpd_requirements' +import { queryCpdUserSummariesTool } from '@/tools/thrive/query_cpd_user_summaries' +import { removeAudienceManagerTool } from '@/tools/thrive/remove_audience_manager' +import { removeAudienceMemberTool } from '@/tools/thrive/remove_audience_member' +import { removeUserTagsTool } from '@/tools/thrive/remove_user_tags' +import { replaceAudienceManagersTool } from '@/tools/thrive/replace_audience_managers' +import { replaceAudienceMembersTool } from '@/tools/thrive/replace_audience_members' +import { searchUsersTool } from '@/tools/thrive/search_users' +import { suspendUserTool } from '@/tools/thrive/suspend_user' +import { updateAssignmentTool } from '@/tools/thrive/update_assignment' +import { updateAudienceTool } from '@/tools/thrive/update_audience' +import { updateUserTool } from '@/tools/thrive/update_user' +import { updateUserSkillsTool } from '@/tools/thrive/update_user_skills' + +export const thriveCreateUserTool = createUserTool +export const thriveUpdateUserTool = updateUserTool +export const thriveDeleteUserTool = deleteUserTool +export const thriveSuspendUserTool = suspendUserTool +export const thriveSearchUsersTool = searchUsersTool +export const thriveGetUserByIdTool = getUserByIdTool +export const thriveGetUserByRefTool = getUserByRefTool + +export const thriveListAudiencesTool = listAudiencesTool +export const thriveCreateAudienceTool = createAudienceTool +export const thriveGetAudienceTool = getAudienceTool +export const thriveUpdateAudienceTool = updateAudienceTool +export const thriveDeleteAudienceTool = deleteAudienceTool +export const thriveListAudienceMembersTool = listAudienceMembersTool +export const thriveAddAudienceMembersTool = addAudienceMembersTool +export const thriveReplaceAudienceMembersTool = replaceAudienceMembersTool +export const thriveRemoveAudienceMemberTool = removeAudienceMemberTool +export const thriveListAudienceManagersTool = listAudienceManagersTool +export const thriveAddAudienceManagersTool = addAudienceManagersTool +export const thriveReplaceAudienceManagersTool = replaceAudienceManagersTool +export const thriveRemoveAudienceManagerTool = removeAudienceManagerTool + +export const thriveListAssignmentsTool = listAssignmentsTool +export const thriveCreateAssignmentTool = createAssignmentTool +export const thriveGetAssignmentTool = getAssignmentTool +export const thriveUpdateAssignmentTool = updateAssignmentTool +export const thriveDeleteAssignmentTool = deleteAssignmentTool +export const thriveListEnrolmentsTool = listEnrolmentsTool +export const thriveGetEnrolmentTool = getEnrolmentTool + +export const thriveListCompletionsTool = listCompletionsTool +export const thriveGetCompletionTool = getCompletionTool +export const thriveCreateCompletionTool = createCompletionTool + +export const thriveGetContentTool = getContentTool +export const thriveQueryContentTool = queryContentTool + +export const thriveGetActivityTool = getActivityTool +export const thriveQueryActivitiesTool = queryActivitiesTool + +export const thriveGetCpdCategoryTool = getCpdCategoryTool +export const thriveQueryCpdCategoriesTool = queryCpdCategoriesTool +export const thriveGetCpdEntryTool = getCpdEntryTool +export const thriveQueryCpdEntriesTool = queryCpdEntriesTool +export const thriveGetCpdRequirementTool = getCpdRequirementTool +export const thriveQueryCpdRequirementsTool = queryCpdRequirementsTool +export const thriveQueryCpdUserSummariesTool = queryCpdUserSummariesTool + +export const thriveListTagsTool = listTagsTool +export const thriveGetTagTool = getTagTool +export const thriveAddUserTagsTool = addUserTagsTool +export const thriveRemoveUserTagsTool = removeUserTagsTool +export const thriveUpdateUserSkillsTool = updateUserSkillsTool +export const thriveGetSkillLevelsTool = getSkillLevelsTool diff --git a/apps/sim/tools/thrive/list_assignments.ts b/apps/sim/tools/thrive/list_assignments.ts new file mode 100644 index 00000000000..17ce1dadab7 --- /dev/null +++ b/apps/sim/tools/thrive/list_assignments.ts @@ -0,0 +1,93 @@ +import type { + ThriveListAssignmentsParams, + ThriveListAssignmentsResponse, +} from '@/tools/thrive/types' +import { THRIVE_ASSIGNMENT_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const listAssignmentsTool: ToolConfig< + ThriveListAssignmentsParams, + ThriveListAssignmentsResponse +> = { + id: 'thrive_list_assignments', + name: 'Thrive List Assignments', + description: 'List compliance assignments in Thrive, optionally filtered by audience.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by audience ID or audience reference', + }, + updatedSince: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only items updated on or after this date/time (ISO 8601)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-100, default 100)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/assignments`) + appendThriveQuery(url, 'audienceId', params.audienceId) + appendThriveQuery(url, 'updatedSince', params.updatedSince) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to list assignments') + return { success: true, output: { assignments: Array.isArray(data) ? data : [] } } + }, + + outputs: { + assignments: { + type: 'array', + description: 'The matching assignments', + items: { type: 'object', properties: THRIVE_ASSIGNMENT_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/thrive/list_audience_managers.ts b/apps/sim/tools/thrive/list_audience_managers.ts new file mode 100644 index 00000000000..cf795931b5d --- /dev/null +++ b/apps/sim/tools/thrive/list_audience_managers.ts @@ -0,0 +1,64 @@ +import type { + ThriveListAudienceManagersParams, + ThriveListAudienceManagersResponse, +} from '@/tools/thrive/types' +import { THRIVE_AUDIENCE_MANAGER_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const listAudienceManagersTool: ToolConfig< + ThriveListAudienceManagersParams, + ThriveListAudienceManagersResponse +> = { + id: 'thrive_list_audience_managers', + name: 'Thrive List Audience Managers', + description: 'List the managers of a Thrive audience.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}/managers`, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to list audience managers') + return { success: true, output: { managers: Array.isArray(data) ? data : [] } } + }, + + outputs: { + managers: { + type: 'array', + description: 'The audience managers', + items: { type: 'object', properties: THRIVE_AUDIENCE_MANAGER_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/thrive/list_audience_members.ts b/apps/sim/tools/thrive/list_audience_members.ts new file mode 100644 index 00000000000..e6435c50aed --- /dev/null +++ b/apps/sim/tools/thrive/list_audience_members.ts @@ -0,0 +1,98 @@ +import type { + ThriveListAudienceMembersParams, + ThriveListAudienceMembersResponse, +} from '@/tools/thrive/types' +import { + THRIVE_AUDIENCE_MEMBER_OUTPUT_PROPERTIES, + THRIVE_PAGINATION_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const listAudienceMembersTool: ToolConfig< + ThriveListAudienceMembersParams, + ThriveListAudienceMembersResponse +> = { + id: 'thrive_list_audience_members', + name: 'Thrive List Audience Members', + description: 'List the members of a Thrive audience with pagination.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 100)', + }, + }, + + request: { + url: (params) => { + const url = new URL( + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}/members` + ) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to list audience members') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The audience members', + items: { type: 'object', properties: THRIVE_AUDIENCE_MEMBER_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/list_audiences.ts b/apps/sim/tools/thrive/list_audiences.ts new file mode 100644 index 00000000000..76dc540b5f2 --- /dev/null +++ b/apps/sim/tools/thrive/list_audiences.ts @@ -0,0 +1,99 @@ +import type { ThriveListAudiencesParams, ThriveListAudiencesResponse } from '@/tools/thrive/types' +import { + THRIVE_AUDIENCE_OUTPUT_PROPERTIES, + THRIVE_PAGINATION_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const listAudiencesTool: ToolConfig = + { + id: 'thrive_list_audiences', + name: 'Thrive List Audiences', + description: 'List audiences and structures in Thrive with pagination.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + apiControlled: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Filter to only return audiences which are / are not API controlled', + }, + updatedSince: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only audiences updated on or after this date/time (ISO 8601)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 100)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/audiences`) + appendThriveQuery(url, 'apiControlled', params.apiControlled) + appendThriveQuery(url, 'updatedSince', params.updatedSince) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to list audiences') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The matching audiences', + items: { type: 'object', properties: THRIVE_AUDIENCE_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, + } diff --git a/apps/sim/tools/thrive/list_completions.ts b/apps/sim/tools/thrive/list_completions.ts new file mode 100644 index 00000000000..a822c78be77 --- /dev/null +++ b/apps/sim/tools/thrive/list_completions.ts @@ -0,0 +1,115 @@ +import type { + ThriveListCompletionsParams, + ThriveListCompletionsResponse, +} from '@/tools/thrive/types' +import { THRIVE_COMPLETION_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const listCompletionsTool: ToolConfig< + ThriveListCompletionsParams, + ThriveListCompletionsResponse +> = { + id: 'thrive_list_completions', + name: 'Thrive List Completions', + description: + 'List learning completion records in Thrive, optionally filtered by user or content.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + contentId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by content', + }, + isRPL: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Filter by completions imported via Recognition of Prior Learning (RPL)', + }, + userId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by user', + }, + completedDateRangeStart: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by completedDate (completedDate >= this date/date-time)', + }, + completedDateRangeEnd: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by completedDate (completedDate <= this date/date-time)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 1000)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/learning/completions`) + appendThriveQuery(url, 'contentId', params.contentId) + appendThriveQuery(url, 'isRPL', params.isRPL) + appendThriveQuery(url, 'userId', params.userId) + appendThriveQuery(url, 'completedDateRangeStart', params.completedDateRangeStart) + appendThriveQuery(url, 'completedDateRangeEnd', params.completedDateRangeEnd) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to list completions') + return { success: true, output: { completions: Array.isArray(data) ? data : [] } } + }, + + outputs: { + completions: { + type: 'array', + description: 'The matching completion records', + items: { type: 'object', properties: THRIVE_COMPLETION_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/thrive/list_enrolments.ts b/apps/sim/tools/thrive/list_enrolments.ts new file mode 100644 index 00000000000..078ee688254 --- /dev/null +++ b/apps/sim/tools/thrive/list_enrolments.ts @@ -0,0 +1,106 @@ +import type { ThriveListEnrolmentsParams, ThriveListEnrolmentsResponse } from '@/tools/thrive/types' +import { THRIVE_ENROLMENT_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const listEnrolmentsTool: ToolConfig< + ThriveListEnrolmentsParams, + ThriveListEnrolmentsResponse +> = { + id: 'thrive_list_enrolments', + name: 'Thrive List Enrolments', + description: 'List enrolments for a compliance assignment in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + assignmentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The assignment ID', + }, + updatedAtFrom: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date to filter enrolments from (ISO 8601)', + }, + updatedAtTo: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date to filter enrolments up to (ISO 8601). Requires updatedAtFrom.', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Filter by enrolment status: archived, complete, open, overdue, scheduled, or unassigned', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-100, default 100)', + }, + }, + + request: { + url: (params) => { + const url = new URL( + `${getThriveBaseUrl(params.host, 'v1')}/assignments/${encodeURIComponent(params.assignmentId)}/enrolments` + ) + appendThriveQuery(url, 'updatedAtFrom', params.updatedAtFrom) + appendThriveQuery(url, 'updatedAtTo', params.updatedAtTo) + appendThriveQuery(url, 'status', params.status) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to list enrolments') + return { success: true, output: { enrolments: Array.isArray(data) ? data : [] } } + }, + + outputs: { + enrolments: { + type: 'array', + description: 'The matching enrolments', + items: { type: 'object', properties: THRIVE_ENROLMENT_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/thrive/list_tags.ts b/apps/sim/tools/thrive/list_tags.ts new file mode 100644 index 00000000000..87d0a783129 --- /dev/null +++ b/apps/sim/tools/thrive/list_tags.ts @@ -0,0 +1,91 @@ +import type { ThriveListTagsParams, ThriveListTagsResponse } from '@/tools/thrive/types' +import { + THRIVE_PAGINATION_OUTPUT_PROPERTIES, + THRIVE_TAG_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const listTagsTool: ToolConfig = { + id: 'thrive_list_tags', + name: 'Thrive List Tags', + description: 'List tags in Thrive and return tag information with pagination.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 100)', + }, + updatedSince: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only tags updated on or after this date/time (ISO 8601)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/tags`) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + appendThriveQuery(url, 'updatedSince', params.updatedSince) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to list tags') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The tags', + items: { type: 'object', properties: THRIVE_TAG_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/query_activities.ts b/apps/sim/tools/thrive/query_activities.ts new file mode 100644 index 00000000000..7e10a9268ac --- /dev/null +++ b/apps/sim/tools/thrive/query_activities.ts @@ -0,0 +1,132 @@ +import type { + ThriveQueryActivitiesParams, + ThriveQueryActivitiesResponse, +} from '@/tools/thrive/types' +import { + THRIVE_ACTIVITY_OUTPUT_PROPERTIES, + THRIVE_PAGINATION_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const queryActivitiesTool: ToolConfig< + ThriveQueryActivitiesParams, + ThriveQueryActivitiesResponse +> = { + id: 'thrive_query_activities', + name: 'Thrive Query Activities', + description: 'Query activity records in Thrive with pagination and filtering options.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 20)', + }, + actions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'comma-separated activity types e.g. viewed,completed', + }, + omitActions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'comma-separated activity types e.g. viewed,completed', + }, + contentIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'comma-separated content IDs', + }, + contentType: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by content type', + }, + timestampFrom: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'format YYYY-MM-DD hh:mm:ss', + }, + timestampTo: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'format YYYY-MM-DD hh:mm:ss', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/activities`) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + appendThriveQuery(url, 'actions', params.actions) + appendThriveQuery(url, 'omitActions', params.omitActions) + appendThriveQuery(url, 'contentIds', params.contentIds) + appendThriveQuery(url, 'contentType', params.contentType) + appendThriveQuery(url, 'timestampFrom', params.timestampFrom) + appendThriveQuery(url, 'timestampTo', params.timestampTo) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to query activities') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The matching activity records', + items: { type: 'object', properties: THRIVE_ACTIVITY_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/query_content.ts b/apps/sim/tools/thrive/query_content.ts new file mode 100644 index 00000000000..4ea05b13f6e --- /dev/null +++ b/apps/sim/tools/thrive/query_content.ts @@ -0,0 +1,107 @@ +import type { ThriveQueryContentParams, ThriveQueryContentResponse } from '@/tools/thrive/types' +import { + THRIVE_CONTENT_OUTPUT_PROPERTIES, + THRIVE_PAGINATION_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const queryContentTool: ToolConfig = { + id: 'thrive_query_content', + name: 'Thrive Query Content', + description: 'Query content records in Thrive with pagination and filtering options.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 20)', + }, + types: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated content types (article, assessment, broadcast, cmi5, elearning, event, file, pathway, question, quiz, scorm, url, video, mixed). If both set, omitTypes is ignored.', + }, + omitTypes: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated content types (article, assessment, broadcast, cmi5, elearning, event, file, pathway, question, quiz, scorm, url, video, mixed). If both set, omitTypes is ignored.', + }, + updatedSince: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only items updated on or after this date/time (ISO 8601)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/contents`) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + appendThriveQuery(url, 'types', params.types) + appendThriveQuery(url, 'omitTypes', params.omitTypes) + appendThriveQuery(url, 'updatedSince', params.updatedSince) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to query content') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The matching content records', + items: { type: 'object', properties: THRIVE_CONTENT_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/query_cpd_categories.ts b/apps/sim/tools/thrive/query_cpd_categories.ts new file mode 100644 index 00000000000..f0bc6e6e5af --- /dev/null +++ b/apps/sim/tools/thrive/query_cpd_categories.ts @@ -0,0 +1,97 @@ +import type { + ThriveCpdPaginatedResponse, + ThriveQueryCpdCategoriesParams, +} from '@/tools/thrive/types' +import { + THRIVE_CPD_CATEGORY_OUTPUT_PROPERTIES, + THRIVE_PAGINATION_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const queryCpdCategoriesTool: ToolConfig< + ThriveQueryCpdCategoriesParams, + ThriveCpdPaginatedResponse +> = { + id: 'thrive_query_cpd_categories', + name: 'Thrive Query CPD Categories', + description: 'Query CPD categories in Thrive and return results with pagination.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 100)', + }, + updatedSince: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only items updated on or after this date/time (ISO 8601)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/cpdCategories`) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + appendThriveQuery(url, 'updatedSince', params.updatedSince) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to query CPD categories') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The matching CPD categories', + items: { type: 'object', properties: THRIVE_CPD_CATEGORY_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/query_cpd_entries.ts b/apps/sim/tools/thrive/query_cpd_entries.ts new file mode 100644 index 00000000000..4864c016144 --- /dev/null +++ b/apps/sim/tools/thrive/query_cpd_entries.ts @@ -0,0 +1,101 @@ +import type { ThriveCpdPaginatedResponse, ThriveQueryCpdEntriesParams } from '@/tools/thrive/types' +import { + THRIVE_CPD_ENTRY_OUTPUT_PROPERTIES, + THRIVE_PAGINATION_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const queryCpdEntriesTool: ToolConfig< + ThriveQueryCpdEntriesParams, + ThriveCpdPaginatedResponse +> = { + id: 'thrive_query_cpd_entries', + name: 'Thrive Query CPD Entries', + description: 'Query CPD log entries in Thrive and return results with pagination.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 100)', + }, + entryDateFrom: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter entries after this date (format YYYY-MM-DD hh:mm:ss)', + }, + entryDateTo: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter entries before this date (format YYYY-MM-DD hh:mm:ss)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/cpdEntries`) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + appendThriveQuery(url, 'entryDateFrom', params.entryDateFrom) + appendThriveQuery(url, 'entryDateTo', params.entryDateTo) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to query CPD entries') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The matching CPD entries', + items: { type: 'object', properties: THRIVE_CPD_ENTRY_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/query_cpd_requirements.ts b/apps/sim/tools/thrive/query_cpd_requirements.ts new file mode 100644 index 00000000000..3b7b1c82556 --- /dev/null +++ b/apps/sim/tools/thrive/query_cpd_requirements.ts @@ -0,0 +1,97 @@ +import type { + ThriveCpdPaginatedResponse, + ThriveQueryCpdRequirementsParams, +} from '@/tools/thrive/types' +import { + THRIVE_CPD_REQUIREMENT_OUTPUT_PROPERTIES, + THRIVE_PAGINATION_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const queryCpdRequirementsTool: ToolConfig< + ThriveQueryCpdRequirementsParams, + ThriveCpdPaginatedResponse +> = { + id: 'thrive_query_cpd_requirements', + name: 'Thrive Query CPD Requirements', + description: 'Query CPD requirement summaries in Thrive and return results with pagination.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 100)', + }, + updatedSince: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only items updated on or after this date/time (ISO 8601)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/cpdRequirementSummaries`) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + appendThriveQuery(url, 'updatedSince', params.updatedSince) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to query CPD requirements') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The matching CPD requirements', + items: { type: 'object', properties: THRIVE_CPD_REQUIREMENT_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/query_cpd_user_summaries.ts b/apps/sim/tools/thrive/query_cpd_user_summaries.ts new file mode 100644 index 00000000000..512ff7aacf5 --- /dev/null +++ b/apps/sim/tools/thrive/query_cpd_user_summaries.ts @@ -0,0 +1,111 @@ +import type { + ThriveCpdPaginatedResponse, + ThriveQueryCpdUserSummariesParams, +} from '@/tools/thrive/types' +import { + THRIVE_CPD_USER_SUMMARY_OUTPUT_PROPERTIES, + THRIVE_PAGINATION_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const queryCpdUserSummariesTool: ToolConfig< + ThriveQueryCpdUserSummariesParams, + ThriveCpdPaginatedResponse +> = { + id: 'thrive_query_cpd_user_summaries', + name: 'Thrive Query CPD User Summaries', + description: 'Query CPD user log summaries in Thrive and return results with pagination.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + entryDateFrom: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Filter entries after this date (format YYYY-MM-DDThh:mm:ss)', + }, + entryDateTo: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Filter entries before this date (format YYYY-MM-DDThh:mm:ss)', + }, + userIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated user IDs to filter by', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 100)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/cpdUserLogSummaries`) + appendThriveQuery(url, 'entryDateFrom', params.entryDateFrom) + appendThriveQuery(url, 'entryDateTo', params.entryDateTo) + appendThriveQuery(url, 'userIds', params.userIds) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to query CPD user summaries') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The matching CPD user summaries', + items: { type: 'object', properties: THRIVE_CPD_USER_SUMMARY_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/remove_audience_manager.ts b/apps/sim/tools/thrive/remove_audience_manager.ts new file mode 100644 index 00000000000..b9ecc9cb094 --- /dev/null +++ b/apps/sim/tools/thrive/remove_audience_manager.ts @@ -0,0 +1,62 @@ +import type { ThriveDeleteResponse, ThriveRemoveAudienceManagerParams } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const removeAudienceManagerTool: ToolConfig< + ThriveRemoveAudienceManagerParams, + ThriveDeleteResponse +> = { + id: 'thrive_remove_audience_manager', + name: 'Thrive Remove Audience Manager', + description: 'Remove a single manager from a Thrive audience.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The user email, ref, or id to remove as a manager', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}/managers/${encodeURIComponent(params.userId)}`, + method: 'DELETE', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to remove audience manager') + return { success: true, output: { success: data?.success ?? true } } + }, + + outputs: { + success: { type: 'boolean', description: 'Whether the audience manager was removed' }, + }, +} diff --git a/apps/sim/tools/thrive/remove_audience_member.ts b/apps/sim/tools/thrive/remove_audience_member.ts new file mode 100644 index 00000000000..7f44a6d9e44 --- /dev/null +++ b/apps/sim/tools/thrive/remove_audience_member.ts @@ -0,0 +1,62 @@ +import type { ThriveDeleteResponse, ThriveRemoveAudienceMemberParams } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const removeAudienceMemberTool: ToolConfig< + ThriveRemoveAudienceMemberParams, + ThriveDeleteResponse +> = { + id: 'thrive_remove_audience_member', + name: 'Thrive Remove Audience Member', + description: 'Remove a single member from a Thrive audience.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + userRef: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The user email, ref, or id to remove', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}/members/${encodeURIComponent(params.userRef)}`, + method: 'DELETE', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to remove audience member') + return { success: true, output: { success: data?.success ?? true } } + }, + + outputs: { + success: { type: 'boolean', description: 'Whether the audience member was removed' }, + }, +} diff --git a/apps/sim/tools/thrive/remove_user_tags.ts b/apps/sim/tools/thrive/remove_user_tags.ts new file mode 100644 index 00000000000..e8d655546fe --- /dev/null +++ b/apps/sim/tools/thrive/remove_user_tags.ts @@ -0,0 +1,73 @@ +import type { ThriveMessageResponse, ThriveRemoveUserTagsParams } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveArray, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const removeUserTagsTool: ToolConfig = { + id: 'thrive_remove_user_tags', + name: 'Thrive Remove User Tags', + description: 'Remove one or more tags from a learner in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The learner ID', + }, + tags: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'JSON array of tag names to remove (1-100). Example: ["leadership"]', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/users/${encodeURIComponent(params.userId)}/tags`, + method: 'PATCH', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => ({ + op: 'remove', + path: 'tags', + value: parseThriveArray(params.tags, 'tags'), + }), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to remove tags from learner') + return { + success: true, + output: { status: data?.status ?? null, message: data?.message ?? null }, + } + }, + + outputs: { + status: { type: 'number', description: 'The HTTP status code of the operation' }, + message: { type: 'string', description: 'A human-readable result message' }, + }, +} diff --git a/apps/sim/tools/thrive/replace_audience_managers.ts b/apps/sim/tools/thrive/replace_audience_managers.ts new file mode 100644 index 00000000000..07543847689 --- /dev/null +++ b/apps/sim/tools/thrive/replace_audience_managers.ts @@ -0,0 +1,79 @@ +import type { ThriveAddUsersResponse, ThriveAudienceManagersParams } from '@/tools/thrive/types' +import { THRIVE_ADD_USERS_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveArray, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const replaceAudienceManagersTool: ToolConfig< + ThriveAudienceManagersParams, + ThriveAddUsersResponse +> = { + id: 'thrive_replace_audience_managers', + name: 'Thrive Replace Audience Managers', + description: + "Replace a Thrive audience's entire manager list with the given managers (does not support an empty array).", + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + managers: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'JSON array of manager objects that replaces the whole manager list (1-100, no empty array). Each: {"reference":"user@example.com","permissions":{"audienceManager":{"manageContent":true,"assignments":true},"peopleManager":{"canViewLearnPage":true,"insights":false,"manage":false},"administrator":{"canAddAudienceManagers":false}}}', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}/managers`, + method: 'PATCH', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => JSON.stringify(parseThriveArray(params.managers, 'managers')), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to replace audience managers') + return { + success: true, + output: { result: { success: data?.success ?? null, failure: data?.failure } }, + } + }, + + outputs: { + result: { + type: 'object', + description: + 'The add/replace result, with successfully and unsuccessfully processed entities', + properties: THRIVE_ADD_USERS_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/replace_audience_members.ts b/apps/sim/tools/thrive/replace_audience_members.ts new file mode 100644 index 00000000000..e2f1e8722ba --- /dev/null +++ b/apps/sim/tools/thrive/replace_audience_members.ts @@ -0,0 +1,79 @@ +import type { ThriveAddUsersResponse, ThriveAudienceUsersParams } from '@/tools/thrive/types' +import { THRIVE_ADD_USERS_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveArray, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const replaceAudienceMembersTool: ToolConfig< + ThriveAudienceUsersParams, + ThriveAddUsersResponse +> = { + id: 'thrive_replace_audience_members', + name: 'Thrive Replace Audience Members', + description: + "Replace a Thrive audience's entire members list with the given users (does not support an empty array).", + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + users: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'JSON array of user emails/refs/ids that replaces the whole members list (1-100, no empty array). Example: ["user@example.com"]', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}/members`, + method: 'PATCH', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => JSON.stringify(parseThriveArray(params.users, 'users')), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to replace audience members') + return { + success: true, + output: { result: { success: data?.success ?? null, failure: data?.failure } }, + } + }, + + outputs: { + result: { + type: 'object', + description: + 'The add/replace result, with successfully and unsuccessfully processed entities', + properties: THRIVE_ADD_USERS_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/search_users.ts b/apps/sim/tools/thrive/search_users.ts new file mode 100644 index 00000000000..bbbb30df7c4 --- /dev/null +++ b/apps/sim/tools/thrive/search_users.ts @@ -0,0 +1,112 @@ +import type { ThriveSearchUsersParams, ThriveSearchUsersResponse } from '@/tools/thrive/types' +import { + THRIVE_PAGINATION_OUTPUT_PROPERTIES, + THRIVE_USER_OUTPUT_PROPERTIES, +} from '@/tools/thrive/types' +import { + appendThriveQuery, + getThriveBaseUrl, + getThriveHeaders, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const searchUsersTool: ToolConfig = { + id: 'thrive_search_users', + name: 'Thrive Search Users', + description: 'Search users in Thrive and return basic user information with pagination.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination (default 1)', + }, + perPage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-1000, default 100)', + }, + updatedSince: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only users updated on or after this date/time (ISO 8601)', + }, + statuses: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated statuses to include: active, inactive, expired, new', + }, + omitStatuses: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated statuses to exclude: active, inactive, expired, new', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by a single status: active, inactive, expired, or new', + }, + }, + + request: { + url: (params) => { + const url = new URL(`${getThriveBaseUrl(params.host, 'v1')}/users`) + appendThriveQuery(url, 'page', params.page) + appendThriveQuery(url, 'perPage', params.perPage) + appendThriveQuery(url, 'updatedSince', params.updatedSince) + appendThriveQuery(url, 'statuses', params.statuses) + appendThriveQuery(url, 'omitStatuses', params.omitStatuses) + appendThriveQuery(url, 'status', params.status) + return url.toString() + }, + method: 'GET', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to search users') + return { + success: true, + output: { results: data?.results ?? [], pagination: data?.pagination ?? null }, + } + }, + + outputs: { + results: { + type: 'array', + description: 'The matching users', + items: { type: 'object', properties: THRIVE_USER_OUTPUT_PROPERTIES }, + }, + pagination: { + type: 'object', + description: 'Pagination details', + properties: THRIVE_PAGINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/suspend_user.ts b/apps/sim/tools/thrive/suspend_user.ts new file mode 100644 index 00000000000..54d2fad0c7b --- /dev/null +++ b/apps/sim/tools/thrive/suspend_user.ts @@ -0,0 +1,69 @@ +import type { ThriveSuspendUserParams, ThriveUserResponse } from '@/tools/thrive/types' +import { THRIVE_USER_LIFECYCLE_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const suspendUserTool: ToolConfig = { + id: 'thrive_suspend_user', + name: 'Thrive Suspend User', + description: 'Suspend a user in Thrive by ref, marking the account inactive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + ref: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The user ref to suspend', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The date this individual left your organisation (ISO 8601)', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v2')}/users/ref/${encodeURIComponent(params.ref)}/suspend`, + method: 'PATCH', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => { + const body: Record = {} + if (params.endDate) body.endDate = params.endDate + return body + }, + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to suspend user') + return { success: true, output: { user: data ?? null } } + }, + + outputs: { + user: { + type: 'object', + description: 'The suspended user', + properties: THRIVE_USER_LIFECYCLE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/types.ts b/apps/sim/tools/thrive/types.ts new file mode 100644 index 00000000000..aa576d67e84 --- /dev/null +++ b/apps/sim/tools/thrive/types.ts @@ -0,0 +1,856 @@ +import type { OutputProperty, ToolResponse } from '@/tools/types' + +/** + * Credentials and region shared by every Thrive tool. + * Thrive uses HTTP Basic auth (Tenant ID as username, API key as password). + */ +export interface ThriveBaseParams { + tenantId: string + apiKey: string + /** Region-specific API host, e.g. `public.api.learn.link`. Defaults to Production. */ + host?: string +} + +export interface ThrivePagination { + totalResults: number + totalPages: number + page: number + perPage: number +} + +export const THRIVE_PAGINATION_OUTPUT_PROPERTIES: Record = { + totalResults: { type: 'number', description: 'Total number of results matching the query' }, + totalPages: { type: 'number', description: 'Total number of pages available' }, + page: { type: 'number', description: 'Current page number' }, + perPage: { type: 'number', description: 'Number of results per page' }, +} + +const USER_POSITION_OUTPUT_PROPERTIES: Record = { + id: { type: 'string', description: 'The user ID' }, + manager: { + type: 'object', + description: 'Line manager details', + properties: { + id: { type: 'string', description: "The manager's user ID", nullable: true }, + name: { type: 'string', description: "The manager's full name", nullable: true }, + ref: { type: 'string', description: "The manager's unique reference", nullable: true }, + }, + }, + ouId: { type: 'string', description: 'The organisational unit ID', nullable: true }, + isActive: { type: 'boolean', description: 'Whether the position is active' }, + startDate: { type: 'string', description: 'Start date (ISO 8601)', nullable: true }, + endDate: { type: 'string', description: 'End date (ISO 8601)', nullable: true }, + createdAt: { type: 'string', description: 'Creation timestamp (ISO 8601)', nullable: true }, + updatedAt: { type: 'string', description: 'Last-update timestamp (ISO 8601)', nullable: true }, +} + +/** Full `User` schema returned by search-users and get-user-by-id. */ +export const THRIVE_USER_OUTPUT_PROPERTIES: Record = { + id: { type: 'string', description: "The user's ID" }, + ref: { type: 'string', description: "The user's ref", nullable: true }, + firstName: { type: 'string', description: "The user's first name", nullable: true }, + lastName: { type: 'string', description: "The user's last name", nullable: true }, + email: { type: 'string', description: "The user's email", nullable: true }, + role: { type: 'string', description: "The user's role", nullable: true }, + status: { type: 'string', description: "The user's status", nullable: true }, + positions: { + type: 'array', + description: "The user's positions", + items: { type: 'object', properties: USER_POSITION_OUTPUT_PROPERTIES }, + }, + additionalFields: { type: 'json', description: 'Custom field values', nullable: true }, + languageCode: { type: 'string', description: "The user's language code", nullable: true }, + deleted: { type: 'boolean', description: 'Whether the user has been deleted' }, + compliance: { type: 'number', description: "The user's compliance score" }, + level: { type: 'number', description: "The user's level" }, + firstLogin: { type: 'string', description: 'First login timestamp (ISO 8601)', nullable: true }, + lastLogin: { type: 'string', description: 'Last login timestamp (ISO 8601)', nullable: true }, + tags: { type: 'json', description: 'Tag membership (e.g. skills)' }, + usersFollowing: { + type: 'array', + description: 'IDs of users this user follows', + items: { type: 'string' }, + }, + tagsFollowing: { + type: 'array', + description: 'Tags this user follows', + items: { type: 'string' }, + }, + createdAt: { type: 'string', description: 'Creation timestamp (ISO 8601)', nullable: true }, + updatedAt: { type: 'string', description: 'Last-update timestamp (ISO 8601)', nullable: true }, + hasPicture: { type: 'boolean', description: 'Whether the user has a profile picture' }, + timeZone: { type: 'string', description: "The user's time zone", nullable: true }, + summary: { type: 'string', description: "The user's summary", nullable: true }, + relevancy: { type: 'number', description: "The user's relevancy score" }, + rank: { type: 'json', description: "The user's rank details" }, + agreedTerms: { + type: 'boolean', + description: 'Whether the user agreed to the terms', + nullable: true, + }, + onboarded: { + type: 'boolean', + description: 'Whether the user has been onboarded', + nullable: true, + }, + audiences: { + type: 'array', + description: 'Audience IDs the user belongs to', + items: { type: 'string' }, + }, + singleSignOn: { type: 'boolean', description: 'Whether the user uses single sign-on' }, +} + +/** Smaller `BasicUser` schema returned by get-user-by-ref. */ +export const THRIVE_BASIC_USER_OUTPUT_PROPERTIES: Record = { + id: { type: 'string', description: "The user's ID" }, + ref: { type: 'string', description: "The user's ref", nullable: true }, + firstName: { type: 'string', description: "The user's first name", nullable: true }, + lastName: { type: 'string', description: "The user's last name", nullable: true }, + email: { type: 'string', description: "The user's email", nullable: true }, + role: { type: 'string', description: "The user's role", nullable: true }, + status: { type: 'string', description: "The user's status", nullable: true }, + positions: { + type: 'array', + description: "The user's positions", + items: { type: 'object', properties: USER_POSITION_OUTPUT_PROPERTIES }, + }, + additionalFields: { type: 'json', description: 'Custom field values', nullable: true }, + languageCode: { type: 'string', description: "The user's language code", nullable: true }, +} + +/** `UserLifecycleResource` returned by create/update/suspend user. */ +export const THRIVE_USER_LIFECYCLE_OUTPUT_PROPERTIES: Record = { + id: { type: 'string', description: 'The user ID' }, + loginMethod: { type: 'string', description: 'How the user logs in' }, + ref: { type: 'string', description: "Your organisation's unique identifier for the user" }, + email: { type: 'string', description: 'The email address for the user' }, + firstName: { type: 'string', description: 'The given name of the individual' }, + lastName: { type: 'string', description: 'The family name of the individual' }, + role: { type: 'string', description: 'Role assigned to this individual' }, + jobTitle: { type: 'string', description: "Name of this individual's role" }, + managerRef: { type: 'string', description: "The line manager's ref", nullable: true }, + startDate: { type: 'string', description: 'Date started with the organisation', nullable: true }, + endDate: { type: 'string', description: 'Date left the organisation', nullable: true }, + timeZone: { type: 'string', description: "The user's preferred timezone" }, + languageCode: { type: 'string', description: "The user's preferred language" }, + active: { type: 'boolean', description: 'Whether the account is active or suspended' }, + createdAt: { type: 'string', description: 'Date/time the user was created' }, + updatedAt: { type: 'string', description: 'Date/time the user was last modified' }, + sso: { type: 'boolean', description: 'Whether the account is managed by an auth provider' }, + domain: { + type: 'string', + description: 'Domain this individual is associated with', + nullable: true, + }, + additionalFields: { + type: 'json', + description: 'Custom field values for this user', + nullable: true, + }, +} + +export const THRIVE_AUDIENCE_OUTPUT_PROPERTIES: Record = { + id: { type: 'string', description: 'The id of the audience' }, + name: { type: 'string', description: 'The name of the audience' }, + reference: { type: 'string', description: 'The external reference for the audience' }, + apiControlled: { type: 'boolean', description: 'Whether the audience is API controlled' }, + category: { type: 'string', description: 'Either "audience" or "structure"' }, + type: { type: 'string', description: 'Either "manual" or "smart"' }, + parent: { + type: 'object', + description: 'Parent audience/structure information', + nullable: true, + properties: { + name: { type: 'string', description: 'The name of the parent audience' }, + reference: { type: 'string', description: 'The external reference for the parent' }, + id: { type: 'string', description: 'The id of the parent audience/structure' }, + }, + }, + createdAt: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updatedAt: { type: 'string', description: 'Last-update timestamp (ISO 8601)' }, +} + +export const THRIVE_AUDIENCE_MEMBER_OUTPUT_PROPERTIES: Record = { + userId: { type: 'string', description: "The user's id" }, + reference: { type: 'string', description: "The user's reference" }, + email: { type: 'string', description: "The user's email" }, +} + +export const THRIVE_AUDIENCE_MANAGER_OUTPUT_PROPERTIES: Record = { + userId: { type: 'string', description: "The user's id" }, + reference: { type: 'string', description: "The user's reference" }, + email: { type: 'string', description: "The user's email" }, + permissions: { + type: 'object', + description: 'The manager permissions', + properties: { + audienceManager: { type: 'json', description: 'Audience manager permissions' }, + peopleManager: { type: 'json', description: 'People manager permissions' }, + administrator: { + type: 'json', + description: 'Administrator permissions (structures only)', + nullable: true, + }, + }, + }, +} + +/** Superset of SuccessAddUsers / PartialSuccessAddUsers. */ +export const THRIVE_ADD_USERS_OUTPUT_PROPERTIES: Record = { + success: { + type: 'object', + description: 'Successfully processed entities', + properties: { + count: { type: 'number', description: 'Number of successfully processed entities' }, + entities: { + type: 'array', + description: 'The successfully processed entities', + items: { + type: 'object', + properties: { reference: { type: 'string', description: 'The entity reference' } }, + }, + }, + }, + }, + failure: { + type: 'object', + description: 'Unsuccessfully processed entities', + optional: true, + properties: { + count: { type: 'number', description: 'Number of unsuccessfully processed entities' }, + entities: { + type: 'array', + description: 'The unsuccessfully processed entities', + items: { + type: 'object', + properties: { + reason: { type: 'string', description: 'The reason for the failure' }, + reference: { type: 'string', description: 'The entity reference' }, + }, + }, + }, + }, + }, +} + +export const THRIVE_ASSIGNMENT_OUTPUT_PROPERTIES: Record = { + id: { type: 'string', description: 'The assignment ID' }, + audienceId: { type: 'string', description: 'The audience ID' }, + primaryContentId: { type: 'string', description: 'The content ID for the primary content' }, + alternativeContentIds: { + type: 'array', + description: 'Content IDs that can also complete the assignment', + items: { type: 'string' }, + }, + hideAlternativeContent: { + type: 'boolean', + description: 'Whether to hide the alternative content', + }, + completionPeriod: { + type: 'number', + description: 'Number of days required to complete the assignment', + }, + recurrence: { + type: 'number', + description: 'Number of days until the assignment reoccurs', + nullable: true, + }, + isActive: { type: 'boolean', description: 'Whether the assignment is active' }, + isDeleted: { type: 'boolean', description: 'Whether the assignment is deleted' }, + createdAt: { type: 'string', description: 'Creation timestamp (ISO 8601)', nullable: true }, + deletedAt: { type: 'string', description: 'Deletion timestamp (ISO 8601)', nullable: true }, + updatedAt: { type: 'string', description: 'Last-update timestamp (ISO 8601)', nullable: true }, +} + +export const THRIVE_ENROLMENT_OUTPUT_PROPERTIES: Record = { + id: { type: 'string', description: 'The enrolment ID' }, + userId: { type: 'string', description: 'The assignee user ID' }, + assignmentId: { type: 'string', description: 'The assignment ID' }, + audienceId: { type: 'string', description: 'The audience ID' }, + primaryContentId: { type: 'string', description: 'The assigned content ID' }, + status: { type: 'string', description: 'Enrolment status' }, + availableDate: { type: 'string', description: 'Date a scheduled enrolment becomes open' }, + dueDate: { type: 'string', description: 'Date after which a scheduled enrolment is overdue' }, + lastCompletedAt: { type: 'string', description: 'Date a scheduled enrolment was last completed' }, + history: { + type: 'array', + description: 'Event-log history entries', + items: { + type: 'object', + properties: { + type: { type: 'string', description: 'The type of the logged event' }, + completionId: { type: 'string', description: 'The completion ID' }, + previousStatus: { type: 'string', description: 'The previous enrolment status' }, + nextStatus: { type: 'string', description: 'The next enrolment status' }, + createdAt: { type: 'string', description: 'Date the event was logged' }, + updatedAt: { type: 'string', description: 'Date the event was last modified' }, + }, + }, + }, + updatedAt: { type: 'string', description: 'Date the enrolment was last updated' }, +} + +export const THRIVE_COMPLETION_OUTPUT_PROPERTIES: Record = { + id: { type: 'string', description: 'The completion ID' }, + userId: { type: 'string', description: 'The user ID' }, + contentId: { type: 'string', description: 'The content ID for the content completed' }, + contentVersion: { type: 'number', description: 'The version of the content' }, + skills: { + type: 'array', + description: 'The skills acquired by completing this content', + items: { type: 'string' }, + }, + completionType: { type: 'string', description: 'The type of completion record' }, + hadDueDate: { type: 'boolean', description: 'Whether the completion had a due date' }, + isRPL: { type: 'boolean', description: 'Whether the completion was imported via RPL' }, + completedAt: { type: 'string', description: 'Timestamp when the completion occurred (ISO 8601)' }, + activeUntil: { + type: 'string', + description: 'Timestamp the completion is valid until (ISO 8601)', + }, +} + +const CONTENT_HISTORY_OUTPUT_PROPERTIES: Record = { + action: { type: 'string', description: 'Type of change or event recorded' }, + timestamp: { type: 'string', description: 'When the action occurred (ISO 8601)' }, + performedBy: { + type: 'object', + description: 'The actor that performed the action', + properties: { + type: { type: 'string', description: 'Kind of actor (e.g. user or system)' }, + value: { type: 'string', description: 'Identifier or value of the actor' }, + }, + }, +} + +export const THRIVE_CONTENT_OUTPUT_PROPERTIES: Record = { + id: { type: 'string', description: 'Unique identifier for the content' }, + title: { type: 'string', description: 'Title of the content' }, + description: { type: 'string', description: 'Detailed description (may contain HTML)' }, + tags: { + type: 'array', + description: 'Tags associated with this content', + items: { type: 'string' }, + }, + type: { type: 'string', description: 'The kind of artifact associated with this content' }, + createdAt: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updatedAt: { type: 'string', description: 'Last-update timestamp (ISO 8601)' }, + author: { type: 'string', description: 'User ID who authored the content', nullable: true }, + isOfficial: { type: 'boolean', description: 'Whether the content is recognised as official' }, + duration: { + type: 'object', + description: 'Expected time to complete the content', + nullable: true, + properties: { + value: { type: 'number', description: 'Duration value', nullable: true }, + unit: { type: 'string', description: "The unit of the duration (always 'minutes')" }, + }, + }, + contentHistory: { + type: 'array', + description: 'Chronological history of actions on this content', + items: { type: 'object', properties: CONTENT_HISTORY_OUTPUT_PROPERTIES }, + }, +} + +export const THRIVE_ACTIVITY_OUTPUT_PROPERTIES: Record = { + type: { type: 'string', description: 'The activity action type' }, + name: { type: 'string', description: 'The name of the activity' }, + id: { type: 'string', description: 'Unique ID for this activity record' }, + user: { type: 'string', description: 'User ID who triggered the activity' }, + date: { type: 'string', description: 'Timestamp when the activity occurred (ISO 8601)' }, + contextId: { type: 'string', description: 'Identifier for the context item' }, + contextType: { type: 'string', description: 'What this activity was in relation to' }, + data: { type: 'json', description: 'Unstructured activity data; shape varies by type' }, + with: { type: 'json', description: 'Additional context information', nullable: true }, +} + +export const THRIVE_CPD_CATEGORY_OUTPUT_PROPERTIES: Record = { + categoryId: { type: 'string', description: 'Unique ID for this category record' }, + name: { type: 'string', description: 'Name of the category of CPD activity' }, +} + +export const THRIVE_CPD_ENTRY_OUTPUT_PROPERTIES: Record = { + logEntryId: { type: 'string', description: 'Unique ID for this activity record' }, + userId: { type: 'string', description: 'User ID who triggered this activity record' }, + activity: { + type: 'object', + description: 'The content item associated with the CPD log entry', + properties: { + type: { type: 'string', description: 'The type of content (e.g. file, article, video)' }, + name: { type: 'string', description: 'The name of the content item' }, + }, + }, + category: { + type: 'object', + description: 'The CPD category', + properties: { + categoryId: { type: 'string', description: 'Unique ID for this category record' }, + name: { type: 'string', description: 'Name of the category of CPD activity' }, + }, + }, + entryDate: { + type: 'string', + description: 'The date and time the CPD entry was logged (ISO 8601)', + }, + durationMinutes: { type: 'number', description: 'Minutes logged as CPD from this activity' }, + description: { type: 'string', description: 'Summary or reflective statement', nullable: true }, + isVerified: { + type: 'boolean', + description: 'Whether the activity was generated from verified system activity', + }, +} + +export const THRIVE_CPD_REQUIREMENT_OUTPUT_PROPERTIES: Record = { + audienceRequirementId: { type: 'string', description: 'Unique ID for this requirement record' }, + audienceId: { type: 'string', description: 'ID of the audience this requirement applies to' }, + requiredMinutes: { type: 'number', description: 'Number of minutes required for CPD completion' }, + createdAt: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updatedAt: { type: 'string', description: 'Last-update timestamp (ISO 8601)', nullable: true }, +} + +export const THRIVE_CPD_USER_SUMMARY_OUTPUT_PROPERTIES: Record = { + userId: { type: 'string', description: 'ID of the user this summary is for' }, + durationMinutes: { + type: 'number', + description: 'Total CPD minutes logged by the user in the period', + }, +} + +export const THRIVE_TAG_OUTPUT_PROPERTIES: Record = { + tag: { type: 'string', description: 'The name of the tag' }, + id: { type: 'string', description: 'The ID of the tag' }, + contents: { + type: 'array', + description: 'IDs of contents using this tag', + items: { type: 'string' }, + }, + campaigns: { + type: 'array', + description: 'IDs of campaigns using this tag', + items: { type: 'string' }, + }, + interests: { + type: 'array', + description: 'IDs of users interested in this tag', + items: { type: 'string' }, + }, + skills: { + type: 'array', + description: 'IDs of users skilled in this tag', + items: { type: 'string' }, + }, +} + +export const THRIVE_SKILL_LEVEL_OUTPUT_PROPERTIES: Record = { + name: { type: 'string', description: 'The name of the skill level' }, + isEnabled: { type: 'boolean', description: 'Whether the skill level is enabled' }, + value: { type: 'number', description: 'The numeric value of the skill level' }, +} + +// ─── Users ─────────────────────────────────────────────────────────────────── + +export interface ThriveCreateUserParams extends ThriveBaseParams { + ref: string + firstName: string + lastName: string + email?: string + loginMethod?: string + role?: string + jobTitle?: string + managerRef?: string + startDate?: string + endDate?: string + timeZone?: string + languageCode?: string + sso?: boolean + domain?: string + additionalFields?: string +} + +export interface ThriveUpdateUserParams extends ThriveBaseParams { + ref: string + firstName?: string + lastName?: string + email?: string + loginMethod?: string + role?: string + jobTitle?: string + managerRef?: string + startDate?: string + endDate?: string + timeZone?: string + languageCode?: string + sso?: boolean + domain?: string + additionalFields?: string +} + +export interface ThriveDeleteUserParams extends ThriveBaseParams { + ref: string +} + +export interface ThriveSuspendUserParams extends ThriveBaseParams { + ref: string + endDate?: string +} + +export interface ThriveSearchUsersParams extends ThriveBaseParams { + page?: number + perPage?: number + updatedSince?: string + statuses?: string + omitStatuses?: string + status?: string +} + +export interface ThriveGetUserByIdParams extends ThriveBaseParams { + id: string +} + +export interface ThriveGetUserByRefParams extends ThriveBaseParams { + ref: string +} + +export interface ThriveUserResponse extends ToolResponse { + output: { user: Record } +} + +export interface ThriveSearchUsersResponse extends ToolResponse { + output: { results: Record[]; pagination: ThrivePagination | null } +} + +export interface ThriveDeleteResponse extends ToolResponse { + output: { success: boolean } +} + +// ─── Audiences ───────────────────────────────────────────────────────────── + +export interface ThriveListAudiencesParams extends ThriveBaseParams { + apiControlled?: boolean + updatedSince?: string + page?: number + perPage?: number +} + +export interface ThriveCreateAudienceParams extends ThriveBaseParams { + name?: string + reference?: string + parentId?: string + category?: string +} + +export interface ThriveGetAudienceParams extends ThriveBaseParams { + audienceId: string +} + +export interface ThriveUpdateAudienceParams extends ThriveBaseParams { + audienceId: string + name?: string + reference?: string + parentId?: string +} + +export interface ThriveDeleteAudienceParams extends ThriveBaseParams { + audienceId: string +} + +export interface ThriveListAudienceMembersParams extends ThriveBaseParams { + audienceId: string + page?: number + perPage?: number +} + +export interface ThriveAudienceUsersParams extends ThriveBaseParams { + audienceId: string + users: string +} + +export interface ThriveRemoveAudienceMemberParams extends ThriveBaseParams { + audienceId: string + userRef: string +} + +export interface ThriveListAudienceManagersParams extends ThriveBaseParams { + audienceId: string +} + +export interface ThriveAudienceManagersParams extends ThriveBaseParams { + audienceId: string + managers: string +} + +export interface ThriveRemoveAudienceManagerParams extends ThriveBaseParams { + audienceId: string + userId: string +} + +export interface ThriveAudienceResponse extends ToolResponse { + output: { audience: Record } +} + +export interface ThriveListAudiencesResponse extends ToolResponse { + output: { results: Record[]; pagination: ThrivePagination | null } +} + +export interface ThriveListAudienceMembersResponse extends ToolResponse { + output: { results: Record[]; pagination: ThrivePagination | null } +} + +export interface ThriveListAudienceManagersResponse extends ToolResponse { + output: { managers: Record[] } +} + +export interface ThriveAddUsersResponse extends ToolResponse { + output: { result: { success: Record | null; failure?: Record } } +} + +export interface ThriveMessageResponse extends ToolResponse { + output: { status: number; message: string } +} + +// ─── Assignments & Enrolments ────────────────────────────────────────────── + +export interface ThriveListAssignmentsParams extends ThriveBaseParams { + audienceId?: string + updatedSince?: string + page?: number + perPage?: number +} + +export interface ThriveCreateAssignmentParams extends ThriveBaseParams { + audienceId: string + contentId: string + alternativeContentIds?: string + hideAlternativeContent?: boolean + completionPeriod?: number + recurrence?: number +} + +export interface ThriveGetAssignmentParams extends ThriveBaseParams { + assignmentId: string +} + +export interface ThriveUpdateAssignmentParams extends ThriveBaseParams { + assignmentId: string + audienceId: string + contentId?: string + completionPeriod?: number + recurrence?: number + alternativeContentIds?: string +} + +export interface ThriveDeleteAssignmentParams extends ThriveBaseParams { + assignmentId: string + audienceId: string +} + +export interface ThriveListEnrolmentsParams extends ThriveBaseParams { + assignmentId: string + updatedAtFrom?: string + updatedAtTo?: string + status?: string + page?: number + perPage?: number +} + +export interface ThriveGetEnrolmentParams extends ThriveBaseParams { + assignmentId: string + enrolmentId: string +} + +export interface ThriveAssignmentResponse extends ToolResponse { + output: { assignment: Record } +} + +export interface ThriveListAssignmentsResponse extends ToolResponse { + output: { assignments: Record[] } +} + +export interface ThriveEnrolmentResponse extends ToolResponse { + output: { enrolment: Record } +} + +export interface ThriveListEnrolmentsResponse extends ToolResponse { + output: { enrolments: Record[] } +} + +// ─── Completions ─────────────────────────────────────────────────────────── + +export interface ThriveListCompletionsParams extends ThriveBaseParams { + contentId?: string + isRPL?: boolean + userId?: string + completedDateRangeStart?: string + completedDateRangeEnd?: string + page?: number + perPage?: number +} + +export interface ThriveGetCompletionParams extends ThriveBaseParams { + id: string +} + +export interface ThriveCreateCompletionParams extends ThriveBaseParams { + userId: string + contentId: string + completedAt: string +} + +export interface ThriveCompletionResponse extends ToolResponse { + output: { completion: Record } +} + +export interface ThriveListCompletionsResponse extends ToolResponse { + output: { completions: Record[] } +} + +export interface ThriveCreateCompletionResponse extends ToolResponse { + output: { statementId: string | null } +} + +// ─── Content ─────────────────────────────────────────────────────────────── + +export interface ThriveGetContentParams extends ThriveBaseParams { + id: string +} + +export interface ThriveQueryContentParams extends ThriveBaseParams { + page?: number + perPage?: number + types?: string + omitTypes?: string + updatedSince?: string +} + +export interface ThriveContentResponse extends ToolResponse { + output: { content: Record } +} + +export interface ThriveQueryContentResponse extends ToolResponse { + output: { results: Record[]; pagination: ThrivePagination | null } +} + +// ─── Activities ──────────────────────────────────────────────────────────── + +export interface ThriveGetActivityParams extends ThriveBaseParams { + id: string +} + +export interface ThriveQueryActivitiesParams extends ThriveBaseParams { + page?: number + perPage?: number + actions?: string + omitActions?: string + contentIds?: string + contentType?: string + timestampFrom?: string + timestampTo?: string +} + +export interface ThriveActivityResponse extends ToolResponse { + output: { activity: Record } +} + +export interface ThriveQueryActivitiesResponse extends ToolResponse { + output: { results: Record[]; pagination: ThrivePagination | null } +} + +// ─── CPD ─────────────────────────────────────────────────────────────────── + +export interface ThriveGetCpdCategoryParams extends ThriveBaseParams { + categoryId: string +} + +export interface ThriveQueryCpdCategoriesParams extends ThriveBaseParams { + page?: number + perPage?: number + updatedSince?: string +} + +export interface ThriveGetCpdEntryParams extends ThriveBaseParams { + logEntryId: string +} + +export interface ThriveQueryCpdEntriesParams extends ThriveBaseParams { + page?: number + perPage?: number + entryDateFrom?: string + entryDateTo?: string +} + +export interface ThriveGetCpdRequirementParams extends ThriveBaseParams { + audienceRequirementId: string +} + +export interface ThriveQueryCpdRequirementsParams extends ThriveBaseParams { + page?: number + perPage?: number + updatedSince?: string +} + +export interface ThriveQueryCpdUserSummariesParams extends ThriveBaseParams { + entryDateFrom: string + entryDateTo: string + userIds?: string + page?: number + perPage?: number +} + +export interface ThriveCpdCategoryResponse extends ToolResponse { + output: { category: Record } +} + +export interface ThriveCpdEntryResponse extends ToolResponse { + output: { entry: Record } +} + +export interface ThriveCpdRequirementResponse extends ToolResponse { + output: { requirement: Record } +} + +export interface ThriveCpdPaginatedResponse extends ToolResponse { + output: { results: Record[]; pagination: ThrivePagination | null } +} + +// ─── Tags & Skills ───────────────────────────────────────────────────────── + +export interface ThriveListTagsParams extends ThriveBaseParams { + page?: number + perPage?: number + updatedSince?: string +} + +export interface ThriveGetTagParams extends ThriveBaseParams { + tagId: string +} + +export interface ThriveAddUserTagsParams extends ThriveBaseParams { + userId: string + tags: string +} + +export interface ThriveRemoveUserTagsParams extends ThriveBaseParams { + userId: string + tags: string +} + +export interface ThriveUpdateUserSkillsParams extends ThriveBaseParams { + userId: string + skills: string +} + +export interface ThriveGetSkillLevelsParams extends ThriveBaseParams {} + +export interface ThriveTagResponse extends ToolResponse { + output: { tag: Record } +} + +export interface ThriveListTagsResponse extends ToolResponse { + output: { results: Record[]; pagination: ThrivePagination | null } +} + +export interface ThriveSkillLevelsResponse extends ToolResponse { + output: { levels: Record[] } +} diff --git a/apps/sim/tools/thrive/update_assignment.ts b/apps/sim/tools/thrive/update_assignment.ts new file mode 100644 index 00000000000..181d3891aa4 --- /dev/null +++ b/apps/sim/tools/thrive/update_assignment.ts @@ -0,0 +1,111 @@ +import type { ThriveAssignmentResponse, ThriveUpdateAssignmentParams } from '@/tools/thrive/types' +import { THRIVE_ASSIGNMENT_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveArray, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const updateAssignmentTool: ToolConfig< + ThriveUpdateAssignmentParams, + ThriveAssignmentResponse +> = { + id: 'thrive_update_assignment', + name: 'Thrive Update Assignment', + description: 'Update a compliance assignment in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + assignmentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The assignment ID', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience ID', + }, + contentId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The content ID for the primary content', + }, + completionPeriod: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'The number of days required to complete the assignment', + }, + recurrence: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'The number of days until the assignment will reoccur', + }, + alternativeContentIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'JSON array of content IDs that can also complete the assignment', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/assignments/${encodeURIComponent(params.assignmentId)}`, + method: 'PATCH', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => { + const body: Record = { + audienceId: params.audienceId, + } + if (params.contentId) body.contentId = params.contentId + if (params.completionPeriod !== undefined) body.completionPeriod = params.completionPeriod + if (params.recurrence !== undefined) body.recurrence = params.recurrence + if (params.alternativeContentIds) { + body.alternativeContentIds = parseThriveArray( + params.alternativeContentIds, + 'alternativeContentIds' + ) + } + return body + }, + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to update assignment') + return { success: true, output: { assignment: data ?? null } } + }, + + outputs: { + assignment: { + type: 'object', + description: 'The updated assignment', + properties: THRIVE_ASSIGNMENT_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/update_audience.ts b/apps/sim/tools/thrive/update_audience.ts new file mode 100644 index 00000000000..c1f2a251fd0 --- /dev/null +++ b/apps/sim/tools/thrive/update_audience.ts @@ -0,0 +1,83 @@ +import type { ThriveAudienceResponse, ThriveUpdateAudienceParams } from '@/tools/thrive/types' +import { THRIVE_AUDIENCE_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { getThriveBaseUrl, getThriveHeaders, parseThriveResponse } from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const updateAudienceTool: ToolConfig = { + id: 'thrive_update_audience', + name: 'Thrive Update Audience', + description: 'Update an audience in Thrive, optionally moving it to a new parent.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + audienceId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The audience id or audience reference', + }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The name of the audience (max 100 characters)', + }, + reference: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The external reference for the audience (max 100 characters)', + }, + parentId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The id of the parent audience/structure to move the audience to', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/audiences/${encodeURIComponent(params.audienceId)}`, + method: 'PATCH', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => { + const body: Record = {} + if (params.name) body.name = params.name + if (params.reference) body.reference = params.reference + if (params.parentId) body.parentId = params.parentId + return body + }, + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to update audience') + return { success: true, output: { audience: data ?? null } } + }, + + outputs: { + audience: { + type: 'object', + description: 'The updated audience', + properties: THRIVE_AUDIENCE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/update_user.ts b/apps/sim/tools/thrive/update_user.ts new file mode 100644 index 00000000000..b6144795950 --- /dev/null +++ b/apps/sim/tools/thrive/update_user.ts @@ -0,0 +1,167 @@ +import type { ThriveUpdateUserParams, ThriveUserResponse } from '@/tools/thrive/types' +import { THRIVE_USER_LIFECYCLE_OUTPUT_PROPERTIES } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveJsonObject, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const updateUserTool: ToolConfig = { + id: 'thrive_update_user', + name: 'Thrive Update User', + description: 'Update an existing user in Thrive by ref. Only the fields provided are changed.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + ref: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The user ref to update', + }, + firstName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The given name of the individual', + }, + lastName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The family name of the individual', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The email address for the user', + }, + loginMethod: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "How the user logs in: 'email' or 'ref'", + }, + role: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "Role assigned: 'administrator', 'learneradmin', or 'learner'", + }, + jobTitle: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "Name of this individual's role in your organisation", + }, + managerRef: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "Your organisation's unique identifier for this individual's line manager", + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date this individual started with your organisation (ISO 8601)', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date this individual left your organisation (ISO 8601)', + }, + timeZone: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "The user's preferred timezone", + }, + languageCode: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: "The user's preferred language (e.g. 'en-gb')", + }, + sso: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether the account is managed by an authentication provider', + }, + domain: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Domain this individual is associated with', + }, + additionalFields: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'JSON object of custom field key-value pairs. Example: {"department":"Sales"}', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v2')}/users/ref/${encodeURIComponent(params.ref)}`, + method: 'PATCH', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => { + const body: Record = {} + if (params.firstName !== undefined) body.firstName = params.firstName + if (params.lastName !== undefined) body.lastName = params.lastName + if (params.email !== undefined) body.email = params.email + if (params.loginMethod !== undefined) body.loginMethod = params.loginMethod + if (params.role !== undefined) body.role = params.role + if (params.jobTitle !== undefined) body.jobTitle = params.jobTitle + if (params.managerRef !== undefined) body.managerRef = params.managerRef + if (params.startDate !== undefined) body.startDate = params.startDate + if (params.endDate !== undefined) body.endDate = params.endDate + if (params.timeZone !== undefined) body.timeZone = params.timeZone + if (params.languageCode !== undefined) body.languageCode = params.languageCode + if (params.sso !== undefined) body.sso = params.sso + if (params.domain !== undefined) body.domain = params.domain + if (params.additionalFields) { + body.additionalFields = parseThriveJsonObject(params.additionalFields, 'additionalFields') + } + return body + }, + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to update user') + return { success: true, output: { user: data ?? null } } + }, + + outputs: { + user: { + type: 'object', + description: 'The updated user', + properties: THRIVE_USER_LIFECYCLE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/thrive/update_user_skills.ts b/apps/sim/tools/thrive/update_user_skills.ts new file mode 100644 index 00000000000..340363aa742 --- /dev/null +++ b/apps/sim/tools/thrive/update_user_skills.ts @@ -0,0 +1,75 @@ +import type { ThriveMessageResponse, ThriveUpdateUserSkillsParams } from '@/tools/thrive/types' +import { + getThriveBaseUrl, + getThriveHeaders, + parseThriveArray, + parseThriveResponse, +} from '@/tools/thrive/utils' +import type { ToolConfig } from '@/tools/types' + +export const updateUserSkillsTool: ToolConfig = + { + id: 'thrive_update_user_skills', + name: 'Thrive Update User Skills', + description: 'Update skills and levels for a learner in Thrive.', + version: '1.0.0', + + params: { + tenantId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive Tenant ID (used as the Basic auth username)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Thrive API key (used as the Basic auth password)', + }, + host: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Region-specific API host', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The learner ID', + }, + skills: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'JSON array of skill objects (1-100). Each: {"tagName":"leadership","level":1,"targetLevel":3}. level/targetLevel optional (min -1).', + }, + }, + + request: { + url: (params) => + `${getThriveBaseUrl(params.host, 'v1')}/users/${encodeURIComponent(params.userId)}/skills`, + method: 'PATCH', + headers: (params) => getThriveHeaders(params.tenantId, params.apiKey), + body: (params) => ({ + op: 'update', + path: 'skills', + value: parseThriveArray(params.skills, 'skills'), + }), + }, + + transformResponse: async (response: Response): Promise => { + const data = await parseThriveResponse(response, 'Failed to update skills for learner') + return { + success: true, + output: { status: data?.status ?? null, message: data?.message ?? null }, + } + }, + + outputs: { + status: { type: 'number', description: 'The HTTP status code of the operation' }, + message: { type: 'string', description: 'A human-readable result message' }, + }, + } diff --git a/apps/sim/tools/thrive/utils.ts b/apps/sim/tools/thrive/utils.ts new file mode 100644 index 00000000000..5893e5b67c3 --- /dev/null +++ b/apps/sim/tools/thrive/utils.ts @@ -0,0 +1,106 @@ +/** + * Default Thrive API host (Production, all regions except MEA). + * The Thrive REST API is reached at `https://{host}/rest/{version}`. + */ +export const THRIVE_DEFAULT_HOST = 'public.api.learn.link' + +/** + * Builds the base REST URL for a Thrive API request. + * + * @param host - The region-specific API host (e.g. `public.api.learn.link`). + * @param version - The API version: `v1` for most resources, `v2` for user lifecycle. + */ +export function getThriveBaseUrl(host: string | undefined, version: 'v1' | 'v2'): string { + const resolvedHost = host?.trim() || THRIVE_DEFAULT_HOST + return `https://${resolvedHost}/rest/${version}` +} + +/** + * Builds the HTTP Basic authentication headers for a Thrive API request. + * The Tenant ID is used as the username and the API key as the password. + */ +export function getThriveHeaders(tenantId: string, apiKey: string): Record { + return { + Authorization: `Basic ${btoa(`${tenantId}:${apiKey}`)}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + } +} + +/** + * Appends a query parameter to a URL when the value is defined and non-empty. + * Numbers are coerced to strings; empty strings and `undefined`/`null` are skipped. + */ +export function appendThriveQuery( + url: URL, + key: string, + value: string | number | boolean | undefined | null +): void { + if (value === undefined || value === null) return + const stringValue = typeof value === 'string' ? value.trim() : String(value) + if (stringValue === '') return + url.searchParams.set(key, stringValue) +} + +/** + * Parses a value that may be a JSON array string (from LLM/user input) or an + * already-parsed array into a typed array. An empty/whitespace string yields an + * empty array; malformed JSON throws so the caller sees a clear error rather + * than silently sending an empty collection. + */ +export function parseThriveArray(value: unknown, fieldName = 'array'): T[] { + if (Array.isArray(value)) return value as T[] + if (typeof value === 'string') { + const trimmed = value.trim() + if (trimmed === '') return [] + let parsed: unknown + try { + parsed = JSON.parse(trimmed) + } catch { + throw new Error(`Invalid JSON provided for ${fieldName}: expected a JSON array`) + } + return Array.isArray(parsed) ? (parsed as T[]) : [parsed as T] + } + return [] +} + +/** + * Parses a JSON object string (from LLM/user input), throwing a descriptive + * error on malformed JSON so it is never silently dropped. + */ +export function parseThriveJsonObject(value: string, fieldName: string): Record { + try { + return JSON.parse(value) + } catch { + throw new Error(`Invalid JSON provided for ${fieldName}: expected a JSON object`) + } +} + +/** + * Reads a Thrive API response, throwing a descriptive error on non-2xx status. + * Tolerates empty bodies (returned by some delete endpoints) and non-JSON bodies. + */ +export async function parseThriveResponse( + response: Response, + fallbackError: string +): Promise { + const text = await response.text() + let data: any = {} + if (text) { + try { + data = JSON.parse(text) + } catch { + data = { raw: text } + } + } + + if (!response.ok) { + const message = + (data && typeof data.message === 'string' && data.message) || + (data && typeof data.error === 'string' && data.error) || + fallbackError + throw new Error(message) + } + + return data as T +}