diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 44b5e1de985..6614c9f6036 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -3483,6 +3483,25 @@ export function MySQLIcon(props: SVGProps) { ) } +export function FireworksIcon(props: SVGProps) { + return ( + + + + ) +} + export function OpenRouterIcon(props: SVGProps) { return ( ) => ( ) -export function HIPAABadgeIcon(props: SVGProps) { - const id = useId() - const clipId = `hipaa_clip_${id}` - - return ( - - - - - - - - - - - - - ) -} - export function GoogleFormsIcon(props: SVGProps) { return ( diff --git a/apps/docs/content/docs/de/blocks/guardrails.mdx b/apps/docs/content/docs/de/blocks/guardrails.mdx index 78c1f39d8e0..9d322fe219a 100644 --- a/apps/docs/content/docs/de/blocks/guardrails.mdx +++ b/apps/docs/content/docs/de/blocks/guardrails.mdx @@ -131,7 +131,7 @@ Erkennt personenbezogene Daten mithilfe von Microsoft Presidio. Unterstützt üb **Anwendungsfälle:** - Blockieren von Inhalten mit sensiblen persönlichen Informationen - Maskieren von personenbezogenen Daten vor der Protokollierung oder Speicherung -- Einhaltung der DSGVO, HIPAA und anderer Datenschutzbestimmungen +- Einhaltung der DSGVO und anderer Datenschutzbestimmungen - Bereinigung von Benutzereingaben vor der Verarbeitung ## Konfiguration diff --git a/apps/docs/content/docs/en/blocks/guardrails.mdx b/apps/docs/content/docs/en/blocks/guardrails.mdx index ad91892dbca..f363a4c5f07 100644 --- a/apps/docs/content/docs/en/blocks/guardrails.mdx +++ b/apps/docs/content/docs/en/blocks/guardrails.mdx @@ -132,7 +132,7 @@ Detects personally identifiable information using Microsoft Presidio. Supports o **Use Cases:** - Block content containing sensitive personal information - Mask PII before logging or storing data -- Compliance with GDPR, HIPAA, and other privacy regulations +- Compliance with GDPR and other privacy regulations - Sanitize user inputs before processing ## Configuration diff --git a/apps/docs/content/docs/en/tools/rippling.mdx b/apps/docs/content/docs/en/tools/rippling.mdx index b029318cf27..31945d94aca 100644 --- a/apps/docs/content/docs/en/tools/rippling.mdx +++ b/apps/docs/content/docs/en/tools/rippling.mdx @@ -1,6 +1,6 @@ --- title: Rippling -description: Manage employees, leave, departments, and company data in Rippling +description: Manage workers, departments, custom objects, and company data in Rippling --- import { BlockInfoCard } from "@/components/ui/block-info-card" @@ -11,139 +11,303 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" /> {/* MANUAL-CONTENT-START:intro */} -[Rippling](https://www.rippling.com/) is a unified workforce management platform that brings together HR, IT, and Finance into a single system. Rippling lets companies manage payroll, benefits, devices, apps, and more — all from one place — while automating the tedious manual work that typically bogs down HR teams. Its robust API provides programmatic access to employee data, organizational structure, leave management, and onboarding workflows. +[Rippling](https://www.rippling.com/) is a unified workforce management platform that brings together HR, IT, and Finance into a single system. Rippling lets companies manage payroll, benefits, devices, apps, and more — all from one place — while automating the tedious manual work that typically bogs down HR teams. Its Platform REST API provides programmatic access to workers, users, departments, teams, custom objects, business partners, supergroups, and more. **Why Rippling?** -- **Unified Employee System of Record:** A single source of truth for employee profiles, departments, teams, levels, and work locations — no more syncing data across disconnected tools. -- **Leave Management:** Full visibility into leave requests, balances, and types with the ability to approve or decline requests programmatically. -- **Company Insights:** Access company activity events, custom fields, and organizational hierarchy to power reporting and compliance workflows. -- **Onboarding Automation:** Push candidates directly into Rippling's onboarding flow, eliminating manual data entry when bringing on new hires. -- **Group Management:** Create and update groups for third-party app provisioning via SCIM-compatible endpoints. +- **Comprehensive Workforce Data:** Access workers, users, companies, entitlements, departments, teams, titles, employment types, job functions, work locations, and custom fields — your complete organizational graph in one API. +- **Custom Objects Platform:** Create, manage, and query custom objects with typed fields, records, and bulk operations — extend Rippling's data model to fit your business. +- **Business Partner Management:** Track vendors, contractors, and external partners with full CRUD operations and grouping support via business partner groups. +- **Supergroup Access Control:** Manage dynamic permission groups with granular inclusion and exclusion member lists for fine-grained access control. +- **Platform Extensibility:** Build custom apps, pages, settings, and object categories that live natively inside Rippling's UI. +- **Automated Reporting:** Trigger report runs programmatically and poll for results to build fully automated reporting pipelines. **Using Rippling in Sim** -Sim's Rippling integration connects your agentic workflows directly to your Rippling account using an API key. With 19 operations spanning employees, departments, teams, leave, groups, and candidates, you can build powerful HR automations without writing backend code. +Sim's Rippling integration connects your agentic workflows directly to your Rippling account using an API key. With 86 operations spanning workers, users, departments, teams, titles, work locations, business partners, supergroups, custom objects, custom apps, custom pages, custom settings, object categories, reports, and draft hires, you can build powerful HR and platform automations without writing backend code. **Key benefits of using Rippling in Sim:** -- **Employee directory automation:** List, search, and retrieve employee details — including terminated employees — to power onboarding checklists, offboarding workflows, and org chart updates. -- **Leave workflow automation:** Monitor leave requests, check balances, and programmatically approve or decline requests based on custom business rules. -- **Organizational intelligence:** Query departments, teams, levels, work locations, and custom fields to build dynamic org reports or trigger actions based on structural changes. -- **Candidate onboarding:** Push candidates from your ATS or recruiting pipeline directly into Rippling's onboarding flow, complete with job title, department, and start date. -- **Activity monitoring:** Track company activity events to build audit trails, compliance dashboards, or alert workflows when key changes occur. - -Whether you're automating new hire onboarding, building leave approval workflows, or syncing employee data across your tool stack, Rippling in Sim gives you direct, secure access to your HR platform — no middleware required. Simply configure your API key, select the operation you need, and let Sim handle the rest. +- **Worker and user management:** List, search, and retrieve worker and user details to power onboarding checklists, offboarding workflows, and org chart updates. +- **Organizational intelligence:** Query departments, teams, titles, employment types, job functions, work locations, and custom fields to build dynamic org reports or trigger actions based on structural changes. +- **Custom object automation:** Create custom objects, define fields, and manage records — including bulk create, update, and delete operations — to extend Rippling's data model for your workflows. +- **Business partner workflows:** Manage vendors and external partners with full lifecycle operations including grouping and categorization. +- **Platform app development:** Create and manage custom apps, pages, settings, and object categories that extend Rippling's native functionality. +- **Report automation:** Trigger report runs and poll for results to build automated reporting pipelines without manual intervention. +- **Draft hire onboarding:** Push one or more draft hires into Rippling's onboarding flow in a single call, complete with all required employee data. + +Whether you're automating new hire onboarding, managing custom object data, building organizational reports, or extending Rippling's platform with custom apps, Rippling in Sim gives you direct, secure access to the full Rippling Platform REST API — no middleware required. Simply configure your API key, select the operation you need, and let Sim handle the rest. {/* MANUAL-CONTENT-END */} ## Usage Instructions -Integrate Rippling into your workflow. Manage employees, departments, teams, leave requests, work locations, groups, candidates, and company information. +Integrate Rippling Platform into your workflow. Manage workers, users, departments, teams, titles, work locations, business partners, supergroups, custom objects, custom apps, custom pages, custom settings, object categories, reports, and draft hires. ## Tools -### `rippling_list_employees` +### `rippling_list_workers` -List all employees in Rippling with optional pagination +List all workers with optional filtering and pagination #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `limit` | number | No | Maximum number of employees to return \(default 100, max 100\) | -| `offset` | number | No | Offset for pagination | +| `filter` | string | No | Filter expression | +| `expand` | string | No | Comma-separated fields to expand | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `employees` | array | List of employees | -| ↳ `id` | string | Employee ID | -| ↳ `firstName` | string | First name | -| ↳ `lastName` | string | Last name | -| ↳ `workEmail` | string | Work email address | -| ↳ `personalEmail` | string | Personal email address | -| ↳ `roleState` | string | Employment status | -| ↳ `department` | string | Department name or ID | +| `workers` | array | List of workers | +| ↳ `id` | string | Worker ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `user_id` | string | Associated user ID | +| ↳ `is_manager` | boolean | Whether the worker is a manager | +| ↳ `manager_id` | string | Manager worker ID | +| ↳ `legal_entity_id` | string | Legal entity ID | +| ↳ `country` | string | Worker country code | +| ↳ `start_date` | string | Employment start date | +| ↳ `end_date` | string | Employment end date | +| ↳ `number` | number | Worker number | +| ↳ `work_email` | string | Work email address | +| ↳ `personal_email` | string | Personal email address | +| ↳ `status` | string | Worker status \(INIT, HIRED, ACCEPTED, ACTIVE, TERMINATED\) | +| ↳ `employment_type_id` | string | Employment type ID | +| ↳ `department_id` | string | Department ID | +| ↳ `teams_id` | json | Array of team IDs | | ↳ `title` | string | Job title | -| ↳ `startDate` | string | Employment start date | -| ↳ `endDate` | string | Employment end date | -| ↳ `manager` | string | Manager ID or name | -| ↳ `phone` | string | Phone number | -| `totalCount` | number | Number of employees returned on this page | +| ↳ `level_id` | string | Level ID | +| ↳ `compensation_id` | string | Compensation ID | +| ↳ `overtime_exemption` | string | Overtime exemption status \(EXEMPT, NON_EXEMPT\) | +| ↳ `title_effective_date` | string | Title effective date | +| ↳ `business_partners_id` | json | Array of business partner IDs | +| ↳ `location` | json | Worker location \(type, work_location_id\) | +| ↳ `gender` | string | Gender | +| ↳ `date_of_birth` | string | Date of birth | +| ↳ `race` | string | Race | +| ↳ `ethnicity` | string | Ethnicity | +| ↳ `citizenship` | string | Citizenship country code | +| ↳ `termination_details` | json | Termination details | +| ↳ `custom_fields` | json | Custom fields \(expandable\) | +| ↳ `country_fields` | json | Country-specific fields | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_worker` + +Get a specific worker by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `expand` | string | No | Comma-separated fields to expand | -### `rippling_get_employee` +#### Output -Get details for a specific employee by ID +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Worker ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `user_id` | string | User ID | +| `is_manager` | boolean | Is manager | +| `manager_id` | string | Manager ID | +| `legal_entity_id` | string | Legal entity ID | +| `country` | string | Country | +| `start_date` | string | Start date | +| `end_date` | string | End date | +| `number` | number | Worker number | +| `work_email` | string | Work email | +| `personal_email` | string | Personal email | +| `status` | string | Status | +| `employment_type_id` | string | Employment type ID | +| `department_id` | string | Department ID | +| `teams_id` | json | Team IDs | +| `title` | string | Job title | +| `level_id` | string | Level ID | +| `compensation_id` | string | Compensation ID | +| `overtime_exemption` | string | Overtime exemption | +| `title_effective_date` | string | Title effective date | +| `business_partners_id` | json | Business partner IDs | +| `location` | json | Worker location | +| `gender` | string | Gender | +| `date_of_birth` | string | Date of birth | +| `race` | string | Race | +| `ethnicity` | string | Ethnicity | +| `citizenship` | string | Citizenship | +| `termination_details` | json | Termination details | +| `custom_fields` | json | Custom fields | +| `country_fields` | json | Country-specific fields | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_list_users` + +List all users with optional pagination #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `employeeId` | string | Yes | The ID of the employee to retrieve | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `id` | string | Employee ID | -| `firstName` | string | First name | -| `lastName` | string | Last name | -| `workEmail` | string | Work email address | -| `personalEmail` | string | Personal email address | -| `roleState` | string | Employment status | -| `department` | string | Department name or ID | -| `title` | string | Job title | -| `startDate` | string | Employment start date | -| `endDate` | string | Employment end date | -| `manager` | string | Manager ID or name | -| `phone` | string | Phone number | +| `users` | array | List of users | +| ↳ `id` | string | User ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `active` | boolean | Whether the user is active | +| ↳ `username` | string | Unique username | +| ↳ `display_name` | string | Display name | +| ↳ `preferred_language` | string | Preferred language | +| ↳ `locale` | string | Locale | +| ↳ `timezone` | string | Timezone \(IANA format\) | +| ↳ `number` | string | Permanent profile number | +| ↳ `name` | json | User name object \(given_name, family_name, etc.\) | +| ↳ `emails` | json | Array of email objects | +| ↳ `phone_numbers` | json | Array of phone number objects | +| ↳ `addresses` | json | Array of address objects | +| ↳ `photos` | json | Array of photo objects | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_user` + +Get a specific user by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | -### `rippling_list_employees_with_terminated` +#### Output -List all employees in Rippling including terminated employees with optional pagination +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | User ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `active` | boolean | Is active | +| `username` | string | Username | +| `display_name` | string | Display name | +| `preferred_language` | string | Preferred language | +| `locale` | string | Locale | +| `timezone` | string | Timezone | +| `number` | string | Profile number | +| `name` | json | User name object | +| `emails` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `addresses` | json | Addresses | +| `photos` | json | Photos | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_list_companies` + +List all companies #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `limit` | number | No | Maximum number of employees to return \(default 100, max 100\) | -| `offset` | number | No | Offset for pagination | +| `expand` | string | No | Comma-separated fields to expand | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `employees` | array | List of employees including terminated | -| ↳ `id` | string | Employee ID | -| ↳ `firstName` | string | First name | -| ↳ `lastName` | string | Last name | -| ↳ `workEmail` | string | Work email address | -| ↳ `personalEmail` | string | Personal email address | -| ↳ `roleState` | string | Employment status | -| ↳ `department` | string | Department name or ID | -| ↳ `title` | string | Job title | -| ↳ `startDate` | string | Employment start date | -| ↳ `endDate` | string | Employment end date | -| ↳ `manager` | string | Manager ID or name | +| `companies` | array | List of companies | +| ↳ `id` | string | Company ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Company name | +| ↳ `legal_name` | string | Legal name | +| ↳ `doing_business_as_name` | string | DBA name | | ↳ `phone` | string | Phone number | -| `totalCount` | number | Number of employees returned on this page | +| ↳ `primary_email` | string | Primary email | +| ↳ `parent_legal_entity_id` | string | Parent legal entity ID | +| ↳ `legal_entities_id` | json | Array of legal entity IDs | +| ↳ `physical_address` | json | Physical address of the holding entity | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_current_user` + +Get SSO information for the current user + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `expand` | string | No | Comma-separated fields to expand | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | User ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `work_email` | string | Work email | +| `company_id` | string | Company ID | +| `company` | json | Expanded company object | + +### `rippling_list_entitlements` + +List all entitlements + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `entitlements` | array | List of entitlements | +| ↳ `id` | string | Entitlement ID | +| ↳ `description` | string | Entitlement description | +| ↳ `display_name` | string | Display name | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | ### `rippling_list_departments` -List all departments in the Rippling organization +List all departments #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `limit` | number | No | Maximum number of departments to return | -| `offset` | number | No | Offset for pagination | +| `expand` | string | No | Comma-separated fields to expand | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | #### Output @@ -151,21 +315,112 @@ List all departments in the Rippling organization | --------- | ---- | ----------- | | `departments` | array | List of departments | | ↳ `id` | string | Department ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | | ↳ `name` | string | Department name | -| ↳ `parent` | string | Parent department ID | -| `totalCount` | number | Number of departments returned on this page | +| ↳ `parent_id` | string | Parent department ID | +| ↳ `reference_code` | string | Reference code | +| ↳ `department_hierarchy_id` | json | Array of department IDs in hierarchy | +| ↳ `parent` | json | Expanded parent department | +| ↳ `department_hierarchy` | json | Expanded department hierarchy | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_department` + +Get a specific department by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `expand` | string | No | Comma-separated fields to expand | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Department ID | +| `created_at` | string | Record creation date | +| `updated_at` | string | Record update date | +| `name` | string | Department name | +| `parent_id` | string | Parent department ID | +| `reference_code` | string | Reference code | +| `department_hierarchy_id` | json | Array of department IDs in hierarchy | +| `parent` | json | Expanded parent department | +| `department_hierarchy` | json | Expanded department hierarchy | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_create_department` + +Create a new department + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `name` | string | Yes | Department name | +| `parentId` | string | No | Parent department ID | +| `referenceCode` | string | No | Reference code | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Department ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `parent_id` | string | Parent department ID | +| `reference_code` | string | Reference code | +| `department_hierarchy_id` | json | Department hierarchy IDs | +| `parent` | json | Expanded parent department | +| `department_hierarchy` | json | Expanded department hierarchy | + +### `rippling_update_department` + +Update an existing department + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `name` | string | No | Department name | +| `parentId` | string | No | Parent department ID | +| `referenceCode` | string | No | Reference code | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Department ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `parent_id` | string | Parent department ID | +| `reference_code` | string | Reference code | +| `department_hierarchy_id` | json | Department hierarchy IDs | +| `parent` | json | Expanded parent department | +| `department_hierarchy` | json | Expanded department hierarchy | ### `rippling_list_teams` -List all teams in Rippling +List all teams #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `limit` | number | No | Maximum number of teams to return | -| `offset` | number | No | Offset for pagination | +| `expand` | string | No | Comma-separated fields to expand | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | #### Output @@ -173,334 +428,1885 @@ List all teams in Rippling | --------- | ---- | ----------- | | `teams` | array | List of teams | | ↳ `id` | string | Team ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | | ↳ `name` | string | Team name | -| ↳ `parent` | string | Parent team ID | -| `totalCount` | number | Number of teams returned on this page | +| ↳ `parent_id` | string | Parent team ID | +| ↳ `parent` | json | Expanded parent team | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | -### `rippling_list_levels` +### `rippling_get_team` -List all position levels in Rippling +Get a specific team by ID #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `limit` | number | No | Maximum number of levels to return | -| `offset` | number | No | Offset for pagination | +| `id` | string | Yes | No description | +| `expand` | string | No | Comma-separated fields to expand | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `levels` | array | List of position levels | -| ↳ `id` | string | Level ID | -| ↳ `name` | string | Level name | -| ↳ `parent` | string | Parent level ID | -| `totalCount` | number | Number of levels returned on this page | +| `id` | string | Team ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `parent_id` | string | Parent team ID | +| `parent` | json | Expanded parent team | +| `__meta` | json | Metadata including redacted_fields | -### `rippling_list_work_locations` +### `rippling_list_employment_types` -List all work locations in Rippling +List all employment types #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `limit` | number | No | Maximum number of work locations to return | -| `offset` | number | No | Offset for pagination | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `workLocations` | array | List of work locations | -| ↳ `id` | string | Work location ID | -| ↳ `nickname` | string | Location nickname | -| ↳ `street` | string | Street address | -| ↳ `city` | string | City | -| ↳ `state` | string | State or province | -| ↳ `zip` | string | ZIP or postal code | -| ↳ `country` | string | Country | -| `totalCount` | number | Number of work locations returned on this page | +| `employmentTypes` | array | List of employmentTypes | +| ↳ `id` | string | Employment type ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `label` | string | Employment type label | +| ↳ `name` | string | Employment type name | +| ↳ `type` | string | Type \(CONTRACTOR, EMPLOYEE\) | +| ↳ `compensation_time_period` | string | Compensation period \(HOURLY, SALARIED\) | +| ↳ `amount_worked` | string | Amount worked \(PART-TIME, FULL-TIME, TEMPORARY\) | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_employment_type` + +Get a specific employment type by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Employment type ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `label` | string | Label | +| `name` | string | Name | +| `type` | string | Type \(CONTRACTOR, EMPLOYEE\) | +| `compensation_time_period` | string | Compensation period \(HOURLY, SALARIED\) | +| `amount_worked` | string | Amount worked \(PART-TIME, FULL-TIME, TEMPORARY\) | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_list_titles` + +List all titles + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `titles` | array | List of titles | +| ↳ `id` | string | Title ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Title name | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_title` + +Get a specific title by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Title ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Title name | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_create_title` + +Create a new title + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `name` | string | Yes | No description | + +#### Output -### `rippling_get_company` +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Title ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Title name | -Get details for the current company in Rippling +### `rippling_update_title` + +Update an existing title #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `name` | string | No | No description | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `id` | string | Company ID | -| `name` | string | Company name | -| `address` | json | Company address with street, city, state, zip, country | -| `email` | string | Company email address | -| `phone` | string | Company phone number | -| `workLocations` | array | List of work location IDs | +| `id` | string | Title ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Title name | -### `rippling_get_company_activity` +### `rippling_delete_title` -Get activity events for the current company in Rippling +Delete a title #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `startDate` | string | No | Start date filter in ISO format \(e.g. 2024-01-01\) | -| `endDate` | string | No | End date filter in ISO format \(e.g. 2024-12-31\) | -| `limit` | number | No | Maximum number of activity events to return | -| `next` | string | No | Cursor for fetching the next page of results | +| `id` | string | Yes | ID of the resource to delete | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `events` | array | List of company activity events | -| ↳ `id` | string | Event ID | -| ↳ `type` | string | Event type | -| ↳ `description` | string | Event description | -| ↳ `createdAt` | string | Event creation timestamp | -| ↳ `actor` | json | Actor who triggered the event \(id, name\) | -| `totalCount` | number | Number of activity events returned on this page | -| `nextCursor` | string | Cursor for fetching the next page of results | +| `deleted` | boolean | Whether the resource was deleted | ### `rippling_list_custom_fields` -List all custom fields defined in Rippling +List all custom fields #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `limit` | number | No | Maximum number of custom fields to return | -| `offset` | number | No | Offset for pagination | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `customFields` | array | List of custom fields | +| `customFields` | array | List of customFields | | ↳ `id` | string | Custom field ID | -| ↳ `type` | string | Field type | -| ↳ `title` | string | Field title | -| ↳ `mandatory` | boolean | Whether the field is mandatory | -| `totalCount` | number | Number of custom fields returned on this page | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Field name | +| ↳ `description` | string | Field description | +| ↳ `required` | boolean | Whether the field is required | +| ↳ `type` | string | Field type \(TEXT, DATE, NUMBER, CURRENCY, etc.\) | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | -### `rippling_get_current_user` +### `rippling_list_job_functions` -Get the current authenticated user details +List all job functions #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `id` | string | User ID | -| `workEmail` | string | Work email address | -| `company` | string | Company ID | +| `jobFunctions` | array | List of jobFunctions | +| ↳ `id` | string | Job function ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Job function name | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_job_function` + +Get a specific job function by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Job function ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_list_work_locations` + +List all work locations + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `workLocations` | array | List of workLocations | +| ↳ `id` | string | Work location ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Location name | +| ↳ `address` | json | Address object | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_work_location` + +Get a specific work location by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Location ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `address` | json | Address object | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_create_work_location` + +Create a new work location + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `name` | string | Yes | Location name | +| `streetAddress` | string | Yes | Street address | +| `locality` | string | No | No description | +| `region` | string | No | State/region | +| `postalCode` | string | No | Postal code | +| `country` | string | No | Country code | +| `addressType` | string | No | Address type \(HOME, WORK, OTHER\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Location ID | +| `created_at` | string | Created timestamp | +| `updated_at` | string | Updated timestamp | +| `name` | string | Name | +| `address` | json | Address | + +### `rippling_update_work_location` + +Update a work location + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `name` | string | No | Location name | +| `streetAddress` | string | No | Street address | +| `locality` | string | No | No description | +| `region` | string | No | State/region | +| `postalCode` | string | No | Postal code | +| `country` | string | No | Country code | +| `addressType` | string | No | Address type \(HOME, WORK, OTHER\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Location ID | +| `created_at` | string | Created timestamp | +| `updated_at` | string | Updated timestamp | +| `name` | string | Name | +| `address` | json | Address | + +### `rippling_delete_work_location` + +Delete a work location + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | ID of the resource to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the resource was deleted | + +### `rippling_list_business_partners` + +List all business partners + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `filter` | string | No | Filter expression | +| `expand` | string | No | Comma-separated fields to expand | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `businessPartners` | array | List of businessPartners | +| ↳ `id` | string | Business partner ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `business_partner_group_id` | string | Business partner group ID | +| ↳ `worker_id` | string | Worker ID | +| ↳ `client_group_id` | string | Client group ID | +| ↳ `client_group_member_count` | number | Client group member count | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_business_partner` + +Get a specific business partner by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `expand` | string | No | Comma-separated fields to expand | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `business_partner_group_id` | string | Group ID | +| `worker_id` | string | Worker ID | +| `client_group_id` | string | Client group ID | +| `client_group_member_count` | number | Client group member count | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_create_business_partner` + +Create a new business partner + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `businessPartnerGroupId` | string | Yes | Business partner group ID | +| `workerId` | string | Yes | Worker ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `business_partner_group_id` | string | Group ID | +| `worker_id` | string | Worker ID | +| `client_group_id` | string | Client group ID | +| `client_group_member_count` | number | Client group member count | + +### `rippling_delete_business_partner` + +Delete a business partner + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | ID of the resource to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the resource was deleted | + +### `rippling_list_business_partner_groups` + +List all business partner groups + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `expand` | string | No | Comma-separated fields to expand | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `businessPartnerGroups` | array | List of businessPartnerGroups | +| ↳ `id` | string | Business partner group ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Group name | +| ↳ `domain` | string | Domain \(HR, IT, FINANCE, RECRUITING, OTHER\) | +| ↳ `default_business_partner_id` | string | Default business partner ID | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_business_partner_group` + +Get a specific business partner group by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `expand` | string | No | Comma-separated fields to expand | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `domain` | string | Domain | +| `default_business_partner_id` | string | Default partner ID | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_create_business_partner_group` + +Create a new business partner group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `name` | string | Yes | Group name | +| `domain` | string | No | Domain \(HR, IT, FINANCE, RECRUITING, OTHER\) | +| `defaultBusinessPartnerId` | string | No | Default business partner ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `domain` | string | Domain | +| `default_business_partner_id` | string | Default partner ID | + +### `rippling_delete_business_partner_group` + +Delete a business partner group + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | ID of the resource to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the resource was deleted | + +### `rippling_list_supergroups` + +List all supergroups + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `filter` | string | No | Filter expression \(filterable fields: app_owner_id, group_type\) | +| `orderBy` | string | No | Sort field. Prefix with - for descending | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `supergroups` | array | List of supergroups | +| ↳ `id` | string | Supergroup ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `display_name` | string | Display name | +| ↳ `description` | string | Description | +| ↳ `app_owner_id` | string | App owner ID | +| ↳ `group_type` | string | Group type | +| ↳ `name` | string | Name | +| ↳ `sub_group_type` | string | Sub group type | +| ↳ `read_only` | boolean | Whether the group is read only | +| ↳ `parent` | string | Parent group ID | +| ↳ `mutually_exclusive_key` | string | Mutually exclusive key | +| ↳ `cumulatively_exhaustive_default` | boolean | Whether the group is the cumulatively exhaustive default | +| ↳ `include_terminated` | boolean | Whether the group includes terminated roles | +| ↳ `allow_non_employees` | boolean | Whether the group allows non-employees | +| ↳ `can_override_role_states` | boolean | Whether the group can override role states | +| ↳ `priority` | number | Group priority | +| ↳ `is_invisible` | boolean | Whether the group is invisible | +| ↳ `ignore_prov_group_matching` | boolean | Whether to ignore provisioning group matching | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_supergroup` + +Get a specific supergroup by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Supergroup ID | +| `created_at` | string | Record creation date | +| `updated_at` | string | Record update date | +| `display_name` | string | Display name | +| `description` | string | Description | +| `app_owner_id` | string | App owner ID | +| `group_type` | string | Group type | +| `name` | string | Name | +| `sub_group_type` | string | Sub group type | +| `read_only` | boolean | Whether the group is read only | +| `parent` | string | Parent group ID | +| `mutually_exclusive_key` | string | Mutually exclusive key | +| `cumulatively_exhaustive_default` | boolean | Whether the group is the cumulatively exhaustive default | +| `include_terminated` | boolean | Whether the group includes terminated roles | +| `allow_non_employees` | boolean | Whether the group allows non-employees | +| `can_override_role_states` | boolean | Whether the group can override role states | +| `priority` | number | Group priority | +| `is_invisible` | boolean | Whether the group is invisible | +| `ignore_prov_group_matching` | boolean | Whether to ignore provisioning group matching | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_list_supergroup_members` + +List members of a supergroup + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `groupId` | string | Yes | Supergroup ID | +| `expand` | string | No | Fields to expand \(e.g., worker\) | +| `orderBy` | string | No | Sort field | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `members` | array | List of members | +| ↳ `id` | string | Member ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `full_name` | string | Full name | +| ↳ `work_email` | string | Work email | +| ↳ `worker_id` | string | Worker ID | +| ↳ `worker` | json | Expanded worker object | +| `totalCount` | number | Number of members returned | +| `nextLink` | string | Next page link | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_list_supergroup_inclusion_members` + +List inclusion members of a supergroup + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `groupId` | string | Yes | Supergroup ID | +| `expand` | string | No | Fields to expand \(e.g., worker\) | +| `orderBy` | string | No | Sort field | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `members` | array | List of members | +| ↳ `id` | string | Member ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `full_name` | string | Full name | +| ↳ `work_email` | string | Work email | +| ↳ `worker_id` | string | Worker ID | +| ↳ `worker` | json | Expanded worker object | +| `totalCount` | number | Number of members returned | +| `nextLink` | string | Next page link | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_list_supergroup_exclusion_members` + +List exclusion members of a supergroup + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `groupId` | string | Yes | Supergroup ID | +| `expand` | string | No | Fields to expand \(e.g., worker\) | +| `orderBy` | string | No | Sort field | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `members` | array | List of members | +| ↳ `id` | string | Member ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `full_name` | string | Full name | +| ↳ `work_email` | string | Work email | +| ↳ `worker_id` | string | Worker ID | +| ↳ `worker` | json | Expanded worker object | +| `totalCount` | number | Number of members returned | +| `nextLink` | string | Next page link | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_update_supergroup_inclusion_members` + +Update inclusion members of a supergroup + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `groupId` | string | Yes | Supergroup ID | +| `operations` | json | Yes | Operations array \[\{op: "add"\|"remove", value: \[\{id: "member_id"\}\]\}\] | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ok` | boolean | Whether the operation succeeded | + +### `rippling_update_supergroup_exclusion_members` + +Update exclusion members of a supergroup + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `groupId` | string | Yes | Supergroup ID | +| `operations` | json | Yes | Operations array \[\{op: "add"\|"remove", value: \[\{id: "member_id"\}\]\}\] | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ok` | boolean | Whether the operation succeeded | + +### `rippling_list_custom_objects` + +List all custom objects + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `customObjects` | array | List of customObjects | +| ↳ `id` | string | Custom object ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Object name | +| ↳ `description` | string | Description | +| ↳ `api_name` | string | API name | +| ↳ `plural_label` | string | Plural label | +| ↳ `category_id` | string | Category ID | +| ↳ `native_category_id` | string | Native category ID | +| ↳ `managed_package_install_id` | string | Package install ID | +| ↳ `owner_id` | string | Owner ID | +| ↳ `enable_history` | boolean | Whether history is enabled | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | + +### `rippling_get_custom_object` + +Get a custom object by API name + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | custom object api name | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `description` | string | Description | +| `api_name` | string | API name | +| `plural_label` | string | Plural label | +| `category_id` | string | Category ID | +| `enable_history` | boolean | History enabled | +| `native_category_id` | string | Native category ID | +| `managed_package_install_id` | string | Package install ID | +| `owner_id` | string | Owner ID | + +### `rippling_create_custom_object` + +Create a new custom object + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `name` | string | Yes | No description | +| `description` | string | No | Description | +| `category` | string | No | Category | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `description` | string | Description | +| `api_name` | string | API name | +| `plural_label` | string | Plural label | +| `category_id` | string | Category ID | +| `enable_history` | boolean | History enabled | +| `native_category_id` | string | Native category ID | +| `managed_package_install_id` | string | Package install ID | +| `owner_id` | string | Owner ID | + +### `rippling_update_custom_object` + +Update a custom object + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `name` | string | No | No description | +| `description` | string | No | Description | +| `category` | string | No | Category | +| `pluralLabel` | string | No | Plural label | +| `ownerRole` | string | No | Owner role | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `description` | string | Description | +| `api_name` | string | API name | +| `plural_label` | string | Plural label | +| `category_id` | string | Category ID | +| `enable_history` | boolean | History enabled | +| `native_category_id` | string | Native category ID | +| `managed_package_install_id` | string | Package install ID | +| `owner_id` | string | Owner ID | + +### `rippling_delete_custom_object` + +Delete a custom object + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | ID of the resource to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the resource was deleted | + +### `rippling_list_custom_object_fields` + +List all fields for a custom object + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `fields` | array | List of fields | +| ↳ `id` | string | Field ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Field name | +| ↳ `custom_object` | string | Parent custom object | +| ↳ `description` | string | Description | +| ↳ `api_name` | string | API name | +| ↳ `data_type` | json | Data type configuration | +| ↳ `is_unique` | boolean | Whether the field is unique | +| ↳ `is_immutable` | boolean | Whether the field is immutable | +| ↳ `is_standard` | boolean | Whether the field is standard | +| ↳ `enable_history` | boolean | Whether history is enabled | +| ↳ `managed_package_install_id` | string | Package install ID | +| `totalCount` | number | Number of fields returned | +| `nextLink` | string | Next page link | + +### `rippling_get_custom_object_field` + +Get a specific field of a custom object + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `fieldApiName` | string | Yes | Field API name | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Field ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `custom_object` | string | Custom object | +| `description` | string | Description | +| `api_name` | string | API name | +| `data_type` | json | Data type configuration | +| `is_unique` | boolean | Is unique | +| `is_immutable` | boolean | Is immutable | +| `is_standard` | boolean | Is standard | +| `enable_history` | boolean | History enabled | +| `managed_package_install_id` | string | Package install ID | + +### `rippling_create_custom_object_field` + +Create a field on a custom object + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `name` | string | Yes | Field name | +| `description` | string | No | Description | +| `dataType` | json | Yes | Data type configuration | +| `required` | boolean | No | Whether the field is required | +| `rqlDefinition` | json | No | RQL definition object | +| `isUnique` | boolean | No | Whether field is unique | +| `formulaAttrMetas` | json | No | Formula attribute metadata | +| `section` | json | No | Section configuration | +| `enableHistory` | boolean | No | Enable history tracking | +| `derivedFieldFormula` | string | No | Derived field formula expression | +| `derivedAggregatedField` | json | No | Derived aggregated field configuration | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Field ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `custom_object` | string | Custom object | +| `description` | string | Description | +| `api_name` | string | API name | +| `data_type` | json | Data type configuration | +| `is_unique` | boolean | Is unique | +| `is_immutable` | boolean | Is immutable | +| `is_standard` | boolean | Is standard | +| `enable_history` | boolean | History enabled | +| `managed_package_install_id` | string | Package install ID | + +### `rippling_update_custom_object_field` + +Update a field on a custom object + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `fieldApiName` | string | Yes | Field API name | +| `name` | string | No | Field name | +| `description` | string | No | Description | +| `dataType` | json | No | Data type | +| `required` | boolean | No | Whether the field is required | +| `rqlDefinition` | json | No | RQL definition object | +| `isUnique` | boolean | No | Is unique | +| `formulaAttrMetas` | json | No | Formula attribute metadata | +| `section` | json | No | Section configuration | +| `enableHistory` | boolean | No | Enable history | +| `derivedFieldFormula` | string | No | Derived field formula expression | +| `nameFieldDetails` | json | No | Name field details configuration | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Field ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `custom_object` | string | Custom object | +| `description` | string | Description | +| `api_name` | string | API name | +| `data_type` | json | Data type configuration | +| `is_unique` | boolean | Is unique | +| `is_immutable` | boolean | Is immutable | +| `is_standard` | boolean | Is standard | +| `enable_history` | boolean | History enabled | +| `managed_package_install_id` | string | Package install ID | + +### `rippling_delete_custom_object_field` + +Delete a field from a custom object + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `fieldApiName` | string | Yes | Field API name | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the field was deleted | + +### `rippling_list_custom_object_records` + +List all records for a custom object + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `records` | array | List of records | +| ↳ `id` | string | Record ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Record name | +| ↳ `external_id` | string | External ID | +| ↳ `created_by` | json | Created by user \(id, display_value, image\) | +| ↳ `last_modified_by` | json | Last modified by user \(id, display_value, image\) | +| ↳ `owner_role` | json | Owner role \(id, display_value, image\) | +| ↳ `system_updated_at` | string | System update timestamp | +| ↳ `data` | json | Full record data including dynamic fields | +| `totalCount` | number | Number of records returned | +| `nextLink` | string | Next page link | + +### `rippling_get_custom_object_record` + +Get a specific custom object record + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `codrId` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Record ID | +| `created_at` | string | Record creation date | +| `updated_at` | string | Record update date | +| `name` | string | Record name | +| `external_id` | string | External ID | +| `created_by` | json | Created by user \(id, display_value, image\) | +| `last_modified_by` | json | Last modified by user \(id, display_value, image\) | +| `owner_role` | json | Owner role \(id, display_value, image\) | +| `system_updated_at` | string | System update timestamp | +| `data` | json | Full record data | + +### `rippling_get_custom_object_record_by_external_id` + +Get a custom object record by external ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `externalId` | string | Yes | External ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Record ID | +| `created_at` | string | Record creation date | +| `updated_at` | string | Record update date | +| `name` | string | Record name | +| `external_id` | string | External ID | +| `created_by` | json | Created by user \(id, display_value, image\) | +| `last_modified_by` | json | Last modified by user \(id, display_value, image\) | +| `owner_role` | json | Owner role \(id, display_value, image\) | +| `system_updated_at` | string | System update timestamp | +| `data` | json | Full record data | + +### `rippling_query_custom_object_records` + +Query custom object records with filters + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `query` | string | No | Query expression | +| `limit` | number | No | Maximum number of records to return | +| `cursor` | string | No | Pagination cursor | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `records` | array | Matching records | +| ↳ `id` | string | Record ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Record name | +| ↳ `external_id` | string | External ID | +| ↳ `created_by` | json | Created by user \(id, display_value, image\) | +| ↳ `last_modified_by` | json | Last modified by user \(id, display_value, image\) | +| ↳ `owner_role` | json | Owner role \(id, display_value, image\) | +| ↳ `system_updated_at` | string | System update timestamp | +| ↳ `data` | json | Full record data | +| `totalCount` | number | Number of records returned | +| `cursor` | string | Cursor for next page of results | + +### `rippling_create_custom_object_record` + +Create a custom object record + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `externalId` | string | No | External ID for the record | +| `data` | json | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Record ID | +| `created_at` | string | Record creation date | +| `updated_at` | string | Record update date | +| `name` | string | Record name | +| `external_id` | string | External ID | +| `created_by` | json | Created by user \(id, display_value, image\) | +| `last_modified_by` | json | Last modified by user \(id, display_value, image\) | +| `owner_role` | json | Owner role \(id, display_value, image\) | +| `system_updated_at` | string | System update timestamp | +| `data` | json | Full record data | + +### `rippling_update_custom_object_record` + +Update a custom object record + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `codrId` | string | Yes | Record ID | +| `externalId` | string | No | External ID for the record | +| `data` | json | No | Updated record data | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Record ID | +| `created_at` | string | Record creation date | +| `updated_at` | string | Record update date | +| `name` | string | Record name | +| `external_id` | string | External ID | +| `created_by` | json | Created by user \(id, display_value, image\) | +| `last_modified_by` | json | Last modified by user \(id, display_value, image\) | +| `owner_role` | json | Owner role \(id, display_value, image\) | +| `system_updated_at` | string | System update timestamp | +| `data` | json | Full record data | + +### `rippling_delete_custom_object_record` + +Delete a custom object record + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `codrId` | string | Yes | Record ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the record was deleted | + +### `rippling_bulk_create_custom_object_records` + +Bulk create custom object records + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `rowsToWrite` | json | Yes | Array of records to create \[\{external_id?, data\}\] | +| `allOrNothing` | boolean | No | If true, fail entire batch on any error | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `createdRecords` | array | Created custom object records | +| `totalCount` | number | Number of records created | + +### `rippling_bulk_update_custom_object_records` + +Bulk update custom object records + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `rowsToUpdate` | json | Yes | Array of records to update | +| `allOrNothing` | boolean | No | If true, fail entire batch on any error | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `updatedRecords` | array | Updated custom object records | +| `totalCount` | number | Number of records updated | + +### `rippling_bulk_delete_custom_object_records` + +Bulk delete custom object records + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `customObjectApiName` | string | Yes | Custom object API name | +| `rowsToDelete` | json | Yes | Array of records to delete | +| `allOrNothing` | boolean | No | If true, fail entire batch on any error | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the bulk delete succeeded | + +### `rippling_list_custom_apps` + +List all custom apps + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `customApps` | array | List of customApps | +| ↳ `id` | string | App ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | App name | +| ↳ `api_name` | string | API name | +| ↳ `description` | string | Description | +| ↳ `icon` | string | Icon URL | +| ↳ `pages` | json | Array of page summaries | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_custom_app` + +Get a specific custom app + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | App ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `api_name` | string | API name | +| `description` | string | Description | +| `icon` | string | Icon URL | +| `pages` | json | Array of page summaries | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_create_custom_app` + +Create a new custom app + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `name` | string | Yes | No description | +| `apiName` | string | Yes | No description | +| `description` | string | No | Description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | App ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `api_name` | string | API name | +| `description` | string | Description | +| `icon` | string | Icon URL | +| `pages` | json | Array of page summaries | + +### `rippling_update_custom_app` + +Update a custom app + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `name` | string | No | No description | +| `apiName` | string | No | API name | +| `description` | string | No | Description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | App ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `api_name` | string | API name | +| `description` | string | Description | +| `icon` | string | Icon URL | +| `pages` | json | Array of page summaries | + +### `rippling_delete_custom_app` + +Delete a custom app + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | ID of the resource to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the resource was deleted | + +### `rippling_list_custom_pages` + +List all custom pages + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `customPages` | array | List of customPages | +| ↳ `id` | string | Page ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Page name | +| ↳ `components` | json | Page components | +| ↳ `actions` | json | Page actions | +| ↳ `canvas_actions` | json | Canvas actions | +| ↳ `variables` | json | Page variables | +| ↳ `media` | json | Page media | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_custom_page` + +Get a specific custom page + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Page ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `components` | json | Page components | +| `actions` | json | Page actions | +| `canvas_actions` | json | Canvas actions | +| `variables` | json | Page variables | +| `media` | json | Page media | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_create_custom_page` + +Create a new custom page + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `name` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Page ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `components` | json | Page components | +| `actions` | json | Page actions | +| `canvas_actions` | json | Canvas actions | +| `variables` | json | Page variables | +| `media` | json | Page media | + +### `rippling_update_custom_page` + +Update a custom page + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `name` | string | No | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Page ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `components` | json | Page components | +| `actions` | json | Page actions | +| `canvas_actions` | json | Canvas actions | +| `variables` | json | Page variables | +| `media` | json | Page media | + +### `rippling_delete_custom_page` + +Delete a custom page + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | ID of the resource to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the resource was deleted | + +### `rippling_list_custom_settings` + +List all custom settings + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `orderBy` | string | No | Sort field. Prefix with - for descending | +| `cursor` | string | No | Pagination cursor from previous response | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `customSettings` | array | List of custom settings | +| ↳ `id` | string | Setting ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `display_name` | string | Display name | +| ↳ `api_name` | string | API name | +| ↳ `data_type` | string | Data type | +| ↳ `secret_value` | string | Secret value | +| ↳ `string_value` | string | String value | +| ↳ `number_value` | number | Number value | +| ↳ `boolean_value` | boolean | Boolean value | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_get_custom_setting` + +Get a specific custom setting + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Setting ID | +| `created_at` | string | Record creation date | +| `updated_at` | string | Record update date | +| `display_name` | string | Display name | +| `api_name` | string | API name | +| `data_type` | string | Data type | +| `secret_value` | string | Secret value | +| `string_value` | string | String value | +| `number_value` | number | Number value | +| `boolean_value` | boolean | Boolean value | +| `__meta` | json | Metadata including redacted_fields | + +### `rippling_create_custom_setting` + +Create a new custom setting + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `displayName` | string | No | Display name | +| `apiName` | string | No | Unique API name | +| `dataType` | string | No | Data type of the setting | +| `secretValue` | string | No | Secret value \(for secret data type\) | +| `stringValue` | string | No | String value \(for string data type\) | +| `numberValue` | number | No | Number value \(for number data type\) | +| `booleanValue` | boolean | No | Boolean value \(for boolean data type\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Setting ID | +| `created_at` | string | Record creation date | +| `updated_at` | string | Record update date | +| `display_name` | string | Display name | +| `api_name` | string | API name | +| `data_type` | string | Data type | +| `secret_value` | string | Secret value | +| `string_value` | string | String value | +| `number_value` | number | Number value | +| `boolean_value` | boolean | Boolean value | + +### `rippling_update_custom_setting` + +Update a custom setting + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | No description | +| `displayName` | string | No | Display name | +| `apiName` | string | No | Unique API name | +| `dataType` | string | No | Data type of the setting | +| `secretValue` | string | No | Secret value \(for secret data type\) | +| `stringValue` | string | No | String value \(for string data type\) | +| `numberValue` | number | No | Number value \(for number data type\) | +| `booleanValue` | boolean | No | Boolean value \(for boolean data type\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Setting ID | +| `created_at` | string | Record creation date | +| `updated_at` | string | Record update date | +| `display_name` | string | Display name | +| `api_name` | string | API name | +| `data_type` | string | Data type | +| `secret_value` | string | Secret value | +| `string_value` | string | String value | +| `number_value` | number | Number value | +| `boolean_value` | boolean | Boolean value | + +### `rippling_delete_custom_setting` + +Delete a custom setting + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rippling API key | +| `id` | string | Yes | ID of the resource to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deleted` | boolean | Whether the resource was deleted | -### `rippling_list_leave_requests` +### `rippling_list_object_categories` -List leave requests in Rippling with optional filtering by date range and status +List all object categories #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `startDate` | string | No | Filter by start date \(ISO date string\) | -| `endDate` | string | No | Filter by end date \(ISO date string\) | -| `status` | string | No | Filter by status \(e.g. pending, approved, declined\) | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `leaveRequests` | array | List of leave requests | -| ↳ `id` | string | Leave request ID | -| ↳ `requestedBy` | string | Employee ID who requested leave | -| ↳ `status` | string | Request status \(pending/approved/declined\) | -| ↳ `startDate` | string | Leave start date | -| ↳ `endDate` | string | Leave end date | -| ↳ `reason` | string | Reason for leave | -| ↳ `leaveType` | string | Type of leave | -| ↳ `createdAt` | string | When the request was created | -| `totalCount` | number | Total number of leave requests returned | +| `objectCategories` | array | List of objectCategories | +| ↳ `id` | string | Category ID | +| ↳ `created_at` | string | Record creation date | +| ↳ `updated_at` | string | Record update date | +| ↳ `name` | string | Category name | +| ↳ `description` | string | Description | +| `totalCount` | number | Number of items returned | +| `nextLink` | string | Link to next page of results | -### `rippling_process_leave_request` +### `rippling_get_object_category` -Approve or decline a leave request in Rippling +Get a specific object category #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `leaveRequestId` | string | Yes | The ID of the leave request to process | -| `action` | string | Yes | Action to take on the leave request \(approve or decline\) | +| `id` | string | Yes | No description | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `id` | string | Leave request ID | -| `status` | string | Updated status of the leave request | -| `requestedBy` | string | Employee ID who requested leave | -| `startDate` | string | Leave start date | -| `endDate` | string | Leave end date | +| `id` | string | Category ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `description` | string | Description | -### `rippling_list_leave_balances` +### `rippling_create_object_category` -List leave balances for all employees in Rippling +Create a new object category #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `limit` | number | No | Maximum number of leave balances to return | -| `offset` | number | No | Offset for pagination | +| `name` | string | Yes | Category name | +| `description` | string | No | Description | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `leaveBalances` | array | List of employee leave balances | -| ↳ `employeeId` | string | Employee ID | -| ↳ `balances` | array | Leave balance entries | -| ↳ `leaveType` | string | Type of leave | -| ↳ `minutesRemaining` | number | Minutes of leave remaining | -| `totalCount` | number | Number of leave balances returned on this page | +| `id` | string | Category ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `description` | string | Description | -### `rippling_get_leave_balance` +### `rippling_update_object_category` -Get leave balance for a specific employee by role ID +Update a object category #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `roleId` | string | Yes | The employee/role ID to retrieve leave balance for | +| `id` | string | Yes | No description | +| `name` | string | No | Category name | +| `description` | string | No | Description | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `employeeId` | string | Employee ID | -| `balances` | array | Leave balance entries | -| ↳ `leaveType` | string | Type of leave | -| ↳ `minutesRemaining` | number | Minutes of leave remaining | +| `id` | string | Category ID | +| `created_at` | string | Creation date | +| `updated_at` | string | Update date | +| `name` | string | Name | +| `description` | string | Description | -### `rippling_list_leave_types` +### `rippling_delete_object_category` -List company leave types configured in Rippling +Delete a object category #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `managedBy` | string | No | Filter leave types by manager | +| `id` | string | Yes | ID of the resource to delete | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `leaveTypes` | array | List of company leave types | -| ↳ `id` | string | Leave type ID | -| ↳ `name` | string | Leave type name | -| ↳ `managedBy` | string | Manager of this leave type | -| `totalCount` | number | Total number of leave types returned | +| `deleted` | boolean | Whether the resource was deleted | -### `rippling_create_group` +### `rippling_get_report_run` -Create a new group in Rippling +Get a report run by ID #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `name` | string | Yes | Name of the group | -| `spokeId` | string | Yes | Third-party app identifier | -| `users` | json | No | Array of user ID strings to add to the group | +| `runId` | string | Yes | No description | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `id` | string | Group ID | -| `name` | string | Group name | -| `spokeId` | string | Third-party app identifier | -| `users` | array | Array of user IDs in the group | -| `version` | number | Group version number | +| `id` | string | Report run ID | +| `report_id` | string | Report ID | +| `status` | string | Run status | +| `file_url` | string | URL to download the report file | +| `expires_at` | string | Expiration timestamp for the file URL | +| `output_type` | string | Output format \(JSON or CSV\) | +| `__meta` | json | Metadata including redacted_fields | -### `rippling_update_group` +### `rippling_trigger_report_run` -Update an existing group in Rippling +Trigger a new report run #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `groupId` | string | Yes | The ID of the group to update | -| `name` | string | No | New name for the group | -| `spokeId` | string | No | Third-party app identifier | -| `users` | json | No | Array of user ID strings to set for the group | -| `version` | number | No | Group version number for optimistic concurrency | +| `reportId` | string | Yes | Report ID to run | +| `includeObjectIds` | boolean | No | Include object IDs in the report | +| `includeTotalRows` | boolean | No | Include total row count | +| `formatDateFields` | json | No | Date field formatting configuration | +| `formatCurrencyFields` | json | No | Currency field formatting configuration | +| `outputType` | string | No | Output type \(JSON or CSV\) | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `id` | string | Group ID | -| `name` | string | Group name | -| `spokeId` | string | Third-party app identifier | -| `users` | array | Array of user IDs in the group | -| `version` | number | Group version number | +| `id` | string | Report run ID | +| `report_id` | string | Report ID | +| `status` | string | Run status | +| `file_url` | string | URL to download the report file | +| `expires_at` | string | Expiration timestamp for the file URL | +| `output_type` | string | Output format \(JSON or CSV\) | -### `rippling_push_candidate` +### `rippling_create_draft_hires` -Push a candidate to onboarding in Rippling +Create bulk draft hires #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rippling API key | -| `firstName` | string | Yes | Candidate first name | -| `lastName` | string | Yes | Candidate last name | -| `email` | string | Yes | Candidate email address | -| `phone` | string | No | Candidate phone number | -| `jobTitle` | string | No | Job title for the candidate | -| `department` | string | No | Department for the candidate | -| `startDate` | string | No | Start date in ISO 8601 format \(e.g., 2025-01-15\) | +| `draftHires` | json | Yes | Array of draft hire objects | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `id` | string | Candidate ID | -| `firstName` | string | Candidate first name | -| `lastName` | string | Candidate last name | -| `email` | string | Candidate email address | -| `status` | string | Candidate onboarding status | +| `invalidItems` | json | Failed draft hires | +| `successfulResults` | json | Successful draft hires | +| `totalInvalid` | number | Number of failures | +| `totalSuccessful` | number | Number of successes | diff --git a/apps/docs/content/docs/en/tools/tailscale.mdx b/apps/docs/content/docs/en/tools/tailscale.mdx index 17d71352e3a..aa03f5c9b04 100644 --- a/apps/docs/content/docs/en/tools/tailscale.mdx +++ b/apps/docs/content/docs/en/tools/tailscale.mdx @@ -10,6 +10,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" color="#2E2D2D" /> +{/* MANUAL-CONTENT-START:intro */} ## Overview [Tailscale](https://tailscale.com) is a zero-config mesh VPN built on WireGuard that makes it easy to connect devices, services, and users across any network. The Tailscale block lets you automate network management tasks like device provisioning, access control, route management, and DNS configuration directly from your Sim workflows. @@ -39,6 +40,14 @@ Every operation requires a **tailnet** parameter. This is typically your organiz - **Key lifecycle**: Create, list, inspect, and revoke auth keys - **User auditing**: List all users in the tailnet and their roles - **Policy review**: Retrieve the current ACL policy for inspection or backup +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Interact with the Tailscale API to manage devices, DNS, ACLs, auth keys, users, and routes across your tailnet. + + ## Tools @@ -100,8 +109,6 @@ Get details of a specific device by ID | `blocksIncomingConnections` | boolean | Whether the device blocks incoming connections | | `lastSeen` | string | Last seen timestamp | | `created` | string | Creation timestamp | -| `enabledRoutes` | array | Approved subnet routes | -| `advertisedRoutes` | array | Requested subnet routes | | `isExternal` | boolean | Whether the device is external | | `updateAvailable` | boolean | Whether an update is available | | `machineKey` | string | Machine key | @@ -263,6 +270,7 @@ Set the DNS nameservers for the tailnet | Parameter | Type | Description | | --------- | ---- | ----------- | | `dns` | array | Updated list of DNS nameserver addresses | +| `magicDNS` | boolean | Whether MagicDNS is enabled | ### `tailscale_get_dns_preferences` @@ -375,7 +383,7 @@ Create a new auth key for the tailnet to pre-authorize devices | `reusable` | boolean | No | Whether the key can be used more than once | | `ephemeral` | boolean | No | Whether devices authenticated with this key are ephemeral | | `preauthorized` | boolean | No | Whether devices are pre-authorized \(skip manual approval\) | -| `tags` | string | Yes | Comma-separated list of tags for devices using this key \(e.g., "tag:server,tag:prod"\) | +| `tags` | string | No | Comma-separated list of tags for devices using this key \(e.g., "tag:server,tag:prod"\) | | `description` | string | No | Description for the auth key | | `expirySeconds` | number | No | Key expiry time in seconds \(default: 90 days\) | diff --git a/apps/docs/content/docs/es/blocks/guardrails.mdx b/apps/docs/content/docs/es/blocks/guardrails.mdx index fd0697af10e..41b804ee89e 100644 --- a/apps/docs/content/docs/es/blocks/guardrails.mdx +++ b/apps/docs/content/docs/es/blocks/guardrails.mdx @@ -131,7 +131,7 @@ Detecta información de identificación personal utilizando Microsoft Presidio. **Casos de uso:** - Bloquear contenido que contiene información personal sensible - Enmascarar PII antes de registrar o almacenar datos -- Cumplimiento de GDPR, HIPAA y otras regulaciones de privacidad +- Cumplimiento de GDPR y otras regulaciones de privacidad - Sanear entradas de usuario antes del procesamiento ## Configuración diff --git a/apps/docs/content/docs/fr/blocks/guardrails.mdx b/apps/docs/content/docs/fr/blocks/guardrails.mdx index 83d2e0582db..9b3e218d2bf 100644 --- a/apps/docs/content/docs/fr/blocks/guardrails.mdx +++ b/apps/docs/content/docs/fr/blocks/guardrails.mdx @@ -131,7 +131,7 @@ Détecte les informations personnelles identifiables à l'aide de Microsoft Pres **Cas d'utilisation :** - Bloquer le contenu contenant des informations personnelles sensibles - Masquer les PII avant de journaliser ou stocker des données -- Conformité avec le RGPD, HIPAA et autres réglementations sur la confidentialité +- Conformité avec le RGPD et autres réglementations sur la confidentialité - Assainir les entrées utilisateur avant traitement ## Configuration diff --git a/apps/docs/content/docs/ja/blocks/guardrails.mdx b/apps/docs/content/docs/ja/blocks/guardrails.mdx index 5e908dc8c67..85083fa8e8c 100644 --- a/apps/docs/content/docs/ja/blocks/guardrails.mdx +++ b/apps/docs/content/docs/ja/blocks/guardrails.mdx @@ -131,7 +131,7 @@ Microsoft Presidioを使用して個人を特定できる情報を検出しま **ユースケース:** - 機密性の高い個人情報を含むコンテンツをブロック - データのログ記録や保存前にPIIをマスク -- GDPR、HIPAA、その他のプライバシー規制への準拠 +- GDPR、その他のプライバシー規制への準拠 - 処理前のユーザー入力のサニタイズ ## 設定 diff --git a/apps/docs/content/docs/zh/blocks/guardrails.mdx b/apps/docs/content/docs/zh/blocks/guardrails.mdx index 76871f884df..cd2854e25ca 100644 --- a/apps/docs/content/docs/zh/blocks/guardrails.mdx +++ b/apps/docs/content/docs/zh/blocks/guardrails.mdx @@ -131,7 +131,7 @@ Guardrails 模块通过针对多种验证类型检查内容,验证并保护您 **使用场景:** - 阻止包含敏感个人信息的内容 - 在记录或存储数据之前屏蔽 PII -- 符合 GDPR、HIPAA 和其他隐私法规 +- 符合 GDPR 和其他隐私法规 - 在处理之前清理用户输入 ## 配置 diff --git a/apps/sim/app/(home)/components/enterprise/enterprise.tsx b/apps/sim/app/(home)/components/enterprise/enterprise.tsx index da8a88461c6..798ac88eff8 100644 --- a/apps/sim/app/(home)/components/enterprise/enterprise.tsx +++ b/apps/sim/app/(home)/components/enterprise/enterprise.tsx @@ -4,11 +4,11 @@ * SEO: * - `
`. * - `

` for the section title. - * - Compliance certs (SOC 2, HIPAA) as visible `` text. + * - Compliance cert (SOC 2) as visible `` text. * - Enterprise CTA links to contact form via `` with `rel="noopener noreferrer"`. * * GEO: - * - Entity-rich: "Sim is SOC 2 and HIPAA compliant" — not "We are compliant." + * - Entity-rich: "Sim is SOC 2 compliant" — not "We are compliant." * - `
    ` checklist of features (SSO, RBAC, audit logs, SLA, on-premise deployment) * as an atomic answer block for "What enterprise features does Sim offer?". */ @@ -66,7 +66,7 @@ const FEATURE_TAGS = [ function TrustStrip() { return (
    - {/* SOC 2 + HIPAA combined */} + {/* SOC 2 */}
    - SOC 2 & HIPAA + SOC 2 - Type II · PHI protected → + Type II →
    diff --git a/apps/sim/app/(home)/components/hero/hero.tsx b/apps/sim/app/(home)/components/hero/hero.tsx index 96953d4f7de..a257e2c1155 100644 --- a/apps/sim/app/(home)/components/hero/hero.tsx +++ b/apps/sim/app/(home)/components/hero/hero.tsx @@ -42,7 +42,7 @@ export default function Hero() { 1,000+ integrations and LLMs — including OpenAI, Claude, Gemini, Mistral, and xAI — to deploy and orchestrate agentic workflows. Create agents, workflows, knowledge bases, tables, and docs. Trusted by over 100,000 builders at startups and Fortune 500 companies. SOC2 and - HIPAA compliant. + SOC2 compliant.

    + + + + + ) +} + +function JsonlIcon({ className }: { className?: string }) { + return ( + + + + + + + + ) +} + +function ZipIcon({ className }: { className?: string }) { + return ( + + + + + + + + + ) +} + +const COLUMNS: PreviewColumn[] = [ + { id: 'name', header: 'Name' }, + { id: 'size', header: 'Size' }, + { id: 'type', header: 'Type' }, + { id: 'created', header: 'Created' }, + { id: 'owner', header: 'Owner' }, +] + +const ROWS: PreviewRow[] = [ + { + id: '1', + cells: { + name: { icon: , label: 'Q1 Performance Report.pdf' }, + size: { label: '2.4 MB' }, + type: { icon: , label: 'PDF' }, + created: { label: '3 hours ago' }, + owner: ownerCell('T', 'Theo L.'), + }, + }, + { + id: '2', + cells: { + name: { icon: , label: 'product-screenshots.zip' }, + size: { label: '18.7 MB' }, + type: { icon: , label: 'ZIP' }, + created: { label: '1 day ago' }, + owner: ownerCell('A', 'Alex M.'), + }, + }, + { + id: '3', + cells: { + name: { icon: , label: 'training-dataset.jsonl' }, + size: { label: '892 KB' }, + type: { icon: , label: 'JSONL' }, + created: { label: '3 days ago' }, + owner: ownerCell('J', 'Jordan P.'), + }, + }, + { + id: '4', + cells: { + name: { icon: , label: 'brand-guidelines.pdf' }, + size: { label: '5.1 MB' }, + type: { icon: , label: 'PDF' }, + created: { label: '1 week ago' }, + owner: ownerCell('S', 'Sarah K.'), + }, + }, + { + id: '5', + cells: { + name: { icon: , label: 'customer-interviews.mp3' }, + size: { label: '45.2 MB' }, + type: { icon: , label: 'Audio' }, + created: { label: 'March 20th, 2026' }, + owner: ownerCell('V', 'Vik M.'), + }, + }, + { + id: '6', + cells: { + name: { icon: , label: 'onboarding-playbook.docx' }, + size: { label: '1.1 MB' }, + type: { icon: , label: 'DOCX' }, + created: { label: 'March 14th, 2026' }, + owner: ownerCell('S', 'Sarah K.'), + }, + }, +] + +/** + * Static landing preview of the Files workspace page. + */ +export function LandingPreviewFiles() { + return ( + + ) +} diff --git a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-knowledge/landing-preview-knowledge.tsx b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-knowledge/landing-preview-knowledge.tsx new file mode 100644 index 00000000000..f56f87ce7e9 --- /dev/null +++ b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-knowledge/landing-preview-knowledge.tsx @@ -0,0 +1,105 @@ +import { Database } from '@/components/emcn/icons' +import { + AirtableIcon, + AsanaIcon, + ConfluenceIcon, + GoogleDocsIcon, + GoogleDriveIcon, + JiraIcon, + SalesforceIcon, + SlackIcon, + ZendeskIcon, +} from '@/components/icons' +import type { + PreviewColumn, + PreviewRow, +} from '@/app/(home)/components/landing-preview/components/landing-preview-resource/landing-preview-resource' +import { LandingPreviewResource } from '@/app/(home)/components/landing-preview/components/landing-preview-resource/landing-preview-resource' + +const DB_ICON = + +function connectorIcons(icons: React.ComponentType<{ className?: string }>[]) { + return { + content: ( +
    + {icons.map((Icon, i) => ( + + ))} +
    + ), + } +} + +const COLUMNS: PreviewColumn[] = [ + { id: 'name', header: 'Name' }, + { id: 'documents', header: 'Documents' }, + { id: 'tokens', header: 'Tokens' }, + { id: 'connectors', header: 'Connectors' }, + { id: 'created', header: 'Created' }, +] + +const ROWS: PreviewRow[] = [ + { + id: '1', + cells: { + name: { icon: DB_ICON, label: 'Product Documentation' }, + documents: { label: '847' }, + tokens: { label: '1,284,392' }, + connectors: connectorIcons([AsanaIcon, GoogleDocsIcon]), + created: { label: '2 days ago' }, + }, + }, + { + id: '2', + cells: { + name: { icon: DB_ICON, label: 'Customer Support KB' }, + documents: { label: '234' }, + tokens: { label: '892,104' }, + connectors: connectorIcons([ZendeskIcon, SlackIcon]), + created: { label: '1 week ago' }, + }, + }, + { + id: '3', + cells: { + name: { icon: DB_ICON, label: 'Engineering Wiki' }, + documents: { label: '1,203' }, + tokens: { label: '2,847,293' }, + connectors: connectorIcons([ConfluenceIcon, JiraIcon]), + created: { label: 'March 12th, 2026' }, + }, + }, + { + id: '4', + cells: { + name: { icon: DB_ICON, label: 'Marketing Assets' }, + documents: { label: '189' }, + tokens: { label: '634,821' }, + connectors: connectorIcons([GoogleDriveIcon, AirtableIcon]), + created: { label: 'March 5th, 2026' }, + }, + }, + { + id: '5', + cells: { + name: { icon: DB_ICON, label: 'Sales Playbook' }, + documents: { label: '92' }, + tokens: { label: '418,570' }, + connectors: connectorIcons([SalesforceIcon]), + created: { label: 'February 28th, 2026' }, + }, + }, +] + +export function LandingPreviewKnowledge() { + return ( + + ) +} diff --git a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx new file mode 100644 index 00000000000..f1406bb81dc --- /dev/null +++ b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx @@ -0,0 +1,321 @@ +'use client' + +import { useMemo, useState } from 'react' +import { Download } from 'lucide-react' +import { ArrowUpDown, Badge, Library, ListFilter, Search } from '@/components/emcn' +import type { BadgeProps } from '@/components/emcn/components/badge/badge' +import { cn } from '@/lib/core/utils/cn' + +interface LogRow { + id: string + workflowName: string + workflowColor: string + date: string + status: 'completed' | 'error' | 'running' + cost: string + trigger: 'webhook' | 'api' | 'schedule' | 'manual' | 'mcp' | 'chat' + triggerLabel: string + duration: string +} + +type BadgeVariant = BadgeProps['variant'] + +const STATUS_VARIANT: Record = { + completed: 'gray', + error: 'red', + running: 'amber', +} + +const STATUS_LABELS: Record = { + completed: 'Completed', + error: 'Error', + running: 'Running', +} + +const TRIGGER_VARIANT: Record = { + webhook: 'orange', + api: 'blue', + schedule: 'green', + manual: 'gray-secondary', + mcp: 'cyan', + chat: 'purple', +} + +const MOCK_LOGS: LogRow[] = [ + { + id: '1', + workflowName: 'Customer Onboarding', + workflowColor: '#4f8ef7', + date: 'Apr 1 10:42 AM', + status: 'running', + cost: '-', + trigger: 'webhook', + triggerLabel: 'Webhook', + duration: '-', + }, + { + id: '2', + workflowName: 'Lead Enrichment', + workflowColor: '#33C482', + date: 'Apr 1 09:15 AM', + status: 'error', + cost: '318 credits', + trigger: 'api', + triggerLabel: 'API', + duration: '2.7s', + }, + { + id: '3', + workflowName: 'Email Campaign', + workflowColor: '#a855f7', + date: 'Apr 1 08:30 AM', + status: 'completed', + cost: '89 credits', + trigger: 'schedule', + triggerLabel: 'Schedule', + duration: '0.8s', + }, + { + id: '4', + workflowName: 'Data Pipeline', + workflowColor: '#f97316', + date: 'Mar 31 10:14 PM', + status: 'completed', + cost: '241 credits', + trigger: 'webhook', + triggerLabel: 'Webhook', + duration: '4.1s', + }, + { + id: '5', + workflowName: 'Invoice Processing', + workflowColor: '#ec4899', + date: 'Mar 31 08:45 PM', + status: 'completed', + cost: '112 credits', + trigger: 'manual', + triggerLabel: 'Manual', + duration: '0.9s', + }, + { + id: '6', + workflowName: 'Support Triage', + workflowColor: '#0ea5e9', + date: 'Mar 31 07:22 PM', + status: 'completed', + cost: '197 credits', + trigger: 'api', + triggerLabel: 'API', + duration: '1.6s', + }, + { + id: '7', + workflowName: 'Content Moderator', + workflowColor: '#f59e0b', + date: 'Mar 31 06:11 PM', + status: 'error', + cost: '284 credits', + trigger: 'schedule', + triggerLabel: 'Schedule', + duration: '3.2s', + }, +] + +type SortKey = 'workflowName' | 'date' | 'status' | 'cost' | 'trigger' | 'duration' + +const COL_HEADERS: { key: SortKey; label: string }[] = [ + { key: 'workflowName', label: 'Workflow' }, + { key: 'date', label: 'Date' }, + { key: 'status', label: 'Status' }, + { key: 'cost', label: 'Cost' }, + { key: 'trigger', label: 'Trigger' }, + { key: 'duration', label: 'Duration' }, +] + +export function LandingPreviewLogs() { + const [search, setSearch] = useState('') + const [sortKey, setSortKey] = useState(null) + const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc') + const [activeTab, setActiveTab] = useState<'logs' | 'dashboard'>('logs') + + function handleSort(key: SortKey) { + if (sortKey === key) { + setSortDir((d) => (d === 'asc' ? 'desc' : 'asc')) + } else { + setSortKey(key) + setSortDir('asc') + } + } + + const sorted = useMemo(() => { + const q = search.toLowerCase() + const filtered = q + ? MOCK_LOGS.filter( + (log) => + log.workflowName.toLowerCase().includes(q) || + log.triggerLabel.toLowerCase().includes(q) || + STATUS_LABELS[log.status].toLowerCase().includes(q) + ) + : MOCK_LOGS + + if (!sortKey) return filtered + return [...filtered].sort((a, b) => { + const av = sortKey === 'cost' ? a.cost.replace(/\D/g, '') : a[sortKey] + const bv = sortKey === 'cost' ? b.cost.replace(/\D/g, '') : b[sortKey] + const cmp = av.localeCompare(bv, undefined, { numeric: true, sensitivity: 'base' }) + return sortDir === 'asc' ? cmp : -cmp + }) + }, [search, sortKey, sortDir]) + + return ( +
    + {/* Header */} +
    +
    +
    + +

    Logs

    +
    +
    +
    + + Export +
    + + +
    +
    +
    + + {/* Options bar */} +
    +
    +
    + + setSearch(e.target.value)} + placeholder='Search logs...' + className='flex-1 bg-transparent text-[var(--text-body)] text-caption outline-none placeholder:text-[var(--text-subtle)]' + /> +
    +
    +
    + + Filter +
    + +
    +
    +
    + + {/* Table — uses for pixel-perfect column alignment with headers */} +
    +
    + + + + + + + + + + + {COL_HEADERS.map(({ key, label }) => ( + + ))} + + + + {sorted.map((log) => ( + + + + + + + + + ))} + +
    + +
    +
    +
    + + {log.workflowName} + +
    +
    + {log.date} + + + {STATUS_LABELS[log.status]} + + + {log.cost} + + + {log.triggerLabel} + + + {log.duration} +
    +
    +
    + ) +} diff --git a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-resource/landing-preview-resource.tsx b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-resource/landing-preview-resource.tsx new file mode 100644 index 00000000000..309b3f2488a --- /dev/null +++ b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-resource/landing-preview-resource.tsx @@ -0,0 +1,211 @@ +'use client' + +import type { ReactNode } from 'react' +import { useMemo, useState } from 'react' +import { ArrowUpDown, ListFilter, Plus, Search } from '@/components/emcn' +import { cn } from '@/lib/core/utils/cn' + +export interface PreviewColumn { + id: string + header: string + width?: number +} + +export interface PreviewCell { + icon?: ReactNode + label?: string + content?: ReactNode +} + +export interface PreviewRow { + id: string + cells: Record +} + +interface LandingPreviewResourceProps { + icon: React.ComponentType<{ className?: string }> + title: string + createLabel: string + searchPlaceholder: string + columns: PreviewColumn[] + rows: PreviewRow[] + onRowClick?: (id: string) => void +} + +export function ownerCell(initial: string, name: string): PreviewCell { + return { + icon: ( + + {initial} + + ), + label: name, + } +} + +export function LandingPreviewResource({ + icon: Icon, + title, + createLabel, + searchPlaceholder, + columns, + rows, + onRowClick, +}: LandingPreviewResourceProps) { + const [search, setSearch] = useState('') + const [sortColId, setSortColId] = useState(null) + const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc') + + function handleSortClick(colId: string) { + if (sortColId === colId) { + setSortDir((d) => (d === 'asc' ? 'desc' : 'asc')) + } else { + setSortColId(colId) + setSortDir('asc') + } + } + + const sorted = useMemo(() => { + const q = search.toLowerCase() + const filtered = q + ? rows.filter((row) => + Object.values(row.cells).some((cell) => cell.label?.toLowerCase().includes(q)) + ) + : rows + + if (!sortColId) return filtered + return [...filtered].sort((a, b) => { + const av = a.cells[sortColId]?.label ?? '' + const bv = b.cells[sortColId]?.label ?? '' + const cmp = av.localeCompare(bv, undefined, { numeric: true, sensitivity: 'base' }) + return sortDir === 'asc' ? cmp : -cmp + }) + }, [rows, search, sortColId, sortDir]) + + return ( +
    + {/* Header */} +
    +
    +
    + +

    {title}

    +
    +
    + + {createLabel} +
    +
    +
    + + {/* Options bar */} +
    +
    +
    + + setSearch(e.target.value)} + placeholder={searchPlaceholder} + className='flex-1 bg-transparent text-[var(--text-body)] text-caption outline-none placeholder:text-[var(--text-subtle)]' + /> +
    +
    +
    + + Filter +
    + +
    +
    +
    + + {/* Table */} +
    + + + {columns.map((col, i) => ( + + ))} + + + + {columns.map((col) => ( + + ))} + + + + {sorted.map((row) => ( + onRowClick?.(row.id)} + className={cn( + 'transition-colors hover-hover:bg-[var(--surface-3)]', + onRowClick && 'cursor-pointer' + )} + > + {columns.map((col, colIdx) => { + const cell = row.cells[col.id] + return ( + + ) + })} + + ))} + +
    + +
    + {cell?.content ? ( + cell.content + ) : ( + + {cell?.icon && ( + + {cell.icon} + + )} + {cell?.label ?? '—'} + + )} +
    +
    +
    + ) +} diff --git a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-scheduled-tasks/landing-preview-scheduled-tasks.tsx b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-scheduled-tasks/landing-preview-scheduled-tasks.tsx new file mode 100644 index 00000000000..0750f4536ce --- /dev/null +++ b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-scheduled-tasks/landing-preview-scheduled-tasks.tsx @@ -0,0 +1,88 @@ +import { Calendar } from '@/components/emcn/icons' +import type { + PreviewColumn, + PreviewRow, +} from '@/app/(home)/components/landing-preview/components/landing-preview-resource/landing-preview-resource' +import { LandingPreviewResource } from '@/app/(home)/components/landing-preview/components/landing-preview-resource/landing-preview-resource' + +const CAL_ICON = + +const COLUMNS: PreviewColumn[] = [ + { id: 'task', header: 'Task' }, + { id: 'schedule', header: 'Schedule', width: 240 }, + { id: 'nextRun', header: 'Next Run' }, + { id: 'lastRun', header: 'Last Run' }, +] + +const ROWS: PreviewRow[] = [ + { + id: '1', + cells: { + task: { icon: CAL_ICON, label: 'Sync CRM contacts' }, + schedule: { label: 'Recurring, every day at 9:00 AM' }, + nextRun: { label: 'Tomorrow' }, + lastRun: { label: '2 hours ago' }, + }, + }, + { + id: '2', + cells: { + task: { icon: CAL_ICON, label: 'Generate weekly report' }, + schedule: { label: 'Recurring, every Monday at 8:00 AM' }, + nextRun: { label: 'In 5 days' }, + lastRun: { label: '6 days ago' }, + }, + }, + { + id: '3', + cells: { + task: { icon: CAL_ICON, label: 'Clean up stale files' }, + schedule: { label: 'Recurring, every Sunday at midnight' }, + nextRun: { label: 'In 2 days' }, + lastRun: { label: '6 days ago' }, + }, + }, + { + id: '4', + cells: { + task: { icon: CAL_ICON, label: 'Send performance digest' }, + schedule: { label: 'Recurring, every Friday at 5:00 PM' }, + nextRun: { label: 'In 3 days' }, + lastRun: { label: '3 days ago' }, + }, + }, + { + id: '5', + cells: { + task: { icon: CAL_ICON, label: 'Backup production data' }, + schedule: { label: 'Recurring, every 4 hours' }, + nextRun: { label: 'In 2 hours' }, + lastRun: { label: '2 hours ago' }, + }, + }, + { + id: '6', + cells: { + task: { icon: CAL_ICON, label: 'Scrape competitor pricing' }, + schedule: { label: 'Recurring, every Tuesday at 6:00 AM' }, + nextRun: { label: 'In 6 days' }, + lastRun: { label: '1 week ago' }, + }, + }, +] + +/** + * Static landing preview of the Scheduled Tasks workspace page. + */ +export function LandingPreviewScheduledTasks() { + return ( + + ) +} diff --git a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx index c4df8ecc85d..de104c6286c 100644 --- a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx +++ b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx @@ -10,14 +10,25 @@ import { Settings, Table, } from '@/components/emcn/icons' +import { cn } from '@/lib/core/utils/cn' import type { PreviewWorkflow } from '@/app/(home)/components/landing-preview/components/landing-preview-workflow/workflow-data' +export type SidebarView = + | 'home' + | 'workflow' + | 'tables' + | 'files' + | 'knowledge' + | 'logs' + | 'scheduled-tasks' + interface LandingPreviewSidebarProps { workflows: PreviewWorkflow[] activeWorkflowId: string - activeView: 'home' | 'workflow' + activeView: SidebarView onSelectWorkflow: (id: string) => void onSelectHome: () => void + onSelectNav: (id: SidebarView) => void } /** @@ -39,7 +50,7 @@ const C = { const WORKSPACE_NAV = [ { id: 'tables', label: 'Tables', icon: Table }, { id: 'files', label: 'Files', icon: File }, - { id: 'knowledge-base', label: 'Knowledge Base', icon: Database }, + { id: 'knowledge', label: 'Knowledge Base', icon: Database }, { id: 'scheduled-tasks', label: 'Scheduled Tasks', icon: Calendar }, { id: 'logs', label: 'Logs', icon: Library }, ] as const @@ -49,20 +60,42 @@ const FOOTER_NAV = [ { id: 'settings', label: 'Settings', icon: Settings }, ] as const -function StaticNavItem({ +function NavItem({ icon: Icon, label, + isActive, + onClick, }: { icon: React.ComponentType<{ className?: string; style?: React.CSSProperties }> label: string + isActive?: boolean + onClick?: () => void }) { + if (!onClick) { + return ( +
    + + + {label} + +
    + ) + } + return ( -
    +
    + ) } @@ -77,13 +110,16 @@ export function LandingPreviewSidebar({ activeView, onSelectWorkflow, onSelectHome, + onSelectNav, }: LandingPreviewSidebarProps) { const isHomeActive = activeView === 'home' return (
    {/* Workspace Header */}
    @@ -116,21 +152,17 @@ export function LandingPreviewSidebar({ - +
    {/* Workspace */} @@ -142,7 +174,13 @@ export function LandingPreviewSidebar({
    {WORKSPACE_NAV.map((item) => ( - + onSelectNav(item.id)} + /> ))}
    @@ -164,14 +202,10 @@ export function LandingPreviewSidebar({ key={workflow.id} type='button' onClick={() => onSelectWorkflow(workflow.id)} - className='group mx-0.5 flex h-[28px] w-full items-center gap-2 rounded-[8px] px-2 transition-colors' - style={{ backgroundColor: isActive ? C.SURFACE_ACTIVE : 'transparent' }} - onMouseEnter={(e) => { - if (!isActive) e.currentTarget.style.backgroundColor = C.SURFACE_ACTIVE - }} - onMouseLeave={(e) => { - if (!isActive) e.currentTarget.style.backgroundColor = 'transparent' - }} + className={cn( + 'mx-0.5 flex h-[28px] w-full items-center gap-2 rounded-[8px] px-2 transition-colors hover-hover:bg-[#363636]', + isActive && 'bg-[#363636]' + )} >
    {FOOTER_NAV.map((item) => ( - + ))}
    diff --git a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-tables/landing-preview-tables.tsx b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-tables/landing-preview-tables.tsx new file mode 100644 index 00000000000..42539519a59 --- /dev/null +++ b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-tables/landing-preview-tables.tsx @@ -0,0 +1,552 @@ +'use client' + +import { useState } from 'react' +import { Checkbox } from '@/components/emcn' +import { + ChevronDown, + Columns3, + Rows3, + Table, + TypeBoolean, + TypeNumber, + TypeText, +} from '@/components/emcn/icons' +import { cn } from '@/lib/core/utils/cn' +import type { + PreviewColumn, + PreviewRow, +} from '@/app/(home)/components/landing-preview/components/landing-preview-resource/landing-preview-resource' +import { + LandingPreviewResource, + ownerCell, +} from '@/app/(home)/components/landing-preview/components/landing-preview-resource/landing-preview-resource' + +const CELL = 'border-[var(--border)] border-r border-b px-2 py-[7px] align-middle select-none' +const CELL_CHECKBOX = + 'border-[var(--border)] border-r border-b px-1 py-[7px] align-middle select-none' +const CELL_HEADER = + 'border-[var(--border)] border-r border-b bg-[var(--bg)] p-0 text-left align-middle' +const CELL_HEADER_CHECKBOX = + 'border-[var(--border)] border-r border-b bg-[var(--bg)] px-1 py-[7px] text-center align-middle' +const CELL_CONTENT = + 'relative min-h-[20px] min-w-0 overflow-clip text-ellipsis whitespace-nowrap text-small' +const SELECTION_OVERLAY = + 'pointer-events-none absolute -top-px -right-px -bottom-px -left-px z-[5] border-[2px] border-[var(--selection)]' + +const LIST_COLUMNS: PreviewColumn[] = [ + { id: 'name', header: 'Name' }, + { id: 'columns', header: 'Columns' }, + { id: 'rows', header: 'Rows' }, + { id: 'created', header: 'Created' }, + { id: 'owner', header: 'Owner' }, +] + +const TABLE_METAS: Record = { + '1': 'Customer Leads', + '2': 'Product Catalog', + '3': 'Campaign Analytics', + '4': 'User Profiles', + '5': 'Invoice Records', +} + +const TABLE_ICON = +const COLUMNS_ICON = +const ROWS_ICON = + +const LIST_ROWS: PreviewRow[] = [ + { + id: '1', + cells: { + name: { icon: TABLE_ICON, label: 'Customer Leads' }, + columns: { icon: COLUMNS_ICON, label: '8' }, + rows: { icon: ROWS_ICON, label: '2,847' }, + created: { label: '2 days ago' }, + owner: ownerCell('S', 'Sarah K.'), + }, + }, + { + id: '2', + cells: { + name: { icon: TABLE_ICON, label: 'Product Catalog' }, + columns: { icon: COLUMNS_ICON, label: '12' }, + rows: { icon: ROWS_ICON, label: '1,203' }, + created: { label: '5 days ago' }, + owner: ownerCell('A', 'Alex M.'), + }, + }, + { + id: '3', + cells: { + name: { icon: TABLE_ICON, label: 'Campaign Analytics' }, + columns: { icon: COLUMNS_ICON, label: '6' }, + rows: { icon: ROWS_ICON, label: '534' }, + created: { label: '1 week ago' }, + owner: ownerCell('W', 'Emaan K.'), + }, + }, + { + id: '4', + cells: { + name: { icon: TABLE_ICON, label: 'User Profiles' }, + columns: { icon: COLUMNS_ICON, label: '15' }, + rows: { icon: ROWS_ICON, label: '18,492' }, + created: { label: '2 weeks ago' }, + owner: ownerCell('J', 'Jordan P.'), + }, + }, + { + id: '5', + cells: { + name: { icon: TABLE_ICON, label: 'Invoice Records' }, + columns: { icon: COLUMNS_ICON, label: '9' }, + rows: { icon: ROWS_ICON, label: '742' }, + created: { label: 'March 15th, 2026' }, + owner: ownerCell('S', 'Sarah K.'), + }, + }, +] + +interface SpreadsheetColumn { + id: string + label: string + type: 'text' | 'number' | 'boolean' + width: number +} + +interface SpreadsheetRow { + id: string + cells: Record +} + +const COLUMN_TYPE_ICONS = { + text: TypeText, + number: TypeNumber, + boolean: TypeBoolean, +} as const + +const SPREADSHEET_DATA: Record = { + '1': { + columns: [ + { id: 'name', label: 'Name', type: 'text', width: 160 }, + { id: 'email', label: 'Email', type: 'text', width: 200 }, + { id: 'company', label: 'Company', type: 'text', width: 160 }, + { id: 'score', label: 'Score', type: 'number', width: 100 }, + { id: 'qualified', label: 'Qualified', type: 'boolean', width: 120 }, + ], + rows: [ + { + id: '1', + cells: { + name: 'Alice Johnson', + email: 'alice@acme.com', + company: 'Acme Corp', + score: '87', + qualified: 'true', + }, + }, + { + id: '2', + cells: { + name: 'Bob Williams', + email: 'bob@techco.io', + company: 'TechCo', + score: '62', + qualified: 'false', + }, + }, + { + id: '3', + cells: { + name: 'Carol Davis', + email: 'carol@startup.co', + company: 'StartupCo', + score: '94', + qualified: 'true', + }, + }, + { + id: '4', + cells: { + name: 'Dan Miller', + email: 'dan@bigcorp.com', + company: 'BigCorp', + score: '71', + qualified: 'true', + }, + }, + { + id: '5', + cells: { + name: 'Eva Chen', + email: 'eva@design.io', + company: 'Design IO', + score: '45', + qualified: 'false', + }, + }, + { + id: '6', + cells: { + name: 'Frank Lee', + email: 'frank@ventures.co', + company: 'Ventures', + score: '88', + qualified: 'true', + }, + }, + ], + }, + '2': { + columns: [ + { id: 'sku', label: 'SKU', type: 'text', width: 120 }, + { id: 'name', label: 'Product Name', type: 'text', width: 200 }, + { id: 'price', label: 'Price', type: 'number', width: 100 }, + { id: 'stock', label: 'In Stock', type: 'number', width: 120 }, + { id: 'active', label: 'Active', type: 'boolean', width: 90 }, + ], + rows: [ + { + id: '1', + cells: { + sku: 'PRD-001', + name: 'Wireless Headphones', + price: '79.99', + stock: '234', + active: 'true', + }, + }, + { + id: '2', + cells: { sku: 'PRD-002', name: 'USB-C Hub', price: '49.99', stock: '89', active: 'true' }, + }, + { + id: '3', + cells: { + sku: 'PRD-003', + name: 'Laptop Stand', + price: '39.99', + stock: '0', + active: 'false', + }, + }, + { + id: '4', + cells: { + sku: 'PRD-004', + name: 'Mechanical Keyboard', + price: '129.99', + stock: '52', + active: 'true', + }, + }, + { + id: '5', + cells: { sku: 'PRD-005', name: 'Webcam HD', price: '89.99', stock: '17', active: 'true' }, + }, + { + id: '6', + cells: { + sku: 'PRD-006', + name: 'Mouse Pad XL', + price: '24.99', + stock: '0', + active: 'false', + }, + }, + ], + }, + '3': { + columns: [ + { id: 'campaign', label: 'Campaign', type: 'text', width: 180 }, + { id: 'clicks', label: 'Clicks', type: 'number', width: 100 }, + { id: 'conversions', label: 'Conversions', type: 'number', width: 140 }, + { id: 'spend', label: 'Spend ($)', type: 'number', width: 130 }, + { id: 'active', label: 'Active', type: 'boolean', width: 90 }, + ], + rows: [ + { + id: '1', + cells: { + campaign: 'Spring Sale 2026', + clicks: '12,847', + conversions: '384', + spend: '2,400', + active: 'true', + }, + }, + { + id: '2', + cells: { + campaign: 'Email Reactivation', + clicks: '3,201', + conversions: '97', + spend: '450', + active: 'false', + }, + }, + { + id: '3', + cells: { + campaign: 'Referral Program', + clicks: '8,923', + conversions: '210', + spend: '1,100', + active: 'true', + }, + }, + { + id: '4', + cells: { + campaign: 'Product Launch', + clicks: '24,503', + conversions: '891', + spend: '5,800', + active: 'true', + }, + }, + { + id: '5', + cells: { + campaign: 'Retargeting Q1', + clicks: '6,712', + conversions: '143', + spend: '980', + active: 'false', + }, + }, + ], + }, + '4': { + columns: [ + { id: 'username', label: 'Username', type: 'text', width: 140 }, + { id: 'email', label: 'Email', type: 'text', width: 200 }, + { id: 'plan', label: 'Plan', type: 'text', width: 120 }, + { id: 'seats', label: 'Seats', type: 'number', width: 100 }, + { id: 'active', label: 'Active', type: 'boolean', width: 100 }, + ], + rows: [ + { + id: '1', + cells: { + username: 'alice_j', + email: 'alice@acme.com', + plan: 'Pro', + seats: '5', + active: 'true', + }, + }, + { + id: '2', + cells: { + username: 'bobw', + email: 'bob@techco.io', + plan: 'Starter', + seats: '1', + active: 'true', + }, + }, + { + id: '3', + cells: { + username: 'carol_d', + email: 'carol@startup.co', + plan: 'Enterprise', + seats: '25', + active: 'true', + }, + }, + { + id: '4', + cells: { + username: 'dan.m', + email: 'dan@bigcorp.com', + plan: 'Pro', + seats: '10', + active: 'false', + }, + }, + { + id: '5', + cells: { + username: 'eva_chen', + email: 'eva@design.io', + plan: 'Starter', + seats: '1', + active: 'true', + }, + }, + { + id: '6', + cells: { + username: 'frank_lee', + email: 'frank@ventures.co', + plan: 'Enterprise', + seats: '50', + active: 'true', + }, + }, + ], + }, + '5': { + columns: [ + { id: 'invoice', label: 'Invoice #', type: 'text', width: 140 }, + { id: 'client', label: 'Client', type: 'text', width: 160 }, + { id: 'amount', label: 'Amount ($)', type: 'number', width: 130 }, + { id: 'paid', label: 'Paid', type: 'boolean', width: 80 }, + ], + rows: [ + { + id: '1', + cells: { invoice: 'INV-2026-001', client: 'Acme Corp', amount: '4,800.00', paid: 'true' }, + }, + { + id: '2', + cells: { invoice: 'INV-2026-002', client: 'TechCo', amount: '1,200.00', paid: 'true' }, + }, + { + id: '3', + cells: { invoice: 'INV-2026-003', client: 'StartupCo', amount: '750.00', paid: 'false' }, + }, + { + id: '4', + cells: { invoice: 'INV-2026-004', client: 'BigCorp', amount: '12,500.00', paid: 'true' }, + }, + { + id: '5', + cells: { invoice: 'INV-2026-005', client: 'Design IO', amount: '3,300.00', paid: 'false' }, + }, + ], + }, +} + +interface SpreadsheetViewProps { + tableId: string + tableName: string + onBack: () => void +} + +function SpreadsheetView({ tableId, tableName, onBack }: SpreadsheetViewProps) { + const data = SPREADSHEET_DATA[tableId] ?? SPREADSHEET_DATA['1'] + const [selectedCell, setSelectedCell] = useState<{ row: string; col: string } | null>(null) + + return ( +
    + {/* Breadcrumb header — matches real ResourceHeader breadcrumb layout */} +
    +
    +
    + Tables + + / + + {tableName} + + + + + + {/* Spreadsheet — matches exact real table editor structure */} +
    +
    + + + {data.columns.map((col) => ( + + ))} + + + + + ) + })} + + + + {data.rows.map((row, rowIdx) => ( + + + {data.columns.map((col) => { + const isSelected = selectedCell?.row === row.id && selectedCell?.col === col.id + const cellValue = row.cells[col.id] ?? '' + return ( + + ) + })} + + ))} + +
    + {data.columns.map((col) => { + const Icon = COLUMN_TYPE_ICONS[col.type] ?? TypeText + return ( + +
    + + + {col.label} + + +
    +
    + + {rowIdx + 1} + + setSelectedCell({ row: row.id, col: col.id })} + className={cn( + CELL, + 'relative cursor-default text-[var(--text-body)]', + isSelected && 'bg-[rgba(37,99,235,0.06)]' + )} + > + {isSelected &&
    } +
    + {col.type === 'boolean' ? ( +
    + +
    + ) : ( + cellValue + )} +
    +
    + + + ) +} + +export function LandingPreviewTables() { + const [openTableId, setOpenTableId] = useState(null) + + if (openTableId !== null) { + return ( + setOpenTableId(null)} + /> + ) + } + + return ( + setOpenTableId(id)} + /> + ) +} diff --git a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx index 58248fcc72c..262b677e131 100644 --- a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx +++ b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx @@ -228,13 +228,13 @@ export const PreviewBlockNode = memo(function PreviewBlockNode({ {tools && tools.length > 0 && (
    Tools -
    +
    {tools.map((tool) => { const ToolIcon = BLOCK_ICONS[tool.type] return (
    Agent (KB + Notion tools) -> Slack + */ +const CUSTOMER_SUPPORT_WORKFLOW: PreviewWorkflow = { + id: 'wf-customer-support', + name: 'Customer Support Agent', + color: '#0EA5E9', + blocks: [ + { + id: 'gmail-1', + name: 'Gmail', + type: 'gmail', + bgColor: '#E0E0E0', + rows: [ + { title: 'Event', value: 'New Email' }, + { title: 'Label', value: 'Support' }, + ], + position: { x: 80, y: 140 }, + hideTargetHandle: true, + }, + { + id: 'agent-3', + name: 'Support Agent', + type: 'agent', + bgColor: '#701ffc', + rows: [ + { title: 'Model', value: 'gpt-5.4' }, + { title: 'System Prompt', value: 'Resolve customer issues...' }, + ], + tools: [ + { name: 'Knowledge', type: 'knowledge_base', bgColor: '#10B981' }, + { name: 'Notion', type: 'notion', bgColor: '#181C1E' }, + ], + position: { x: 420, y: 40 }, + }, + { + id: 'slack-3', + name: 'Slack', + type: 'slack', + bgColor: '#611f69', + rows: [ + { title: 'Channel', value: '#support' }, + { title: 'Operation', value: 'Send Message' }, + ], + position: { x: 420, y: 260 }, + hideSourceHandle: true, + }, + ], + edges: [ + { id: 'e-cs-1', source: 'gmail-1', target: 'agent-3' }, + { id: 'e-cs-2', source: 'gmail-1', target: 'slack-3' }, + ], +} + /** * Empty "New Agent" workflow — a single note prompting the user to start building */ @@ -153,6 +207,7 @@ const NEW_AGENT_WORKFLOW: PreviewWorkflow = { export const PREVIEW_WORKFLOWS: PreviewWorkflow[] = [ SELF_HEALING_CRM_WORKFLOW, IT_SERVICE_WORKFLOW, + CUSTOMER_SUPPORT_WORKFLOW, NEW_AGENT_WORKFLOW, ] diff --git a/apps/sim/app/(home)/components/landing-preview/landing-preview.tsx b/apps/sim/app/(home)/components/landing-preview/landing-preview.tsx index 646c830a735..bdce9f565ae 100644 --- a/apps/sim/app/(home)/components/landing-preview/landing-preview.tsx +++ b/apps/sim/app/(home)/components/landing-preview/landing-preview.tsx @@ -2,9 +2,15 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { motion, type Variants } from 'framer-motion' +import { LandingPreviewFiles } from '@/app/(home)/components/landing-preview/components/landing-preview-files/landing-preview-files' import { LandingPreviewHome } from '@/app/(home)/components/landing-preview/components/landing-preview-home/landing-preview-home' +import { LandingPreviewKnowledge } from '@/app/(home)/components/landing-preview/components/landing-preview-knowledge/landing-preview-knowledge' +import { LandingPreviewLogs } from '@/app/(home)/components/landing-preview/components/landing-preview-logs/landing-preview-logs' import { LandingPreviewPanel } from '@/app/(home)/components/landing-preview/components/landing-preview-panel/landing-preview-panel' +import { LandingPreviewScheduledTasks } from '@/app/(home)/components/landing-preview/components/landing-preview-scheduled-tasks/landing-preview-scheduled-tasks' +import type { SidebarView } from '@/app/(home)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar' import { LandingPreviewSidebar } from '@/app/(home)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar' +import { LandingPreviewTables } from '@/app/(home)/components/landing-preview/components/landing-preview-tables/landing-preview-tables' import { LandingPreviewWorkflow } from '@/app/(home)/components/landing-preview/components/landing-preview-workflow/landing-preview-workflow' import { EASE_OUT, @@ -46,18 +52,16 @@ const panelVariants: Variants = { * Interactive workspace preview for the hero section. * * Renders a lightweight replica of the Sim workspace with: - * - A sidebar with two selectable workflows + * - A sidebar with selectable workflows and workspace nav items * - A ReactFlow canvas showing the active workflow's blocks and edges + * - Static previews of Tables, Files, Knowledge Base, Logs, and Scheduled Tasks * - A panel with a functional copilot input (stores prompt + redirects to /signup) * - * Everything except the workflow items and the copilot input is non-interactive. - * On mount the sidebar slides from left and the panel from right. The canvas - * background stays fully opaque; individual block nodes animate in with a - * staggered fade. Edges draw left-to-right. Animations only fire on initial - * load — workflow switches render instantly. + * Only workflow items, the home button, workspace nav items, and the copilot input + * are interactive. Animations only fire on initial load. */ export function LandingPreview() { - const [activeView, setActiveView] = useState<'home' | 'workflow'>('workflow') + const [activeView, setActiveView] = useState('workflow') const [activeWorkflowId, setActiveWorkflowId] = useState(PREVIEW_WORKFLOWS[0].id) const isInitialMount = useRef(true) @@ -74,11 +78,34 @@ export function LandingPreview() { setActiveView('home') }, []) + const handleSelectNav = useCallback((id: SidebarView) => { + setActiveView(id) + }, []) + const activeWorkflow = PREVIEW_WORKFLOWS.find((w) => w.id === activeWorkflowId) ?? PREVIEW_WORKFLOWS[0] const isWorkflowView = activeView === 'workflow' + function renderContent() { + switch (activeView) { + case 'workflow': + return + case 'home': + return + case 'tables': + return + case 'files': + return + case 'knowledge': + return + case 'logs': + return + case 'scheduled-tasks': + return + } + } + return (
    @@ -104,11 +132,7 @@ export function LandingPreview() { : 'relative flex min-w-0 flex-1 flex-col overflow-hidden' } > - {isWorkflowView ? ( - - ) : ( - - )} + {renderContent()}
    { code: 'return "Email sent to user"', params: { email: { - from: 'Waleed Latif ', + from: 'Dr. Shaw ', to: 'User ', }, }, @@ -378,7 +378,7 @@ describe('Function Execute API Route', () => { async () => { const emailData = { id: '123', - from: 'Waleed Latif ', + from: 'Dr. Shaw ', to: 'User ', subject: 'Test Email', bodyText: 'Hello world', diff --git a/apps/sim/app/llms-full.txt/route.ts b/apps/sim/app/llms-full.txt/route.ts index 7e89c74c73d..915a2354143 100644 --- a/apps/sim/app/llms-full.txt/route.ts +++ b/apps/sim/app/llms-full.txt/route.ts @@ -9,7 +9,7 @@ export async function GET() { ## Overview -Sim lets teams create agents, workflows, knowledge bases, tables, and docs. Over 100,000 builders use Sim — from startups to Fortune 500 companies. Teams connect their tools and data, build agents that execute real workflows across systems, and manage them with full observability. SOC2 and HIPAA compliant. +Sim lets teams create agents, workflows, knowledge bases, tables, and docs. Over 100,000 builders use Sim — from startups to Fortune 500 companies. Teams connect their tools and data, build agents that execute real workflows across systems, and manage them with full observability. SOC2 compliant. ## Product Details @@ -17,7 +17,7 @@ Sim lets teams create agents, workflows, knowledge bases, tables, and docs. Over - **Category**: AI Agent Platform / Agentic Workflow Orchestration - **Deployment**: Cloud (SaaS) and Self-hosted options - **Pricing**: Free tier, Pro ($25/month, 6K credits), Max ($100/month, 25K credits), Team plans available, Enterprise (custom) -- **Compliance**: SOC2 Type II, HIPAA compliant +- **Compliance**: SOC2 Type II ## Core Concepts diff --git a/apps/sim/app/llms.txt/route.ts b/apps/sim/app/llms.txt/route.ts index 21fefd84c36..79c79d086ec 100644 --- a/apps/sim/app/llms.txt/route.ts +++ b/apps/sim/app/llms.txt/route.ts @@ -7,7 +7,7 @@ export async function GET() { > Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows. -Sim lets teams create agents, workflows, knowledge bases, tables, and docs. Over 100,000 builders use Sim — from startups to Fortune 500 companies. SOC2 and HIPAA compliant. +Sim lets teams create agents, workflows, knowledge bases, tables, and docs. Over 100,000 builders use Sim — from startups to Fortune 500 companies. SOC2 compliant. ## Core Pages diff --git a/apps/sim/app/page.tsx b/apps/sim/app/page.tsx index a873661326e..c01b92647ef 100644 --- a/apps/sim/app/page.tsx +++ b/apps/sim/app/page.tsx @@ -14,7 +14,7 @@ export const metadata: Metadata = { description: 'Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to orchestrate agentic workflows.', keywords: - 'AI agents, agentic workforce, open-source AI agent platform, agentic workflows, LLM orchestration, AI automation, knowledge base, workflow builder, AI integrations, SOC2 compliant, HIPAA compliant, enterprise AI', + 'AI agents, agentic workforce, open-source AI agent platform, agentic workflows, LLM orchestration, AI automation, knowledge base, workflow builder, AI integrations, SOC2 compliant, enterprise AI', authors: [{ name: 'Sim' }], creator: 'Sim', publisher: 'Sim', diff --git a/apps/sim/blocks/blocks/rippling.ts b/apps/sim/blocks/blocks/rippling.ts index ad491435d00..e74133bb67e 100644 --- a/apps/sim/blocks/blocks/rippling.ts +++ b/apps/sim/blocks/blocks/rippling.ts @@ -2,12 +2,199 @@ import { RipplingIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' +/** Operations that support the filter query parameter */ +const FILTER_OPS = ['list_workers', 'list_business_partners', 'list_supergroups'] as const + +/** Operations that support the expand query parameter */ +const EXPAND_OPS = [ + 'list_workers', + 'list_business_partners', + 'list_business_partner_groups', + 'list_companies', + 'list_departments', + 'list_teams', + 'list_supergroup_members', + 'list_supergroup_inclusion_members', + 'list_supergroup_exclusion_members', + 'get_worker', + 'get_business_partner', + 'get_business_partner_group', + 'get_current_user', + 'get_department', + 'get_team', +] as const + +/** Operations that support the order_by query parameter */ +const ORDER_BY_OPS = [ + 'list_workers', + 'list_business_partners', + 'list_business_partner_groups', + 'list_companies', + 'list_custom_fields', + 'list_custom_settings', + 'list_departments', + 'list_employment_types', + 'list_job_functions', + 'list_supergroups', + 'list_teams', + 'list_titles', + 'list_users', + 'list_work_locations', + 'list_supergroup_members', + 'list_supergroup_inclusion_members', + 'list_supergroup_exclusion_members', +] as const + +/** Operations that support cursor pagination */ +const CURSOR_OPS = [ + 'list_workers', + 'list_business_partners', + 'list_business_partner_groups', + 'list_companies', + 'list_custom_fields', + 'list_custom_settings', + 'list_departments', + 'list_employment_types', + 'list_job_functions', + 'list_teams', + 'list_titles', + 'list_users', + 'list_work_locations', + 'query_custom_object_records', +] as const + +/** Operations that require a resource ID */ +const ID_OPS = [ + 'get_worker', + 'get_user', + 'get_department', + 'update_department', + 'get_team', + 'get_employment_type', + 'get_title', + 'update_title', + 'delete_title', + 'get_job_function', + 'get_work_location', + 'update_work_location', + 'delete_work_location', + 'get_business_partner', + 'delete_business_partner', + 'get_business_partner_group', + 'delete_business_partner_group', + 'get_supergroup', + 'list_supergroup_members', + 'list_supergroup_inclusion_members', + 'list_supergroup_exclusion_members', + 'update_supergroup_inclusion_members', + 'update_supergroup_exclusion_members', + 'get_custom_object', + 'update_custom_object', + 'delete_custom_object', + 'get_custom_object_field', + 'update_custom_object_field', + 'delete_custom_object_field', + 'get_custom_object_record', + 'update_custom_object_record', + 'delete_custom_object_record', + 'get_custom_app', + 'update_custom_app', + 'delete_custom_app', + 'get_custom_page', + 'update_custom_page', + 'delete_custom_page', + 'get_custom_setting', + 'update_custom_setting', + 'delete_custom_setting', + 'get_object_category', + 'update_object_category', + 'delete_object_category', + 'get_report_run', + 'trigger_report_run', +] as const + +/** Operations that accept a name field */ +const NAME_OPS = [ + 'create_department', + 'update_department', + 'create_title', + 'update_title', + 'create_work_location', + 'update_work_location', + 'create_business_partner_group', + 'create_custom_object', + 'update_custom_object', + 'create_custom_object_field', + 'update_custom_object_field', + 'create_custom_app', + 'update_custom_app', + 'create_custom_page', + 'update_custom_page', + 'create_object_category', + 'update_object_category', +] as const + +/** Operations that require customObjectId */ +const CUSTOM_OBJECT_ID_OPS = [ + 'list_custom_object_fields', + 'get_custom_object_field', + 'create_custom_object_field', + 'update_custom_object_field', + 'delete_custom_object_field', + 'list_custom_object_records', + 'get_custom_object_record', + 'get_custom_object_record_by_external_id', + 'query_custom_object_records', + 'create_custom_object_record', + 'update_custom_object_record', + 'delete_custom_object_record', + 'bulk_create_custom_object_records', + 'bulk_update_custom_object_records', + 'bulk_delete_custom_object_records', +] as const + +/** Operations that accept a description field */ +const DESCRIPTION_OPS = [ + 'create_custom_object', + 'update_custom_object', + 'create_custom_object_field', + 'update_custom_object_field', + 'create_custom_app', + 'update_custom_app', + 'create_object_category', + 'update_object_category', +] as const + +/** Operations for work location address fields */ +const WORK_LOCATION_WRITE_OPS = ['create_work_location', 'update_work_location'] as const + +/** Operations for custom object create/update */ +const CUSTOM_OBJECT_WRITE_OPS = ['create_custom_object', 'update_custom_object'] as const + +/** Operations for custom object field configuration */ +const CUSTOM_OBJECT_FIELD_WRITE_OPS = [ + 'create_custom_object_field', + 'update_custom_object_field', +] as const + +/** Operations for custom setting fields */ +const CUSTOM_SETTING_WRITE_OPS = ['create_custom_setting', 'update_custom_setting'] as const + +/** Operations where data JSON is passed through directly (not spread) */ +const DATA_PASSTHROUGH_OPS = [ + 'create_custom_object_record', + 'update_custom_object_record', + 'create_draft_hires', + 'update_supergroup_inclusion_members', + 'update_supergroup_exclusion_members', +] as const + export const RipplingBlock: BlockConfig = { type: 'rippling', name: 'Rippling', - description: 'Manage employees, leave, departments, and company data in Rippling', + description: 'Manage workers, departments, custom objects, and company data in Rippling', longDescription: - 'Integrate Rippling into your workflow. Manage employees, departments, teams, leave requests, work locations, groups, candidates, and company information.', + 'Integrate Rippling Platform into your workflow. Manage workers, users, departments, teams, titles, work locations, business partners, supergroups, custom objects, custom apps, custom pages, custom settings, object categories, reports, and draft hires.', docsLink: 'https://docs.sim.ai/tools/rippling', category: 'tools', integrationType: IntegrationType.HR, @@ -22,267 +209,712 @@ export const RipplingBlock: BlockConfig = { title: 'Operation', type: 'dropdown', options: [ - { label: 'List Employees', id: 'list_employees' }, - { label: 'Get Employee', id: 'get_employee' }, - { label: 'List Employees (Including Terminated)', id: 'list_employees_with_terminated' }, + // Workers + { label: 'List Workers', id: 'list_workers' }, + { label: 'Get Worker', id: 'get_worker' }, + // Users + { label: 'List Users', id: 'list_users' }, + { label: 'Get User', id: 'get_user' }, + // Companies + { label: 'List Companies', id: 'list_companies' }, + // Current User + { label: 'Get Current User', id: 'get_current_user' }, + // Entitlements + { label: 'List Entitlements', id: 'list_entitlements' }, + // Departments { label: 'List Departments', id: 'list_departments' }, + { label: 'Get Department', id: 'get_department' }, + { label: 'Create Department', id: 'create_department' }, + { label: 'Update Department', id: 'update_department' }, + // Teams { label: 'List Teams', id: 'list_teams' }, - { label: 'List Levels', id: 'list_levels' }, - { label: 'List Work Locations', id: 'list_work_locations' }, - { label: 'Get Company', id: 'get_company' }, - { label: 'Get Company Activity', id: 'get_company_activity' }, + { label: 'Get Team', id: 'get_team' }, + // Employment Types + { label: 'List Employment Types', id: 'list_employment_types' }, + { label: 'Get Employment Type', id: 'get_employment_type' }, + // Titles + { label: 'List Titles', id: 'list_titles' }, + { label: 'Get Title', id: 'get_title' }, + { label: 'Create Title', id: 'create_title' }, + { label: 'Update Title', id: 'update_title' }, + { label: 'Delete Title', id: 'delete_title' }, + // Custom Fields { label: 'List Custom Fields', id: 'list_custom_fields' }, - { label: 'Get Current User', id: 'get_current_user' }, - { label: 'List Leave Requests', id: 'list_leave_requests' }, - { label: 'Approve/Decline Leave Request', id: 'process_leave_request' }, - { label: 'List Leave Balances', id: 'list_leave_balances' }, - { label: 'Get Leave Balance', id: 'get_leave_balance' }, - { label: 'List Leave Types', id: 'list_leave_types' }, - { label: 'Create Group', id: 'create_group' }, - { label: 'Update Group', id: 'update_group' }, - { label: 'Push Candidate', id: 'push_candidate' }, + // Job Functions + { label: 'List Job Functions', id: 'list_job_functions' }, + { label: 'Get Job Function', id: 'get_job_function' }, + // Work Locations + { label: 'List Work Locations', id: 'list_work_locations' }, + { label: 'Get Work Location', id: 'get_work_location' }, + { label: 'Create Work Location', id: 'create_work_location' }, + { label: 'Update Work Location', id: 'update_work_location' }, + { label: 'Delete Work Location', id: 'delete_work_location' }, + // Business Partners + { label: 'List Business Partners', id: 'list_business_partners' }, + { label: 'Get Business Partner', id: 'get_business_partner' }, + { label: 'Create Business Partner', id: 'create_business_partner' }, + { label: 'Delete Business Partner', id: 'delete_business_partner' }, + // Business Partner Groups + { label: 'List Business Partner Groups', id: 'list_business_partner_groups' }, + { label: 'Get Business Partner Group', id: 'get_business_partner_group' }, + { label: 'Create Business Partner Group', id: 'create_business_partner_group' }, + { label: 'Delete Business Partner Group', id: 'delete_business_partner_group' }, + // Supergroups + { label: 'List Supergroups', id: 'list_supergroups' }, + { label: 'Get Supergroup', id: 'get_supergroup' }, + { label: 'List Supergroup Members', id: 'list_supergroup_members' }, + { label: 'List Supergroup Inclusion Members', id: 'list_supergroup_inclusion_members' }, + { label: 'List Supergroup Exclusion Members', id: 'list_supergroup_exclusion_members' }, + { + label: 'Update Supergroup Inclusion Members', + id: 'update_supergroup_inclusion_members', + }, + { + label: 'Update Supergroup Exclusion Members', + id: 'update_supergroup_exclusion_members', + }, + // Custom Objects + { label: 'List Custom Objects', id: 'list_custom_objects' }, + { label: 'Get Custom Object', id: 'get_custom_object' }, + { label: 'Create Custom Object', id: 'create_custom_object' }, + { label: 'Update Custom Object', id: 'update_custom_object' }, + { label: 'Delete Custom Object', id: 'delete_custom_object' }, + // Custom Object Fields + { label: 'List Custom Object Fields', id: 'list_custom_object_fields' }, + { label: 'Get Custom Object Field', id: 'get_custom_object_field' }, + { label: 'Create Custom Object Field', id: 'create_custom_object_field' }, + { label: 'Update Custom Object Field', id: 'update_custom_object_field' }, + { label: 'Delete Custom Object Field', id: 'delete_custom_object_field' }, + // Custom Object Records + { label: 'List Custom Object Records', id: 'list_custom_object_records' }, + { label: 'Get Custom Object Record', id: 'get_custom_object_record' }, + { + label: 'Get Custom Object Record by External ID', + id: 'get_custom_object_record_by_external_id', + }, + { label: 'Query Custom Object Records', id: 'query_custom_object_records' }, + { label: 'Create Custom Object Record', id: 'create_custom_object_record' }, + { label: 'Update Custom Object Record', id: 'update_custom_object_record' }, + { label: 'Delete Custom Object Record', id: 'delete_custom_object_record' }, + { + label: 'Bulk Create Custom Object Records', + id: 'bulk_create_custom_object_records', + }, + { + label: 'Bulk Update Custom Object Records', + id: 'bulk_update_custom_object_records', + }, + { + label: 'Bulk Delete Custom Object Records', + id: 'bulk_delete_custom_object_records', + }, + // Custom Apps + { label: 'List Custom Apps', id: 'list_custom_apps' }, + { label: 'Get Custom App', id: 'get_custom_app' }, + { label: 'Create Custom App', id: 'create_custom_app' }, + { label: 'Update Custom App', id: 'update_custom_app' }, + { label: 'Delete Custom App', id: 'delete_custom_app' }, + // Custom Pages + { label: 'List Custom Pages', id: 'list_custom_pages' }, + { label: 'Get Custom Page', id: 'get_custom_page' }, + { label: 'Create Custom Page', id: 'create_custom_page' }, + { label: 'Update Custom Page', id: 'update_custom_page' }, + { label: 'Delete Custom Page', id: 'delete_custom_page' }, + // Custom Settings + { label: 'List Custom Settings', id: 'list_custom_settings' }, + { label: 'Get Custom Setting', id: 'get_custom_setting' }, + { label: 'Create Custom Setting', id: 'create_custom_setting' }, + { label: 'Update Custom Setting', id: 'update_custom_setting' }, + { label: 'Delete Custom Setting', id: 'delete_custom_setting' }, + // Object Categories + { label: 'List Object Categories', id: 'list_object_categories' }, + { label: 'Get Object Category', id: 'get_object_category' }, + { label: 'Create Object Category', id: 'create_object_category' }, + { label: 'Update Object Category', id: 'update_object_category' }, + { label: 'Delete Object Category', id: 'delete_object_category' }, + // Report Runs + { label: 'Get Report Run', id: 'get_report_run' }, + { label: 'Trigger Report Run', id: 'trigger_report_run' }, + // Draft Hires + { label: 'Create Draft Hires', id: 'create_draft_hires' }, ], - value: () => 'list_employees', + value: () => 'list_workers', }, - // Employee ID - for get_employee { - id: 'employeeId', - title: 'Employee ID', + id: 'id', + title: 'Resource ID', type: 'short-input', - placeholder: 'Enter the employee ID', - required: { field: 'operation', value: 'get_employee' }, - condition: { field: 'operation', value: 'get_employee' }, + placeholder: 'Enter the resource ID', + condition: { field: 'operation', value: [...ID_OPS] }, + required: { field: 'operation', value: [...ID_OPS] }, }, - // Leave Request fields { - id: 'leaveRequestId', - title: 'Leave Request ID', + id: 'customObjectId', + title: 'Custom Object API Name', type: 'short-input', - placeholder: 'Enter the leave request ID', - required: { field: 'operation', value: 'process_leave_request' }, - condition: { field: 'operation', value: 'process_leave_request' }, + placeholder: 'Enter the custom object API name (e.g. my_object__c)', + condition: { field: 'operation', value: [...CUSTOM_OBJECT_ID_OPS] }, + required: { field: 'operation', value: [...CUSTOM_OBJECT_ID_OPS] }, }, { - id: 'action', - title: 'Action', - type: 'dropdown', - options: [ - { label: 'Approve', id: 'approve' }, - { label: 'Decline', id: 'decline' }, - ], - value: () => 'approve', - required: { field: 'operation', value: 'process_leave_request' }, - condition: { field: 'operation', value: 'process_leave_request' }, + id: 'externalId', + title: 'External ID', + type: 'short-input', + placeholder: 'Enter the external ID', + condition: { + field: 'operation', + value: [ + 'get_custom_object_record_by_external_id', + 'create_custom_object_record', + 'update_custom_object_record', + ], + }, + required: { field: 'operation', value: 'get_custom_object_record_by_external_id' }, }, - // Leave balance - role ID { - id: 'roleId', - title: 'Employee/Role ID', + id: 'name', + title: 'Name', type: 'short-input', - placeholder: 'Enter the employee or role ID', - required: { field: 'operation', value: 'get_leave_balance' }, - condition: { field: 'operation', value: 'get_leave_balance' }, + placeholder: 'Enter the resource name', + condition: { field: 'operation', value: [...NAME_OPS] }, + required: { + field: 'operation', + value: [ + 'create_department', + 'create_title', + 'create_work_location', + 'create_business_partner_group', + 'create_custom_object', + 'create_custom_object_field', + 'create_custom_app', + 'create_custom_page', + 'create_object_category', + ], + }, }, - // Group fields + // Department fields { - id: 'groupName', - title: 'Group Name', + id: 'parentId', + title: 'Parent ID', type: 'short-input', - placeholder: 'Enter group name', - required: { field: 'operation', value: 'create_group' }, - condition: { field: 'operation', value: ['create_group', 'update_group'] }, + placeholder: 'Enter the parent resource ID', + mode: 'advanced', + condition: { + field: 'operation', + value: ['create_department', 'update_department'], + }, }, { - id: 'spokeId', - title: 'Spoke ID', + id: 'referenceCode', + title: 'Reference Code', type: 'short-input', - placeholder: 'Third-party app identifier', - required: { field: 'operation', value: 'create_group' }, - condition: { field: 'operation', value: ['create_group', 'update_group'] }, + placeholder: 'Enter reference code', + mode: 'advanced', + condition: { + field: 'operation', + value: ['create_department', 'update_department'], + }, }, + // Description (shared across many create/update operations) { - id: 'groupId', - title: 'Group ID', + id: 'description', + title: 'Description', type: 'short-input', - placeholder: 'Enter the group ID to update', - required: { field: 'operation', value: 'update_group' }, - condition: { field: 'operation', value: 'update_group' }, + placeholder: 'Enter a description', + mode: 'advanced', + condition: { field: 'operation', value: [...DESCRIPTION_OPS] }, }, + // Custom App fields { - id: 'users', - title: 'User IDs', - type: 'long-input', - placeholder: '["user-id-1", "user-id-2"]', + id: 'apiName', + title: 'API Name', + type: 'short-input', + placeholder: 'Enter the API name (e.g. my_app)', + condition: { field: 'operation', value: ['create_custom_app', 'update_custom_app'] }, + required: { field: 'operation', value: 'create_custom_app' }, + }, + // Business Partner fields + { + id: 'businessPartnerGroupId', + title: 'Business Partner Group ID', + type: 'short-input', + placeholder: 'Enter the business partner group ID', + condition: { field: 'operation', value: 'create_business_partner' }, + required: { field: 'operation', value: 'create_business_partner' }, + }, + { + id: 'workerId', + title: 'Worker ID', + type: 'short-input', + placeholder: 'Enter the worker ID', + condition: { field: 'operation', value: 'create_business_partner' }, + required: { field: 'operation', value: 'create_business_partner' }, + }, + // Business Partner Group fields + { + id: 'domain', + title: 'Domain', + type: 'dropdown', + options: [ + { label: 'HR', id: 'HR' }, + { label: 'IT', id: 'IT' }, + { label: 'Finance', id: 'FINANCE' }, + { label: 'Recruiting', id: 'RECRUITING' }, + { label: 'Other', id: 'OTHER' }, + ], mode: 'advanced', - condition: { field: 'operation', value: ['create_group', 'update_group'] }, + condition: { field: 'operation', value: 'create_business_partner_group' }, }, { - id: 'groupVersion', - title: 'Version', + id: 'defaultBusinessPartnerId', + title: 'Default Business Partner ID', type: 'short-input', - placeholder: 'Group version number', + placeholder: 'Enter the default business partner worker ID', mode: 'advanced', - condition: { field: 'operation', value: 'update_group' }, + condition: { field: 'operation', value: 'create_business_partner_group' }, }, - // Candidate fields + // Work Location address fields { - id: 'firstName', - title: 'First Name', + id: 'streetAddress', + title: 'Street Address', type: 'short-input', - placeholder: 'Candidate first name', - required: { field: 'operation', value: 'push_candidate' }, - condition: { field: 'operation', value: 'push_candidate' }, + placeholder: 'Enter the street address', + condition: { field: 'operation', value: [...WORK_LOCATION_WRITE_OPS] }, + required: { field: 'operation', value: 'create_work_location' }, }, { - id: 'lastName', - title: 'Last Name', + id: 'locality', + title: 'City', type: 'short-input', - placeholder: 'Candidate last name', - required: { field: 'operation', value: 'push_candidate' }, - condition: { field: 'operation', value: 'push_candidate' }, + placeholder: 'Enter the city', + mode: 'advanced', + condition: { field: 'operation', value: [...WORK_LOCATION_WRITE_OPS] }, }, { - id: 'email', - title: 'Email', + id: 'region', + title: 'State/Region', type: 'short-input', - placeholder: 'Candidate email address', - required: { field: 'operation', value: 'push_candidate' }, - condition: { field: 'operation', value: 'push_candidate' }, + placeholder: 'Enter the state or region', + mode: 'advanced', + condition: { field: 'operation', value: [...WORK_LOCATION_WRITE_OPS] }, }, { - id: 'candidatePhone', - title: 'Phone', + id: 'postalCode', + title: 'Postal Code', type: 'short-input', - placeholder: 'Candidate phone number', + placeholder: 'Enter the postal code', mode: 'advanced', - condition: { field: 'operation', value: 'push_candidate' }, + condition: { field: 'operation', value: [...WORK_LOCATION_WRITE_OPS] }, }, { - id: 'jobTitle', - title: 'Job Title', + id: 'addressCountry', + title: 'Country', type: 'short-input', - placeholder: 'Job title for the candidate', + placeholder: 'Enter the country code (e.g. US)', + mode: 'advanced', + condition: { field: 'operation', value: [...WORK_LOCATION_WRITE_OPS] }, + }, + { + id: 'addressType', + title: 'Address Type', + type: 'dropdown', + options: [ + { label: 'Home', id: 'HOME' }, + { label: 'Work', id: 'WORK' }, + { label: 'Other', id: 'OTHER' }, + ], mode: 'advanced', - condition: { field: 'operation', value: 'push_candidate' }, + condition: { field: 'operation', value: [...WORK_LOCATION_WRITE_OPS] }, }, + // Custom Object fields { - id: 'candidateDepartment', - title: 'Department', + id: 'category', + title: 'Category', type: 'short-input', - placeholder: 'Department for the candidate', + placeholder: 'Enter the category ID', mode: 'advanced', - condition: { field: 'operation', value: 'push_candidate' }, + condition: { field: 'operation', value: [...CUSTOM_OBJECT_WRITE_OPS] }, }, { - id: 'candidateStartDate', - title: 'Start Date', + id: 'pluralLabel', + title: 'Plural Label', type: 'short-input', - placeholder: 'YYYY-MM-DD', + placeholder: 'Enter the plural label', mode: 'advanced', - condition: { field: 'operation', value: 'push_candidate' }, - wandConfig: { - enabled: true, - prompt: - 'Generate a date in YYYY-MM-DD format for a candidate start date. Return ONLY the date string - no explanations, no extra text.', - generationType: 'timestamp', - }, + condition: { field: 'operation', value: 'update_custom_object' }, }, - // Date filters for leave requests and company activity { - id: 'startDate', - title: 'Start Date', + id: 'ownerRole', + title: 'Owner Role', type: 'short-input', - placeholder: 'YYYY-MM-DD', + placeholder: 'Enter the owner role', mode: 'advanced', - condition: { field: 'operation', value: ['list_leave_requests', 'get_company_activity'] }, + condition: { field: 'operation', value: 'update_custom_object' }, + }, + // Custom Object Field configuration + { + id: 'dataType', + title: 'Data Type', + type: 'long-input', + placeholder: '{ "type": "TEXT" }', + condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] }, + required: { field: 'operation', value: 'create_custom_object_field' }, wandConfig: { enabled: true, - prompt: - 'Generate a date in YYYY-MM-DD format for filtering by start date. Return ONLY the date string - no explanations, no extra text.', - generationType: 'timestamp', + prompt: `Generate a JSON object for a Rippling custom object field data type based on the user's description. +Common types: TEXT, NUMBER, BOOLEAN, DATE, DATETIME, LOOKUP, FORMULA, ROLLUP. +Examples: +- "text field" -> { "type": "TEXT" } +- "number field" -> { "type": "NUMBER" } +- "date field" -> { "type": "DATE" } +- "lookup to workers" -> { "type": "LOOKUP", "referenced_object": "worker" } +Return ONLY the JSON - no explanations, no extra text.`, + placeholder: 'Describe the field type', }, }, { - id: 'endDate', - title: 'End Date', + id: 'fieldRequired', + title: 'Required', + type: 'dropdown', + options: [ + { label: 'No', id: 'false' }, + { label: 'Yes', id: 'true' }, + ], + mode: 'advanced', + condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] }, + }, + { + id: 'isUnique', + title: 'Unique', + type: 'dropdown', + options: [ + { label: 'No', id: 'false' }, + { label: 'Yes', id: 'true' }, + ], + mode: 'advanced', + condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] }, + }, + { + id: 'enableHistory', + title: 'Enable History', + type: 'dropdown', + options: [ + { label: 'No', id: 'false' }, + { label: 'Yes', id: 'true' }, + ], + mode: 'advanced', + condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] }, + }, + { + id: 'rqlDefinition', + title: 'RQL Definition', + type: 'long-input', + placeholder: '{ ... }', + mode: 'advanced', + condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] }, + }, + { + id: 'formulaAttrMetas', + title: 'Formula Attr Metas', + type: 'long-input', + placeholder: '{ ... }', + mode: 'advanced', + condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] }, + }, + { + id: 'fieldSection', + title: 'Section', + type: 'long-input', + placeholder: '{ ... }', + mode: 'advanced', + condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] }, + }, + { + id: 'derivedFieldFormula', + title: 'Derived Field Formula', type: 'short-input', - placeholder: 'YYYY-MM-DD', + placeholder: 'Enter the formula expression', mode: 'advanced', - condition: { field: 'operation', value: ['list_leave_requests', 'get_company_activity'] }, - wandConfig: { - enabled: true, - prompt: - 'Generate a date in YYYY-MM-DD format for filtering by end date. Return ONLY the date string - no explanations, no extra text.', - generationType: 'timestamp', - }, + condition: { field: 'operation', value: [...CUSTOM_OBJECT_FIELD_WRITE_OPS] }, + }, + { + id: 'derivedAggregatedField', + title: 'Derived Aggregated Field', + type: 'long-input', + placeholder: '{ ... }', + mode: 'advanced', + condition: { field: 'operation', value: 'create_custom_object_field' }, + }, + { + id: 'nameFieldDetails', + title: 'Name Field Details', + type: 'long-input', + placeholder: '{ ... }', + mode: 'advanced', + condition: { field: 'operation', value: 'update_custom_object_field' }, + }, + // Custom Setting fields + { + id: 'displayName', + title: 'Display Name', + type: 'short-input', + placeholder: 'Enter the display name', + condition: { field: 'operation', value: [...CUSTOM_SETTING_WRITE_OPS] }, }, { - id: 'status', - title: 'Status Filter', + id: 'settingApiName', + title: 'Setting API Name', type: 'short-input', - placeholder: 'e.g., pending, approved, declined', + placeholder: 'Enter the unique API name', mode: 'advanced', - condition: { field: 'operation', value: 'list_leave_requests' }, + condition: { field: 'operation', value: [...CUSTOM_SETTING_WRITE_OPS] }, }, { - id: 'managedBy', - title: 'Managed By', + id: 'settingDataType', + title: 'Setting Data Type', type: 'short-input', - placeholder: 'Filter by manager', + placeholder: 'Enter the data type', mode: 'advanced', - condition: { field: 'operation', value: 'list_leave_types' }, + condition: { field: 'operation', value: [...CUSTOM_SETTING_WRITE_OPS] }, }, - // Pagination - shared across list operations (offset-based) { - id: 'limit', - title: 'Limit', + id: 'secretValue', + title: 'Secret Value', + type: 'short-input', + placeholder: 'Enter the secret value', + password: true, + mode: 'advanced', + condition: { field: 'operation', value: [...CUSTOM_SETTING_WRITE_OPS] }, + }, + { + id: 'stringValue', + title: 'String Value', + type: 'short-input', + placeholder: 'Enter the string value', + condition: { field: 'operation', value: [...CUSTOM_SETTING_WRITE_OPS] }, + }, + { + id: 'settingNumberValue', + title: 'Number Value', type: 'short-input', - placeholder: '100', + placeholder: 'Enter the number value', + mode: 'advanced', + condition: { field: 'operation', value: [...CUSTOM_SETTING_WRITE_OPS] }, + }, + { + id: 'settingBooleanValue', + title: 'Boolean Value', + type: 'dropdown', + options: [ + { label: 'False', id: 'false' }, + { label: 'True', id: 'true' }, + ], + mode: 'advanced', + condition: { field: 'operation', value: [...CUSTOM_SETTING_WRITE_OPS] }, + }, + // Report Run fields + { + id: 'includeObjectIds', + title: 'Include Object IDs', + type: 'dropdown', + options: [ + { label: 'No', id: 'false' }, + { label: 'Yes', id: 'true' }, + ], + mode: 'advanced', + condition: { field: 'operation', value: 'trigger_report_run' }, + }, + { + id: 'includeTotalRows', + title: 'Include Total Rows', + type: 'dropdown', + options: [ + { label: 'No', id: 'false' }, + { label: 'Yes', id: 'true' }, + ], + mode: 'advanced', + condition: { field: 'operation', value: 'trigger_report_run' }, + }, + { + id: 'formatDateFields', + title: 'Format Date Fields', + type: 'long-input', + placeholder: '{ "date_format": "ISO_8601" }', + mode: 'advanced', + condition: { field: 'operation', value: 'trigger_report_run' }, + }, + { + id: 'formatCurrencyFields', + title: 'Format Currency Fields', + type: 'long-input', + placeholder: '{ ... }', mode: 'advanced', + condition: { field: 'operation', value: 'trigger_report_run' }, + }, + { + id: 'outputType', + title: 'Output Type', + type: 'dropdown', + options: [ + { label: 'JSON', id: 'JSON' }, + { label: 'CSV', id: 'CSV' }, + ], + mode: 'advanced', + condition: { field: 'operation', value: 'trigger_report_run' }, + }, + // Data JSON - only for passthrough operations (custom object records, draft hires, supergroup ops) + { + id: 'data', + title: 'Data', + type: 'long-input', + placeholder: '{ "key": "value" }', + condition: { field: 'operation', value: [...DATA_PASSTHROUGH_OPS] }, + wandConfig: { + enabled: true, + prompt: `Generate a JSON object for a Rippling API request based on the user's description. +For custom object records: { "field_name": "value", ... } +For draft hires: { "draft_hires": [{ "personal_info": { "first_name": "", "last_name": "", "email": "" }, "employment_info": { "start_date": "", "title": "" }, "work_location_info": { "work_location_id": "" } }] } +For supergroup member updates: { "Operations": [{ "op": "add", "path": "members", "value": [{ "value": "worker_id" }] }] } +Return ONLY the JSON - no explanations, no extra text.`, + placeholder: 'Describe the data to send', + }, + required: { + field: 'operation', + value: [ + 'create_custom_object_record', + 'create_draft_hires', + 'update_supergroup_inclusion_members', + 'update_supergroup_exclusion_members', + ], + }, + }, + // Records JSON for bulk operations + { + id: 'records', + title: 'Records', + type: 'long-input', + placeholder: '[{ "data": { ... } }, ...]', condition: { field: 'operation', value: [ - 'list_employees', - 'list_employees_with_terminated', - 'list_departments', - 'list_teams', - 'list_levels', - 'list_work_locations', - 'list_custom_fields', - 'list_leave_balances', - 'get_company_activity', + 'bulk_create_custom_object_records', + 'bulk_update_custom_object_records', + 'bulk_delete_custom_object_records', + ], + }, + required: { + field: 'operation', + value: [ + 'bulk_create_custom_object_records', + 'bulk_update_custom_object_records', + 'bulk_delete_custom_object_records', ], }, }, { - id: 'offset', - title: 'Offset', - type: 'short-input', - placeholder: '0', + id: 'allOrNothing', + title: 'All Or Nothing', + type: 'dropdown', + options: [ + { label: 'No (partial success allowed)', id: 'false' }, + { label: 'Yes (fail entire batch on any error)', id: 'true' }, + ], + value: () => 'false', mode: 'advanced', condition: { field: 'operation', value: [ - 'list_employees', - 'list_employees_with_terminated', - 'list_departments', - 'list_teams', - 'list_levels', - 'list_work_locations', - 'list_custom_fields', - 'list_leave_balances', + 'bulk_create_custom_object_records', + 'bulk_update_custom_object_records', + 'bulk_delete_custom_object_records', ], }, }, - // Cursor-based pagination for company activity + // Query fields for custom object record queries + { + id: 'query', + title: 'Query', + type: 'long-input', + placeholder: 'Enter query expression', + condition: { field: 'operation', value: 'query_custom_object_records' }, + wandConfig: { + enabled: true, + prompt: `Generate a query expression for the Rippling Custom Object Records Query API based on the user's description. +The query should filter custom object records by their field values. +Return ONLY the query expression - no explanations, no extra text.`, + placeholder: 'Describe which records to find', + }, + }, { - id: 'nextCursor', - title: 'Next Page Cursor', + id: 'limit', + title: 'Limit', type: 'short-input', - placeholder: 'Cursor from previous response', + placeholder: 'Max results to return', mode: 'advanced', - condition: { field: 'operation', value: 'get_company_activity' }, + condition: { field: 'operation', value: 'query_custom_object_records' }, }, + // Common query parameters + { + id: 'filter', + title: 'Filter', + type: 'short-input', + placeholder: 'OData filter expression', + mode: 'advanced', + condition: { field: 'operation', value: [...FILTER_OPS] }, + wandConfig: { + enabled: true, + prompt: `Generate an OData filter expression for the Rippling API based on the user's description. +Examples: +- "active workers" -> status eq 'ACTIVE' +- "workers in engineering" -> department eq 'Engineering' +- "created after January" -> created_at gt '2024-01-01' +Return ONLY the filter expression - no explanations, no extra text.`, + placeholder: 'Describe what to filter for', + }, + }, + { + id: 'expand', + title: 'Expand', + type: 'short-input', + placeholder: 'Fields to expand', + mode: 'advanced', + condition: { field: 'operation', value: [...EXPAND_OPS] }, + wandConfig: { + enabled: true, + prompt: `Generate a comma-separated list of fields to expand for a Rippling API request based on the user's description. +Common expandable fields: department, parent, worker, business_partner_group, client_group, parent_legal_entity, legal_entities, department_hierarchy. +Return ONLY the comma-separated field names - no explanations, no extra text.`, + placeholder: 'Describe which related data to include', + }, + }, + { + id: 'orderBy', + title: 'Order By', + type: 'short-input', + placeholder: 'e.g., name asc', + mode: 'advanced', + condition: { field: 'operation', value: [...ORDER_BY_OPS] }, + wandConfig: { + enabled: true, + prompt: `Generate a sort expression for the Rippling API based on the user's description. +Format: field_name for ascending, -field_name for descending. Common fields: id, created_at, updated_at, name. +Examples: +- "newest first" -> -created_at +- "alphabetical" -> name +- "recently updated" -> -updated_at +Return ONLY the sort expression - no explanations, no extra text.`, + placeholder: 'Describe how to sort results', + }, + }, + { + id: 'cursor', + title: 'Cursor', + type: 'short-input', + placeholder: 'Pagination cursor from previous response', + mode: 'advanced', + condition: { field: 'operation', value: [...CURSOR_OPS] }, + }, + // API Key { id: 'apiKey', title: 'API Key', @@ -295,25 +927,116 @@ export const RipplingBlock: BlockConfig = { tools: { access: [ - 'rippling_list_employees', - 'rippling_get_employee', - 'rippling_list_employees_with_terminated', + // Workers + 'rippling_list_workers', + 'rippling_get_worker', + // Users + 'rippling_list_users', + 'rippling_get_user', + // Companies + 'rippling_list_companies', + // Current User + 'rippling_get_current_user', + // Entitlements + 'rippling_list_entitlements', + // Departments 'rippling_list_departments', + 'rippling_get_department', + 'rippling_create_department', + 'rippling_update_department', + // Teams 'rippling_list_teams', - 'rippling_list_levels', - 'rippling_list_work_locations', - 'rippling_get_company', - 'rippling_get_company_activity', + 'rippling_get_team', + // Employment Types + 'rippling_list_employment_types', + 'rippling_get_employment_type', + // Titles + 'rippling_list_titles', + 'rippling_get_title', + 'rippling_create_title', + 'rippling_update_title', + 'rippling_delete_title', + // Custom Fields 'rippling_list_custom_fields', - 'rippling_get_current_user', - 'rippling_list_leave_requests', - 'rippling_process_leave_request', - 'rippling_list_leave_balances', - 'rippling_get_leave_balance', - 'rippling_list_leave_types', - 'rippling_create_group', - 'rippling_update_group', - 'rippling_push_candidate', + // Job Functions + 'rippling_list_job_functions', + 'rippling_get_job_function', + // Work Locations + 'rippling_list_work_locations', + 'rippling_get_work_location', + 'rippling_create_work_location', + 'rippling_update_work_location', + 'rippling_delete_work_location', + // Business Partners + 'rippling_list_business_partners', + 'rippling_get_business_partner', + 'rippling_create_business_partner', + 'rippling_delete_business_partner', + // Business Partner Groups + 'rippling_list_business_partner_groups', + 'rippling_get_business_partner_group', + 'rippling_create_business_partner_group', + 'rippling_delete_business_partner_group', + // Supergroups + 'rippling_list_supergroups', + 'rippling_get_supergroup', + 'rippling_list_supergroup_members', + 'rippling_list_supergroup_inclusion_members', + 'rippling_list_supergroup_exclusion_members', + 'rippling_update_supergroup_inclusion_members', + 'rippling_update_supergroup_exclusion_members', + // Custom Objects + 'rippling_list_custom_objects', + 'rippling_get_custom_object', + 'rippling_create_custom_object', + 'rippling_update_custom_object', + 'rippling_delete_custom_object', + // Custom Object Fields + 'rippling_list_custom_object_fields', + 'rippling_get_custom_object_field', + 'rippling_create_custom_object_field', + 'rippling_update_custom_object_field', + 'rippling_delete_custom_object_field', + // Custom Object Records + 'rippling_list_custom_object_records', + 'rippling_get_custom_object_record', + 'rippling_get_custom_object_record_by_external_id', + 'rippling_query_custom_object_records', + 'rippling_create_custom_object_record', + 'rippling_update_custom_object_record', + 'rippling_delete_custom_object_record', + 'rippling_bulk_create_custom_object_records', + 'rippling_bulk_update_custom_object_records', + 'rippling_bulk_delete_custom_object_records', + // Custom Apps + 'rippling_list_custom_apps', + 'rippling_get_custom_app', + 'rippling_create_custom_app', + 'rippling_update_custom_app', + 'rippling_delete_custom_app', + // Custom Pages + 'rippling_list_custom_pages', + 'rippling_get_custom_page', + 'rippling_create_custom_page', + 'rippling_update_custom_page', + 'rippling_delete_custom_page', + // Custom Settings + 'rippling_list_custom_settings', + 'rippling_get_custom_setting', + 'rippling_create_custom_setting', + 'rippling_update_custom_setting', + 'rippling_delete_custom_setting', + // Object Categories + 'rippling_list_object_categories', + 'rippling_get_object_category', + 'rippling_create_object_category', + 'rippling_update_object_category', + 'rippling_delete_object_category', + // Report Runs + 'rippling_get_report_run', + 'rippling_trigger_report_run', + // Draft Hires + 'rippling_create_draft_hires', ], config: { tool: (params) => `rippling_${params.operation}`, @@ -321,44 +1044,321 @@ export const RipplingBlock: BlockConfig = { const mapped: Record = { apiKey: params.apiKey, } + const op = params.operation as string - if (params.employeeId) mapped.employeeId = params.employeeId - if (params.leaveRequestId) mapped.leaveRequestId = params.leaveRequestId - if (params.action) mapped.action = params.action - if (params.roleId) mapped.roleId = params.roleId - if (params.spokeId) mapped.spokeId = params.spokeId - if (params.groupId) mapped.groupId = params.groupId - if (params.firstName) mapped.firstName = params.firstName - if (params.lastName) mapped.lastName = params.lastName - if (params.email) mapped.email = params.email - if (params.jobTitle) mapped.jobTitle = params.jobTitle - if (params.startDate && params.operation !== 'push_candidate') - mapped.startDate = params.startDate - if (params.endDate && params.operation !== 'push_candidate') mapped.endDate = params.endDate - if (params.status) mapped.status = params.status - if (params.managedBy) mapped.managedBy = params.managedBy + // Common fields + if (params.id != null && params.id !== '') mapped.id = params.id + if (params.customObjectId != null && params.customObjectId !== '') + mapped.customObjectId = params.customObjectId + if (params.externalId != null && params.externalId !== '') + mapped.externalId = params.externalId + if (params.name != null && params.name !== '') mapped.name = params.name + if (params.parentId != null && params.parentId !== '') mapped.parentId = params.parentId + if (params.referenceCode != null && params.referenceCode !== '') + mapped.referenceCode = params.referenceCode + if (params.description != null && params.description !== '') + mapped.description = params.description + if (params.apiName != null && params.apiName !== '') mapped.apiName = params.apiName - if (params.limit != null && params.limit !== '') mapped.limit = Number(params.limit) - if (params.offset != null && params.offset !== '') mapped.offset = Number(params.offset) - if (params.groupVersion != null && params.groupVersion !== '') - mapped.version = Number(params.groupVersion) - if (params.groupName) mapped.name = params.groupName - if (params.candidatePhone) mapped.phone = params.candidatePhone - if (params.candidateDepartment) mapped.department = params.candidateDepartment - if (params.candidateStartDate && params.operation === 'push_candidate') - mapped.startDate = params.candidateStartDate - if (params.nextCursor) mapped.next = params.nextCursor - - if (params.users) { + // Business Partner fields + if (params.businessPartnerGroupId != null && params.businessPartnerGroupId !== '') + mapped.businessPartnerGroupId = params.businessPartnerGroupId + if (params.workerId != null && params.workerId !== '') mapped.workerId = params.workerId + + // Business Partner Group fields + if (params.domain != null && params.domain !== '') mapped.domain = params.domain + if (params.defaultBusinessPartnerId != null && params.defaultBusinessPartnerId !== '') + mapped.defaultBusinessPartnerId = params.defaultBusinessPartnerId + + // Work Location address fields + if (params.streetAddress != null && params.streetAddress !== '') + mapped.streetAddress = params.streetAddress + if (params.locality != null && params.locality !== '') mapped.locality = params.locality + if (params.region != null && params.region !== '') mapped.region = params.region + if (params.postalCode != null && params.postalCode !== '') + mapped.postalCode = params.postalCode + if (params.addressCountry != null && params.addressCountry !== '') + mapped.country = params.addressCountry + if (params.addressType != null && params.addressType !== '') + mapped.addressType = params.addressType + + // Custom Object fields + if (params.category != null && params.category !== '') mapped.category = params.category + if (params.pluralLabel != null && params.pluralLabel !== '') + mapped.pluralLabel = params.pluralLabel + if (params.ownerRole != null && params.ownerRole !== '') mapped.ownerRole = params.ownerRole + + // Custom Object Field configuration + if (params.dataType != null && params.dataType !== '') { try { - mapped.users = - typeof params.users === 'string' ? JSON.parse(params.users) : params.users + mapped.dataType = + typeof params.dataType === 'string' ? JSON.parse(params.dataType) : params.dataType } catch { throw new Error( - 'Invalid JSON for "User IDs" field. Expected an array like ["user-id-1", "user-id-2"].' + 'Invalid JSON in "Data Type" field. Expected a valid JSON object like { "type": "TEXT" }.' ) } } + if ( + params.fieldRequired != null && + params.fieldRequired !== '' && + params.fieldRequired !== 'false' + ) + mapped.required = params.fieldRequired === 'true' + if (params.isUnique != null && params.isUnique !== '' && params.isUnique !== 'false') + mapped.isUnique = params.isUnique === 'true' + if ( + params.enableHistory != null && + params.enableHistory !== '' && + params.enableHistory !== 'false' + ) + mapped.enableHistory = params.enableHistory === 'true' + if (params.rqlDefinition != null && params.rqlDefinition !== '') { + try { + mapped.rqlDefinition = + typeof params.rqlDefinition === 'string' + ? JSON.parse(params.rqlDefinition) + : params.rqlDefinition + } catch { + throw new Error('Invalid JSON in "RQL Definition" field.') + } + } + if (params.formulaAttrMetas != null && params.formulaAttrMetas !== '') { + try { + mapped.formulaAttrMetas = + typeof params.formulaAttrMetas === 'string' + ? JSON.parse(params.formulaAttrMetas) + : params.formulaAttrMetas + } catch { + throw new Error('Invalid JSON in "Formula Attr Metas" field.') + } + } + if (params.fieldSection != null && params.fieldSection !== '') { + try { + mapped.section = + typeof params.fieldSection === 'string' + ? JSON.parse(params.fieldSection) + : params.fieldSection + } catch { + throw new Error('Invalid JSON in "Section" field.') + } + } + if (params.derivedFieldFormula != null && params.derivedFieldFormula !== '') + mapped.derivedFieldFormula = params.derivedFieldFormula + if (params.derivedAggregatedField != null && params.derivedAggregatedField !== '') { + try { + mapped.derivedAggregatedField = + typeof params.derivedAggregatedField === 'string' + ? JSON.parse(params.derivedAggregatedField) + : params.derivedAggregatedField + } catch { + throw new Error('Invalid JSON in "Derived Aggregated Field" field.') + } + } + if (params.nameFieldDetails != null && params.nameFieldDetails !== '') { + try { + mapped.nameFieldDetails = + typeof params.nameFieldDetails === 'string' + ? JSON.parse(params.nameFieldDetails) + : params.nameFieldDetails + } catch { + throw new Error('Invalid JSON in "Name Field Details" field.') + } + } + + // Custom Setting fields + if (params.displayName != null && params.displayName !== '') + mapped.displayName = params.displayName + if (params.settingApiName != null && params.settingApiName !== '') + mapped.apiName = params.settingApiName + if (params.settingDataType != null && params.settingDataType !== '') + mapped.dataType = params.settingDataType + if (params.secretValue != null && params.secretValue !== '') + mapped.secretValue = params.secretValue + if (params.stringValue != null && params.stringValue !== '') + mapped.stringValue = params.stringValue + if (params.settingNumberValue != null && params.settingNumberValue !== '') + mapped.numberValue = Number(params.settingNumberValue) + if (params.settingBooleanValue != null && params.settingBooleanValue !== '') + mapped.booleanValue = params.settingBooleanValue === 'true' + + // Report Run fields + if ( + params.includeObjectIds != null && + params.includeObjectIds !== '' && + params.includeObjectIds !== 'false' + ) + mapped.includeObjectIds = params.includeObjectIds === 'true' + if ( + params.includeTotalRows != null && + params.includeTotalRows !== '' && + params.includeTotalRows !== 'false' + ) + mapped.includeTotalRows = params.includeTotalRows === 'true' + if (params.formatDateFields != null && params.formatDateFields !== '') { + try { + mapped.formatDateFields = + typeof params.formatDateFields === 'string' + ? JSON.parse(params.formatDateFields) + : params.formatDateFields + } catch { + throw new Error('Invalid JSON in "Format Date Fields" field.') + } + } + if (params.formatCurrencyFields != null && params.formatCurrencyFields !== '') { + try { + mapped.formatCurrencyFields = + typeof params.formatCurrencyFields === 'string' + ? JSON.parse(params.formatCurrencyFields) + : params.formatCurrencyFields + } catch { + throw new Error('Invalid JSON in "Format Currency Fields" field.') + } + } + if (params.outputType != null && params.outputType !== '') + mapped.outputType = params.outputType + + // All Or Nothing for bulk operations + if ( + params.allOrNothing != null && + params.allOrNothing !== '' && + params.allOrNothing !== 'false' + ) + mapped.allOrNothing = params.allOrNothing === 'true' + + // Common query parameters + if (params.filter != null && params.filter !== '') mapped.filter = params.filter + if (params.expand != null && params.expand !== '') mapped.expand = params.expand + if (params.orderBy != null && params.orderBy !== '') mapped.orderBy = params.orderBy + if (params.cursor != null && params.cursor !== '') mapped.cursor = params.cursor + + // Query and limit for custom object record queries + if (params.query != null && params.query !== '') mapped.query = params.query + if (params.limit != null && params.limit !== '') mapped.limit = Number(params.limit) + + // Data JSON - only for passthrough operations + if (params.data != null && params.data !== '') { + try { + mapped.data = typeof params.data === 'string' ? JSON.parse(params.data) : params.data + } catch { + throw new Error('Invalid JSON in "Data (JSON)" field.') + } + } + + // Records JSON for bulk operations + if (params.records != null && params.records !== '') { + try { + mapped.records = + typeof params.records === 'string' ? JSON.parse(params.records) : params.records + } catch { + throw new Error('Invalid JSON in "Records (JSON)" field.') + } + } + + // --- ID remapping --- + + // Custom object tools expect customObjectApiName, not customObjectId + if (mapped.customObjectId != null) { + mapped.customObjectApiName = mapped.customObjectId + mapped.customObjectId = undefined + } + + // Supergroup member tools expect groupId, not id + if ( + [ + 'list_supergroup_members', + 'list_supergroup_inclusion_members', + 'list_supergroup_exclusion_members', + 'update_supergroup_inclusion_members', + 'update_supergroup_exclusion_members', + ].includes(op) + ) { + if (mapped.id != null) { + mapped.groupId = mapped.id + mapped.id = undefined + } + } + + // Custom object get/update/delete expect customObjectApiName for the object itself + if (['get_custom_object', 'update_custom_object', 'delete_custom_object'].includes(op)) { + if (mapped.id != null) { + mapped.customObjectApiName = mapped.id + mapped.id = undefined + } + } + + // Custom object field tools expect fieldApiName, not id + if ( + [ + 'get_custom_object_field', + 'update_custom_object_field', + 'delete_custom_object_field', + ].includes(op) + ) { + if (mapped.id != null) { + mapped.fieldApiName = mapped.id + mapped.id = undefined + } + } + + // Custom object record tools expect codrId, not id + if ( + [ + 'get_custom_object_record', + 'update_custom_object_record', + 'delete_custom_object_record', + ].includes(op) + ) { + if (mapped.id != null) { + mapped.codrId = mapped.id + mapped.id = undefined + } + } + + // Report run tools + if (op === 'get_report_run') { + if (mapped.id != null) { + mapped.runId = mapped.id + mapped.id = undefined + } + } + if (op === 'trigger_report_run') { + if (mapped.id != null) { + mapped.reportId = mapped.id + mapped.id = undefined + } + } + + // Bulk operations: map records to specific param names + if (op === 'bulk_create_custom_object_records' && mapped.records != null) { + mapped.rowsToWrite = mapped.records + mapped.records = undefined + } + if (op === 'bulk_update_custom_object_records' && mapped.records != null) { + mapped.rowsToUpdate = mapped.records + mapped.records = undefined + } + if (op === 'bulk_delete_custom_object_records' && mapped.records != null) { + mapped.rowsToDelete = mapped.records + mapped.records = undefined + } + + // Draft hires: map data to draftHires + if (op === 'create_draft_hires' && mapped.data != null) { + mapped.draftHires = mapped.data + mapped.data = undefined + } + + // Supergroup member updates: map data to operations + if ( + ['update_supergroup_inclusion_members', 'update_supergroup_exclusion_members'].includes( + op + ) && + mapped.data != null + ) { + mapped.operations = mapped.data + mapped.data = undefined + } return mapped }, @@ -367,71 +1367,109 @@ export const RipplingBlock: BlockConfig = { inputs: { operation: { type: 'string', description: 'Operation to perform' }, - employeeId: { type: 'string', description: 'Employee ID' }, - leaveRequestId: { type: 'string', description: 'Leave request ID' }, - action: { type: 'string', description: 'Action to take (approve or decline)' }, - roleId: { type: 'string', description: 'Employee/role ID for leave balance' }, - groupName: { type: 'string', description: 'Group name' }, - spokeId: { type: 'string', description: 'Third-party app identifier' }, - groupId: { type: 'string', description: 'Group ID to update' }, - users: { type: 'json', description: 'Array of user IDs' }, - firstName: { type: 'string', description: 'Candidate first name' }, - lastName: { type: 'string', description: 'Candidate last name' }, - email: { type: 'string', description: 'Candidate email' }, - candidatePhone: { type: 'string', description: 'Candidate phone number' }, - jobTitle: { type: 'string', description: 'Job title' }, - candidateDepartment: { type: 'string', description: 'Department' }, - candidateStartDate: { type: 'string', description: 'Start date (ISO format)' }, - startDate: { type: 'string', description: 'Filter start date' }, - endDate: { type: 'string', description: 'Filter end date' }, - status: { type: 'string', description: 'Leave request status filter' }, - managedBy: { type: 'string', description: 'Filter leave types by manager' }, - limit: { type: 'number', description: 'Maximum number of results' }, - offset: { type: 'number', description: 'Pagination offset' }, - nextCursor: { type: 'string', description: 'Cursor for next page (company activity)' }, + id: { type: 'string', description: 'Resource ID' }, + customObjectId: { type: 'string', description: 'Custom object API name' }, + externalId: { type: 'string', description: 'External ID' }, + name: { type: 'string', description: 'Resource name' }, + parentId: { type: 'string', description: 'Parent resource ID' }, + referenceCode: { type: 'string', description: 'Reference code' }, + description: { type: 'string', description: 'Resource description' }, + apiName: { type: 'string', description: 'API name for custom app' }, + businessPartnerGroupId: { + type: 'string', + description: 'Business partner group ID', + }, + workerId: { type: 'string', description: 'Worker ID' }, + domain: { type: 'string', description: 'Business partner group domain' }, + defaultBusinessPartnerId: { type: 'string', description: 'Default business partner ID' }, + streetAddress: { type: 'string', description: 'Street address' }, + locality: { type: 'string', description: 'City' }, + region: { type: 'string', description: 'State/Region' }, + postalCode: { type: 'string', description: 'Postal code' }, + addressCountry: { type: 'string', description: 'Country code' }, + addressType: { type: 'string', description: 'Address type' }, + category: { type: 'string', description: 'Category' }, + pluralLabel: { type: 'string', description: 'Plural label' }, + ownerRole: { type: 'string', description: 'Owner role' }, + dataType: { type: 'json', description: 'Data type configuration' }, + fieldRequired: { type: 'boolean', description: 'Whether the field is required' }, + isUnique: { type: 'boolean', description: 'Whether the field is unique' }, + enableHistory: { type: 'boolean', description: 'Enable history tracking' }, + rqlDefinition: { type: 'json', description: 'RQL definition' }, + formulaAttrMetas: { type: 'json', description: 'Formula attribute metadata' }, + fieldSection: { type: 'json', description: 'Section configuration' }, + derivedFieldFormula: { type: 'string', description: 'Derived field formula' }, + derivedAggregatedField: { type: 'json', description: 'Derived aggregated field' }, + nameFieldDetails: { type: 'json', description: 'Name field details' }, + displayName: { type: 'string', description: 'Display name' }, + settingApiName: { type: 'string', description: 'Setting API name' }, + settingDataType: { type: 'string', description: 'Setting data type' }, + secretValue: { type: 'string', description: 'Secret value' }, + stringValue: { type: 'string', description: 'String value' }, + settingNumberValue: { type: 'number', description: 'Number value' }, + settingBooleanValue: { type: 'boolean', description: 'Boolean value' }, + includeObjectIds: { type: 'boolean', description: 'Include object IDs in report' }, + includeTotalRows: { type: 'boolean', description: 'Include total row count' }, + formatDateFields: { type: 'json', description: 'Date field formatting' }, + formatCurrencyFields: { type: 'json', description: 'Currency field formatting' }, + outputType: { type: 'string', description: 'Report output type' }, + data: { type: 'json', description: 'JSON data for custom object records' }, + records: { type: 'json', description: 'JSON array for bulk operations' }, + allOrNothing: { type: 'boolean', description: 'Fail entire batch on any error' }, + query: { type: 'string', description: 'Query expression' }, + limit: { type: 'number', description: 'Max results' }, + filter: { type: 'string', description: 'OData filter expression' }, + expand: { type: 'string', description: 'Fields to expand' }, + orderBy: { type: 'string', description: 'Ordering expression' }, + cursor: { type: 'string', description: 'Pagination cursor' }, apiKey: { type: 'string', description: 'Rippling API key' }, }, outputs: { - employees: { - type: 'array', - description: - 'List of employees (id, firstName, lastName, workEmail, roleState, department, title)', - }, - departments: { type: 'array', description: 'List of departments (id, name, parent)' }, - teams: { type: 'array', description: 'List of teams (id, name, parent)' }, - levels: { type: 'array', description: 'List of position levels (id, name, parent)' }, - workLocations: { - type: 'array', - description: 'List of work locations (id, nickname, street, city, state, zip, country)', - }, - customFields: { - type: 'array', - description: 'List of custom fields (id, type, title, mandatory)', - }, - events: { - type: 'array', - description: 'List of company activity events (id, type, description, createdAt, actor)', - }, - leaveRequests: { - type: 'array', - description: 'List of leave requests (id, requestedBy, status, startDate, endDate)', - }, - leaveBalances: { type: 'array', description: 'List of leave balances (employeeId, balances)' }, - leaveTypes: { type: 'array', description: 'List of leave types (id, name, managedBy)' }, - totalCount: { type: 'number', description: 'Total number of items returned' }, id: { type: 'string', description: 'Resource ID' }, name: { type: 'string', description: 'Resource name' }, - workEmail: { type: 'string', description: 'Work email address' }, - company: { type: 'string', description: 'Company ID' }, - status: { type: 'string', description: 'Status of the resource' }, - users: { type: 'array', description: 'Array of user IDs in a group' }, - version: { type: 'number', description: 'Group version number' }, - address: { type: 'json', description: 'Company address (street, city, state, zip, country)' }, - email: { type: 'string', description: 'Email address' }, - phone: { type: 'string', description: 'Phone number' }, - balances: { type: 'array', description: 'Leave balance entries (leaveType, minutesRemaining)' }, - employeeId: { type: 'string', description: 'Employee ID' }, - nextCursor: { type: 'string', description: 'Cursor for fetching the next page of results' }, + status: { type: 'string', description: 'Resource status' }, + created_at: { type: 'string', description: 'Creation timestamp' }, + updated_at: { type: 'string', description: 'Last update timestamp' }, + workers: { type: 'array', description: 'List of workers' }, + users: { type: 'array', description: 'List of users' }, + companies: { type: 'array', description: 'List of companies' }, + departments: { type: 'array', description: 'List of departments' }, + teams: { type: 'array', description: 'List of teams' }, + titles: { type: 'array', description: 'List of titles' }, + workLocations: { type: 'array', description: 'List of work locations' }, + employmentTypes: { type: 'array', description: 'List of employment types' }, + jobFunctions: { type: 'array', description: 'List of job functions' }, + entitlements: { type: 'array', description: 'List of entitlements' }, + customFields: { type: 'array', description: 'List of custom fields' }, + supergroups: { type: 'array', description: 'List of supergroups' }, + members: { type: 'array', description: 'List of group members' }, + businessPartners: { type: 'array', description: 'List of business partners' }, + businessPartnerGroups: { type: 'array', description: 'List of business partner groups' }, + customObjects: { type: 'array', description: 'List of custom objects' }, + fields: { type: 'array', description: 'List of custom object fields' }, + records: { type: 'array', description: 'List of custom object records' }, + customApps: { type: 'array', description: 'List of custom apps' }, + customPages: { type: 'array', description: 'List of custom pages' }, + customSettings: { type: 'array', description: 'List of custom settings' }, + objectCategories: { type: 'array', description: 'List of object categories' }, + totalCount: { type: 'number', description: 'Total number of items returned' }, + nextLink: { type: 'string', description: 'URL or cursor for the next page of results' }, + cursor: { type: 'string', description: 'Cursor for next page of query results' }, + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + createdRecords: { type: 'array', description: 'Bulk created custom object records' }, + updatedRecords: { type: 'array', description: 'Bulk updated custom object records' }, + report_id: { type: 'string', description: 'Report ID' }, + file_url: { type: 'string', description: 'URL to download the report file' }, + expires_at: { type: 'string', description: 'Expiration timestamp for the file URL' }, + output_type: { type: 'string', description: 'Report output format (JSON or CSV)' }, + invalidItems: { type: 'array', description: 'Invalid items from draft hires' }, + successfulResults: { type: 'array', description: 'Successful draft hire results' }, + totalInvalid: { type: 'number', description: 'Count of invalid items' }, + totalSuccessful: { type: 'number', description: 'Count of successful items' }, + success: { type: 'boolean', description: 'Whether the operation succeeded' }, + ok: { type: 'boolean', description: 'Whether the supergroup member update succeeded' }, + data: { type: 'json', description: 'Record data including custom fields' }, + __meta: { type: 'json', description: 'Metadata including redacted_fields' }, }, } diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index f89c7fddc07..6614c9f6036 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -3680,34 +3680,6 @@ export const SOC2BadgeIcon = (props: SVGProps) => ( ) -export function HIPAABadgeIcon(props: SVGProps) { - const id = useId() - const clipId = `hipaa_clip_${id}` - - return ( - - - - - - - - - - - - - ) -} - export function GoogleFormsIcon(props: SVGProps) { return ( diff --git a/apps/sim/content/blog/enterprise/index.mdx b/apps/sim/content/blog/enterprise/index.mdx index f97fbeef10e..d979d568c23 100644 --- a/apps/sim/content/blog/enterprise/index.mdx +++ b/apps/sim/content/blog/enterprise/index.mdx @@ -130,8 +130,6 @@ This is useful for internal platforms, customer-facing deployments, or any scena Sim maintains **SOC 2 Type II** certification with annual audits covering security, availability, and confidentiality controls. We share our SOC 2 report directly with prospective customers under NDA. -**HIPAA** — Business Associate Agreements available for healthcare organizations. Requires self-hosted deployment or dedicated infrastructure. - **Data Retention** — Configure how long workflow execution traces, inputs, and outputs are stored before automatic deletion. We work with enterprise customers to set retention policies that match their compliance requirements. We provide penetration test reports, architecture documentation, and completed security questionnaires (SIG, CAIQ, and custom formats) for your vendor review process. diff --git a/apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx b/apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx index f570590bd5e..9026829f56f 100644 --- a/apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx +++ b/apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx @@ -163,7 +163,7 @@ Beyond the 80+ built-in integrations, Sim supports the Model Context Protocol (M #### Flexible Deployment Options -Sim offers both cloud-hosted and self-hosted deployment options. Organizations can run Sim on their own infrastructure for complete control, or use the managed cloud service for simplicity. The platform is SOC2 and HIPAA compliant, ensuring enterprise-level security. +Sim offers both cloud-hosted and self-hosted deployment options. Organizations can run Sim on their own infrastructure for complete control, or use the managed cloud service for simplicity. The platform is SOC2 compliant, ensuring enterprise-level security. #### Production-Ready Infrastructure diff --git a/apps/sim/ee/whitelabeling/metadata.ts b/apps/sim/ee/whitelabeling/metadata.ts index a3c58182b4f..16dce2da97d 100644 --- a/apps/sim/ee/whitelabeling/metadata.ts +++ b/apps/sim/ee/whitelabeling/metadata.ts @@ -9,7 +9,7 @@ export function generateBrandedMetadata(override: Partial = {}): Metad const brand = getBrandConfig() const defaultTitle = brand.name - const summaryFull = `Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows. Create agents, workflows, knowledge bases, tables, and docs. Trusted by over 100,000 builders — from startups to Fortune 500 companies. SOC2 and HIPAA compliant.` + const summaryFull = `Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows. Create agents, workflows, knowledge bases, tables, and docs. Trusted by over 100,000 builders — from startups to Fortune 500 companies. SOC2 compliant.` const summaryShort = `Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows.` return { @@ -132,7 +132,7 @@ export function generateStructuredData() { '@type': 'SoftwareApplication', name: 'Sim', description: - 'Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows. Create agents, workflows, knowledge bases, tables, and docs. Trusted by over 100,000 builders. SOC2 and HIPAA compliant.', + 'Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows. Create agents, workflows, knowledge bases, tables, and docs. Trusted by over 100,000 builders. SOC2 compliant.', url: getBaseUrl(), applicationCategory: 'BusinessApplication', operatingSystem: 'Web', diff --git a/apps/sim/lib/workflows/migrations/subblock-migrations.ts b/apps/sim/lib/workflows/migrations/subblock-migrations.ts index fa0cc41ddcb..34ebb293333 100644 --- a/apps/sim/lib/workflows/migrations/subblock-migrations.ts +++ b/apps/sim/lib/workflows/migrations/subblock-migrations.ts @@ -27,6 +27,30 @@ export const SUBBLOCK_ID_MIGRATIONS: Record> = { emailType: '_removed_emailType', phoneType: '_removed_phoneType', }, + rippling: { + action: '_removed_action', + candidateDepartment: '_removed_candidateDepartment', + candidatePhone: '_removed_candidatePhone', + candidateStartDate: '_removed_candidateStartDate', + email: '_removed_email', + employeeId: '_removed_employeeId', + endDate: '_removed_endDate', + firstName: '_removed_firstName', + groupId: '_removed_groupId', + groupName: '_removed_groupName', + groupVersion: '_removed_groupVersion', + jobTitle: '_removed_jobTitle', + lastName: '_removed_lastName', + leaveRequestId: '_removed_leaveRequestId', + managedBy: '_removed_managedBy', + nextCursor: '_removed_nextCursor', + offset: '_removed_offset', + roleId: '_removed_roleId', + spokeId: '_removed_spokeId', + startDate: '_removed_startDate', + status: '_removed_status', + users: '_removed_users', + }, } /** diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index a9618191982..09da0ce4bf5 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -1898,25 +1898,92 @@ import { revenuecatUpdateSubscriberAttributesTool, } from '@/tools/revenuecat' import { - ripplingCreateGroupTool, - ripplingGetCompanyActivityTool, - ripplingGetCompanyTool, + ripplingBulkCreateCustomObjectRecordsTool, + ripplingBulkDeleteCustomObjectRecordsTool, + ripplingBulkUpdateCustomObjectRecordsTool, + ripplingCreateBusinessPartnerGroupTool, + ripplingCreateBusinessPartnerTool, + ripplingCreateCustomAppTool, + ripplingCreateCustomObjectFieldTool, + ripplingCreateCustomObjectRecordTool, + ripplingCreateCustomObjectTool, + ripplingCreateCustomPageTool, + ripplingCreateCustomSettingTool, + ripplingCreateDepartmentTool, + ripplingCreateDraftHiresTool, + ripplingCreateObjectCategoryTool, + ripplingCreateTitleTool, + ripplingCreateWorkLocationTool, + ripplingDeleteBusinessPartnerGroupTool, + ripplingDeleteBusinessPartnerTool, + ripplingDeleteCustomAppTool, + ripplingDeleteCustomObjectFieldTool, + ripplingDeleteCustomObjectRecordTool, + ripplingDeleteCustomObjectTool, + ripplingDeleteCustomPageTool, + ripplingDeleteCustomSettingTool, + ripplingDeleteObjectCategoryTool, + ripplingDeleteTitleTool, + ripplingDeleteWorkLocationTool, + ripplingGetBusinessPartnerGroupTool, + ripplingGetBusinessPartnerTool, ripplingGetCurrentUserTool, - ripplingGetEmployeeTool, - ripplingGetLeaveBalanceTool, + ripplingGetCustomAppTool, + ripplingGetCustomObjectFieldTool, + ripplingGetCustomObjectRecordByExternalIdTool, + ripplingGetCustomObjectRecordTool, + ripplingGetCustomObjectTool, + ripplingGetCustomPageTool, + ripplingGetCustomSettingTool, + ripplingGetDepartmentTool, + ripplingGetEmploymentTypeTool, + ripplingGetJobFunctionTool, + ripplingGetObjectCategoryTool, + ripplingGetReportRunTool, + ripplingGetSupergroupTool, + ripplingGetTeamTool, + ripplingGetTitleTool, + ripplingGetUserTool, + ripplingGetWorkerTool, + ripplingGetWorkLocationTool, + ripplingListBusinessPartnerGroupsTool, + ripplingListBusinessPartnersTool, + ripplingListCompaniesTool, + ripplingListCustomAppsTool, ripplingListCustomFieldsTool, + ripplingListCustomObjectFieldsTool, + ripplingListCustomObjectRecordsTool, + ripplingListCustomObjectsTool, + ripplingListCustomPagesTool, + ripplingListCustomSettingsTool, ripplingListDepartmentsTool, - ripplingListEmployeesTool, - ripplingListEmployeesWithTerminatedTool, - ripplingListLeaveBalancesTool, - ripplingListLeaveRequestsTool, - ripplingListLeaveTypesTool, - ripplingListLevelsTool, + ripplingListEmploymentTypesTool, + ripplingListEntitlementsTool, + ripplingListJobFunctionsTool, + ripplingListObjectCategoriesTool, + ripplingListSupergroupExclusionMembersTool, + ripplingListSupergroupInclusionMembersTool, + ripplingListSupergroupMembersTool, + ripplingListSupergroupsTool, ripplingListTeamsTool, + ripplingListTitlesTool, + ripplingListUsersTool, + ripplingListWorkersTool, ripplingListWorkLocationsTool, - ripplingProcessLeaveRequestTool, - ripplingPushCandidateTool, - ripplingUpdateGroupTool, + ripplingQueryCustomObjectRecordsTool, + ripplingTriggerReportRunTool, + ripplingUpdateCustomAppTool, + ripplingUpdateCustomObjectFieldTool, + ripplingUpdateCustomObjectRecordTool, + ripplingUpdateCustomObjectTool, + ripplingUpdateCustomPageTool, + ripplingUpdateCustomSettingTool, + ripplingUpdateDepartmentTool, + ripplingUpdateObjectCategoryTool, + ripplingUpdateSupergroupExclusionMembersTool, + ripplingUpdateSupergroupInclusionMembersTool, + ripplingUpdateTitleTool, + ripplingUpdateWorkLocationTool, } from '@/tools/rippling' import { s3CopyObjectTool, @@ -3621,25 +3688,92 @@ export const tools: Record = { revenuecat_defer_google_subscription: revenuecatDeferGoogleSubscriptionTool, revenuecat_refund_google_subscription: revenuecatRefundGoogleSubscriptionTool, revenuecat_revoke_google_subscription: revenuecatRevokeGoogleSubscriptionTool, - rippling_create_group: ripplingCreateGroupTool, - rippling_get_company: ripplingGetCompanyTool, - rippling_get_company_activity: ripplingGetCompanyActivityTool, + rippling_bulk_create_custom_object_records: ripplingBulkCreateCustomObjectRecordsTool, + rippling_bulk_delete_custom_object_records: ripplingBulkDeleteCustomObjectRecordsTool, + rippling_bulk_update_custom_object_records: ripplingBulkUpdateCustomObjectRecordsTool, + rippling_create_business_partner: ripplingCreateBusinessPartnerTool, + rippling_create_business_partner_group: ripplingCreateBusinessPartnerGroupTool, + rippling_create_custom_app: ripplingCreateCustomAppTool, + rippling_create_custom_object: ripplingCreateCustomObjectTool, + rippling_create_custom_object_field: ripplingCreateCustomObjectFieldTool, + rippling_create_custom_object_record: ripplingCreateCustomObjectRecordTool, + rippling_create_custom_page: ripplingCreateCustomPageTool, + rippling_create_custom_setting: ripplingCreateCustomSettingTool, + rippling_create_department: ripplingCreateDepartmentTool, + rippling_create_draft_hires: ripplingCreateDraftHiresTool, + rippling_create_object_category: ripplingCreateObjectCategoryTool, + rippling_create_title: ripplingCreateTitleTool, + rippling_create_work_location: ripplingCreateWorkLocationTool, + rippling_delete_business_partner: ripplingDeleteBusinessPartnerTool, + rippling_delete_business_partner_group: ripplingDeleteBusinessPartnerGroupTool, + rippling_delete_custom_app: ripplingDeleteCustomAppTool, + rippling_delete_custom_object: ripplingDeleteCustomObjectTool, + rippling_delete_custom_object_field: ripplingDeleteCustomObjectFieldTool, + rippling_delete_custom_object_record: ripplingDeleteCustomObjectRecordTool, + rippling_delete_custom_page: ripplingDeleteCustomPageTool, + rippling_delete_custom_setting: ripplingDeleteCustomSettingTool, + rippling_delete_object_category: ripplingDeleteObjectCategoryTool, + rippling_delete_title: ripplingDeleteTitleTool, + rippling_delete_work_location: ripplingDeleteWorkLocationTool, + rippling_get_business_partner: ripplingGetBusinessPartnerTool, + rippling_get_business_partner_group: ripplingGetBusinessPartnerGroupTool, rippling_get_current_user: ripplingGetCurrentUserTool, - rippling_get_employee: ripplingGetEmployeeTool, - rippling_get_leave_balance: ripplingGetLeaveBalanceTool, + rippling_get_custom_app: ripplingGetCustomAppTool, + rippling_get_custom_object: ripplingGetCustomObjectTool, + rippling_get_custom_object_field: ripplingGetCustomObjectFieldTool, + rippling_get_custom_object_record: ripplingGetCustomObjectRecordTool, + rippling_get_custom_object_record_by_external_id: ripplingGetCustomObjectRecordByExternalIdTool, + rippling_get_custom_page: ripplingGetCustomPageTool, + rippling_get_custom_setting: ripplingGetCustomSettingTool, + rippling_get_department: ripplingGetDepartmentTool, + rippling_get_employment_type: ripplingGetEmploymentTypeTool, + rippling_get_job_function: ripplingGetJobFunctionTool, + rippling_get_object_category: ripplingGetObjectCategoryTool, + rippling_get_report_run: ripplingGetReportRunTool, + rippling_get_supergroup: ripplingGetSupergroupTool, + rippling_get_team: ripplingGetTeamTool, + rippling_get_title: ripplingGetTitleTool, + rippling_get_user: ripplingGetUserTool, + rippling_get_work_location: ripplingGetWorkLocationTool, + rippling_get_worker: ripplingGetWorkerTool, + rippling_list_business_partner_groups: ripplingListBusinessPartnerGroupsTool, + rippling_list_business_partners: ripplingListBusinessPartnersTool, + rippling_list_companies: ripplingListCompaniesTool, + rippling_list_custom_apps: ripplingListCustomAppsTool, rippling_list_custom_fields: ripplingListCustomFieldsTool, + rippling_list_custom_object_fields: ripplingListCustomObjectFieldsTool, + rippling_list_custom_object_records: ripplingListCustomObjectRecordsTool, + rippling_list_custom_objects: ripplingListCustomObjectsTool, + rippling_list_custom_pages: ripplingListCustomPagesTool, + rippling_list_custom_settings: ripplingListCustomSettingsTool, rippling_list_departments: ripplingListDepartmentsTool, - rippling_list_employees: ripplingListEmployeesTool, - rippling_list_employees_with_terminated: ripplingListEmployeesWithTerminatedTool, - rippling_list_leave_balances: ripplingListLeaveBalancesTool, - rippling_list_leave_requests: ripplingListLeaveRequestsTool, - rippling_list_leave_types: ripplingListLeaveTypesTool, - rippling_list_levels: ripplingListLevelsTool, + rippling_list_employment_types: ripplingListEmploymentTypesTool, + rippling_list_entitlements: ripplingListEntitlementsTool, + rippling_list_job_functions: ripplingListJobFunctionsTool, + rippling_list_object_categories: ripplingListObjectCategoriesTool, + rippling_list_supergroup_exclusion_members: ripplingListSupergroupExclusionMembersTool, + rippling_list_supergroup_inclusion_members: ripplingListSupergroupInclusionMembersTool, + rippling_list_supergroup_members: ripplingListSupergroupMembersTool, + rippling_list_supergroups: ripplingListSupergroupsTool, rippling_list_teams: ripplingListTeamsTool, + rippling_list_titles: ripplingListTitlesTool, + rippling_list_users: ripplingListUsersTool, rippling_list_work_locations: ripplingListWorkLocationsTool, - rippling_process_leave_request: ripplingProcessLeaveRequestTool, - rippling_push_candidate: ripplingPushCandidateTool, - rippling_update_group: ripplingUpdateGroupTool, + rippling_list_workers: ripplingListWorkersTool, + rippling_query_custom_object_records: ripplingQueryCustomObjectRecordsTool, + rippling_trigger_report_run: ripplingTriggerReportRunTool, + rippling_update_custom_app: ripplingUpdateCustomAppTool, + rippling_update_custom_object: ripplingUpdateCustomObjectTool, + rippling_update_custom_object_field: ripplingUpdateCustomObjectFieldTool, + rippling_update_custom_object_record: ripplingUpdateCustomObjectRecordTool, + rippling_update_custom_page: ripplingUpdateCustomPageTool, + rippling_update_custom_setting: ripplingUpdateCustomSettingTool, + rippling_update_department: ripplingUpdateDepartmentTool, + rippling_update_object_category: ripplingUpdateObjectCategoryTool, + rippling_update_supergroup_exclusion_members: ripplingUpdateSupergroupExclusionMembersTool, + rippling_update_supergroup_inclusion_members: ripplingUpdateSupergroupInclusionMembersTool, + rippling_update_title: ripplingUpdateTitleTool, + rippling_update_work_location: ripplingUpdateWorkLocationTool, google_drive_copy: googleDriveCopyTool, google_drive_create_folder: googleDriveCreateFolderTool, google_drive_delete: googleDriveDeleteTool, diff --git a/apps/sim/tools/rippling/bulk_create_custom_object_records.ts b/apps/sim/tools/rippling/bulk_create_custom_object_records.ts new file mode 100644 index 00000000000..95bc28cda66 --- /dev/null +++ b/apps/sim/tools/rippling/bulk_create_custom_object_records.ts @@ -0,0 +1,70 @@ +import type { RipplingBulkCreateCustomObjectRecordsParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingBulkCreateCustomObjectRecordsTool: ToolConfig = + { + id: 'rippling_bulk_create_custom_object_records', + name: 'Rippling Bulk Create Custom Object Records', + description: 'Bulk create custom object records', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + rowsToWrite: { + type: 'json', + required: true, + visibility: 'user-or-llm', + description: 'Array of records to create [{external_id?, data}]', + }, + allOrNothing: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'If true, fail entire batch on any error', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/bulk/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { rows_to_write: params.rowsToWrite } + if (params.allOrNothing != null) body.all_or_nothing = params.allOrNothing + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const createdRecords = data.data ?? [] + return { + success: true, + output: { + createdRecords, + totalCount: Array.isArray(createdRecords) ? createdRecords.length : 0, + }, + } + }, + outputs: { + createdRecords: { type: 'array', description: 'Created custom object records' }, + totalCount: { type: 'number', description: 'Number of records created' }, + }, + } diff --git a/apps/sim/tools/rippling/bulk_delete_custom_object_records.ts b/apps/sim/tools/rippling/bulk_delete_custom_object_records.ts new file mode 100644 index 00000000000..d041eab3acf --- /dev/null +++ b/apps/sim/tools/rippling/bulk_delete_custom_object_records.ts @@ -0,0 +1,64 @@ +import type { RipplingBulkDeleteCustomObjectRecordsParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingBulkDeleteCustomObjectRecordsTool: ToolConfig = + { + id: 'rippling_bulk_delete_custom_object_records', + name: 'Rippling Bulk Delete Custom Object Records', + description: 'Bulk delete custom object records', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + rowsToDelete: { + type: 'json', + required: true, + visibility: 'user-or-llm', + description: 'Array of records to delete', + }, + allOrNothing: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'If true, fail entire batch on any error', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/bulk-delete/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { rows_to_delete: params.rowsToDelete } + if (params.allOrNothing != null) body.all_or_nothing = params.allOrNothing + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { + success: true, + output: { deleted: true }, + } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the bulk delete succeeded' }, + }, + } diff --git a/apps/sim/tools/rippling/bulk_update_custom_object_records.ts b/apps/sim/tools/rippling/bulk_update_custom_object_records.ts new file mode 100644 index 00000000000..535a5aeccec --- /dev/null +++ b/apps/sim/tools/rippling/bulk_update_custom_object_records.ts @@ -0,0 +1,70 @@ +import type { RipplingBulkUpdateCustomObjectRecordsParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingBulkUpdateCustomObjectRecordsTool: ToolConfig = + { + id: 'rippling_bulk_update_custom_object_records', + name: 'Rippling Bulk Update Custom Object Records', + description: 'Bulk update custom object records', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + rowsToUpdate: { + type: 'json', + required: true, + visibility: 'user-or-llm', + description: 'Array of records to update', + }, + allOrNothing: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'If true, fail entire batch on any error', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/bulk/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { rows_to_update: params.rowsToUpdate } + if (params.allOrNothing != null) body.all_or_nothing = params.allOrNothing + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const updatedRecords = data.data ?? [] + return { + success: true, + output: { + updatedRecords, + totalCount: Array.isArray(updatedRecords) ? updatedRecords.length : 0, + }, + } + }, + outputs: { + updatedRecords: { type: 'array', description: 'Updated custom object records' }, + totalCount: { type: 'number', description: 'Number of records updated' }, + }, + } diff --git a/apps/sim/tools/rippling/create_business_partner.ts b/apps/sim/tools/rippling/create_business_partner.ts new file mode 100644 index 00000000000..819ecedb01f --- /dev/null +++ b/apps/sim/tools/rippling/create_business_partner.ts @@ -0,0 +1,76 @@ +import type { RipplingCreateBusinessPartnerParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateBusinessPartnerTool: ToolConfig = { + id: 'rippling_create_business_partner', + name: 'Rippling Create Business Partner', + description: 'Create a new business partner', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + businessPartnerGroupId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Business partner group ID', + }, + workerId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Worker ID', + }, + }, + request: { + url: `https://rest.ripplingapis.com/business-partners/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + return { + business_partner_group_id: params.businessPartnerGroupId, + worker_id: params.workerId, + } + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + business_partner_group_id: (data.business_partner_group_id as string) ?? null, + worker_id: (data.worker_id as string) ?? null, + client_group_id: (data.client_group_id as string) ?? null, + client_group_member_count: (data.client_group_member_count as number) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + business_partner_group_id: { type: 'string', description: 'Group ID', optional: true }, + worker_id: { type: 'string', description: 'Worker ID', optional: true }, + client_group_id: { type: 'string', description: 'Client group ID', optional: true }, + client_group_member_count: { + type: 'number', + description: 'Client group member count', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/rippling/create_business_partner_group.ts b/apps/sim/tools/rippling/create_business_partner_group.ts new file mode 100644 index 00000000000..00391117906 --- /dev/null +++ b/apps/sim/tools/rippling/create_business_partner_group.ts @@ -0,0 +1,82 @@ +import type { RipplingCreateBusinessPartnerGroupParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateBusinessPartnerGroupTool: ToolConfig = + { + id: 'rippling_create_business_partner_group', + name: 'Rippling Create Business Partner Group', + description: 'Create a new business partner group', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Group name', + }, + domain: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Domain (HR, IT, FINANCE, RECRUITING, OTHER)', + }, + defaultBusinessPartnerId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Default business partner ID', + }, + }, + request: { + url: `https://rest.ripplingapis.com/business-partner-groups/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { name: params.name } + if (params.domain != null) body.domain = params.domain + if (params.defaultBusinessPartnerId != null) + body.default_business_partner_id = params.defaultBusinessPartnerId + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + domain: (data.domain as string) ?? null, + default_business_partner_id: (data.default_business_partner_id as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + domain: { type: 'string', description: 'Domain', optional: true }, + default_business_partner_id: { + type: 'string', + description: 'Default partner ID', + optional: true, + }, + }, + } diff --git a/apps/sim/tools/rippling/create_custom_app.ts b/apps/sim/tools/rippling/create_custom_app.ts new file mode 100644 index 00000000000..574ca00abc3 --- /dev/null +++ b/apps/sim/tools/rippling/create_custom_app.ts @@ -0,0 +1,69 @@ +import type { RipplingCreateCustomAppParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateCustomAppTool: ToolConfig = { + id: 'rippling_create_custom_app', + name: 'Rippling Create Custom App', + description: 'Create a new custom app', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + name: { type: 'string', required: true, visibility: 'user-or-llm', description: 'App name' }, + apiName: { type: 'string', required: true, visibility: 'user-or-llm', description: 'API name' }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description', + }, + }, + request: { + url: `https://rest.ripplingapis.com/custom-apps/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { name: params.name, api_name: params.apiName } + if (params.description != null) body.description = params.description + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + api_name: (data.api_name as string) ?? null, + description: (data.description as string) ?? null, + icon: (data.icon as string) ?? null, + pages: (data.pages as unknown[]) ?? [], + }, + } + }, + outputs: { + id: { type: 'string', description: 'App ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + api_name: { type: 'string', description: 'API name', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + icon: { type: 'string', description: 'Icon URL', optional: true }, + pages: { type: 'json', description: 'Array of page summaries', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/create_custom_object.ts b/apps/sim/tools/rippling/create_custom_object.ts new file mode 100644 index 00000000000..5da75ccd6bf --- /dev/null +++ b/apps/sim/tools/rippling/create_custom_object.ts @@ -0,0 +1,87 @@ +import type { RipplingCreateCustomObjectParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateCustomObjectTool: ToolConfig = { + id: 'rippling_create_custom_object', + name: 'Rippling Create Custom Object', + description: 'Create a new custom object', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + name: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Object name' }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description', + }, + category: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Category', + }, + }, + request: { + url: `https://rest.ripplingapis.com/custom-objects/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { name: params.name } + if (params.description != null) body.description = params.description + if (params.category != null) body.category = params.category + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + description: (data.description as string) ?? null, + api_name: (data.api_name as string) ?? null, + plural_label: (data.plural_label as string) ?? null, + category_id: (data.category_id as string) ?? null, + enable_history: (data.enable_history as boolean) ?? null, + native_category_id: (data.native_category_id as string) ?? null, + managed_package_install_id: (data.managed_package_install_id as string) ?? null, + owner_id: (data.owner_id as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + api_name: { type: 'string', description: 'API name', optional: true }, + plural_label: { type: 'string', description: 'Plural label', optional: true }, + category_id: { type: 'string', description: 'Category ID', optional: true }, + enable_history: { type: 'boolean', description: 'History enabled', optional: true }, + native_category_id: { type: 'string', description: 'Native category ID', optional: true }, + managed_package_install_id: { + type: 'string', + description: 'Package install ID', + optional: true, + }, + owner_id: { type: 'string', description: 'Owner ID', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/create_custom_object_field.ts b/apps/sim/tools/rippling/create_custom_object_field.ts new file mode 100644 index 00000000000..b7241119703 --- /dev/null +++ b/apps/sim/tools/rippling/create_custom_object_field.ts @@ -0,0 +1,159 @@ +import type { RipplingCreateCustomObjectFieldParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateCustomObjectFieldTool: ToolConfig = + { + id: 'rippling_create_custom_object_field', + name: 'Rippling Create Custom Object Field', + description: 'Create a field on a custom object', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Field name', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description', + }, + dataType: { + type: 'json', + required: true, + visibility: 'user-or-llm', + description: 'Data type configuration', + }, + required: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether the field is required', + }, + rqlDefinition: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'RQL definition object', + }, + isUnique: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether field is unique', + }, + formulaAttrMetas: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Formula attribute metadata', + }, + section: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Section configuration', + }, + enableHistory: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Enable history tracking', + }, + derivedFieldFormula: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Derived field formula expression', + }, + derivedAggregatedField: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Derived aggregated field configuration', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/fields/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { name: params.name, data_type: params.dataType } + if (params.description != null) body.description = params.description + if (params.required != null) body.required = params.required + if (params.rqlDefinition != null) body.rql_definition = params.rqlDefinition + if (params.isUnique != null) body.is_unique = params.isUnique + if (params.formulaAttrMetas != null) body.formula_attr_metas = params.formulaAttrMetas + if (params.section != null) body.section = params.section + if (params.enableHistory != null) body.enable_history = params.enableHistory + if (params.derivedFieldFormula != null) + body.derived_field_formula = params.derivedFieldFormula + if (params.derivedAggregatedField != null) + body.derived_aggregated_field = params.derivedAggregatedField + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + custom_object: (data.custom_object as string) ?? null, + description: (data.description as string) ?? null, + api_name: (data.api_name as string) ?? null, + data_type: data.data_type ?? null, + is_unique: (data.is_unique as boolean) ?? null, + is_immutable: (data.is_immutable as boolean) ?? null, + is_standard: (data.is_standard as boolean) ?? null, + enable_history: (data.enable_history as boolean) ?? null, + managed_package_install_id: (data.managed_package_install_id as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Field ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + custom_object: { type: 'string', description: 'Custom object', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + api_name: { type: 'string', description: 'API name', optional: true }, + data_type: { type: 'json', description: 'Data type configuration', optional: true }, + is_unique: { type: 'boolean', description: 'Is unique', optional: true }, + is_immutable: { type: 'boolean', description: 'Is immutable', optional: true }, + is_standard: { type: 'boolean', description: 'Is standard', optional: true }, + enable_history: { type: 'boolean', description: 'History enabled', optional: true }, + managed_package_install_id: { + type: 'string', + description: 'Package install ID', + optional: true, + }, + }, + } diff --git a/apps/sim/tools/rippling/create_custom_object_record.ts b/apps/sim/tools/rippling/create_custom_object_record.ts new file mode 100644 index 00000000000..19084b4f82d --- /dev/null +++ b/apps/sim/tools/rippling/create_custom_object_record.ts @@ -0,0 +1,87 @@ +import type { RipplingCreateCustomObjectRecordParams } from '@/tools/rippling/types' +import { CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateCustomObjectRecordTool: ToolConfig = + { + id: 'rippling_create_custom_object_record', + name: 'Rippling Create Custom Object Record', + description: 'Create a custom object record', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + externalId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'External ID for the record', + }, + data: { type: 'json', required: true, visibility: 'user-or-llm', description: 'Record data' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { data: params.data } + if (params.externalId != null && params.externalId !== '') + body.external_id = params.externalId + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const json = await response.json() + const record = (json.data ?? json) as Record + const { + id, + created_at, + updated_at, + name, + external_id, + created_by, + last_modified_by, + owner_role, + system_updated_at, + ...dynamicFields + } = record + return { + success: true, + output: { + id: (id as string) ?? '', + created_at: (created_at as string) ?? null, + updated_at: (updated_at as string) ?? null, + name: (name as string) ?? null, + external_id: (external_id as string) ?? null, + created_by: created_by ?? null, + last_modified_by: last_modified_by ?? null, + owner_role: owner_role ?? null, + system_updated_at: (system_updated_at as string) ?? null, + data: dynamicFields, + }, + } + }, + outputs: { + ...CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES, + data: { type: 'json', description: 'Full record data' }, + }, + } diff --git a/apps/sim/tools/rippling/create_custom_page.ts b/apps/sim/tools/rippling/create_custom_page.ts new file mode 100644 index 00000000000..e8c9445e4b4 --- /dev/null +++ b/apps/sim/tools/rippling/create_custom_page.ts @@ -0,0 +1,62 @@ +import type { RipplingCreateCustomPageParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateCustomPageTool: ToolConfig = { + id: 'rippling_create_custom_page', + name: 'Rippling Create CustomPage', + description: 'Create a new custom page', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + name: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Page name' }, + }, + request: { + url: `https://rest.ripplingapis.com/custom-pages/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + return { name: params.name } + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + components: data.components ?? [], + actions: data.actions ?? [], + canvas_actions: data.canvas_actions ?? [], + variables: data.variables ?? [], + media: data.media ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Page ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + components: { type: 'json', description: 'Page components', optional: true }, + actions: { type: 'json', description: 'Page actions', optional: true }, + canvas_actions: { type: 'json', description: 'Canvas actions', optional: true }, + variables: { type: 'json', description: 'Page variables', optional: true }, + media: { type: 'json', description: 'Page media', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/create_custom_setting.ts b/apps/sim/tools/rippling/create_custom_setting.ts new file mode 100644 index 00000000000..0039e5dd27d --- /dev/null +++ b/apps/sim/tools/rippling/create_custom_setting.ts @@ -0,0 +1,105 @@ +import type { RipplingCreateCustomSettingParams } from '@/tools/rippling/types' +import { CUSTOM_SETTING_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateCustomSettingTool: ToolConfig = { + id: 'rippling_create_custom_setting', + name: 'Rippling Create Custom Setting', + description: 'Create a new custom setting', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + displayName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Display name', + }, + apiName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Unique API name', + }, + dataType: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Data type of the setting', + }, + secretValue: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Secret value (for secret data type)', + }, + stringValue: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'String value (for string data type)', + }, + numberValue: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number value (for number data type)', + }, + booleanValue: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Boolean value (for boolean data type)', + }, + }, + request: { + url: `https://rest.ripplingapis.com/custom-settings/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.displayName != null) body.display_name = params.displayName + if (params.apiName != null) body.api_name = params.apiName + if (params.dataType != null) body.data_type = params.dataType + if (params.secretValue != null) body.secret_value = params.secretValue + if (params.stringValue != null) body.string_value = params.stringValue + if (params.numberValue != null) body.number_value = params.numberValue + if (params.booleanValue != null) body.boolean_value = params.booleanValue + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + display_name: (data.display_name as string) ?? null, + api_name: (data.api_name as string) ?? null, + data_type: (data.data_type as string) ?? null, + secret_value: (data.secret_value as string) ?? null, + string_value: (data.string_value as string) ?? null, + number_value: data.number_value ?? null, + boolean_value: data.boolean_value ?? null, + }, + } + }, + outputs: { + ...CUSTOM_SETTING_OUTPUT_PROPERTIES, + }, +} diff --git a/apps/sim/tools/rippling/create_department.ts b/apps/sim/tools/rippling/create_department.ts new file mode 100644 index 00000000000..2953dcb47f0 --- /dev/null +++ b/apps/sim/tools/rippling/create_department.ts @@ -0,0 +1,90 @@ +import type { RipplingCreateDepartmentParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateDepartmentTool: ToolConfig = { + id: 'rippling_create_department', + name: 'Rippling Create Department', + description: 'Create a new department', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Department name', + }, + parentId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Parent department ID', + }, + referenceCode: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Reference code', + }, + }, + request: { + url: `https://rest.ripplingapis.com/departments/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { name: params.name } + if (params.parentId != null) body.parent_id = params.parentId + if (params.referenceCode != null) body.reference_code = params.referenceCode + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + parent_id: (data.parent_id as string) ?? null, + reference_code: (data.reference_code as string) ?? null, + department_hierarchy_id: (data.department_hierarchy_id as unknown[]) ?? [], + parent: data.parent ?? null, + department_hierarchy: data.department_hierarchy ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Department ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + parent_id: { type: 'string', description: 'Parent department ID', optional: true }, + reference_code: { type: 'string', description: 'Reference code', optional: true }, + department_hierarchy_id: { + type: 'json', + description: 'Department hierarchy IDs', + optional: true, + }, + parent: { type: 'json', description: 'Expanded parent department', optional: true }, + department_hierarchy: { + type: 'json', + description: 'Expanded department hierarchy', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/rippling/create_draft_hires.ts b/apps/sim/tools/rippling/create_draft_hires.ts new file mode 100644 index 00000000000..a6e4b06987a --- /dev/null +++ b/apps/sim/tools/rippling/create_draft_hires.ts @@ -0,0 +1,57 @@ +import type { RipplingCreateDraftHiresParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateDraftHiresTool: ToolConfig = { + id: 'rippling_create_draft_hires', + name: 'Rippling Create Draft Hires', + description: 'Create bulk draft hires', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + draftHires: { + type: 'json', + required: true, + visibility: 'user-or-llm', + description: 'Array of draft hire objects', + }, + }, + request: { + url: `https://rest.ripplingapis.com/draft-hires/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + return { draft_hires: params.draftHires } + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + invalidItems: data.invalid_items ?? [], + successfulResults: data.successful_results ?? [], + totalInvalid: (data.invalid_items ?? []).length, + totalSuccessful: (data.successful_results ?? []).length, + }, + } + }, + outputs: { + invalidItems: { type: 'json', description: 'Failed draft hires' }, + successfulResults: { type: 'json', description: 'Successful draft hires' }, + totalInvalid: { type: 'number', description: 'Number of failures' }, + totalSuccessful: { type: 'number', description: 'Number of successes' }, + }, +} diff --git a/apps/sim/tools/rippling/create_group.ts b/apps/sim/tools/rippling/create_group.ts deleted file mode 100644 index d4987eaacfe..00000000000 --- a/apps/sim/tools/rippling/create_group.ts +++ /dev/null @@ -1,91 +0,0 @@ -import type { RipplingCreateGroupParams, RipplingCreateGroupResponse } from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingCreateGroupTool: ToolConfig< - RipplingCreateGroupParams, - RipplingCreateGroupResponse -> = { - id: 'rippling_create_group', - name: 'Rippling Create Group', - description: 'Create a new group in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - name: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'Name of the group', - }, - spokeId: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'Third-party app identifier', - }, - users: { - type: 'json', - required: false, - visibility: 'user-or-llm', - description: 'Array of user ID strings to add to the group', - }, - }, - - request: { - url: 'https://api.rippling.com/platform/api/groups', - method: 'POST', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - 'Content-Type': 'application/json', - }), - body: (params) => { - const body: Record = { - name: params.name, - spokeId: params.spokeId, - } - if (params.users !== undefined) { - body.users = params.users - } - return body - }, - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - - return { - success: true, - output: { - id: (data.id as string) ?? '', - name: (data.name as string) ?? null, - spokeId: (data.spokeId as string) ?? null, - users: (data.users as string[]) ?? [], - version: (data.version as number) ?? null, - }, - } - }, - - outputs: { - id: { type: 'string', description: 'Group ID' }, - name: { type: 'string', description: 'Group name' }, - spokeId: { type: 'string', description: 'Third-party app identifier' }, - users: { - type: 'array', - description: 'Array of user IDs in the group', - items: { type: 'string' }, - }, - version: { type: 'number', description: 'Group version number' }, - }, -} diff --git a/apps/sim/tools/rippling/create_object_category.ts b/apps/sim/tools/rippling/create_object_category.ts new file mode 100644 index 00000000000..011bee80a6b --- /dev/null +++ b/apps/sim/tools/rippling/create_object_category.ts @@ -0,0 +1,67 @@ +import type { RipplingCreateObjectCategoryParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateObjectCategoryTool: ToolConfig = { + id: 'rippling_create_object_category', + name: 'Rippling Create ObjectCategory', + description: 'Create a new object category', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category name', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description', + }, + }, + request: { + url: `https://rest.ripplingapis.com/object-categories/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { name: params.name } + if (params.description != null) body.description = params.description + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + description: (data.description as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Category ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/create_title.ts b/apps/sim/tools/rippling/create_title.ts new file mode 100644 index 00000000000..24cf426cec9 --- /dev/null +++ b/apps/sim/tools/rippling/create_title.ts @@ -0,0 +1,52 @@ +import type { RipplingCreateTitleParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateTitleTool: ToolConfig = { + id: 'rippling_create_title', + name: 'Rippling Create Title', + description: 'Create a new title', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + name: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Title name' }, + }, + request: { + url: `https://rest.ripplingapis.com/titles/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + return { name: params.name } + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Title ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Title name', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/create_work_location.ts b/apps/sim/tools/rippling/create_work_location.ts new file mode 100644 index 00000000000..dac4ea68802 --- /dev/null +++ b/apps/sim/tools/rippling/create_work_location.ts @@ -0,0 +1,98 @@ +import type { RipplingCreateWorkLocationParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingCreateWorkLocationTool: ToolConfig = { + id: 'rippling_create_work_location', + name: 'Rippling Create Work Location', + description: 'Create a new work location', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Location name', + }, + streetAddress: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Street address', + }, + locality: { type: 'string', required: false, visibility: 'user-or-llm', description: 'City' }, + region: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'State/region', + }, + postalCode: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Postal code', + }, + country: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Country code', + }, + addressType: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Address type (HOME, WORK, OTHER)', + }, + }, + request: { + url: `https://rest.ripplingapis.com/work-locations/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { name: params.name } + const address: Record = { street_address: params.streetAddress } + if (params.locality != null) address.locality = params.locality + if (params.region != null) address.region = params.region + if (params.postalCode != null) address.postal_code = params.postalCode + if (params.country != null) address.country = params.country + if (params.addressType != null) address.type = params.addressType + body.address = address + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + address: data.address ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Location ID' }, + created_at: { type: 'string', description: 'Created timestamp', optional: true }, + updated_at: { type: 'string', description: 'Updated timestamp', optional: true }, + name: { type: 'string', description: 'Name' }, + address: { type: 'json', description: 'Address', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/delete_business_partner.ts b/apps/sim/tools/rippling/delete_business_partner.ts new file mode 100644 index 00000000000..8fd48523c90 --- /dev/null +++ b/apps/sim/tools/rippling/delete_business_partner.ts @@ -0,0 +1,39 @@ +import type { RipplingDeleteBusinessPartnerParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteBusinessPartnerTool: ToolConfig = { + id: 'rippling_delete_business_partner', + name: 'Rippling Delete Business Partner', + description: 'Delete a business partner', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the resource to delete', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/business-partners/${encodeURIComponent(params.id.trim())}/`, + method: 'DELETE', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + }, +} diff --git a/apps/sim/tools/rippling/delete_business_partner_group.ts b/apps/sim/tools/rippling/delete_business_partner_group.ts new file mode 100644 index 00000000000..a4c835f77bc --- /dev/null +++ b/apps/sim/tools/rippling/delete_business_partner_group.ts @@ -0,0 +1,43 @@ +import type { RipplingDeleteBusinessPartnerGroupParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteBusinessPartnerGroupTool: ToolConfig = + { + id: 'rippling_delete_business_partner_group', + name: 'Rippling Delete Business Partner Group', + description: 'Delete a business partner group', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the resource to delete', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/business-partner-groups/${encodeURIComponent(params.id.trim())}/`, + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + }, + } diff --git a/apps/sim/tools/rippling/delete_custom_app.ts b/apps/sim/tools/rippling/delete_custom_app.ts new file mode 100644 index 00000000000..619e53a980d --- /dev/null +++ b/apps/sim/tools/rippling/delete_custom_app.ts @@ -0,0 +1,39 @@ +import type { RipplingDeleteCustomAppParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteCustomAppTool: ToolConfig = { + id: 'rippling_delete_custom_app', + name: 'Rippling Delete CustomApp', + description: 'Delete a custom app', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the resource to delete', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-apps/${encodeURIComponent(params.id.trim())}/`, + method: 'DELETE', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + }, +} diff --git a/apps/sim/tools/rippling/delete_custom_object.ts b/apps/sim/tools/rippling/delete_custom_object.ts new file mode 100644 index 00000000000..de2dd29b7ac --- /dev/null +++ b/apps/sim/tools/rippling/delete_custom_object.ts @@ -0,0 +1,39 @@ +import type { RipplingDeleteCustomObjectParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteCustomObjectTool: ToolConfig = { + id: 'rippling_delete_custom_object', + name: 'Rippling Delete Custom Object', + description: 'Delete a custom object', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the resource to delete', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/`, + method: 'DELETE', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + }, +} diff --git a/apps/sim/tools/rippling/delete_custom_object_field.ts b/apps/sim/tools/rippling/delete_custom_object_field.ts new file mode 100644 index 00000000000..d8ad2bdb4cb --- /dev/null +++ b/apps/sim/tools/rippling/delete_custom_object_field.ts @@ -0,0 +1,47 @@ +import type { RipplingDeleteCustomObjectFieldParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteCustomObjectFieldTool: ToolConfig = + { + id: 'rippling_delete_custom_object_field', + name: 'Rippling Delete Custom Object Field', + description: 'Delete a field from a custom object', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + fieldApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Field API name', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/fields/${encodeURIComponent(params.fieldApiName.trim())}/`, + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { deleted: { type: 'boolean', description: 'Whether the field was deleted' } }, + } diff --git a/apps/sim/tools/rippling/delete_custom_object_record.ts b/apps/sim/tools/rippling/delete_custom_object_record.ts new file mode 100644 index 00000000000..13f61435398 --- /dev/null +++ b/apps/sim/tools/rippling/delete_custom_object_record.ts @@ -0,0 +1,47 @@ +import type { RipplingDeleteCustomObjectRecordParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteCustomObjectRecordTool: ToolConfig = + { + id: 'rippling_delete_custom_object_record', + name: 'Rippling Delete Custom Object Record', + description: 'Delete a custom object record', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + codrId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Record ID', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/${encodeURIComponent(params.codrId.trim())}/`, + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { deleted: { type: 'boolean', description: 'Whether the record was deleted' } }, + } diff --git a/apps/sim/tools/rippling/delete_custom_page.ts b/apps/sim/tools/rippling/delete_custom_page.ts new file mode 100644 index 00000000000..822f6401e20 --- /dev/null +++ b/apps/sim/tools/rippling/delete_custom_page.ts @@ -0,0 +1,39 @@ +import type { RipplingDeleteCustomPageParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteCustomPageTool: ToolConfig = { + id: 'rippling_delete_custom_page', + name: 'Rippling Delete CustomPage', + description: 'Delete a custom page', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the resource to delete', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-pages/${encodeURIComponent(params.id.trim())}/`, + method: 'DELETE', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + }, +} diff --git a/apps/sim/tools/rippling/delete_custom_setting.ts b/apps/sim/tools/rippling/delete_custom_setting.ts new file mode 100644 index 00000000000..727555991c2 --- /dev/null +++ b/apps/sim/tools/rippling/delete_custom_setting.ts @@ -0,0 +1,39 @@ +import type { RipplingDeleteCustomSettingParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteCustomSettingTool: ToolConfig = { + id: 'rippling_delete_custom_setting', + name: 'Rippling Delete Custom Setting', + description: 'Delete a custom setting', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the resource to delete', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-settings/${encodeURIComponent(params.id.trim())}/`, + method: 'DELETE', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + }, +} diff --git a/apps/sim/tools/rippling/delete_object_category.ts b/apps/sim/tools/rippling/delete_object_category.ts new file mode 100644 index 00000000000..32714047db7 --- /dev/null +++ b/apps/sim/tools/rippling/delete_object_category.ts @@ -0,0 +1,39 @@ +import type { RipplingDeleteObjectCategoryParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteObjectCategoryTool: ToolConfig = { + id: 'rippling_delete_object_category', + name: 'Rippling Delete ObjectCategory', + description: 'Delete an object category', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the resource to delete', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/object-categories/${encodeURIComponent(params.id.trim())}/`, + method: 'DELETE', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + }, +} diff --git a/apps/sim/tools/rippling/delete_title.ts b/apps/sim/tools/rippling/delete_title.ts new file mode 100644 index 00000000000..12d700d43bd --- /dev/null +++ b/apps/sim/tools/rippling/delete_title.ts @@ -0,0 +1,39 @@ +import type { RipplingDeleteTitleParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteTitleTool: ToolConfig = { + id: 'rippling_delete_title', + name: 'Rippling Delete Title', + description: 'Delete a title', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the resource to delete', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/titles/${encodeURIComponent(params.id.trim())}/`, + method: 'DELETE', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + }, +} diff --git a/apps/sim/tools/rippling/delete_work_location.ts b/apps/sim/tools/rippling/delete_work_location.ts new file mode 100644 index 00000000000..7af46a7ee99 --- /dev/null +++ b/apps/sim/tools/rippling/delete_work_location.ts @@ -0,0 +1,39 @@ +import type { RipplingDeleteWorkLocationParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingDeleteWorkLocationTool: ToolConfig = { + id: 'rippling_delete_work_location', + name: 'Rippling Delete Work Location', + description: 'Delete a work location', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the resource to delete', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/work-locations/${encodeURIComponent(params.id.trim())}/`, + method: 'DELETE', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + return { success: true, output: { deleted: true } } + }, + outputs: { + deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, + }, +} diff --git a/apps/sim/tools/rippling/get_business_partner.ts b/apps/sim/tools/rippling/get_business_partner.ts new file mode 100644 index 00000000000..4a30c6d218b --- /dev/null +++ b/apps/sim/tools/rippling/get_business_partner.ts @@ -0,0 +1,67 @@ +import type { RipplingGetBusinessPartnerParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetBusinessPartnerTool: ToolConfig = { + id: 'rippling_get_business_partner', + name: 'Rippling Get Business Partner', + description: 'Get a specific business partner by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + }, + request: { + url: (params) => { + const base = `https://rest.ripplingapis.com/business-partners/${encodeURIComponent(params.id.trim())}/` + if (params.expand != null) return `${base}?expand=${encodeURIComponent(params.expand)}` + return base + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + business_partner_group_id: (data.business_partner_group_id as string) ?? null, + worker_id: (data.worker_id as string) ?? null, + client_group_id: (data.client_group_id as string) ?? null, + client_group_member_count: (data.client_group_member_count as number) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + business_partner_group_id: { type: 'string', description: 'Group ID', optional: true }, + worker_id: { type: 'string', description: 'Worker ID', optional: true }, + client_group_id: { type: 'string', description: 'Client group ID', optional: true }, + client_group_member_count: { + type: 'number', + description: 'Client group member count', + optional: true, + }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_business_partner_group.ts b/apps/sim/tools/rippling/get_business_partner_group.ts new file mode 100644 index 00000000000..bc9a5bffd9d --- /dev/null +++ b/apps/sim/tools/rippling/get_business_partner_group.ts @@ -0,0 +1,69 @@ +import type { RipplingGetBusinessPartnerGroupParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetBusinessPartnerGroupTool: ToolConfig = + { + id: 'rippling_get_business_partner_group', + name: 'Rippling Get Business Partner Group', + description: 'Get a specific business partner group by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + }, + request: { + url: (params) => { + const base = `https://rest.ripplingapis.com/business-partner-groups/${encodeURIComponent(params.id.trim())}/` + if (params.expand != null) return `${base}?expand=${encodeURIComponent(params.expand)}` + return base + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + domain: (data.domain as string) ?? null, + default_business_partner_id: (data.default_business_partner_id as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + domain: { type: 'string', description: 'Domain', optional: true }, + default_business_partner_id: { + type: 'string', + description: 'Default partner ID', + optional: true, + }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, + } diff --git a/apps/sim/tools/rippling/get_company.ts b/apps/sim/tools/rippling/get_company.ts deleted file mode 100644 index fe3c0973f6b..00000000000 --- a/apps/sim/tools/rippling/get_company.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { RipplingGetCompanyParams, RipplingGetCompanyResponse } from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingGetCompanyTool: ToolConfig< - RipplingGetCompanyParams, - RipplingGetCompanyResponse -> = { - id: 'rippling_get_company', - name: 'Rippling Get Company', - description: 'Get details for the current company in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - }, - - request: { - url: 'https://api.rippling.com/platform/api/companies/current', - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - - const address = data.address ?? {} - - return { - success: true, - output: { - id: data.id ?? '', - name: data.name ?? null, - address: { - street: address.street ?? null, - city: address.city ?? null, - state: address.state ?? null, - zip: address.zip ?? null, - country: address.country ?? null, - }, - email: data.email ?? null, - phone: data.phone ?? null, - workLocations: data.workLocations ?? [], - }, - } - }, - - outputs: { - id: { type: 'string', description: 'Company ID' }, - name: { type: 'string', description: 'Company name', optional: true }, - address: { - type: 'json', - description: 'Company address with street, city, state, zip, country', - }, - email: { type: 'string', description: 'Company email address', optional: true }, - phone: { type: 'string', description: 'Company phone number', optional: true }, - workLocations: { - type: 'array', - description: 'List of work location IDs', - items: { type: 'string' }, - }, - }, -} diff --git a/apps/sim/tools/rippling/get_company_activity.ts b/apps/sim/tools/rippling/get_company_activity.ts deleted file mode 100644 index 41e27591407..00000000000 --- a/apps/sim/tools/rippling/get_company_activity.ts +++ /dev/null @@ -1,125 +0,0 @@ -import type { - RipplingGetCompanyActivityParams, - RipplingGetCompanyActivityResponse, -} from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingGetCompanyActivityTool: ToolConfig< - RipplingGetCompanyActivityParams, - RipplingGetCompanyActivityResponse -> = { - id: 'rippling_get_company_activity', - name: 'Rippling Get Company Activity', - description: 'Get activity events for the current company in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - startDate: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Start date filter in ISO format (e.g. 2024-01-01)', - }, - endDate: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'End date filter in ISO format (e.g. 2024-12-31)', - }, - limit: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Maximum number of activity events to return', - }, - next: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Cursor for fetching the next page of results', - }, - }, - - request: { - url: (params) => { - const query = new URLSearchParams() - if (params.startDate) query.set('startDate', params.startDate) - if (params.endDate) query.set('endDate', params.endDate) - if (params.limit != null) query.set('limit', String(params.limit)) - if (params.next) query.set('next', params.next) - const qs = query.toString() - return `https://api.rippling.com/platform/api/company_activity${qs ? `?${qs}` : ''}` - }, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - const nextCursor = Array.isArray(data) ? null : ((data.next as string) ?? null) - - const events = results.map((event: Record) => { - const actor = (event.actor as Record) ?? {} - return { - id: (event.id as string) ?? '', - type: (event.type as string) ?? null, - description: (event.description as string) ?? null, - createdAt: (event.createdAt as string) ?? null, - actor: { - id: (actor.id as string) ?? null, - name: (actor.name as string) ?? null, - }, - } - }) - - return { - success: true, - output: { - events, - totalCount: events.length, - nextCursor, - }, - } - }, - - outputs: { - events: { - type: 'array', - description: 'List of company activity events', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Event ID' }, - type: { type: 'string', description: 'Event type' }, - description: { type: 'string', description: 'Event description' }, - createdAt: { type: 'string', description: 'Event creation timestamp' }, - actor: { type: 'json', description: 'Actor who triggered the event (id, name)' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Number of activity events returned on this page', - }, - nextCursor: { - type: 'string', - description: 'Cursor for fetching the next page of results', - optional: true, - }, - }, -} diff --git a/apps/sim/tools/rippling/get_current_user.ts b/apps/sim/tools/rippling/get_current_user.ts index 3ee6ebb14c4..79b8a4ae4ba 100644 --- a/apps/sim/tools/rippling/get_current_user.ts +++ b/apps/sim/tools/rippling/get_current_user.ts @@ -1,18 +1,11 @@ -import type { - RipplingGetCurrentUserParams, - RipplingGetCurrentUserResponse, -} from '@/tools/rippling/types' +import type { RipplingGetCurrentUserParams } from '@/tools/rippling/types' import type { ToolConfig } from '@/tools/types' -export const ripplingGetCurrentUserTool: ToolConfig< - RipplingGetCurrentUserParams, - RipplingGetCurrentUserResponse -> = { +export const ripplingGetCurrentUserTool: ToolConfig = { id: 'rippling_get_current_user', name: 'Rippling Get Current User', - description: 'Get the current authenticated user details', + description: 'Get SSO information for the current user', version: '1.0.0', - params: { apiKey: { type: 'string', @@ -20,38 +13,47 @@ export const ripplingGetCurrentUserTool: ToolConfig< visibility: 'user-only', description: 'Rippling API key', }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, }, - request: { - url: 'https://api.rippling.com/platform/api/me', + url: (params) => { + const query = new URLSearchParams() + if (params.expand != null) query.set('expand', params.expand) + const qs = query.toString() + return `https://rest.ripplingapis.com/sso-me/${qs ? `?${qs}` : ''}` + }, method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), }, - transformResponse: async (response: Response) => { if (!response.ok) { const errorText = await response.text() throw new Error(`Rippling API error (${response.status}): ${errorText}`) } - const data = await response.json() - return { success: true, output: { - id: data.id ?? '', - workEmail: data.workEmail ?? null, + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + work_email: (data.work_email as string) ?? null, + company_id: (data.company_id as string) ?? null, company: data.company ?? null, }, } }, - outputs: { id: { type: 'string', description: 'User ID' }, - workEmail: { type: 'string', description: 'Work email address', optional: true }, - company: { type: 'string', description: 'Company ID', optional: true }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + work_email: { type: 'string', description: 'Work email', optional: true }, + company_id: { type: 'string', description: 'Company ID', optional: true }, + company: { type: 'json', description: 'Expanded company object', optional: true }, }, } diff --git a/apps/sim/tools/rippling/get_custom_app.ts b/apps/sim/tools/rippling/get_custom_app.ts new file mode 100644 index 00000000000..79cdfb0900f --- /dev/null +++ b/apps/sim/tools/rippling/get_custom_app.ts @@ -0,0 +1,56 @@ +import type { RipplingGetCustomAppParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetCustomAppTool: ToolConfig = { + id: 'rippling_get_custom_app', + name: 'Rippling Get CustomApp', + description: 'Get a specific custom app', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-apps/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + api_name: (data.api_name as string) ?? null, + description: (data.description as string) ?? null, + icon: (data.icon as string) ?? null, + pages: (data.pages as unknown[]) ?? [], + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'App ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + api_name: { type: 'string', description: 'API name', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + icon: { type: 'string', description: 'Icon URL', optional: true }, + pages: { type: 'json', description: 'Array of page summaries', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_custom_object.ts b/apps/sim/tools/rippling/get_custom_object.ts new file mode 100644 index 00000000000..c9a7140710e --- /dev/null +++ b/apps/sim/tools/rippling/get_custom_object.ts @@ -0,0 +1,71 @@ +import type { RipplingGetCustomObjectParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetCustomObjectTool: ToolConfig = { + id: 'rippling_get_custom_object', + name: 'Rippling Get Custom Object', + description: 'Get a custom object by API name', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'custom object api name', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + description: (data.description as string) ?? null, + api_name: (data.api_name as string) ?? null, + plural_label: (data.plural_label as string) ?? null, + category_id: (data.category_id as string) ?? null, + enable_history: (data.enable_history as boolean) ?? null, + native_category_id: (data.native_category_id as string) ?? null, + managed_package_install_id: (data.managed_package_install_id as string) ?? null, + owner_id: (data.owner_id as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + api_name: { type: 'string', description: 'API name', optional: true }, + plural_label: { type: 'string', description: 'Plural label', optional: true }, + category_id: { type: 'string', description: 'Category ID', optional: true }, + enable_history: { type: 'boolean', description: 'History enabled', optional: true }, + native_category_id: { type: 'string', description: 'Native category ID', optional: true }, + managed_package_install_id: { + type: 'string', + description: 'Package install ID', + optional: true, + }, + owner_id: { type: 'string', description: 'Owner ID', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_custom_object_field.ts b/apps/sim/tools/rippling/get_custom_object_field.ts new file mode 100644 index 00000000000..41ed2bcbc01 --- /dev/null +++ b/apps/sim/tools/rippling/get_custom_object_field.ts @@ -0,0 +1,79 @@ +import type { RipplingGetCustomObjectFieldParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetCustomObjectFieldTool: ToolConfig = { + id: 'rippling_get_custom_object_field', + name: 'Rippling Get Custom Object Field', + description: 'Get a specific field of a custom object', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + fieldApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Field API name', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/fields/${encodeURIComponent(params.fieldApiName.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + custom_object: (data.custom_object as string) ?? null, + description: (data.description as string) ?? null, + api_name: (data.api_name as string) ?? null, + data_type: data.data_type ?? null, + is_unique: (data.is_unique as boolean) ?? null, + is_immutable: (data.is_immutable as boolean) ?? null, + is_standard: (data.is_standard as boolean) ?? null, + enable_history: (data.enable_history as boolean) ?? null, + managed_package_install_id: (data.managed_package_install_id as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Field ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + custom_object: { type: 'string', description: 'Custom object', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + api_name: { type: 'string', description: 'API name', optional: true }, + data_type: { type: 'json', description: 'Data type configuration', optional: true }, + is_unique: { type: 'boolean', description: 'Is unique', optional: true }, + is_immutable: { type: 'boolean', description: 'Is immutable', optional: true }, + is_standard: { type: 'boolean', description: 'Is standard', optional: true }, + enable_history: { type: 'boolean', description: 'History enabled', optional: true }, + managed_package_install_id: { + type: 'string', + description: 'Package install ID', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/rippling/get_custom_object_record.ts b/apps/sim/tools/rippling/get_custom_object_record.ts new file mode 100644 index 00000000000..3f06af70d09 --- /dev/null +++ b/apps/sim/tools/rippling/get_custom_object_record.ts @@ -0,0 +1,69 @@ +import type { RipplingGetCustomObjectRecordParams } from '@/tools/rippling/types' +import { CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetCustomObjectRecordTool: ToolConfig = { + id: 'rippling_get_custom_object_record', + name: 'Rippling Get Custom Object Record', + description: 'Get a specific custom object record', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + codrId: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Record ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/${encodeURIComponent(params.codrId.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const record = await response.json() + const { + id, + created_at, + updated_at, + name, + external_id, + created_by, + last_modified_by, + owner_role, + system_updated_at, + ...dynamicFields + } = record + return { + success: true, + output: { + id: (id as string) ?? '', + created_at: (created_at as string) ?? null, + updated_at: (updated_at as string) ?? null, + name: (name as string) ?? null, + external_id: (external_id as string) ?? null, + created_by: created_by ?? null, + last_modified_by: last_modified_by ?? null, + owner_role: owner_role ?? null, + system_updated_at: (system_updated_at as string) ?? null, + data: dynamicFields, + }, + } + }, + outputs: { + ...CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES, + data: { type: 'json', description: 'Full record data' }, + }, +} diff --git a/apps/sim/tools/rippling/get_custom_object_record_by_external_id.ts b/apps/sim/tools/rippling/get_custom_object_record_by_external_id.ts new file mode 100644 index 00000000000..af7d14c56a0 --- /dev/null +++ b/apps/sim/tools/rippling/get_custom_object_record_by_external_id.ts @@ -0,0 +1,78 @@ +import type { RipplingGetCustomObjectRecordByExternalIdParams } from '@/tools/rippling/types' +import { CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetCustomObjectRecordByExternalIdTool: ToolConfig = + { + id: 'rippling_get_custom_object_record_by_external_id', + name: 'Rippling Get Record By External ID', + description: 'Get a custom object record by external ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + externalId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'External ID', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/external_id/${encodeURIComponent(params.externalId.trim())}/`, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const record = await response.json() + const { + id, + created_at, + updated_at, + name, + external_id, + created_by, + last_modified_by, + owner_role, + system_updated_at, + ...dynamicFields + } = record + return { + success: true, + output: { + id: (id as string) ?? '', + created_at: (created_at as string) ?? null, + updated_at: (updated_at as string) ?? null, + name: (name as string) ?? null, + external_id: (external_id as string) ?? null, + created_by: created_by ?? null, + last_modified_by: last_modified_by ?? null, + owner_role: owner_role ?? null, + system_updated_at: (system_updated_at as string) ?? null, + data: dynamicFields, + }, + } + }, + outputs: { + ...CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES, + data: { type: 'json', description: 'Full record data' }, + }, + } diff --git a/apps/sim/tools/rippling/get_custom_page.ts b/apps/sim/tools/rippling/get_custom_page.ts new file mode 100644 index 00000000000..60031f78e4d --- /dev/null +++ b/apps/sim/tools/rippling/get_custom_page.ts @@ -0,0 +1,58 @@ +import type { RipplingGetCustomPageParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetCustomPageTool: ToolConfig = { + id: 'rippling_get_custom_page', + name: 'Rippling Get CustomPage', + description: 'Get a specific custom page', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-pages/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + components: data.components ?? [], + actions: data.actions ?? [], + canvas_actions: data.canvas_actions ?? [], + variables: data.variables ?? [], + media: data.media ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Page ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + components: { type: 'json', description: 'Page components', optional: true }, + actions: { type: 'json', description: 'Page actions', optional: true }, + canvas_actions: { type: 'json', description: 'Canvas actions', optional: true }, + variables: { type: 'json', description: 'Page variables', optional: true }, + media: { type: 'json', description: 'Page media', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_custom_setting.ts b/apps/sim/tools/rippling/get_custom_setting.ts new file mode 100644 index 00000000000..ca5383c2f03 --- /dev/null +++ b/apps/sim/tools/rippling/get_custom_setting.ts @@ -0,0 +1,52 @@ +import type { RipplingGetCustomSettingParams } from '@/tools/rippling/types' +import { CUSTOM_SETTING_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetCustomSettingTool: ToolConfig = { + id: 'rippling_get_custom_setting', + name: 'Rippling Get Custom Setting', + description: 'Get a specific custom setting', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-settings/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + display_name: (data.display_name as string) ?? null, + api_name: (data.api_name as string) ?? null, + data_type: (data.data_type as string) ?? null, + secret_value: (data.secret_value as string) ?? null, + string_value: (data.string_value as string) ?? null, + number_value: data.number_value ?? null, + boolean_value: data.boolean_value ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + ...CUSTOM_SETTING_OUTPUT_PROPERTIES, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_department.ts b/apps/sim/tools/rippling/get_department.ts new file mode 100644 index 00000000000..fff961b83d1 --- /dev/null +++ b/apps/sim/tools/rippling/get_department.ts @@ -0,0 +1,60 @@ +import type { RipplingGetDepartmentParams } from '@/tools/rippling/types' +import { DEPARTMENT_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetDepartmentTool: ToolConfig = { + id: 'rippling_get_department', + name: 'Rippling Get Department', + description: 'Get a specific department by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + }, + request: { + url: (params) => { + const base = `https://rest.ripplingapis.com/departments/${encodeURIComponent(params.id.trim())}/` + if (params.expand != null) return `${base}?expand=${encodeURIComponent(params.expand)}` + return base + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + parent_id: (data.parent_id as string) ?? null, + reference_code: (data.reference_code as string) ?? null, + department_hierarchy_id: (data.department_hierarchy_id as unknown[]) ?? [], + parent: data.parent ?? null, + department_hierarchy: data.department_hierarchy ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + ...DEPARTMENT_OUTPUT_PROPERTIES, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_employee.ts b/apps/sim/tools/rippling/get_employee.ts deleted file mode 100644 index a9e0c569110..00000000000 --- a/apps/sim/tools/rippling/get_employee.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { RipplingGetEmployeeParams, RipplingGetEmployeeResponse } from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingGetEmployeeTool: ToolConfig< - RipplingGetEmployeeParams, - RipplingGetEmployeeResponse -> = { - id: 'rippling_get_employee', - name: 'Rippling Get Employee', - description: 'Get details for a specific employee by ID', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - employeeId: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'The ID of the employee to retrieve', - }, - }, - - request: { - url: (params) => - `https://api.rippling.com/platform/api/employees/${encodeURIComponent(params.employeeId.trim())}`, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const emp = await response.json() - - return { - success: true, - output: { - id: emp.id ?? '', - firstName: emp.firstName ?? null, - lastName: emp.lastName ?? null, - workEmail: emp.workEmail ?? null, - personalEmail: emp.personalEmail ?? null, - roleState: emp.roleState ?? null, - department: emp.department ?? null, - title: emp.title ?? null, - startDate: emp.startDate ?? null, - endDate: emp.endDate ?? null, - manager: emp.manager ?? null, - phone: emp.phone ?? null, - }, - } - }, - - outputs: { - id: { type: 'string', description: 'Employee ID' }, - firstName: { type: 'string', description: 'First name', optional: true }, - lastName: { type: 'string', description: 'Last name', optional: true }, - workEmail: { type: 'string', description: 'Work email address', optional: true }, - personalEmail: { type: 'string', description: 'Personal email address', optional: true }, - roleState: { type: 'string', description: 'Employment status', optional: true }, - department: { type: 'string', description: 'Department name or ID', optional: true }, - title: { type: 'string', description: 'Job title', optional: true }, - startDate: { type: 'string', description: 'Employment start date', optional: true }, - endDate: { type: 'string', description: 'Employment end date', optional: true }, - manager: { type: 'string', description: 'Manager ID or name', optional: true }, - phone: { type: 'string', description: 'Phone number', optional: true }, - }, -} diff --git a/apps/sim/tools/rippling/get_employment_type.ts b/apps/sim/tools/rippling/get_employment_type.ts new file mode 100644 index 00000000000..78ec082470c --- /dev/null +++ b/apps/sim/tools/rippling/get_employment_type.ts @@ -0,0 +1,67 @@ +import type { RipplingGetEmploymentTypeParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetEmploymentTypeTool: ToolConfig = { + id: 'rippling_get_employment_type', + name: 'Rippling Get Employment Type', + description: 'Get a specific employment type by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/employment-types/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + label: (data.label as string) ?? null, + name: (data.name as string) ?? null, + type: (data.type as string) ?? null, + compensation_time_period: (data.compensation_time_period as string) ?? null, + amount_worked: (data.amount_worked as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Employment type ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + label: { type: 'string', description: 'Label', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + type: { type: 'string', description: 'Type (CONTRACTOR, EMPLOYEE)', optional: true }, + compensation_time_period: { + type: 'string', + description: 'Compensation period (HOURLY, SALARIED)', + optional: true, + }, + amount_worked: { + type: 'string', + description: 'Amount worked (PART-TIME, FULL-TIME, TEMPORARY)', + optional: true, + }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_job_function.ts b/apps/sim/tools/rippling/get_job_function.ts new file mode 100644 index 00000000000..c3a46d39246 --- /dev/null +++ b/apps/sim/tools/rippling/get_job_function.ts @@ -0,0 +1,48 @@ +import type { RipplingGetJobFunctionParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetJobFunctionTool: ToolConfig = { + id: 'rippling_get_job_function', + name: 'Rippling Get Job Function', + description: 'Get a specific job function by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/job-functions/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Job function ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_leave_balance.ts b/apps/sim/tools/rippling/get_leave_balance.ts deleted file mode 100644 index d8f8b303221..00000000000 --- a/apps/sim/tools/rippling/get_leave_balance.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { - RipplingGetLeaveBalanceParams, - RipplingGetLeaveBalanceResponse, -} from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingGetLeaveBalanceTool: ToolConfig< - RipplingGetLeaveBalanceParams, - RipplingGetLeaveBalanceResponse -> = { - id: 'rippling_get_leave_balance', - name: 'Rippling Get Leave Balance', - description: 'Get leave balance for a specific employee by role ID', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - roleId: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'The employee/role ID to retrieve leave balance for', - }, - }, - - request: { - url: (params) => - `https://api.rippling.com/platform/api/leave_balances/${encodeURIComponent(params.roleId.trim())}`, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - - return { - success: true, - output: { - employeeId: data.employeeId ?? '', - balances: (Array.isArray(data.balances) ? data.balances : []).map( - (b: Record) => ({ - leaveType: (b.leaveType as string) ?? '', - minutesRemaining: (b.minutesRemaining as number) ?? 0, - }) - ), - }, - } - }, - - outputs: { - employeeId: { type: 'string', description: 'Employee ID' }, - balances: { - type: 'array', - description: 'Leave balance entries', - items: { - type: 'json', - properties: { - leaveType: { type: 'string', description: 'Type of leave' }, - minutesRemaining: { type: 'number', description: 'Minutes of leave remaining' }, - }, - }, - }, - }, -} diff --git a/apps/sim/tools/rippling/get_object_category.ts b/apps/sim/tools/rippling/get_object_category.ts new file mode 100644 index 00000000000..14c100af1b6 --- /dev/null +++ b/apps/sim/tools/rippling/get_object_category.ts @@ -0,0 +1,51 @@ +import type { RipplingGetObjectCategoryParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetObjectCategoryTool: ToolConfig = { + id: 'rippling_get_object_category', + name: 'Rippling Get ObjectCategory', + description: 'Get a specific object category', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/object-categories/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + description: (data.description as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Category ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_report_run.ts b/apps/sim/tools/rippling/get_report_run.ts new file mode 100644 index 00000000000..3d7e1b87f87 --- /dev/null +++ b/apps/sim/tools/rippling/get_report_run.ts @@ -0,0 +1,61 @@ +import type { RipplingGetReportRunParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetReportRunTool: ToolConfig = { + id: 'rippling_get_report_run', + name: 'Rippling Get Report Run', + description: 'Get a report run by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + runId: { type: 'string', required: true, visibility: 'user-or-llm', description: 'run id' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/report-runs/${encodeURIComponent(params.runId.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const result = data.result as Record | null + return { + success: true, + output: { + id: (data.id as string) ?? '', + report_id: (data.report_id as string) ?? null, + status: (data.status as string) ?? null, + file_url: (result?.file_url as string) ?? null, + expires_at: (result?.expires_at as string) ?? null, + output_type: (result?.output_type as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Report run ID' }, + report_id: { type: 'string', description: 'Report ID', optional: true }, + status: { type: 'string', description: 'Run status', optional: true }, + file_url: { type: 'string', description: 'URL to download the report file', optional: true }, + expires_at: { + type: 'string', + description: 'Expiration timestamp for the file URL', + optional: true, + }, + output_type: { type: 'string', description: 'Output format (JSON or CSV)', optional: true }, + __meta: { + type: 'json', + description: 'Metadata including redacted_fields', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/rippling/get_supergroup.ts b/apps/sim/tools/rippling/get_supergroup.ts new file mode 100644 index 00000000000..cd40a6789d9 --- /dev/null +++ b/apps/sim/tools/rippling/get_supergroup.ts @@ -0,0 +1,61 @@ +import type { RipplingGetSupergroupParams } from '@/tools/rippling/types' +import { SUPERGROUP_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetSupergroupTool: ToolConfig = { + id: 'rippling_get_supergroup', + name: 'Rippling Get Supergroup', + description: 'Get a specific supergroup by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/supergroups/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + display_name: (data.display_name as string) ?? null, + description: (data.description as string) ?? null, + app_owner_id: (data.app_owner_id as string) ?? null, + group_type: (data.group_type as string) ?? null, + name: (data.name as string) ?? null, + sub_group_type: (data.sub_group_type as string) ?? null, + read_only: (data.read_only as boolean) ?? null, + parent: (data.parent as string) ?? null, + mutually_exclusive_key: (data.mutually_exclusive_key as string) ?? null, + cumulatively_exhaustive_default: (data.cumulatively_exhaustive_default as boolean) ?? null, + include_terminated: (data.include_terminated as boolean) ?? null, + allow_non_employees: (data.allow_non_employees as boolean) ?? null, + can_override_role_states: (data.can_override_role_states as boolean) ?? null, + priority: (data.priority as number) ?? null, + is_invisible: (data.is_invisible as boolean) ?? null, + ignore_prov_group_matching: (data.ignore_prov_group_matching as boolean) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + ...SUPERGROUP_OUTPUT_PROPERTIES, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_team.ts b/apps/sim/tools/rippling/get_team.ts new file mode 100644 index 00000000000..06488e4684c --- /dev/null +++ b/apps/sim/tools/rippling/get_team.ts @@ -0,0 +1,61 @@ +import type { RipplingGetTeamParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetTeamTool: ToolConfig = { + id: 'rippling_get_team', + name: 'Rippling Get Team', + description: 'Get a specific team by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + }, + request: { + url: (params) => { + const base = `https://rest.ripplingapis.com/teams/${encodeURIComponent(params.id.trim())}/` + if (params.expand != null) return `${base}?expand=${encodeURIComponent(params.expand)}` + return base + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + parent_id: (data.parent_id as string) ?? null, + parent: data.parent ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Team ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + parent_id: { type: 'string', description: 'Parent team ID', optional: true }, + parent: { type: 'json', description: 'Expanded parent team', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_title.ts b/apps/sim/tools/rippling/get_title.ts new file mode 100644 index 00000000000..a5457434f0a --- /dev/null +++ b/apps/sim/tools/rippling/get_title.ts @@ -0,0 +1,48 @@ +import type { RipplingGetTitleParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetTitleTool: ToolConfig = { + id: 'rippling_get_title', + name: 'Rippling Get Title', + description: 'Get a specific title by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/titles/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Title ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Title name', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_user.ts b/apps/sim/tools/rippling/get_user.ts new file mode 100644 index 00000000000..36d800134c7 --- /dev/null +++ b/apps/sim/tools/rippling/get_user.ts @@ -0,0 +1,69 @@ +import type { RipplingGetUserParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetUserTool: ToolConfig = { + id: 'rippling_get_user', + name: 'Rippling Get User', + description: 'Get a specific user by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => `https://rest.ripplingapis.com/users/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + active: (data.active as boolean) ?? null, + username: (data.username as string) ?? null, + display_name: (data.display_name as string) ?? null, + preferred_language: (data.preferred_language as string) ?? null, + locale: (data.locale as string) ?? null, + timezone: (data.timezone as string) ?? null, + number: (data.number as string) ?? null, + name: data.name ?? null, + emails: data.emails ?? [], + phone_numbers: data.phone_numbers ?? [], + addresses: data.addresses ?? [], + photos: data.photos ?? [], + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'User ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + active: { type: 'boolean', description: 'Is active', optional: true }, + username: { type: 'string', description: 'Username', optional: true }, + display_name: { type: 'string', description: 'Display name', optional: true }, + preferred_language: { type: 'string', description: 'Preferred language', optional: true }, + locale: { type: 'string', description: 'Locale', optional: true }, + timezone: { type: 'string', description: 'Timezone', optional: true }, + number: { type: 'string', description: 'Profile number', optional: true }, + name: { type: 'json', description: 'User name object', optional: true }, + emails: { type: 'json', description: 'Email addresses', optional: true }, + phone_numbers: { type: 'json', description: 'Phone numbers', optional: true }, + addresses: { type: 'json', description: 'Addresses', optional: true }, + photos: { type: 'json', description: 'Photos', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_work_location.ts b/apps/sim/tools/rippling/get_work_location.ts new file mode 100644 index 00000000000..972826c0f93 --- /dev/null +++ b/apps/sim/tools/rippling/get_work_location.ts @@ -0,0 +1,50 @@ +import type { RipplingGetWorkLocationParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetWorkLocationTool: ToolConfig = { + id: 'rippling_get_work_location', + name: 'Rippling Get Work Location', + description: 'Get a specific work location by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/work-locations/${encodeURIComponent(params.id.trim())}/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + address: data.address ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Location ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + address: { type: 'json', description: 'Address object', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/get_worker.ts b/apps/sim/tools/rippling/get_worker.ts new file mode 100644 index 00000000000..18c9cff92de --- /dev/null +++ b/apps/sim/tools/rippling/get_worker.ts @@ -0,0 +1,113 @@ +import type { RipplingGetWorkerParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingGetWorkerTool: ToolConfig = { + id: 'rippling_get_worker', + name: 'Rippling Get Worker', + description: 'Get a specific worker by ID', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Resource ID' }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + }, + request: { + url: (params) => { + const base = `https://rest.ripplingapis.com/workers/${encodeURIComponent(params.id.trim())}/` + if (params.expand != null) return `${base}?expand=${encodeURIComponent(params.expand)}` + return base + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + user_id: (data.user_id as string) ?? null, + is_manager: (data.is_manager as boolean) ?? null, + manager_id: (data.manager_id as string) ?? null, + legal_entity_id: (data.legal_entity_id as string) ?? null, + country: (data.country as string) ?? null, + start_date: (data.start_date as string) ?? null, + end_date: (data.end_date as string) ?? null, + number: (data.number as number) ?? null, + work_email: (data.work_email as string) ?? null, + personal_email: (data.personal_email as string) ?? null, + status: (data.status as string) ?? null, + employment_type_id: (data.employment_type_id as string) ?? null, + department_id: (data.department_id as string) ?? null, + teams_id: (data.teams_id as unknown[]) ?? [], + title: (data.title as string) ?? null, + level_id: (data.level_id as string) ?? null, + compensation_id: (data.compensation_id as string) ?? null, + overtime_exemption: (data.overtime_exemption as string) ?? null, + title_effective_date: (data.title_effective_date as string) ?? null, + business_partners_id: (data.business_partners_id as unknown[]) ?? [], + location: data.location ?? null, + gender: (data.gender as string) ?? null, + date_of_birth: (data.date_of_birth as string) ?? null, + race: (data.race as string) ?? null, + ethnicity: (data.ethnicity as string) ?? null, + citizenship: (data.citizenship as string) ?? null, + termination_details: data.termination_details ?? null, + custom_fields: data.custom_fields ?? null, + country_fields: data.country_fields ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Worker ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + user_id: { type: 'string', description: 'User ID', optional: true }, + is_manager: { type: 'boolean', description: 'Is manager', optional: true }, + manager_id: { type: 'string', description: 'Manager ID', optional: true }, + legal_entity_id: { type: 'string', description: 'Legal entity ID', optional: true }, + country: { type: 'string', description: 'Country', optional: true }, + start_date: { type: 'string', description: 'Start date', optional: true }, + end_date: { type: 'string', description: 'End date', optional: true }, + number: { type: 'number', description: 'Worker number', optional: true }, + work_email: { type: 'string', description: 'Work email', optional: true }, + personal_email: { type: 'string', description: 'Personal email', optional: true }, + status: { type: 'string', description: 'Status', optional: true }, + employment_type_id: { type: 'string', description: 'Employment type ID', optional: true }, + department_id: { type: 'string', description: 'Department ID', optional: true }, + teams_id: { type: 'json', description: 'Team IDs', optional: true }, + title: { type: 'string', description: 'Job title', optional: true }, + level_id: { type: 'string', description: 'Level ID', optional: true }, + compensation_id: { type: 'string', description: 'Compensation ID', optional: true }, + overtime_exemption: { type: 'string', description: 'Overtime exemption', optional: true }, + title_effective_date: { type: 'string', description: 'Title effective date', optional: true }, + business_partners_id: { type: 'json', description: 'Business partner IDs', optional: true }, + location: { type: 'json', description: 'Worker location', optional: true }, + gender: { type: 'string', description: 'Gender', optional: true }, + date_of_birth: { type: 'string', description: 'Date of birth', optional: true }, + race: { type: 'string', description: 'Race', optional: true }, + ethnicity: { type: 'string', description: 'Ethnicity', optional: true }, + citizenship: { type: 'string', description: 'Citizenship', optional: true }, + termination_details: { type: 'json', description: 'Termination details', optional: true }, + custom_fields: { type: 'json', description: 'Custom fields', optional: true }, + country_fields: { type: 'json', description: 'Country-specific fields', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/index.ts b/apps/sim/tools/rippling/index.ts index 69c3a7f5c32..d6dafdb4d3d 100644 --- a/apps/sim/tools/rippling/index.ts +++ b/apps/sim/tools/rippling/index.ts @@ -1,20 +1,87 @@ -export { ripplingCreateGroupTool } from '@/tools/rippling/create_group' -export { ripplingGetCompanyTool } from '@/tools/rippling/get_company' -export { ripplingGetCompanyActivityTool } from '@/tools/rippling/get_company_activity' -export { ripplingGetCurrentUserTool } from '@/tools/rippling/get_current_user' -export { ripplingGetEmployeeTool } from '@/tools/rippling/get_employee' -export { ripplingGetLeaveBalanceTool } from '@/tools/rippling/get_leave_balance' -export { ripplingListCustomFieldsTool } from '@/tools/rippling/list_custom_fields' -export { ripplingListDepartmentsTool } from '@/tools/rippling/list_departments' -export { ripplingListEmployeesTool } from '@/tools/rippling/list_employees' -export { ripplingListEmployeesWithTerminatedTool } from '@/tools/rippling/list_employees_with_terminated' -export { ripplingListLeaveBalancesTool } from '@/tools/rippling/list_leave_balances' -export { ripplingListLeaveRequestsTool } from '@/tools/rippling/list_leave_requests' -export { ripplingListLeaveTypesTool } from '@/tools/rippling/list_leave_types' -export { ripplingListLevelsTool } from '@/tools/rippling/list_levels' -export { ripplingListTeamsTool } from '@/tools/rippling/list_teams' -export { ripplingListWorkLocationsTool } from '@/tools/rippling/list_work_locations' -export { ripplingProcessLeaveRequestTool } from '@/tools/rippling/process_leave_request' -export { ripplingPushCandidateTool } from '@/tools/rippling/push_candidate' -export * from '@/tools/rippling/types' -export { ripplingUpdateGroupTool } from '@/tools/rippling/update_group' +export { ripplingBulkCreateCustomObjectRecordsTool } from './bulk_create_custom_object_records' +export { ripplingBulkDeleteCustomObjectRecordsTool } from './bulk_delete_custom_object_records' +export { ripplingBulkUpdateCustomObjectRecordsTool } from './bulk_update_custom_object_records' +export { ripplingCreateBusinessPartnerTool } from './create_business_partner' +export { ripplingCreateBusinessPartnerGroupTool } from './create_business_partner_group' +export { ripplingCreateCustomAppTool } from './create_custom_app' +export { ripplingCreateCustomObjectTool } from './create_custom_object' +export { ripplingCreateCustomObjectFieldTool } from './create_custom_object_field' +export { ripplingCreateCustomObjectRecordTool } from './create_custom_object_record' +export { ripplingCreateCustomPageTool } from './create_custom_page' +export { ripplingCreateCustomSettingTool } from './create_custom_setting' +export { ripplingCreateDepartmentTool } from './create_department' +export { ripplingCreateDraftHiresTool } from './create_draft_hires' +export { ripplingCreateObjectCategoryTool } from './create_object_category' +export { ripplingCreateTitleTool } from './create_title' +export { ripplingCreateWorkLocationTool } from './create_work_location' +export { ripplingDeleteBusinessPartnerTool } from './delete_business_partner' +export { ripplingDeleteBusinessPartnerGroupTool } from './delete_business_partner_group' +export { ripplingDeleteCustomAppTool } from './delete_custom_app' +export { ripplingDeleteCustomObjectTool } from './delete_custom_object' +export { ripplingDeleteCustomObjectFieldTool } from './delete_custom_object_field' +export { ripplingDeleteCustomObjectRecordTool } from './delete_custom_object_record' +export { ripplingDeleteCustomPageTool } from './delete_custom_page' +export { ripplingDeleteCustomSettingTool } from './delete_custom_setting' +export { ripplingDeleteObjectCategoryTool } from './delete_object_category' +export { ripplingDeleteTitleTool } from './delete_title' +export { ripplingDeleteWorkLocationTool } from './delete_work_location' +export { ripplingGetBusinessPartnerTool } from './get_business_partner' +export { ripplingGetBusinessPartnerGroupTool } from './get_business_partner_group' +export { ripplingGetCurrentUserTool } from './get_current_user' +export { ripplingGetCustomAppTool } from './get_custom_app' +export { ripplingGetCustomObjectTool } from './get_custom_object' +export { ripplingGetCustomObjectFieldTool } from './get_custom_object_field' +export { ripplingGetCustomObjectRecordTool } from './get_custom_object_record' +export { ripplingGetCustomObjectRecordByExternalIdTool } from './get_custom_object_record_by_external_id' +export { ripplingGetCustomPageTool } from './get_custom_page' +export { ripplingGetCustomSettingTool } from './get_custom_setting' +export { ripplingGetDepartmentTool } from './get_department' +export { ripplingGetEmploymentTypeTool } from './get_employment_type' +export { ripplingGetJobFunctionTool } from './get_job_function' +export { ripplingGetObjectCategoryTool } from './get_object_category' +export { ripplingGetReportRunTool } from './get_report_run' +export { ripplingGetSupergroupTool } from './get_supergroup' +export { ripplingGetTeamTool } from './get_team' +export { ripplingGetTitleTool } from './get_title' +export { ripplingGetUserTool } from './get_user' +export { ripplingGetWorkLocationTool } from './get_work_location' +export { ripplingGetWorkerTool } from './get_worker' +export { ripplingListBusinessPartnerGroupsTool } from './list_business_partner_groups' +export { ripplingListBusinessPartnersTool } from './list_business_partners' +export { ripplingListCompaniesTool } from './list_companies' +export { ripplingListCustomAppsTool } from './list_custom_apps' +export { ripplingListCustomFieldsTool } from './list_custom_fields' +export { ripplingListCustomObjectFieldsTool } from './list_custom_object_fields' +export { ripplingListCustomObjectRecordsTool } from './list_custom_object_records' +export { ripplingListCustomObjectsTool } from './list_custom_objects' +export { ripplingListCustomPagesTool } from './list_custom_pages' +export { ripplingListCustomSettingsTool } from './list_custom_settings' +export { ripplingListDepartmentsTool } from './list_departments' +export { ripplingListEmploymentTypesTool } from './list_employment_types' +export { ripplingListEntitlementsTool } from './list_entitlements' +export { ripplingListJobFunctionsTool } from './list_job_functions' +export { ripplingListObjectCategoriesTool } from './list_object_categories' +export { ripplingListSupergroupExclusionMembersTool } from './list_supergroup_exclusion_members' +export { ripplingListSupergroupInclusionMembersTool } from './list_supergroup_inclusion_members' +export { ripplingListSupergroupMembersTool } from './list_supergroup_members' +export { ripplingListSupergroupsTool } from './list_supergroups' +export { ripplingListTeamsTool } from './list_teams' +export { ripplingListTitlesTool } from './list_titles' +export { ripplingListUsersTool } from './list_users' +export { ripplingListWorkLocationsTool } from './list_work_locations' +export { ripplingListWorkersTool } from './list_workers' +export { ripplingQueryCustomObjectRecordsTool } from './query_custom_object_records' +export { ripplingTriggerReportRunTool } from './trigger_report_run' +export * from './types' +export { ripplingUpdateCustomAppTool } from './update_custom_app' +export { ripplingUpdateCustomObjectTool } from './update_custom_object' +export { ripplingUpdateCustomObjectFieldTool } from './update_custom_object_field' +export { ripplingUpdateCustomObjectRecordTool } from './update_custom_object_record' +export { ripplingUpdateCustomPageTool } from './update_custom_page' +export { ripplingUpdateCustomSettingTool } from './update_custom_setting' +export { ripplingUpdateDepartmentTool } from './update_department' +export { ripplingUpdateObjectCategoryTool } from './update_object_category' +export { ripplingUpdateSupergroupExclusionMembersTool } from './update_supergroup_exclusion_members' +export { ripplingUpdateSupergroupInclusionMembersTool } from './update_supergroup_inclusion_members' +export { ripplingUpdateTitleTool } from './update_title' +export { ripplingUpdateWorkLocationTool } from './update_work_location' diff --git a/apps/sim/tools/rippling/list_business_partner_groups.ts b/apps/sim/tools/rippling/list_business_partner_groups.ts new file mode 100644 index 00000000000..04b29e607a4 --- /dev/null +++ b/apps/sim/tools/rippling/list_business_partner_groups.ts @@ -0,0 +1,86 @@ +import type { RipplingListBusinessPartnerGroupsParams } from '@/tools/rippling/types' +import { BUSINESS_PARTNER_GROUP_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListBusinessPartnerGroupsTool: ToolConfig = + { + id: 'rippling_list_business_partner_groups', + name: 'Rippling List Business Partner Groups', + description: 'List all business partner groups', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.expand != null) query.set('expand', params.expand) + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/business-partner-groups/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + businessPartnerGroups: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + domain: (item.domain as string) ?? null, + default_business_partner_id: (item.default_business_partner_id as string) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + businessPartnerGroups: { + type: 'array', + description: 'List of businessPartnerGroups', + items: { type: 'object', properties: BUSINESS_PARTNER_GROUP_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, + } diff --git a/apps/sim/tools/rippling/list_business_partners.ts b/apps/sim/tools/rippling/list_business_partners.ts new file mode 100644 index 00000000000..0675a81586f --- /dev/null +++ b/apps/sim/tools/rippling/list_business_partners.ts @@ -0,0 +1,90 @@ +import type { RipplingListBusinessPartnersParams } from '@/tools/rippling/types' +import { BUSINESS_PARTNER_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListBusinessPartnersTool: ToolConfig = { + id: 'rippling_list_business_partners', + name: 'Rippling List Business Partners', + description: 'List all business partners', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + filter: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter expression', + }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.filter != null) query.set('filter', params.filter) + if (params.expand != null) query.set('expand', params.expand) + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/business-partners/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + businessPartners: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + business_partner_group_id: (item.business_partner_group_id as string) ?? null, + worker_id: (item.worker_id as string) ?? null, + client_group_id: (item.client_group_id as string) ?? null, + client_group_member_count: (item.client_group_member_count as number) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + businessPartners: { + type: 'array', + description: 'List of businessPartners', + items: { type: 'object', properties: BUSINESS_PARTNER_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_companies.ts b/apps/sim/tools/rippling/list_companies.ts new file mode 100644 index 00000000000..34a1ca76260 --- /dev/null +++ b/apps/sim/tools/rippling/list_companies.ts @@ -0,0 +1,87 @@ +import type { RipplingListCompaniesParams } from '@/tools/rippling/types' +import { COMPANY_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListCompaniesTool: ToolConfig = { + id: 'rippling_list_companies', + name: 'Rippling List Companies', + description: 'List all companies', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.expand != null) query.set('expand', params.expand) + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/companies/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + companies: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + legal_name: (item.legal_name as string) ?? null, + doing_business_as_name: (item.doing_business_as_name as string) ?? null, + phone: (item.phone as string) ?? null, + primary_email: (item.primary_email as string) ?? null, + parent_legal_entity_id: (item.parent_legal_entity_id as string) ?? null, + legal_entities_id: (item.legal_entities_id as unknown[]) ?? [], + physical_address: item.physical_address ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + companies: { + type: 'array', + description: 'List of companies', + items: { type: 'object', properties: COMPANY_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_custom_apps.ts b/apps/sim/tools/rippling/list_custom_apps.ts new file mode 100644 index 00000000000..1288b161f88 --- /dev/null +++ b/apps/sim/tools/rippling/list_custom_apps.ts @@ -0,0 +1,59 @@ +import type { RipplingListCustomAppsParams } from '@/tools/rippling/types' +import { CUSTOM_APP_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListCustomAppsTool: ToolConfig = { + id: 'rippling_list_custom_apps', + name: 'Rippling List CustomApps', + description: 'List all custom apps', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + }, + request: { + url: `https://rest.ripplingapis.com/custom-apps/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + customApps: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + api_name: (item.api_name as string) ?? null, + description: (item.description as string) ?? null, + icon: (item.icon as string) ?? null, + pages: (item.pages as unknown[]) ?? [], + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + customApps: { + type: 'array', + description: 'List of customApps', + items: { type: 'object', properties: CUSTOM_APP_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_custom_fields.ts b/apps/sim/tools/rippling/list_custom_fields.ts index d24204f34ff..40cf097557c 100644 --- a/apps/sim/tools/rippling/list_custom_fields.ts +++ b/apps/sim/tools/rippling/list_custom_fields.ts @@ -1,18 +1,12 @@ -import type { - RipplingListCustomFieldsParams, - RipplingListCustomFieldsResponse, -} from '@/tools/rippling/types' +import type { RipplingListCustomFieldsParams } from '@/tools/rippling/types' +import { CUSTOM_FIELD_OUTPUT_PROPERTIES } from '@/tools/rippling/types' import type { ToolConfig } from '@/tools/types' -export const ripplingListCustomFieldsTool: ToolConfig< - RipplingListCustomFieldsParams, - RipplingListCustomFieldsResponse -> = { +export const ripplingListCustomFieldsTool: ToolConfig = { id: 'rippling_list_custom_fields', name: 'Rippling List Custom Fields', - description: 'List all custom fields defined in Rippling', + description: 'List all custom fields', version: '1.0.0', - params: { apiKey: { type: 'string', @@ -20,77 +14,63 @@ export const ripplingListCustomFieldsTool: ToolConfig< visibility: 'user-only', description: 'Rippling API key', }, - limit: { - type: 'number', + orderBy: { + type: 'string', required: false, visibility: 'user-or-llm', - description: 'Maximum number of custom fields to return', + description: 'Sort field. Prefix with - for descending', }, - offset: { - type: 'number', + cursor: { + type: 'string', required: false, visibility: 'user-or-llm', - description: 'Offset for pagination', + description: 'Pagination cursor from previous response', }, }, - request: { url: (params) => { const query = new URLSearchParams() - if (params.limit != null) query.set('limit', String(params.limit)) - if (params.offset != null) query.set('offset', String(params.offset)) + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) const qs = query.toString() - return `https://api.rippling.com/platform/api/custom_fields${qs ? `?${qs}` : ''}` + return `https://rest.ripplingapis.com/custom-fields/${qs ? `?${qs}` : ''}` }, method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), }, - transformResponse: async (response: Response) => { if (!response.ok) { const errorText = await response.text() throw new Error(`Rippling API error (${response.status}): ${errorText}`) } - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const customFields = results.map((field: Record) => ({ - id: (field.id as string) ?? '', - type: (field.type as string) ?? null, - title: (field.title as string) ?? null, - mandatory: Boolean(field.mandatory), - })) - + const results = data.results ?? [] return { success: true, output: { - customFields, - totalCount: customFields.length, + customFields: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + description: (item.description as string) ?? null, + required: (item.required as boolean) ?? null, + type: (item.type as string) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, }, } }, - outputs: { customFields: { type: 'array', - description: 'List of custom fields', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Custom field ID' }, - type: { type: 'string', description: 'Field type' }, - title: { type: 'string', description: 'Field title' }, - mandatory: { type: 'boolean', description: 'Whether the field is mandatory' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Number of custom fields returned on this page', + description: 'List of customFields', + items: { type: 'object', properties: CUSTOM_FIELD_OUTPUT_PROPERTIES }, }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, }, } diff --git a/apps/sim/tools/rippling/list_custom_object_fields.ts b/apps/sim/tools/rippling/list_custom_object_fields.ts new file mode 100644 index 00000000000..604187b9aa6 --- /dev/null +++ b/apps/sim/tools/rippling/list_custom_object_fields.ts @@ -0,0 +1,73 @@ +import type { RipplingListCustomObjectFieldsParams } from '@/tools/rippling/types' +import { CUSTOM_OBJECT_FIELD_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListCustomObjectFieldsTool: ToolConfig = + { + id: 'rippling_list_custom_object_fields', + name: 'Rippling List Custom Object Fields', + description: 'List all fields for a custom object', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/fields/`, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + fields: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + custom_object: (item.custom_object as string) ?? null, + description: (item.description as string) ?? null, + api_name: (item.api_name as string) ?? null, + data_type: item.data_type ?? null, + is_unique: (item.is_unique as boolean) ?? null, + is_immutable: (item.is_immutable as boolean) ?? null, + is_standard: (item.is_standard as boolean) ?? null, + enable_history: (item.enable_history as boolean) ?? null, + managed_package_install_id: (item.managed_package_install_id as string) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + }, + } + }, + outputs: { + fields: { + type: 'array', + description: 'List of fields', + items: { type: 'object', properties: CUSTOM_OBJECT_FIELD_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of fields returned' }, + nextLink: { type: 'string', description: 'Next page link', optional: true }, + }, + } diff --git a/apps/sim/tools/rippling/list_custom_object_records.ts b/apps/sim/tools/rippling/list_custom_object_records.ts new file mode 100644 index 00000000000..9d9438bd227 --- /dev/null +++ b/apps/sim/tools/rippling/list_custom_object_records.ts @@ -0,0 +1,90 @@ +import type { RipplingListCustomObjectRecordsParams } from '@/tools/rippling/types' +import { CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListCustomObjectRecordsTool: ToolConfig = + { + id: 'rippling_list_custom_object_records', + name: 'Rippling List Custom Object Records', + description: 'List all records for a custom object', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/`, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + records: results.map((item: Record) => { + const { + id, + created_at, + updated_at, + name, + external_id, + created_by, + last_modified_by, + owner_role, + system_updated_at, + ...dynamicFields + } = item + return { + id: (id as string) ?? '', + created_at: (created_at as string) ?? null, + updated_at: (updated_at as string) ?? null, + name: (name as string) ?? null, + external_id: (external_id as string) ?? null, + created_by: created_by ?? null, + last_modified_by: last_modified_by ?? null, + owner_role: owner_role ?? null, + system_updated_at: (system_updated_at as string) ?? null, + data: dynamicFields, + } + }), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + }, + } + }, + outputs: { + records: { + type: 'array', + description: 'List of records', + items: { + type: 'object', + properties: { + ...CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES, + data: { type: 'json', description: 'Full record data including dynamic fields' }, + }, + }, + }, + totalCount: { type: 'number', description: 'Number of records returned' }, + nextLink: { type: 'string', description: 'Next page link', optional: true }, + }, + } diff --git a/apps/sim/tools/rippling/list_custom_objects.ts b/apps/sim/tools/rippling/list_custom_objects.ts new file mode 100644 index 00000000000..66c6d5a7661 --- /dev/null +++ b/apps/sim/tools/rippling/list_custom_objects.ts @@ -0,0 +1,64 @@ +import type { RipplingListCustomObjectsParams } from '@/tools/rippling/types' +import { CUSTOM_OBJECT_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListCustomObjectsTool: ToolConfig = { + id: 'rippling_list_custom_objects', + name: 'Rippling List Custom Objects', + description: 'List all custom objects', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + }, + request: { + url: `https://rest.ripplingapis.com/custom-objects/`, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + customObjects: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + description: (item.description as string) ?? null, + api_name: (item.api_name as string) ?? null, + plural_label: (item.plural_label as string) ?? null, + category_id: (item.category_id as string) ?? null, + enable_history: (item.enable_history as boolean) ?? null, + native_category_id: (item.native_category_id as string) ?? null, + managed_package_install_id: (item.managed_package_install_id as string) ?? null, + owner_id: (item.owner_id as string) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + }, + } + }, + outputs: { + customObjects: { + type: 'array', + description: 'List of customObjects', + items: { type: 'object', properties: CUSTOM_OBJECT_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_custom_pages.ts b/apps/sim/tools/rippling/list_custom_pages.ts new file mode 100644 index 00000000000..05cb0938793 --- /dev/null +++ b/apps/sim/tools/rippling/list_custom_pages.ts @@ -0,0 +1,60 @@ +import type { RipplingListCustomPagesParams } from '@/tools/rippling/types' +import { CUSTOM_PAGE_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListCustomPagesTool: ToolConfig = { + id: 'rippling_list_custom_pages', + name: 'Rippling List CustomPages', + description: 'List all custom pages', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + }, + request: { + url: `https://rest.ripplingapis.com/custom-pages/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + customPages: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + components: item.components ?? [], + actions: item.actions ?? [], + canvas_actions: item.canvas_actions ?? [], + variables: item.variables ?? [], + media: item.media ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + customPages: { + type: 'array', + description: 'List of customPages', + items: { type: 'object', properties: CUSTOM_PAGE_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_custom_settings.ts b/apps/sim/tools/rippling/list_custom_settings.ts new file mode 100644 index 00000000000..a907b5f917e --- /dev/null +++ b/apps/sim/tools/rippling/list_custom_settings.ts @@ -0,0 +1,79 @@ +import type { RipplingListCustomSettingsParams } from '@/tools/rippling/types' +import { CUSTOM_SETTING_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListCustomSettingsTool: ToolConfig = { + id: 'rippling_list_custom_settings', + name: 'Rippling List Custom Settings', + description: 'List all custom settings', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/custom-settings/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + customSettings: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + display_name: (item.display_name as string) ?? null, + api_name: (item.api_name as string) ?? null, + data_type: (item.data_type as string) ?? null, + secret_value: (item.secret_value as string) ?? null, + string_value: (item.string_value as string) ?? null, + number_value: (item.number_value as number) ?? null, + boolean_value: (item.boolean_value as boolean) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + customSettings: { + type: 'array', + description: 'List of custom settings', + items: { type: 'object', properties: CUSTOM_SETTING_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_departments.ts b/apps/sim/tools/rippling/list_departments.ts index 830432af6b1..666cd753cd1 100644 --- a/apps/sim/tools/rippling/list_departments.ts +++ b/apps/sim/tools/rippling/list_departments.ts @@ -1,18 +1,12 @@ -import type { - RipplingListDepartmentsParams, - RipplingListDepartmentsResponse, -} from '@/tools/rippling/types' +import type { RipplingListDepartmentsParams } from '@/tools/rippling/types' +import { DEPARTMENT_OUTPUT_PROPERTIES } from '@/tools/rippling/types' import type { ToolConfig } from '@/tools/types' -export const ripplingListDepartmentsTool: ToolConfig< - RipplingListDepartmentsParams, - RipplingListDepartmentsResponse -> = { +export const ripplingListDepartmentsTool: ToolConfig = { id: 'rippling_list_departments', name: 'Rippling List Departments', - description: 'List all departments in the Rippling organization', + description: 'List all departments', version: '1.0.0', - params: { apiKey: { type: 'string', @@ -20,75 +14,72 @@ export const ripplingListDepartmentsTool: ToolConfig< visibility: 'user-only', description: 'Rippling API key', }, - limit: { - type: 'number', + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + orderBy: { + type: 'string', required: false, visibility: 'user-or-llm', - description: 'Maximum number of departments to return', + description: 'Sort field. Prefix with - for descending', }, - offset: { - type: 'number', + cursor: { + type: 'string', required: false, visibility: 'user-or-llm', - description: 'Offset for pagination', + description: 'Pagination cursor from previous response', }, }, - request: { url: (params) => { const query = new URLSearchParams() - if (params.limit != null) query.set('limit', String(params.limit)) - if (params.offset != null) query.set('offset', String(params.offset)) + if (params.expand != null) query.set('expand', params.expand) + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) const qs = query.toString() - return `https://api.rippling.com/platform/api/departments${qs ? `?${qs}` : ''}` + return `https://rest.ripplingapis.com/departments/${qs ? `?${qs}` : ''}` }, method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), }, - transformResponse: async (response: Response) => { if (!response.ok) { const errorText = await response.text() throw new Error(`Rippling API error (${response.status}): ${errorText}`) } - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const departments = results.map((dept: Record) => ({ - id: (dept.id as string) ?? '', - name: (dept.name as string) ?? null, - parent: (dept.parent as string) ?? null, - })) - + const results = data.results ?? [] return { success: true, output: { - departments, - totalCount: departments.length, + departments: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + parent_id: (item.parent_id as string) ?? null, + reference_code: (item.reference_code as string) ?? null, + department_hierarchy_id: (item.department_hierarchy_id as unknown[]) ?? [], + parent: item.parent ?? null, + department_hierarchy: item.department_hierarchy ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, }, } }, - outputs: { departments: { type: 'array', description: 'List of departments', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Department ID' }, - name: { type: 'string', description: 'Department name' }, - parent: { type: 'string', description: 'Parent department ID' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Number of departments returned on this page', + items: { type: 'object', properties: DEPARTMENT_OUTPUT_PROPERTIES }, }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, }, } diff --git a/apps/sim/tools/rippling/list_employees.ts b/apps/sim/tools/rippling/list_employees.ts deleted file mode 100644 index bf2782e42b6..00000000000 --- a/apps/sim/tools/rippling/list_employees.ts +++ /dev/null @@ -1,112 +0,0 @@ -import type { - RipplingListEmployeesParams, - RipplingListEmployeesResponse, -} from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingListEmployeesTool: ToolConfig< - RipplingListEmployeesParams, - RipplingListEmployeesResponse -> = { - id: 'rippling_list_employees', - name: 'Rippling List Employees', - description: 'List all employees in Rippling with optional pagination', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - limit: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Maximum number of employees to return (default 100, max 100)', - }, - offset: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Offset for pagination', - }, - }, - - request: { - url: (params) => { - const query = new URLSearchParams() - if (params.limit != null) query.set('limit', String(params.limit)) - if (params.offset != null) query.set('offset', String(params.offset)) - const qs = query.toString() - return `https://api.rippling.com/platform/api/employees${qs ? `?${qs}` : ''}` - }, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const employees = results.map((emp: Record) => ({ - id: (emp.id as string) ?? '', - firstName: (emp.firstName as string) ?? null, - lastName: (emp.lastName as string) ?? null, - workEmail: (emp.workEmail as string) ?? null, - personalEmail: (emp.personalEmail as string) ?? null, - roleState: (emp.roleState as string) ?? null, - department: (emp.department as string) ?? null, - title: (emp.title as string) ?? null, - startDate: (emp.startDate as string) ?? null, - endDate: (emp.endDate as string) ?? null, - manager: (emp.manager as string) ?? null, - phone: (emp.phone as string) ?? null, - })) - - return { - success: true, - output: { - employees, - totalCount: employees.length, - }, - } - }, - - outputs: { - employees: { - type: 'array', - description: 'List of employees', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Employee ID' }, - firstName: { type: 'string', description: 'First name' }, - lastName: { type: 'string', description: 'Last name' }, - workEmail: { type: 'string', description: 'Work email address' }, - personalEmail: { type: 'string', description: 'Personal email address' }, - roleState: { type: 'string', description: 'Employment status' }, - department: { type: 'string', description: 'Department name or ID' }, - title: { type: 'string', description: 'Job title' }, - startDate: { type: 'string', description: 'Employment start date' }, - endDate: { type: 'string', description: 'Employment end date' }, - manager: { type: 'string', description: 'Manager ID or name' }, - phone: { type: 'string', description: 'Phone number' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Number of employees returned on this page', - }, - }, -} diff --git a/apps/sim/tools/rippling/list_employees_with_terminated.ts b/apps/sim/tools/rippling/list_employees_with_terminated.ts deleted file mode 100644 index 86924bd9440..00000000000 --- a/apps/sim/tools/rippling/list_employees_with_terminated.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { - RipplingListEmployeesWithTerminatedParams, - RipplingListEmployeesWithTerminatedResponse, -} from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingListEmployeesWithTerminatedTool: ToolConfig< - RipplingListEmployeesWithTerminatedParams, - RipplingListEmployeesWithTerminatedResponse -> = { - id: 'rippling_list_employees_with_terminated', - name: 'Rippling List Employees Including Terminated', - description: - 'List all employees in Rippling including terminated employees with optional pagination', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - limit: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Maximum number of employees to return (default 100, max 100)', - }, - offset: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Offset for pagination', - }, - }, - - request: { - url: (params) => { - const query = new URLSearchParams() - if (params.limit != null) query.set('limit', String(params.limit)) - if (params.offset != null) query.set('offset', String(params.offset)) - const qs = query.toString() - return `https://api.rippling.com/platform/api/employees/include_terminated${qs ? `?${qs}` : ''}` - }, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const employees = results.map((emp: Record) => ({ - id: (emp.id as string) ?? '', - firstName: (emp.firstName as string) ?? null, - lastName: (emp.lastName as string) ?? null, - workEmail: (emp.workEmail as string) ?? null, - personalEmail: (emp.personalEmail as string) ?? null, - roleState: (emp.roleState as string) ?? null, - department: (emp.department as string) ?? null, - title: (emp.title as string) ?? null, - startDate: (emp.startDate as string) ?? null, - endDate: (emp.endDate as string) ?? null, - manager: (emp.manager as string) ?? null, - phone: (emp.phone as string) ?? null, - })) - - return { - success: true, - output: { - employees, - totalCount: employees.length, - }, - } - }, - - outputs: { - employees: { - type: 'array', - description: 'List of employees including terminated', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Employee ID' }, - firstName: { type: 'string', description: 'First name' }, - lastName: { type: 'string', description: 'Last name' }, - workEmail: { type: 'string', description: 'Work email address' }, - personalEmail: { type: 'string', description: 'Personal email address' }, - roleState: { type: 'string', description: 'Employment status' }, - department: { type: 'string', description: 'Department name or ID' }, - title: { type: 'string', description: 'Job title' }, - startDate: { type: 'string', description: 'Employment start date' }, - endDate: { type: 'string', description: 'Employment end date' }, - manager: { type: 'string', description: 'Manager ID or name' }, - phone: { type: 'string', description: 'Phone number' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Number of employees returned on this page', - }, - }, -} diff --git a/apps/sim/tools/rippling/list_employment_types.ts b/apps/sim/tools/rippling/list_employment_types.ts new file mode 100644 index 00000000000..6aaf23eb801 --- /dev/null +++ b/apps/sim/tools/rippling/list_employment_types.ts @@ -0,0 +1,77 @@ +import type { RipplingListEmploymentTypesParams } from '@/tools/rippling/types' +import { EMPLOYMENT_TYPE_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListEmploymentTypesTool: ToolConfig = { + id: 'rippling_list_employment_types', + name: 'Rippling List Employment Types', + description: 'List all employment types', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/employment-types/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + employmentTypes: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + label: (item.label as string) ?? null, + name: (item.name as string) ?? null, + type: (item.type as string) ?? null, + compensation_time_period: (item.compensation_time_period as string) ?? null, + amount_worked: (item.amount_worked as string) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + employmentTypes: { + type: 'array', + description: 'List of employmentTypes', + items: { type: 'object', properties: EMPLOYMENT_TYPE_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_entitlements.ts b/apps/sim/tools/rippling/list_entitlements.ts new file mode 100644 index 00000000000..75c23468099 --- /dev/null +++ b/apps/sim/tools/rippling/list_entitlements.ts @@ -0,0 +1,54 @@ +import type { RipplingListEntitlementsParams } from '@/tools/rippling/types' +import { ENTITLEMENT_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListEntitlementsTool: ToolConfig = { + id: 'rippling_list_entitlements', + name: 'Rippling List Entitlements', + description: 'List all entitlements', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + }, + request: { + url: `https://rest.ripplingapis.com/entitlements/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + entitlements: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + description: (item.description as string) ?? null, + display_name: (item.display_name as string) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + entitlements: { + type: 'array', + description: 'List of entitlements', + items: { type: 'object', properties: ENTITLEMENT_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_job_functions.ts b/apps/sim/tools/rippling/list_job_functions.ts new file mode 100644 index 00000000000..6a4df682ebe --- /dev/null +++ b/apps/sim/tools/rippling/list_job_functions.ts @@ -0,0 +1,73 @@ +import type { RipplingListJobFunctionsParams } from '@/tools/rippling/types' +import { JOB_FUNCTION_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListJobFunctionsTool: ToolConfig = { + id: 'rippling_list_job_functions', + name: 'Rippling List Job Functions', + description: 'List all job functions', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/job-functions/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + jobFunctions: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + jobFunctions: { + type: 'array', + description: 'List of jobFunctions', + items: { type: 'object', properties: JOB_FUNCTION_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_leave_balances.ts b/apps/sim/tools/rippling/list_leave_balances.ts deleted file mode 100644 index ff0e4cc23a3..00000000000 --- a/apps/sim/tools/rippling/list_leave_balances.ts +++ /dev/null @@ -1,107 +0,0 @@ -import type { - RipplingListLeaveBalancesParams, - RipplingListLeaveBalancesResponse, -} from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingListLeaveBalancesTool: ToolConfig< - RipplingListLeaveBalancesParams, - RipplingListLeaveBalancesResponse -> = { - id: 'rippling_list_leave_balances', - name: 'Rippling List Leave Balances', - description: 'List leave balances for all employees in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - limit: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Maximum number of leave balances to return', - }, - offset: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Offset for pagination', - }, - }, - - request: { - url: (params) => { - const query = new URLSearchParams() - if (params.limit != null) query.set('limit', String(params.limit)) - if (params.offset != null) query.set('offset', String(params.offset)) - const qs = query.toString() - return `https://api.rippling.com/platform/api/leave_balances${qs ? `?${qs}` : ''}` - }, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const leaveBalances = results.map((bal: Record) => ({ - employeeId: (bal.employeeId as string) ?? '', - balances: (Array.isArray(bal.balances) ? bal.balances : []).map( - (b: Record) => ({ - leaveType: (b.leaveType as string) ?? '', - minutesRemaining: (b.minutesRemaining as number) ?? 0, - }) - ), - })) - - return { - success: true, - output: { - leaveBalances, - totalCount: leaveBalances.length, - }, - } - }, - - outputs: { - leaveBalances: { - type: 'array', - description: 'List of employee leave balances', - items: { - type: 'json', - properties: { - employeeId: { type: 'string', description: 'Employee ID' }, - balances: { - type: 'array', - description: 'Leave balance entries', - items: { - type: 'json', - properties: { - leaveType: { type: 'string', description: 'Type of leave' }, - minutesRemaining: { type: 'number', description: 'Minutes of leave remaining' }, - }, - }, - }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Number of leave balances returned on this page', - }, - }, -} diff --git a/apps/sim/tools/rippling/list_leave_requests.ts b/apps/sim/tools/rippling/list_leave_requests.ts deleted file mode 100644 index 2f99d267d2f..00000000000 --- a/apps/sim/tools/rippling/list_leave_requests.ts +++ /dev/null @@ -1,111 +0,0 @@ -import type { - RipplingListLeaveRequestsParams, - RipplingListLeaveRequestsResponse, -} from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingListLeaveRequestsTool: ToolConfig< - RipplingListLeaveRequestsParams, - RipplingListLeaveRequestsResponse -> = { - id: 'rippling_list_leave_requests', - name: 'Rippling List Leave Requests', - description: 'List leave requests in Rippling with optional filtering by date range and status', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - startDate: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Filter by start date (ISO date string)', - }, - endDate: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Filter by end date (ISO date string)', - }, - status: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Filter by status (e.g. pending, approved, declined)', - }, - }, - - request: { - url: (params) => { - const query = new URLSearchParams() - if (params.startDate) query.set('startDate', params.startDate) - if (params.endDate) query.set('endDate', params.endDate) - if (params.status) query.set('status', params.status) - const qs = query.toString() - return `https://api.rippling.com/platform/api/leave_requests${qs ? `?${qs}` : ''}` - }, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const leaveRequests = results.map((req: Record) => ({ - id: (req.id as string) ?? '', - requestedBy: (req.requestedBy as string) ?? '', - status: (req.status as string) ?? '', - startDate: (req.startDate as string) ?? '', - endDate: (req.endDate as string) ?? '', - reason: (req.reason as string) ?? null, - leaveType: (req.leaveType as string) ?? null, - createdAt: (req.createdAt as string) ?? null, - })) - - return { - success: true, - output: { - leaveRequests, - totalCount: leaveRequests.length, - }, - } - }, - - outputs: { - leaveRequests: { - type: 'array', - description: 'List of leave requests', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Leave request ID' }, - requestedBy: { type: 'string', description: 'Employee ID who requested leave' }, - status: { type: 'string', description: 'Request status (pending/approved/declined)' }, - startDate: { type: 'string', description: 'Leave start date' }, - endDate: { type: 'string', description: 'Leave end date' }, - reason: { type: 'string', description: 'Reason for leave' }, - leaveType: { type: 'string', description: 'Type of leave' }, - createdAt: { type: 'string', description: 'When the request was created' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Total number of leave requests returned', - }, - }, -} diff --git a/apps/sim/tools/rippling/list_leave_types.ts b/apps/sim/tools/rippling/list_leave_types.ts deleted file mode 100644 index 3c44c569343..00000000000 --- a/apps/sim/tools/rippling/list_leave_types.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type { - RipplingListLeaveTypesParams, - RipplingListLeaveTypesResponse, -} from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingListLeaveTypesTool: ToolConfig< - RipplingListLeaveTypesParams, - RipplingListLeaveTypesResponse -> = { - id: 'rippling_list_leave_types', - name: 'Rippling List Leave Types', - description: 'List company leave types configured in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - managedBy: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Filter leave types by manager', - }, - }, - - request: { - url: (params) => { - const query = new URLSearchParams() - if (params.managedBy) query.set('managedBy', params.managedBy) - const qs = query.toString() - return `https://api.rippling.com/platform/api/company_leave_types${qs ? `?${qs}` : ''}` - }, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const leaveTypes = results.map((lt: Record) => ({ - id: (lt.id as string) ?? '', - name: (lt.name as string) ?? '', - managedBy: (lt.managedBy as string) ?? null, - })) - - return { - success: true, - output: { - leaveTypes, - totalCount: leaveTypes.length, - }, - } - }, - - outputs: { - leaveTypes: { - type: 'array', - description: 'List of company leave types', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Leave type ID' }, - name: { type: 'string', description: 'Leave type name' }, - managedBy: { type: 'string', description: 'Manager of this leave type' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Total number of leave types returned', - }, - }, -} diff --git a/apps/sim/tools/rippling/list_levels.ts b/apps/sim/tools/rippling/list_levels.ts deleted file mode 100644 index 55d679f95c4..00000000000 --- a/apps/sim/tools/rippling/list_levels.ts +++ /dev/null @@ -1,91 +0,0 @@ -import type { RipplingListLevelsParams, RipplingListLevelsResponse } from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingListLevelsTool: ToolConfig< - RipplingListLevelsParams, - RipplingListLevelsResponse -> = { - id: 'rippling_list_levels', - name: 'Rippling List Levels', - description: 'List all position levels in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - limit: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Maximum number of levels to return', - }, - offset: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Offset for pagination', - }, - }, - - request: { - url: (params) => { - const query = new URLSearchParams() - if (params.limit != null) query.set('limit', String(params.limit)) - if (params.offset != null) query.set('offset', String(params.offset)) - const qs = query.toString() - return `https://api.rippling.com/platform/api/levels${qs ? `?${qs}` : ''}` - }, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const levels = results.map((level: Record) => ({ - id: (level.id as string) ?? '', - name: (level.name as string) ?? null, - parent: (level.parent as string) ?? null, - })) - - return { - success: true, - output: { - levels, - totalCount: levels.length, - }, - } - }, - - outputs: { - levels: { - type: 'array', - description: 'List of position levels', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Level ID' }, - name: { type: 'string', description: 'Level name' }, - parent: { type: 'string', description: 'Parent level ID' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Number of levels returned on this page', - }, - }, -} diff --git a/apps/sim/tools/rippling/list_object_categories.ts b/apps/sim/tools/rippling/list_object_categories.ts new file mode 100644 index 00000000000..c74fdd3b4e2 --- /dev/null +++ b/apps/sim/tools/rippling/list_object_categories.ts @@ -0,0 +1,54 @@ +import type { RipplingListObjectCategoriesParams } from '@/tools/rippling/types' +import { OBJECT_CATEGORY_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListObjectCategoriesTool: ToolConfig = { + id: 'rippling_list_object_categories', + name: 'Rippling List Object Categories', + description: 'List all object categories', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + }, + request: { + url: `https://rest.ripplingapis.com/object-categories/`, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + objectCategories: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + description: (item.description as string) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + }, + } + }, + outputs: { + objectCategories: { + type: 'array', + description: 'List of objectCategories', + items: { type: 'object', properties: OBJECT_CATEGORY_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_supergroup_exclusion_members.ts b/apps/sim/tools/rippling/list_supergroup_exclusion_members.ts new file mode 100644 index 00000000000..2a98cdf8fa5 --- /dev/null +++ b/apps/sim/tools/rippling/list_supergroup_exclusion_members.ts @@ -0,0 +1,86 @@ +import type { RipplingListSupergroupExclusionMembersParams } from '@/tools/rippling/types' +import { GROUP_MEMBER_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListSupergroupExclusionMembersTool: ToolConfig = + { + id: 'rippling_list_supergroup_exclusion_members', + name: 'Rippling List Supergroup Exclusion Members', + description: 'List exclusion members of a supergroup', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + groupId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Supergroup ID', + }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Fields to expand (e.g., worker)', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.expand != null) query.set('expand', params.expand) + if (params.orderBy != null) query.set('order_by', params.orderBy) + const qs = query.toString() + return `https://rest.ripplingapis.com/supergroups/${encodeURIComponent(params.groupId.trim())}/exclusion-members/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + members: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + full_name: (item.full_name as string) ?? null, + work_email: (item.work_email as string) ?? null, + worker_id: (item.worker_id as string) ?? null, + worker: item.worker ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + members: { + type: 'array', + description: 'List of members', + items: { type: 'object', properties: GROUP_MEMBER_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of members returned' }, + nextLink: { type: 'string', description: 'Next page link', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, + } diff --git a/apps/sim/tools/rippling/list_supergroup_inclusion_members.ts b/apps/sim/tools/rippling/list_supergroup_inclusion_members.ts new file mode 100644 index 00000000000..bf1a2fc31e2 --- /dev/null +++ b/apps/sim/tools/rippling/list_supergroup_inclusion_members.ts @@ -0,0 +1,86 @@ +import type { RipplingListSupergroupInclusionMembersParams } from '@/tools/rippling/types' +import { GROUP_MEMBER_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListSupergroupInclusionMembersTool: ToolConfig = + { + id: 'rippling_list_supergroup_inclusion_members', + name: 'Rippling List Supergroup Inclusion Members', + description: 'List inclusion members of a supergroup', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + groupId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Supergroup ID', + }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Fields to expand (e.g., worker)', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.expand != null) query.set('expand', params.expand) + if (params.orderBy != null) query.set('order_by', params.orderBy) + const qs = query.toString() + return `https://rest.ripplingapis.com/supergroups/${encodeURIComponent(params.groupId.trim())}/inclusion-members/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + Accept: 'application/json', + }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + members: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + full_name: (item.full_name as string) ?? null, + work_email: (item.work_email as string) ?? null, + worker_id: (item.worker_id as string) ?? null, + worker: item.worker ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + members: { + type: 'array', + description: 'List of members', + items: { type: 'object', properties: GROUP_MEMBER_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of members returned' }, + nextLink: { type: 'string', description: 'Next page link', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, + } diff --git a/apps/sim/tools/rippling/list_supergroup_members.ts b/apps/sim/tools/rippling/list_supergroup_members.ts new file mode 100644 index 00000000000..08ebbccebff --- /dev/null +++ b/apps/sim/tools/rippling/list_supergroup_members.ts @@ -0,0 +1,82 @@ +import type { RipplingListSupergroupMembersParams } from '@/tools/rippling/types' +import { GROUP_MEMBER_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListSupergroupMembersTool: ToolConfig = { + id: 'rippling_list_supergroup_members', + name: 'Rippling List Supergroup Members', + description: 'List members of a supergroup', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + groupId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Supergroup ID', + }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Fields to expand (e.g., worker)', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.expand != null) query.set('expand', params.expand) + if (params.orderBy != null) query.set('order_by', params.orderBy) + const qs = query.toString() + return `https://rest.ripplingapis.com/supergroups/${encodeURIComponent(params.groupId.trim())}/members/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + members: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + full_name: (item.full_name as string) ?? null, + work_email: (item.work_email as string) ?? null, + worker_id: (item.worker_id as string) ?? null, + worker: item.worker ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + members: { + type: 'array', + description: 'List of members', + items: { type: 'object', properties: GROUP_MEMBER_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of members returned' }, + nextLink: { type: 'string', description: 'Next page link', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_supergroups.ts b/apps/sim/tools/rippling/list_supergroups.ts new file mode 100644 index 00000000000..8bd7ca95c54 --- /dev/null +++ b/apps/sim/tools/rippling/list_supergroups.ts @@ -0,0 +1,89 @@ +import type { RipplingListSupergroupsParams } from '@/tools/rippling/types' +import { SUPERGROUP_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListSupergroupsTool: ToolConfig = { + id: 'rippling_list_supergroups', + name: 'Rippling List Supergroups', + description: 'List all supergroups', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + filter: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter expression (filterable fields: app_owner_id, group_type)', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.filter != null) query.set('filter', params.filter) + if (params.orderBy != null) query.set('order_by', params.orderBy) + const qs = query.toString() + return `https://rest.ripplingapis.com/supergroups/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + supergroups: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + display_name: (item.display_name as string) ?? null, + description: (item.description as string) ?? null, + app_owner_id: (item.app_owner_id as string) ?? null, + group_type: (item.group_type as string) ?? null, + name: (item.name as string) ?? null, + sub_group_type: (item.sub_group_type as string) ?? null, + read_only: (item.read_only as boolean) ?? null, + parent: (item.parent as string) ?? null, + mutually_exclusive_key: (item.mutually_exclusive_key as string) ?? null, + cumulatively_exhaustive_default: + (item.cumulatively_exhaustive_default as boolean) ?? null, + include_terminated: (item.include_terminated as boolean) ?? null, + allow_non_employees: (item.allow_non_employees as boolean) ?? null, + can_override_role_states: (item.can_override_role_states as boolean) ?? null, + priority: (item.priority as number) ?? null, + is_invisible: (item.is_invisible as boolean) ?? null, + ignore_prov_group_matching: (item.ignore_prov_group_matching as boolean) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + supergroups: { + type: 'array', + description: 'List of supergroups', + items: { type: 'object', properties: SUPERGROUP_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_teams.ts b/apps/sim/tools/rippling/list_teams.ts index 5a906e609ac..6e977920249 100644 --- a/apps/sim/tools/rippling/list_teams.ts +++ b/apps/sim/tools/rippling/list_teams.ts @@ -1,89 +1,82 @@ -import type { RipplingListTeamsParams, RipplingListTeamsResponse } from '@/tools/rippling/types' +import type { RipplingListTeamsParams } from '@/tools/rippling/types' +import { TEAM_OUTPUT_PROPERTIES } from '@/tools/rippling/types' import type { ToolConfig } from '@/tools/types' -export const ripplingListTeamsTool: ToolConfig = - { - id: 'rippling_list_teams', - name: 'Rippling List Teams', - description: 'List all teams in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - limit: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Maximum number of teams to return', - }, - offset: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Offset for pagination', - }, +export const ripplingListTeamsTool: ToolConfig = { + id: 'rippling_list_teams', + name: 'Rippling List Teams', + description: 'List all teams', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', }, - - request: { - url: (params) => { - const query = new URLSearchParams() - if (params.limit != null) query.set('limit', String(params.limit)) - if (params.offset != null) query.set('offset', String(params.offset)) - const qs = query.toString() - return `https://api.rippling.com/platform/api/teams${qs ? `?${qs}` : ''}` - }, - method: 'GET', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const teams = results.map((team: Record) => ({ - id: (team.id as string) ?? '', - name: (team.name as string) ?? null, - parent: (team.parent as string) ?? null, - })) - - return { - success: true, - output: { - teams, - totalCount: teams.length, - }, - } + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', }, - - outputs: { - teams: { - type: 'array', - description: 'List of teams', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Team ID' }, - name: { type: 'string', description: 'Team name' }, - parent: { type: 'string', description: 'Parent team ID' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Number of teams returned on this page', + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.expand != null) query.set('expand', params.expand) + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/teams/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + teams: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + parent_id: (item.parent_id as string) ?? null, + parent: item.parent ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, }, + } + }, + outputs: { + teams: { + type: 'array', + description: 'List of teams', + items: { type: 'object', properties: TEAM_OUTPUT_PROPERTIES }, }, - } + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_titles.ts b/apps/sim/tools/rippling/list_titles.ts new file mode 100644 index 00000000000..e72a096802b --- /dev/null +++ b/apps/sim/tools/rippling/list_titles.ts @@ -0,0 +1,73 @@ +import type { RipplingListTitlesParams } from '@/tools/rippling/types' +import { TITLE_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListTitlesTool: ToolConfig = { + id: 'rippling_list_titles', + name: 'Rippling List Titles', + description: 'List all titles', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/titles/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + titles: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + titles: { + type: 'array', + description: 'List of titles', + items: { type: 'object', properties: TITLE_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_users.ts b/apps/sim/tools/rippling/list_users.ts new file mode 100644 index 00000000000..135d5700f7f --- /dev/null +++ b/apps/sim/tools/rippling/list_users.ts @@ -0,0 +1,84 @@ +import type { RipplingListUsersParams } from '@/tools/rippling/types' +import { USER_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListUsersTool: ToolConfig = { + id: 'rippling_list_users', + name: 'Rippling List Users', + description: 'List all users with optional pagination', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/users/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + users: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + active: (item.active as boolean) ?? null, + username: (item.username as string) ?? null, + display_name: (item.display_name as string) ?? null, + preferred_language: (item.preferred_language as string) ?? null, + locale: (item.locale as string) ?? null, + timezone: (item.timezone as string) ?? null, + number: (item.number as string) ?? null, + name: item.name ?? null, + emails: item.emails ?? [], + phone_numbers: item.phone_numbers ?? [], + addresses: item.addresses ?? [], + photos: item.photos ?? [], + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + users: { + type: 'array', + description: 'List of users', + items: { type: 'object', properties: USER_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/list_work_locations.ts b/apps/sim/tools/rippling/list_work_locations.ts index db537ffb3c5..02b96d45e02 100644 --- a/apps/sim/tools/rippling/list_work_locations.ts +++ b/apps/sim/tools/rippling/list_work_locations.ts @@ -1,18 +1,12 @@ -import type { - RipplingListWorkLocationsParams, - RipplingListWorkLocationsResponse, -} from '@/tools/rippling/types' +import type { RipplingListWorkLocationsParams } from '@/tools/rippling/types' +import { WORK_LOCATION_OUTPUT_PROPERTIES } from '@/tools/rippling/types' import type { ToolConfig } from '@/tools/types' -export const ripplingListWorkLocationsTool: ToolConfig< - RipplingListWorkLocationsParams, - RipplingListWorkLocationsResponse -> = { +export const ripplingListWorkLocationsTool: ToolConfig = { id: 'rippling_list_work_locations', name: 'Rippling List Work Locations', - description: 'List all work locations in Rippling', + description: 'List all work locations', version: '1.0.0', - params: { apiKey: { type: 'string', @@ -20,27 +14,26 @@ export const ripplingListWorkLocationsTool: ToolConfig< visibility: 'user-only', description: 'Rippling API key', }, - limit: { - type: 'number', + orderBy: { + type: 'string', required: false, visibility: 'user-or-llm', - description: 'Maximum number of work locations to return', + description: 'Sort field. Prefix with - for descending', }, - offset: { - type: 'number', + cursor: { + type: 'string', required: false, visibility: 'user-or-llm', - description: 'Offset for pagination', + description: 'Pagination cursor from previous response', }, }, - request: { url: (params) => { const query = new URLSearchParams() - if (params.limit != null) query.set('limit', String(params.limit)) - if (params.offset != null) query.set('offset', String(params.offset)) + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) const qs = query.toString() - return `https://api.rippling.com/platform/api/work_locations${qs ? `?${qs}` : ''}` + return `https://rest.ripplingapis.com/work-locations/${qs ? `?${qs}` : ''}` }, method: 'GET', headers: (params) => ({ @@ -48,55 +41,37 @@ export const ripplingListWorkLocationsTool: ToolConfig< Accept: 'application/json', }), }, - transformResponse: async (response: Response) => { if (!response.ok) { const errorText = await response.text() throw new Error(`Rippling API error (${response.status}): ${errorText}`) } - const data = await response.json() - const results = Array.isArray(data) ? data : (data.results ?? []) - - const workLocations = results.map((loc: Record) => ({ - id: (loc.id as string) ?? '', - nickname: (loc.nickname as string) ?? null, - street: (loc.street as string) ?? null, - city: (loc.city as string) ?? null, - state: (loc.state as string) ?? null, - zip: (loc.zip as string) ?? null, - country: (loc.country as string) ?? null, - })) - + const results = data.results ?? [] return { success: true, output: { - workLocations, - totalCount: workLocations.length, + workLocations: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + name: (item.name as string) ?? null, + address: item.address ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, }, } }, - outputs: { workLocations: { type: 'array', - description: 'List of work locations', - items: { - type: 'json', - properties: { - id: { type: 'string', description: 'Work location ID' }, - nickname: { type: 'string', description: 'Location nickname' }, - street: { type: 'string', description: 'Street address' }, - city: { type: 'string', description: 'City' }, - state: { type: 'string', description: 'State or province' }, - zip: { type: 'string', description: 'ZIP or postal code' }, - country: { type: 'string', description: 'Country' }, - }, - }, - }, - totalCount: { - type: 'number', - description: 'Number of work locations returned on this page', + description: 'List of workLocations', + items: { type: 'object', properties: WORK_LOCATION_OUTPUT_PROPERTIES }, }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, }, } diff --git a/apps/sim/tools/rippling/list_workers.ts b/apps/sim/tools/rippling/list_workers.ts new file mode 100644 index 00000000000..76305df5100 --- /dev/null +++ b/apps/sim/tools/rippling/list_workers.ts @@ -0,0 +1,115 @@ +import type { RipplingListWorkersParams } from '@/tools/rippling/types' +import { WORKER_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingListWorkersTool: ToolConfig = { + id: 'rippling_list_workers', + name: 'Rippling List Workers', + description: 'List all workers with optional filtering and pagination', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + filter: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter expression', + }, + expand: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated fields to expand', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort field. Prefix with - for descending', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + }, + request: { + url: (params) => { + const query = new URLSearchParams() + if (params.filter != null) query.set('filter', params.filter) + if (params.expand != null) query.set('expand', params.expand) + if (params.orderBy != null) query.set('order_by', params.orderBy) + if (params.cursor != null) query.set('cursor', params.cursor) + const qs = query.toString() + return `https://rest.ripplingapis.com/workers/${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ Authorization: `Bearer ${params.apiKey}`, Accept: 'application/json' }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + workers: results.map((item: Record) => ({ + id: (item.id as string) ?? '', + created_at: (item.created_at as string) ?? null, + updated_at: (item.updated_at as string) ?? null, + user_id: (item.user_id as string) ?? null, + is_manager: (item.is_manager as boolean) ?? null, + manager_id: (item.manager_id as string) ?? null, + legal_entity_id: (item.legal_entity_id as string) ?? null, + country: (item.country as string) ?? null, + start_date: (item.start_date as string) ?? null, + end_date: (item.end_date as string) ?? null, + number: (item.number as number) ?? null, + work_email: (item.work_email as string) ?? null, + personal_email: (item.personal_email as string) ?? null, + status: (item.status as string) ?? null, + employment_type_id: (item.employment_type_id as string) ?? null, + department_id: (item.department_id as string) ?? null, + teams_id: (item.teams_id as unknown[]) ?? [], + title: (item.title as string) ?? null, + level_id: (item.level_id as string) ?? null, + compensation_id: (item.compensation_id as string) ?? null, + overtime_exemption: (item.overtime_exemption as string) ?? null, + title_effective_date: (item.title_effective_date as string) ?? null, + business_partners_id: (item.business_partners_id as unknown[]) ?? [], + location: item.location ?? null, + gender: (item.gender as string) ?? null, + date_of_birth: (item.date_of_birth as string) ?? null, + race: (item.race as string) ?? null, + ethnicity: (item.ethnicity as string) ?? null, + citizenship: (item.citizenship as string) ?? null, + termination_details: item.termination_details ?? null, + custom_fields: item.custom_fields ?? null, + country_fields: item.country_fields ?? null, + })), + totalCount: results.length, + nextLink: (data.next_link as string) ?? null, + __meta: data.__meta ?? null, + }, + } + }, + outputs: { + workers: { + type: 'array', + description: 'List of workers', + items: { type: 'object', properties: WORKER_OUTPUT_PROPERTIES }, + }, + totalCount: { type: 'number', description: 'Number of items returned' }, + nextLink: { type: 'string', description: 'Link to next page of results', optional: true }, + __meta: { type: 'json', description: 'Metadata including redacted_fields', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/process_leave_request.ts b/apps/sim/tools/rippling/process_leave_request.ts deleted file mode 100644 index 4589a3b16f4..00000000000 --- a/apps/sim/tools/rippling/process_leave_request.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { - RipplingProcessLeaveRequestParams, - RipplingProcessLeaveRequestResponse, -} from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingProcessLeaveRequestTool: ToolConfig< - RipplingProcessLeaveRequestParams, - RipplingProcessLeaveRequestResponse -> = { - id: 'rippling_process_leave_request', - name: 'Rippling Process Leave Request', - description: 'Approve or decline a leave request in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - leaveRequestId: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'The ID of the leave request to process', - }, - action: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'Action to take on the leave request (approve or decline)', - }, - }, - - request: { - url: (params) => { - const action = params.action.trim() - if (action !== 'approve' && action !== 'decline') { - throw new Error(`Invalid action "${action}". Must be "approve" or "decline".`) - } - return `https://api.rippling.com/platform/api/leave_requests/${encodeURIComponent(params.leaveRequestId.trim())}/process?action=${encodeURIComponent(action)}` - }, - method: 'POST', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - }), - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - - return { - success: true, - output: { - id: data.id ?? '', - status: data.status ?? '', - requestedBy: data.requestedBy ?? '', - startDate: data.startDate ?? '', - endDate: data.endDate ?? '', - }, - } - }, - - outputs: { - id: { type: 'string', description: 'Leave request ID' }, - status: { type: 'string', description: 'Updated status of the leave request' }, - requestedBy: { type: 'string', description: 'Employee ID who requested leave' }, - startDate: { type: 'string', description: 'Leave start date' }, - endDate: { type: 'string', description: 'Leave end date' }, - }, -} diff --git a/apps/sim/tools/rippling/push_candidate.ts b/apps/sim/tools/rippling/push_candidate.ts deleted file mode 100644 index c98480ba500..00000000000 --- a/apps/sim/tools/rippling/push_candidate.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type { - RipplingPushCandidateParams, - RipplingPushCandidateResponse, -} from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingPushCandidateTool: ToolConfig< - RipplingPushCandidateParams, - RipplingPushCandidateResponse -> = { - id: 'rippling_push_candidate', - name: 'Rippling Push Candidate', - description: 'Push a candidate to onboarding in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - firstName: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'Candidate first name', - }, - lastName: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'Candidate last name', - }, - email: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'Candidate email address', - }, - phone: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Candidate phone number', - }, - jobTitle: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Job title for the candidate', - }, - department: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Department for the candidate', - }, - startDate: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Start date in ISO 8601 format (e.g., 2025-01-15)', - }, - }, - - request: { - url: 'https://api.rippling.com/platform/api/ats_candidates/push_candidate', - method: 'POST', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - 'Content-Type': 'application/json', - }), - body: (params) => { - const body: Record = { - firstName: params.firstName, - lastName: params.lastName, - email: params.email, - } - if (params.phone !== undefined) { - body.phone = params.phone - } - if (params.jobTitle !== undefined) { - body.jobTitle = params.jobTitle - } - if (params.department !== undefined) { - body.department = params.department - } - if (params.startDate !== undefined) { - body.startDate = params.startDate - } - return body - }, - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - - return { - success: true, - output: { - id: (data.id as string) ?? '', - firstName: (data.firstName as string) ?? null, - lastName: (data.lastName as string) ?? null, - email: (data.email as string) ?? null, - status: (data.status as string) ?? null, - }, - } - }, - - outputs: { - id: { type: 'string', description: 'Candidate ID' }, - firstName: { type: 'string', description: 'Candidate first name' }, - lastName: { type: 'string', description: 'Candidate last name' }, - email: { type: 'string', description: 'Candidate email address' }, - status: { type: 'string', description: 'Candidate onboarding status', optional: true }, - }, -} diff --git a/apps/sim/tools/rippling/query_custom_object_records.ts b/apps/sim/tools/rippling/query_custom_object_records.ts new file mode 100644 index 00000000000..3c769c4057c --- /dev/null +++ b/apps/sim/tools/rippling/query_custom_object_records.ts @@ -0,0 +1,116 @@ +import type { RipplingQueryCustomObjectRecordsParams } from '@/tools/rippling/types' +import { CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingQueryCustomObjectRecordsTool: ToolConfig = + { + id: 'rippling_query_custom_object_records', + name: 'Rippling Query Custom Object Records', + description: 'Query custom object records with filters', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + query: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Query expression', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of records to return', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/query/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.query != null) body.query = params.query + if (params.limit != null) body.limit = params.limit + if (params.cursor != null) body.cursor = params.cursor + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const results = data.results ?? [] + return { + success: true, + output: { + records: results.map((item: Record) => { + const { + id, + created_at, + updated_at, + name, + external_id, + created_by, + last_modified_by, + owner_role, + system_updated_at, + ...dynamicFields + } = item + return { + id: (id as string) ?? '', + created_at: (created_at as string) ?? null, + updated_at: (updated_at as string) ?? null, + name: (name as string) ?? null, + external_id: (external_id as string) ?? null, + created_by: created_by ?? null, + last_modified_by: last_modified_by ?? null, + owner_role: owner_role ?? null, + system_updated_at: (system_updated_at as string) ?? null, + data: dynamicFields, + } + }), + totalCount: results.length, + cursor: (data.cursor as string) ?? null, + }, + } + }, + outputs: { + records: { + type: 'array', + description: 'Matching records', + items: { + type: 'object', + properties: { + ...CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES, + data: { type: 'json', description: 'Full record data' }, + }, + }, + }, + totalCount: { type: 'number', description: 'Number of records returned' }, + cursor: { type: 'string', description: 'Cursor for next page of results', optional: true }, + }, + } diff --git a/apps/sim/tools/rippling/trigger_report_run.ts b/apps/sim/tools/rippling/trigger_report_run.ts new file mode 100644 index 00000000000..4a912497b00 --- /dev/null +++ b/apps/sim/tools/rippling/trigger_report_run.ts @@ -0,0 +1,103 @@ +import type { RipplingTriggerReportRunParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingTriggerReportRunTool: ToolConfig = { + id: 'rippling_trigger_report_run', + name: 'Rippling Trigger Report Run', + description: 'Trigger a new report run', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + reportId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Report ID to run', + }, + includeObjectIds: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Include object IDs in the report', + }, + includeTotalRows: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Include total row count', + }, + formatDateFields: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Date field formatting configuration', + }, + formatCurrencyFields: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Currency field formatting configuration', + }, + outputType: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Output type (JSON or CSV)', + }, + }, + request: { + url: `https://rest.ripplingapis.com/report-runs/`, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { report_id: params.reportId } + if (params.includeObjectIds != null) body.include_object_ids = params.includeObjectIds + if (params.includeTotalRows != null) body.include_total_rows = params.includeTotalRows + if (params.formatDateFields != null) body.format_date_fields = params.formatDateFields + if (params.formatCurrencyFields != null) + body.format_currency_fields = params.formatCurrencyFields + if (params.outputType != null) body.output_type = params.outputType + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + const result = data.result as Record | null + return { + success: true, + output: { + id: (data.id as string) ?? '', + report_id: (data.report_id as string) ?? null, + status: (data.status as string) ?? null, + file_url: (result?.file_url as string) ?? null, + expires_at: (result?.expires_at as string) ?? null, + output_type: (result?.output_type as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Report run ID' }, + report_id: { type: 'string', description: 'Report ID', optional: true }, + status: { type: 'string', description: 'Run status', optional: true }, + file_url: { type: 'string', description: 'URL to download the report file', optional: true }, + expires_at: { + type: 'string', + description: 'Expiration timestamp for the file URL', + optional: true, + }, + output_type: { type: 'string', description: 'Output format (JSON or CSV)', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/types.ts b/apps/sim/tools/rippling/types.ts index 7c7211de6ed..633c92cb4e8 100644 --- a/apps/sim/tools/rippling/types.ts +++ b/apps/sim/tools/rippling/types.ts @@ -1,362 +1,845 @@ -import type { ToolResponse } from '@/tools/types' - -export interface RipplingListEmployeesParams { +import type { OutputProperty } from '@/tools/types' + +/** Worker output properties */ +export const WORKER_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Worker ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + user_id: { type: 'string', description: 'Associated user ID' }, + is_manager: { type: 'boolean', description: 'Whether the worker is a manager' }, + manager_id: { type: 'string', description: 'Manager worker ID' }, + legal_entity_id: { type: 'string', description: 'Legal entity ID' }, + country: { type: 'string', description: 'Worker country code' }, + start_date: { type: 'string', description: 'Employment start date' }, + end_date: { type: 'string', description: 'Employment end date' }, + number: { type: 'number', description: 'Worker number' }, + work_email: { type: 'string', description: 'Work email address' }, + personal_email: { type: 'string', description: 'Personal email address' }, + status: { + type: 'string', + description: 'Worker status (INIT, HIRED, ACCEPTED, ACTIVE, TERMINATED)', + }, + employment_type_id: { type: 'string', description: 'Employment type ID' }, + department_id: { type: 'string', description: 'Department ID' }, + teams_id: { type: 'json', description: 'Array of team IDs' }, + title: { type: 'string', description: 'Job title' }, + level_id: { type: 'string', description: 'Level ID' }, + compensation_id: { type: 'string', description: 'Compensation ID' }, + overtime_exemption: { + type: 'string', + description: 'Overtime exemption status (EXEMPT, NON_EXEMPT)', + }, + title_effective_date: { type: 'string', description: 'Title effective date' }, + business_partners_id: { type: 'json', description: 'Array of business partner IDs' }, + location: { type: 'json', description: 'Worker location (type, work_location_id)' }, + gender: { type: 'string', description: 'Gender' }, + date_of_birth: { type: 'string', description: 'Date of birth' }, + race: { type: 'string', description: 'Race' }, + ethnicity: { type: 'string', description: 'Ethnicity' }, + citizenship: { type: 'string', description: 'Citizenship country code' }, + termination_details: { type: 'json', description: 'Termination details' }, + custom_fields: { type: 'json', description: 'Custom fields (expandable)' }, + country_fields: { type: 'json', description: 'Country-specific fields' }, +} as const satisfies Record + +/** User output properties */ +export const USER_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'User ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + active: { type: 'boolean', description: 'Whether the user is active' }, + username: { type: 'string', description: 'Unique username' }, + display_name: { type: 'string', description: 'Display name' }, + preferred_language: { type: 'string', description: 'Preferred language' }, + locale: { type: 'string', description: 'Locale' }, + timezone: { type: 'string', description: 'Timezone (IANA format)' }, + number: { type: 'string', description: 'Permanent profile number' }, + name: { type: 'json', description: 'User name object (given_name, family_name, etc.)' }, + emails: { type: 'json', description: 'Array of email objects' }, + phone_numbers: { type: 'json', description: 'Array of phone number objects' }, + addresses: { type: 'json', description: 'Array of address objects' }, + photos: { type: 'json', description: 'Array of photo objects' }, +} as const satisfies Record + +/** Company output properties */ +export const COMPANY_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Company ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Company name' }, + legal_name: { type: 'string', description: 'Legal name' }, + doing_business_as_name: { type: 'string', description: 'DBA name' }, + phone: { type: 'string', description: 'Phone number' }, + primary_email: { type: 'string', description: 'Primary email' }, + parent_legal_entity_id: { type: 'string', description: 'Parent legal entity ID' }, + legal_entities_id: { type: 'json', description: 'Array of legal entity IDs' }, + physical_address: { type: 'json', description: 'Physical address of the holding entity' }, +} as const satisfies Record + +/** Department output properties */ +export const DEPARTMENT_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Department ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Department name' }, + parent_id: { type: 'string', description: 'Parent department ID' }, + reference_code: { type: 'string', description: 'Reference code' }, + department_hierarchy_id: { type: 'json', description: 'Array of department IDs in hierarchy' }, + parent: { type: 'json', description: 'Expanded parent department' }, + department_hierarchy: { type: 'json', description: 'Expanded department hierarchy' }, +} as const satisfies Record + +/** Team output properties */ +export const TEAM_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Team ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Team name' }, + parent_id: { type: 'string', description: 'Parent team ID' }, + parent: { type: 'json', description: 'Expanded parent team' }, +} as const satisfies Record + +/** Employment type output properties */ +export const EMPLOYMENT_TYPE_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Employment type ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + label: { type: 'string', description: 'Employment type label' }, + name: { type: 'string', description: 'Employment type name' }, + type: { type: 'string', description: 'Type (CONTRACTOR, EMPLOYEE)' }, + compensation_time_period: { + type: 'string', + description: 'Compensation period (HOURLY, SALARIED)', + }, + amount_worked: { type: 'string', description: 'Amount worked (PART-TIME, FULL-TIME, TEMPORARY)' }, +} as const satisfies Record + +/** Title output properties */ +export const TITLE_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Title ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Title name' }, +} as const satisfies Record + +/** Work location output properties */ +export const WORK_LOCATION_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Work location ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Location name' }, + address: { type: 'json', description: 'Address object' }, +} as const satisfies Record + +/** Custom field output properties */ +export const CUSTOM_FIELD_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Custom field ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Field name' }, + description: { type: 'string', description: 'Field description' }, + required: { type: 'boolean', description: 'Whether the field is required' }, + type: { type: 'string', description: 'Field type (TEXT, DATE, NUMBER, CURRENCY, etc.)' }, +} as const satisfies Record + +/** Job function output properties */ +export const JOB_FUNCTION_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Job function ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Job function name' }, +} as const satisfies Record + +/** Entitlement output properties */ +export const ENTITLEMENT_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Entitlement ID' }, + description: { type: 'string', description: 'Entitlement description' }, + display_name: { type: 'string', description: 'Display name' }, +} as const satisfies Record + +/** Business partner output properties */ +export const BUSINESS_PARTNER_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Business partner ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + business_partner_group_id: { type: 'string', description: 'Business partner group ID' }, + worker_id: { type: 'string', description: 'Worker ID' }, + client_group_id: { type: 'string', description: 'Client group ID' }, + client_group_member_count: { type: 'number', description: 'Client group member count' }, +} as const satisfies Record + +/** Business partner group output properties */ +export const BUSINESS_PARTNER_GROUP_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Business partner group ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Group name' }, + domain: { type: 'string', description: 'Domain (HR, IT, FINANCE, RECRUITING, OTHER)' }, + default_business_partner_id: { type: 'string', description: 'Default business partner ID' }, +} as const satisfies Record + +/** Supergroup output properties */ +export const SUPERGROUP_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Supergroup ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + display_name: { type: 'string', description: 'Display name' }, + description: { type: 'string', description: 'Description' }, + app_owner_id: { type: 'string', description: 'App owner ID' }, + group_type: { type: 'string', description: 'Group type' }, + name: { type: 'string', description: 'Name' }, + sub_group_type: { type: 'string', description: 'Sub group type' }, + read_only: { type: 'boolean', description: 'Whether the group is read only' }, + parent: { type: 'string', description: 'Parent group ID' }, + mutually_exclusive_key: { type: 'string', description: 'Mutually exclusive key' }, + cumulatively_exhaustive_default: { + type: 'boolean', + description: 'Whether the group is the cumulatively exhaustive default', + }, + include_terminated: { + type: 'boolean', + description: 'Whether the group includes terminated roles', + }, + allow_non_employees: { + type: 'boolean', + description: 'Whether the group allows non-employees', + }, + can_override_role_states: { + type: 'boolean', + description: 'Whether the group can override role states', + }, + priority: { type: 'number', description: 'Group priority' }, + is_invisible: { type: 'boolean', description: 'Whether the group is invisible' }, + ignore_prov_group_matching: { + type: 'boolean', + description: 'Whether to ignore provisioning group matching', + }, +} as const satisfies Record + +/** Group member output properties */ +export const GROUP_MEMBER_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Member ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + full_name: { type: 'string', description: 'Full name' }, + work_email: { type: 'string', description: 'Work email' }, + worker_id: { type: 'string', description: 'Worker ID' }, + worker: { type: 'json', description: 'Expanded worker object' }, +} as const satisfies Record + +/** Custom object output properties */ +export const CUSTOM_OBJECT_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Custom object ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Object name' }, + description: { type: 'string', description: 'Description' }, + api_name: { type: 'string', description: 'API name' }, + plural_label: { type: 'string', description: 'Plural label' }, + category_id: { type: 'string', description: 'Category ID' }, + native_category_id: { type: 'string', description: 'Native category ID' }, + managed_package_install_id: { type: 'string', description: 'Package install ID' }, + owner_id: { type: 'string', description: 'Owner ID' }, + enable_history: { type: 'boolean', description: 'Whether history is enabled' }, +} as const satisfies Record + +/** Custom object field output properties */ +export const CUSTOM_OBJECT_FIELD_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Field ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Field name' }, + custom_object: { type: 'string', description: 'Parent custom object' }, + description: { type: 'string', description: 'Description' }, + api_name: { type: 'string', description: 'API name' }, + data_type: { type: 'json', description: 'Data type configuration' }, + is_unique: { type: 'boolean', description: 'Whether the field is unique' }, + is_immutable: { type: 'boolean', description: 'Whether the field is immutable' }, + is_standard: { type: 'boolean', description: 'Whether the field is standard' }, + enable_history: { type: 'boolean', description: 'Whether history is enabled' }, + managed_package_install_id: { type: 'string', description: 'Package install ID' }, +} as const satisfies Record + +/** Custom object record output properties */ +export const CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Record ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Record name' }, + external_id: { type: 'string', description: 'External ID' }, + created_by: { type: 'json', description: 'Created by user (id, display_value, image)' }, + last_modified_by: { + type: 'json', + description: 'Last modified by user (id, display_value, image)', + }, + owner_role: { type: 'json', description: 'Owner role (id, display_value, image)' }, + system_updated_at: { type: 'string', description: 'System update timestamp' }, +} as const satisfies Record + +/** Custom app output properties */ +export const CUSTOM_APP_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'App ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'App name' }, + api_name: { type: 'string', description: 'API name' }, + description: { type: 'string', description: 'Description' }, + icon: { type: 'string', description: 'Icon URL' }, + pages: { type: 'json', description: 'Array of page summaries' }, +} as const satisfies Record + +/** Custom page output properties */ +export const CUSTOM_PAGE_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Page ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Page name' }, + components: { type: 'json', description: 'Page components' }, + actions: { type: 'json', description: 'Page actions' }, + canvas_actions: { type: 'json', description: 'Canvas actions' }, + variables: { type: 'json', description: 'Page variables' }, + media: { type: 'json', description: 'Page media' }, +} as const satisfies Record + +/** Custom setting output properties */ +export const CUSTOM_SETTING_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Setting ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + display_name: { type: 'string', description: 'Display name' }, + api_name: { type: 'string', description: 'API name' }, + data_type: { type: 'string', description: 'Data type' }, + secret_value: { type: 'string', description: 'Secret value' }, + string_value: { type: 'string', description: 'String value' }, + number_value: { type: 'number', description: 'Number value' }, + boolean_value: { type: 'boolean', description: 'Boolean value' }, +} as const satisfies Record + +/** Object category output properties */ +export const OBJECT_CATEGORY_OUTPUT_PROPERTIES = { + id: { type: 'string', description: 'Category ID' }, + created_at: { type: 'string', description: 'Record creation date' }, + updated_at: { type: 'string', description: 'Record update date' }, + name: { type: 'string', description: 'Category name' }, + description: { type: 'string', description: 'Description' }, +} as const satisfies Record + +/** Workers */ +export interface RipplingListWorkersParams { apiKey: string - limit?: number - offset?: number + filter?: string + expand?: string + orderBy?: string + cursor?: string +} +export interface RipplingGetWorkerParams { + apiKey: string + id: string + expand?: string } -export interface RipplingGetEmployeeParams { +/** Users */ +export interface RipplingListUsersParams { + apiKey: string + orderBy?: string + cursor?: string +} +export interface RipplingGetUserParams { apiKey: string - employeeId: string + id: string } -export interface RipplingListDepartmentsParams { +/** Companies */ +export interface RipplingListCompaniesParams { apiKey: string - limit?: number - offset?: number + expand?: string + orderBy?: string + cursor?: string } +/** SSO Me */ export interface RipplingGetCurrentUserParams { apiKey: string + expand?: string +} + +/** Entitlements */ +export interface RipplingListEntitlementsParams { + apiKey: string } -export interface RipplingEmployee { +/** Departments */ +export interface RipplingListDepartmentsParams { + apiKey: string + expand?: string + orderBy?: string + cursor?: string +} +export interface RipplingGetDepartmentParams { + apiKey: string id: string - firstName: string | null - lastName: string | null - workEmail: string | null - personalEmail: string | null - roleState: string | null - department: string | null - title: string | null - startDate: string | null - endDate: string | null - manager: string | null - phone: string | null -} - -export interface RipplingDepartment { + expand?: string +} +export interface RipplingCreateDepartmentParams { + apiKey: string + name: string + parentId?: string + referenceCode?: string +} +export interface RipplingUpdateDepartmentParams { + apiKey: string id: string - name: string | null - parent: string | null + name?: string + parentId?: string + referenceCode?: string } -export interface RipplingListEmployeesResponse extends ToolResponse { - output: { - employees: RipplingEmployee[] - totalCount: number - } +/** Teams */ +export interface RipplingListTeamsParams { + apiKey: string + expand?: string + orderBy?: string + cursor?: string } - -export interface RipplingGetEmployeeResponse extends ToolResponse { - output: RipplingEmployee +export interface RipplingGetTeamParams { + apiKey: string + id: string + expand?: string } -export interface RipplingListDepartmentsResponse extends ToolResponse { - output: { - departments: RipplingDepartment[] - totalCount: number - } +/** Employment Types */ +export interface RipplingListEmploymentTypesParams { + apiKey: string + orderBy?: string + cursor?: string } - -export interface RipplingGetCurrentUserResponse extends ToolResponse { - output: { - id: string - workEmail: string | null - company: string | null - } +export interface RipplingGetEmploymentTypeParams { + apiKey: string + id: string } -export interface RipplingGetCompanyParams { +/** Titles */ +export interface RipplingListTitlesParams { apiKey: string + orderBy?: string + cursor?: string } - -export interface RipplingCompanyAddress { - street: string | null - city: string | null - state: string | null - zip: string | null - country: string | null +export interface RipplingGetTitleParams { + apiKey: string + id: string } - -export interface RipplingGetCompanyResponse extends ToolResponse { - output: { - id: string - name: string | null - address: RipplingCompanyAddress - email: string | null - phone: string | null - workLocations: string[] - } +export interface RipplingCreateTitleParams { + apiKey: string + name: string } - -export interface RipplingListCustomFieldsParams { +export interface RipplingUpdateTitleParams { apiKey: string - limit?: number - offset?: number + id: string + name?: string } - -export interface RipplingCustomField { +export interface RipplingDeleteTitleParams { + apiKey: string id: string - type: string | null - title: string | null - mandatory: boolean } -export interface RipplingListCustomFieldsResponse extends ToolResponse { - output: { - customFields: RipplingCustomField[] - totalCount: number - } +/** Work Locations */ +export interface RipplingListWorkLocationsParams { + apiKey: string + orderBy?: string + cursor?: string } - -export interface RipplingListLevelsParams { +export interface RipplingGetWorkLocationParams { apiKey: string - limit?: number - offset?: number + id: string } - -export interface RipplingLevel { +export interface RipplingCreateWorkLocationParams { + apiKey: string + name: string + streetAddress: string + locality?: string + region?: string + postalCode?: string + country?: string + addressType?: string +} +export interface RipplingUpdateWorkLocationParams { + apiKey: string + id: string + name?: string + streetAddress?: string + locality?: string + region?: string + postalCode?: string + country?: string + addressType?: string +} +export interface RipplingDeleteWorkLocationParams { + apiKey: string id: string - name: string | null - parent: string | null } -export interface RipplingListLevelsResponse extends ToolResponse { - output: { - levels: RipplingLevel[] - totalCount: number - } +/** Custom Fields */ +export interface RipplingListCustomFieldsParams { + apiKey: string + orderBy?: string + cursor?: string } -export interface RipplingListTeamsParams { +/** Job Functions */ +export interface RipplingListJobFunctionsParams { apiKey: string - limit?: number - offset?: number + orderBy?: string + cursor?: string } - -export interface RipplingTeam { +export interface RipplingGetJobFunctionParams { + apiKey: string id: string - name: string | null - parent: string | null } -export interface RipplingListTeamsResponse extends ToolResponse { - output: { - teams: RipplingTeam[] - totalCount: number - } +/** Business Partners */ +export interface RipplingListBusinessPartnersParams { + apiKey: string + filter?: string + expand?: string + orderBy?: string + cursor?: string } - -export interface RipplingListWorkLocationsParams { +export interface RipplingGetBusinessPartnerParams { apiKey: string - limit?: number - offset?: number + id: string + expand?: string } - -export interface RipplingWorkLocation { +export interface RipplingCreateBusinessPartnerParams { + apiKey: string + businessPartnerGroupId: string + workerId: string +} +export interface RipplingDeleteBusinessPartnerParams { + apiKey: string id: string - nickname: string | null - street: string | null - city: string | null - state: string | null - zip: string | null - country: string | null } -export interface RipplingListWorkLocationsResponse extends ToolResponse { - output: { - workLocations: RipplingWorkLocation[] - totalCount: number - } +/** Business Partner Groups */ +export interface RipplingListBusinessPartnerGroupsParams { + apiKey: string + expand?: string + orderBy?: string + cursor?: string } - -export interface RipplingGetCompanyActivityParams { +export interface RipplingGetBusinessPartnerGroupParams { apiKey: string - startDate?: string - endDate?: string - limit?: number - next?: string + id: string + expand?: string } - -export interface RipplingActivityActor { - id: string | null - name: string | null +export interface RipplingCreateBusinessPartnerGroupParams { + apiKey: string + name: string + domain?: string + defaultBusinessPartnerId?: string } - -export interface RipplingActivityEvent { +export interface RipplingDeleteBusinessPartnerGroupParams { + apiKey: string id: string - type: string | null - description: string | null - createdAt: string | null - actor: RipplingActivityActor } -export interface RipplingGetCompanyActivityResponse extends ToolResponse { - output: { - events: RipplingActivityEvent[] - totalCount: number - nextCursor: string | null - } +/** Supergroups */ +export interface RipplingListSupergroupsParams { + apiKey: string + filter?: string + orderBy?: string } - -export interface RipplingListEmployeesWithTerminatedParams { +export interface RipplingGetSupergroupParams { apiKey: string - limit?: number - offset?: number + id: string } - -export interface RipplingListEmployeesWithTerminatedResponse extends ToolResponse { - output: { - employees: RipplingEmployee[] - totalCount: number - } +export interface RipplingListSupergroupMembersParams { + apiKey: string + groupId: string + expand?: string + orderBy?: string +} +export interface RipplingListSupergroupInclusionMembersParams { + apiKey: string + groupId: string + expand?: string + orderBy?: string +} +export interface RipplingListSupergroupExclusionMembersParams { + apiKey: string + groupId: string + expand?: string + orderBy?: string +} +export interface RipplingUpdateSupergroupInclusionMembersParams { + apiKey: string + groupId: string + operations: unknown +} +export interface RipplingUpdateSupergroupExclusionMembersParams { + apiKey: string + groupId: string + operations: unknown } -export interface RipplingCreateGroupParams { +/** Custom Objects */ +export interface RipplingListCustomObjectsParams { + apiKey: string +} +export interface RipplingGetCustomObjectParams { + apiKey: string + customObjectApiName: string +} +export interface RipplingCreateCustomObjectParams { apiKey: string name: string - spokeId: string - users?: string[] + description?: string + category?: string } - -export interface RipplingGroup { - id: string - name: string | null - spokeId: string | null - users: string[] - version: number | null +export interface RipplingUpdateCustomObjectParams { + apiKey: string + customObjectApiName: string + name?: string + description?: string + category?: string + pluralLabel?: string + ownerRole?: string } - -export interface RipplingCreateGroupResponse extends ToolResponse { - output: RipplingGroup +export interface RipplingDeleteCustomObjectParams { + apiKey: string + customObjectApiName: string } -export interface RipplingUpdateGroupParams { +/** Custom Object Fields */ +export interface RipplingListCustomObjectFieldsParams { apiKey: string - groupId: string + customObjectApiName: string +} +export interface RipplingGetCustomObjectFieldParams { + apiKey: string + customObjectApiName: string + fieldApiName: string +} +export interface RipplingCreateCustomObjectFieldParams { + apiKey: string + customObjectApiName: string + name: string + description?: string + dataType: unknown + required?: boolean + rqlDefinition?: unknown + isUnique?: boolean + formulaAttrMetas?: unknown + section?: unknown + enableHistory?: boolean + derivedFieldFormula?: string + derivedAggregatedField?: unknown +} +export interface RipplingUpdateCustomObjectFieldParams { + apiKey: string + customObjectApiName: string + fieldApiName: string name?: string - spokeId?: string - users?: string[] - version?: number + description?: string + dataType?: unknown + required?: boolean + rqlDefinition?: unknown + isUnique?: boolean + formulaAttrMetas?: unknown + section?: unknown + enableHistory?: boolean + derivedFieldFormula?: string + nameFieldDetails?: unknown +} +export interface RipplingDeleteCustomObjectFieldParams { + apiKey: string + customObjectApiName: string + fieldApiName: string } -export interface RipplingUpdateGroupResponse extends ToolResponse { - output: RipplingGroup +/** Custom Object Records */ +export interface RipplingListCustomObjectRecordsParams { + apiKey: string + customObjectApiName: string } - -export interface RipplingPushCandidateParams { +export interface RipplingGetCustomObjectRecordParams { apiKey: string - firstName: string - lastName: string - email: string - phone?: string - jobTitle?: string - department?: string - startDate?: string + customObjectApiName: string + codrId: string } - -export interface RipplingPushCandidateResponse extends ToolResponse { - output: { - id: string - firstName: string | null - lastName: string | null - email: string | null - status: string | null - } +export interface RipplingGetCustomObjectRecordByExternalIdParams { + apiKey: string + customObjectApiName: string + externalId: string } - -export interface RipplingListLeaveRequestsParams { +export interface RipplingQueryCustomObjectRecordsParams { + apiKey: string + customObjectApiName: string + query?: string + limit?: number + cursor?: string +} +export interface RipplingCreateCustomObjectRecordParams { + apiKey: string + customObjectApiName: string + externalId?: string + data: unknown +} +export interface RipplingUpdateCustomObjectRecordParams { + apiKey: string + customObjectApiName: string + codrId: string + externalId?: string + data?: unknown +} +export interface RipplingDeleteCustomObjectRecordParams { apiKey: string - startDate?: string - endDate?: string - status?: string + customObjectApiName: string + codrId: string +} +export interface RipplingBulkCreateCustomObjectRecordsParams { + apiKey: string + customObjectApiName: string + rowsToWrite: unknown + allOrNothing?: boolean +} +export interface RipplingBulkUpdateCustomObjectRecordsParams { + apiKey: string + customObjectApiName: string + rowsToUpdate: unknown + allOrNothing?: boolean +} +export interface RipplingBulkDeleteCustomObjectRecordsParams { + apiKey: string + customObjectApiName: string + rowsToDelete: string[] + allOrNothing?: boolean } -export interface RipplingLeaveRequest { +/** Custom Apps */ +export interface RipplingListCustomAppsParams { + apiKey: string +} +export interface RipplingGetCustomAppParams { + apiKey: string id: string - requestedBy: string - status: string - startDate: string - endDate: string - reason: string | null - leaveType: string | null - createdAt: string | null } - -export interface RipplingListLeaveRequestsResponse extends ToolResponse { - output: { - leaveRequests: RipplingLeaveRequest[] - totalCount: number - } +export interface RipplingCreateCustomAppParams { + apiKey: string + name: string + apiName: string + description?: string } - -export interface RipplingProcessLeaveRequestParams { +export interface RipplingUpdateCustomAppParams { apiKey: string - leaveRequestId: string - action: string + id: string + name?: string + apiName?: string + description?: string } - -export interface RipplingProcessLeaveRequestResponse extends ToolResponse { - output: { - id: string - status: string - requestedBy: string - startDate: string - endDate: string - } +export interface RipplingDeleteCustomAppParams { + apiKey: string + id: string } -export interface RipplingListLeaveBalancesParams { +/** Custom Pages */ +export interface RipplingListCustomPagesParams { apiKey: string - limit?: number - offset?: number } - -export interface RipplingLeaveBalanceEntry { - leaveType: string - minutesRemaining: number +export interface RipplingGetCustomPageParams { + apiKey: string + id: string } - -export interface RipplingLeaveBalance { - employeeId: string - balances: RipplingLeaveBalanceEntry[] +export interface RipplingCreateCustomPageParams { + apiKey: string + name: string } - -export interface RipplingListLeaveBalancesResponse extends ToolResponse { - output: { - leaveBalances: RipplingLeaveBalance[] - totalCount: number - } +export interface RipplingUpdateCustomPageParams { + apiKey: string + id: string + name?: string } - -export interface RipplingGetLeaveBalanceParams { +export interface RipplingDeleteCustomPageParams { apiKey: string - roleId: string + id: string } -export interface RipplingGetLeaveBalanceResponse extends ToolResponse { - output: { - employeeId: string - balances: RipplingLeaveBalanceEntry[] - } +/** Custom Settings */ +export interface RipplingListCustomSettingsParams { + apiKey: string + orderBy?: string + cursor?: string } - -export interface RipplingListLeaveTypesParams { +export interface RipplingGetCustomSettingParams { apiKey: string - managedBy?: string + id: string +} +export interface RipplingCreateCustomSettingParams { + apiKey: string + displayName?: string + apiName?: string + dataType?: string + secretValue?: string + stringValue?: string + numberValue?: number + booleanValue?: boolean +} +export interface RipplingUpdateCustomSettingParams { + apiKey: string + id: string + displayName?: string + apiName?: string + dataType?: string + secretValue?: string + stringValue?: string + numberValue?: number + booleanValue?: boolean +} +export interface RipplingDeleteCustomSettingParams { + apiKey: string + id: string } -export interface RipplingLeaveType { +/** Object Categories */ +export interface RipplingListObjectCategoriesParams { + apiKey: string +} +export interface RipplingGetObjectCategoryParams { + apiKey: string id: string +} +export interface RipplingCreateObjectCategoryParams { + apiKey: string name: string - managedBy: string | null + description?: string +} +export interface RipplingUpdateObjectCategoryParams { + apiKey: string + id: string + name?: string + description?: string +} +export interface RipplingDeleteObjectCategoryParams { + apiKey: string + id: string } -export interface RipplingListLeaveTypesResponse extends ToolResponse { - output: { - leaveTypes: RipplingLeaveType[] - totalCount: number - } +/** Report Runs */ +export interface RipplingGetReportRunParams { + apiKey: string + runId: string +} +export interface RipplingTriggerReportRunParams { + apiKey: string + reportId: string + includeObjectIds?: boolean + includeTotalRows?: boolean + formatDateFields?: unknown + formatCurrencyFields?: unknown + outputType?: string +} + +/** Draft Hires */ +export interface RipplingCreateDraftHiresParams { + apiKey: string + draftHires: unknown } diff --git a/apps/sim/tools/rippling/update_custom_app.ts b/apps/sim/tools/rippling/update_custom_app.ts new file mode 100644 index 00000000000..0e03a66199e --- /dev/null +++ b/apps/sim/tools/rippling/update_custom_app.ts @@ -0,0 +1,78 @@ +import type { RipplingUpdateCustomAppParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateCustomAppTool: ToolConfig = { + id: 'rippling_update_custom_app', + name: 'Rippling Update CustomApp', + description: 'Update a custom app', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'App ID' }, + name: { type: 'string', required: false, visibility: 'user-or-llm', description: 'App name' }, + apiName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'API name', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-apps/${encodeURIComponent(params.id.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.name != null) body.name = params.name + if (params.apiName != null) body.api_name = params.apiName + if (params.description != null) body.description = params.description + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + api_name: (data.api_name as string) ?? null, + description: (data.description as string) ?? null, + icon: (data.icon as string) ?? null, + pages: (data.pages as unknown[]) ?? [], + }, + } + }, + outputs: { + id: { type: 'string', description: 'App ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + api_name: { type: 'string', description: 'API name', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + icon: { type: 'string', description: 'Icon URL', optional: true }, + pages: { type: 'json', description: 'Array of page summaries', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/update_custom_object.ts b/apps/sim/tools/rippling/update_custom_object.ts new file mode 100644 index 00000000000..8aaf9f0b45b --- /dev/null +++ b/apps/sim/tools/rippling/update_custom_object.ts @@ -0,0 +1,109 @@ +import type { RipplingUpdateCustomObjectParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateCustomObjectTool: ToolConfig = { + id: 'rippling_update_custom_object', + name: 'Rippling Update Custom Object', + description: 'Update a custom object', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + name: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Name' }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description', + }, + category: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Category', + }, + pluralLabel: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Plural label', + }, + ownerRole: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Owner role', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.name != null) body.name = params.name + if (params.description != null) body.description = params.description + if (params.category != null) body.category = params.category + if (params.pluralLabel != null) body.plural_label = params.pluralLabel + if (params.ownerRole != null) body.owner_role = params.ownerRole + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + description: (data.description as string) ?? null, + api_name: (data.api_name as string) ?? null, + plural_label: (data.plural_label as string) ?? null, + category_id: (data.category_id as string) ?? null, + enable_history: (data.enable_history as boolean) ?? null, + native_category_id: (data.native_category_id as string) ?? null, + managed_package_install_id: (data.managed_package_install_id as string) ?? null, + owner_id: (data.owner_id as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + api_name: { type: 'string', description: 'API name', optional: true }, + plural_label: { type: 'string', description: 'Plural label', optional: true }, + category_id: { type: 'string', description: 'Category ID', optional: true }, + enable_history: { type: 'boolean', description: 'History enabled', optional: true }, + native_category_id: { type: 'string', description: 'Native category ID', optional: true }, + managed_package_install_id: { + type: 'string', + description: 'Package install ID', + optional: true, + }, + owner_id: { type: 'string', description: 'Owner ID', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/update_custom_object_field.ts b/apps/sim/tools/rippling/update_custom_object_field.ts new file mode 100644 index 00000000000..f20e190183d --- /dev/null +++ b/apps/sim/tools/rippling/update_custom_object_field.ts @@ -0,0 +1,166 @@ +import type { RipplingUpdateCustomObjectFieldParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateCustomObjectFieldTool: ToolConfig = + { + id: 'rippling_update_custom_object_field', + name: 'Rippling Update Custom Object Field', + description: 'Update a field on a custom object', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + fieldApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Field API name', + }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Field name', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description', + }, + dataType: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Data type', + }, + required: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether the field is required', + }, + rqlDefinition: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'RQL definition object', + }, + isUnique: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Is unique', + }, + formulaAttrMetas: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Formula attribute metadata', + }, + section: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Section configuration', + }, + enableHistory: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Enable history', + }, + derivedFieldFormula: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Derived field formula expression', + }, + nameFieldDetails: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Name field details configuration', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/fields/${encodeURIComponent(params.fieldApiName.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.name != null) body.name = params.name + if (params.description != null) body.description = params.description + if (params.dataType != null) body.data_type = params.dataType + if (params.required != null) body.required = params.required + if (params.rqlDefinition != null) body.rql_definition = params.rqlDefinition + if (params.isUnique != null) body.is_unique = params.isUnique + if (params.formulaAttrMetas != null) body.formula_attr_metas = params.formulaAttrMetas + if (params.section != null) body.section = params.section + if (params.enableHistory != null) body.enable_history = params.enableHistory + if (params.derivedFieldFormula != null) + body.derived_field_formula = params.derivedFieldFormula + if (params.nameFieldDetails != null) body.name_field_details = params.nameFieldDetails + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + custom_object: (data.custom_object as string) ?? null, + description: (data.description as string) ?? null, + api_name: (data.api_name as string) ?? null, + data_type: data.data_type ?? null, + is_unique: (data.is_unique as boolean) ?? null, + is_immutable: (data.is_immutable as boolean) ?? null, + is_standard: (data.is_standard as boolean) ?? null, + enable_history: (data.enable_history as boolean) ?? null, + managed_package_install_id: (data.managed_package_install_id as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Field ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + custom_object: { type: 'string', description: 'Custom object', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + api_name: { type: 'string', description: 'API name', optional: true }, + data_type: { type: 'json', description: 'Data type configuration', optional: true }, + is_unique: { type: 'boolean', description: 'Is unique', optional: true }, + is_immutable: { type: 'boolean', description: 'Is immutable', optional: true }, + is_standard: { type: 'boolean', description: 'Is standard', optional: true }, + enable_history: { type: 'boolean', description: 'History enabled', optional: true }, + managed_package_install_id: { + type: 'string', + description: 'Package install ID', + optional: true, + }, + }, + } diff --git a/apps/sim/tools/rippling/update_custom_object_record.ts b/apps/sim/tools/rippling/update_custom_object_record.ts new file mode 100644 index 00000000000..4e88fec8e4b --- /dev/null +++ b/apps/sim/tools/rippling/update_custom_object_record.ts @@ -0,0 +1,99 @@ +import type { RipplingUpdateCustomObjectRecordParams } from '@/tools/rippling/types' +import { CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateCustomObjectRecordTool: ToolConfig = + { + id: 'rippling_update_custom_object_record', + name: 'Rippling Update Custom Object Record', + description: 'Update a custom object record', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + customObjectApiName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Custom object API name', + }, + codrId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Record ID', + }, + externalId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'External ID for the record', + }, + data: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Updated record data', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-objects/${encodeURIComponent(params.customObjectApiName.trim())}/records/${encodeURIComponent(params.codrId.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.externalId != null && params.externalId !== '') + body.external_id = params.externalId + if (params.data != null) body.data = params.data + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const json = await response.json() + const record = (json.data ?? json) as Record + const { + id, + created_at, + updated_at, + name, + external_id, + created_by, + last_modified_by, + owner_role, + system_updated_at, + ...dynamicFields + } = record + return { + success: true, + output: { + id: (id as string) ?? '', + created_at: (created_at as string) ?? null, + updated_at: (updated_at as string) ?? null, + name: (name as string) ?? null, + external_id: (external_id as string) ?? null, + created_by: created_by ?? null, + last_modified_by: last_modified_by ?? null, + owner_role: owner_role ?? null, + system_updated_at: (system_updated_at as string) ?? null, + data: dynamicFields, + }, + } + }, + outputs: { + ...CUSTOM_OBJECT_RECORD_OUTPUT_PROPERTIES, + data: { type: 'json', description: 'Full record data' }, + }, + } diff --git a/apps/sim/tools/rippling/update_custom_page.ts b/apps/sim/tools/rippling/update_custom_page.ts new file mode 100644 index 00000000000..68241a99daf --- /dev/null +++ b/apps/sim/tools/rippling/update_custom_page.ts @@ -0,0 +1,66 @@ +import type { RipplingUpdateCustomPageParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateCustomPageTool: ToolConfig = { + id: 'rippling_update_custom_page', + name: 'Rippling Update CustomPage', + description: 'Update a custom page', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Page ID' }, + name: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Page name' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-pages/${encodeURIComponent(params.id.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.name != null) body.name = params.name + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + components: data.components ?? [], + actions: data.actions ?? [], + canvas_actions: data.canvas_actions ?? [], + variables: data.variables ?? [], + media: data.media ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Page ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + components: { type: 'json', description: 'Page components', optional: true }, + actions: { type: 'json', description: 'Page actions', optional: true }, + canvas_actions: { type: 'json', description: 'Canvas actions', optional: true }, + variables: { type: 'json', description: 'Page variables', optional: true }, + media: { type: 'json', description: 'Page media', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/update_custom_setting.ts b/apps/sim/tools/rippling/update_custom_setting.ts new file mode 100644 index 00000000000..1ab1ad6903f --- /dev/null +++ b/apps/sim/tools/rippling/update_custom_setting.ts @@ -0,0 +1,107 @@ +import type { RipplingUpdateCustomSettingParams } from '@/tools/rippling/types' +import { CUSTOM_SETTING_OUTPUT_PROPERTIES } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateCustomSettingTool: ToolConfig = { + id: 'rippling_update_custom_setting', + name: 'Rippling Update Custom Setting', + description: 'Update a custom setting', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Setting ID' }, + displayName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Display name', + }, + apiName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Unique API name', + }, + dataType: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Data type of the setting', + }, + secretValue: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Secret value (for secret data type)', + }, + stringValue: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'String value (for string data type)', + }, + numberValue: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number value (for number data type)', + }, + booleanValue: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Boolean value (for boolean data type)', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/custom-settings/${encodeURIComponent(params.id.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.displayName != null) body.display_name = params.displayName + if (params.apiName != null) body.api_name = params.apiName + if (params.dataType != null) body.data_type = params.dataType + if (params.secretValue != null) body.secret_value = params.secretValue + if (params.stringValue != null) body.string_value = params.stringValue + if (params.numberValue != null) body.number_value = params.numberValue + if (params.booleanValue != null) body.boolean_value = params.booleanValue + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + display_name: (data.display_name as string) ?? null, + api_name: (data.api_name as string) ?? null, + data_type: (data.data_type as string) ?? null, + secret_value: (data.secret_value as string) ?? null, + string_value: (data.string_value as string) ?? null, + number_value: data.number_value ?? null, + boolean_value: data.boolean_value ?? null, + }, + } + }, + outputs: { + ...CUSTOM_SETTING_OUTPUT_PROPERTIES, + }, +} diff --git a/apps/sim/tools/rippling/update_department.ts b/apps/sim/tools/rippling/update_department.ts new file mode 100644 index 00000000000..248f1db694a --- /dev/null +++ b/apps/sim/tools/rippling/update_department.ts @@ -0,0 +1,93 @@ +import type { RipplingUpdateDepartmentParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateDepartmentTool: ToolConfig = { + id: 'rippling_update_department', + name: 'Rippling Update Department', + description: 'Update an existing department', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Department ID' }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Department name', + }, + parentId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Parent department ID', + }, + referenceCode: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Reference code', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/departments/${encodeURIComponent(params.id.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.name != null) body.name = params.name + if (params.parentId != null) body.parent_id = params.parentId + if (params.referenceCode != null) body.reference_code = params.referenceCode + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + parent_id: (data.parent_id as string) ?? null, + reference_code: (data.reference_code as string) ?? null, + department_hierarchy_id: (data.department_hierarchy_id as unknown[]) ?? [], + parent: data.parent ?? null, + department_hierarchy: data.department_hierarchy ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Department ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + parent_id: { type: 'string', description: 'Parent department ID', optional: true }, + reference_code: { type: 'string', description: 'Reference code', optional: true }, + department_hierarchy_id: { + type: 'json', + description: 'Department hierarchy IDs', + optional: true, + }, + parent: { type: 'json', description: 'Expanded parent department', optional: true }, + department_hierarchy: { + type: 'json', + description: 'Expanded department hierarchy', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/rippling/update_group.ts b/apps/sim/tools/rippling/update_group.ts deleted file mode 100644 index c1c7bd54842..00000000000 --- a/apps/sim/tools/rippling/update_group.ts +++ /dev/null @@ -1,115 +0,0 @@ -import type { RipplingUpdateGroupParams, RipplingUpdateGroupResponse } from '@/tools/rippling/types' -import type { ToolConfig } from '@/tools/types' - -export const ripplingUpdateGroupTool: ToolConfig< - RipplingUpdateGroupParams, - RipplingUpdateGroupResponse -> = { - id: 'rippling_update_group', - name: 'Rippling Update Group', - description: 'Update an existing group in Rippling', - version: '1.0.0', - - params: { - apiKey: { - type: 'string', - required: true, - visibility: 'user-only', - description: 'Rippling API key', - }, - groupId: { - type: 'string', - required: true, - visibility: 'user-or-llm', - description: 'The ID of the group to update', - }, - name: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'New name for the group', - }, - spokeId: { - type: 'string', - required: false, - visibility: 'user-or-llm', - description: 'Third-party app identifier', - }, - users: { - type: 'json', - required: false, - visibility: 'user-or-llm', - description: 'Array of user ID strings to set for the group', - }, - version: { - type: 'number', - required: false, - visibility: 'user-or-llm', - description: 'Group version number for optimistic concurrency', - }, - }, - - request: { - url: (params) => - `https://api.rippling.com/platform/api/groups/${encodeURIComponent(params.groupId.trim())}`, - method: 'PUT', - headers: (params) => ({ - Authorization: `Bearer ${params.apiKey}`, - Accept: 'application/json', - 'Content-Type': 'application/json', - }), - body: (params) => { - const body: Record = {} - if (params.name !== undefined) { - body.name = params.name - } - if (params.spokeId !== undefined) { - body.spokeId = params.spokeId - } - if (params.users !== undefined) { - body.users = params.users - } - if (params.version !== undefined) { - body.version = params.version - } - if (Object.keys(body).length === 0) { - throw new Error( - 'At least one field (name, spokeId, users, or version) must be provided to update a group.' - ) - } - return body - }, - }, - - transformResponse: async (response: Response) => { - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Rippling API error (${response.status}): ${errorText}`) - } - - const data = await response.json() - - return { - success: true, - output: { - id: (data.id as string) ?? '', - name: (data.name as string) ?? null, - spokeId: (data.spokeId as string) ?? null, - users: (data.users as string[]) ?? [], - version: (data.version as number) ?? null, - }, - } - }, - - outputs: { - id: { type: 'string', description: 'Group ID' }, - name: { type: 'string', description: 'Group name' }, - spokeId: { type: 'string', description: 'Third-party app identifier' }, - users: { - type: 'array', - description: 'Array of user IDs in the group', - items: { type: 'string' }, - }, - version: { type: 'number', description: 'Group version number' }, - }, -} diff --git a/apps/sim/tools/rippling/update_object_category.ts b/apps/sim/tools/rippling/update_object_category.ts new file mode 100644 index 00000000000..0c76340b273 --- /dev/null +++ b/apps/sim/tools/rippling/update_object_category.ts @@ -0,0 +1,70 @@ +import type { RipplingUpdateObjectCategoryParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateObjectCategoryTool: ToolConfig = { + id: 'rippling_update_object_category', + name: 'Rippling Update ObjectCategory', + description: 'Update an object category', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Category ID' }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Category name', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/object-categories/${encodeURIComponent(params.id.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.name != null) body.name = params.name + if (params.description != null) body.description = params.description + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + description: (data.description as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Category ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Name', optional: true }, + description: { type: 'string', description: 'Description', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/update_supergroup_exclusion_members.ts b/apps/sim/tools/rippling/update_supergroup_exclusion_members.ts new file mode 100644 index 00000000000..709a49976f8 --- /dev/null +++ b/apps/sim/tools/rippling/update_supergroup_exclusion_members.ts @@ -0,0 +1,52 @@ +import type { RipplingUpdateSupergroupExclusionMembersParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateSupergroupExclusionMembersTool: ToolConfig = + { + id: 'rippling_update_supergroup_exclusion_members', + name: 'Rippling Update Supergroup Exclusion Members', + description: 'Update exclusion members of a supergroup', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + groupId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Supergroup ID', + }, + operations: { + type: 'json', + required: true, + visibility: 'user-or-llm', + description: 'Operations array [{op: "add"|"remove", value: [{id: "member_id"}]}]', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/supergroups/${encodeURIComponent(params.groupId.trim())}/exclusion-members/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => ({ Operations: params.operations }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { success: true, output: { ok: data.ok ?? false } } + }, + outputs: { + ok: { type: 'boolean', description: 'Whether the operation succeeded' }, + }, + } diff --git a/apps/sim/tools/rippling/update_supergroup_inclusion_members.ts b/apps/sim/tools/rippling/update_supergroup_inclusion_members.ts new file mode 100644 index 00000000000..3013db248e1 --- /dev/null +++ b/apps/sim/tools/rippling/update_supergroup_inclusion_members.ts @@ -0,0 +1,52 @@ +import type { RipplingUpdateSupergroupInclusionMembersParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateSupergroupInclusionMembersTool: ToolConfig = + { + id: 'rippling_update_supergroup_inclusion_members', + name: 'Rippling Update Supergroup Inclusion Members', + description: 'Update inclusion members of a supergroup', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + groupId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Supergroup ID', + }, + operations: { + type: 'json', + required: true, + visibility: 'user-or-llm', + description: 'Operations array [{op: "add"|"remove", value: [{id: "member_id"}]}]', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/supergroups/${encodeURIComponent(params.groupId.trim())}/inclusion-members/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => ({ Operations: params.operations }), + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { success: true, output: { ok: data.ok ?? false } } + }, + outputs: { + ok: { type: 'boolean', description: 'Whether the operation succeeded' }, + }, + } diff --git a/apps/sim/tools/rippling/update_title.ts b/apps/sim/tools/rippling/update_title.ts new file mode 100644 index 00000000000..41d08c76756 --- /dev/null +++ b/apps/sim/tools/rippling/update_title.ts @@ -0,0 +1,56 @@ +import type { RipplingUpdateTitleParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateTitleTool: ToolConfig = { + id: 'rippling_update_title', + name: 'Rippling Update Title', + description: 'Update an existing title', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Title ID' }, + name: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Title name' }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/titles/${encodeURIComponent(params.id.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.name != null) body.name = params.name + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Title ID' }, + created_at: { type: 'string', description: 'Creation date', optional: true }, + updated_at: { type: 'string', description: 'Update date', optional: true }, + name: { type: 'string', description: 'Title name', optional: true }, + }, +} diff --git a/apps/sim/tools/rippling/update_work_location.ts b/apps/sim/tools/rippling/update_work_location.ts new file mode 100644 index 00000000000..509f3c4c471 --- /dev/null +++ b/apps/sim/tools/rippling/update_work_location.ts @@ -0,0 +1,102 @@ +import type { RipplingUpdateWorkLocationParams } from '@/tools/rippling/types' +import type { ToolConfig } from '@/tools/types' + +export const ripplingUpdateWorkLocationTool: ToolConfig = { + id: 'rippling_update_work_location', + name: 'Rippling Update Work Location', + description: 'Update a work location', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rippling API key', + }, + id: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Location ID' }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Location name', + }, + streetAddress: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Street address', + }, + locality: { type: 'string', required: false, visibility: 'user-or-llm', description: 'City' }, + region: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'State/region', + }, + postalCode: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Postal code', + }, + country: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Country code', + }, + addressType: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Address type (HOME, WORK, OTHER)', + }, + }, + request: { + url: (params) => + `https://rest.ripplingapis.com/work-locations/${encodeURIComponent(params.id.trim())}/`, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.name != null) body.name = params.name + const address: Record = {} + if (params.streetAddress != null) address.street_address = params.streetAddress + if (params.locality != null) address.locality = params.locality + if (params.region != null) address.region = params.region + if (params.postalCode != null) address.postal_code = params.postalCode + if (params.country != null) address.country = params.country + if (params.addressType != null) address.type = params.addressType + if (Object.keys(address).length > 0) body.address = address + return body + }, + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Rippling API error (${response.status}): ${errorText}`) + } + const data = await response.json() + return { + success: true, + output: { + id: (data.id as string) ?? '', + created_at: (data.created_at as string) ?? null, + updated_at: (data.updated_at as string) ?? null, + name: (data.name as string) ?? null, + address: data.address ?? null, + }, + } + }, + outputs: { + id: { type: 'string', description: 'Location ID' }, + created_at: { type: 'string', description: 'Created timestamp', optional: true }, + updated_at: { type: 'string', description: 'Updated timestamp', optional: true }, + name: { type: 'string', description: 'Name' }, + address: { type: 'json', description: 'Address', optional: true }, + }, +}