diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index dae53828ccb..6399bdf2192 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -2279,6 +2279,18 @@ export function JiraIcon(props: SVGProps) { ) } +export function GustoIcon(props: React.SVGProps) { + return ( + + + + + + + + ) +} + export function LinearIcon(props: React.SVGProps) { return ( = { granola: GranolaIcon, greenhouse: GreenhouseIcon, greptile: GreptileIcon, + gusto: GustoIcon, hex: HexIcon, hubspot: HubspotIcon, huggingface: HuggingFaceIcon, diff --git a/apps/docs/content/docs/en/tools/gusto.mdx b/apps/docs/content/docs/en/tools/gusto.mdx new file mode 100644 index 00000000000..128435f07df --- /dev/null +++ b/apps/docs/content/docs/en/tools/gusto.mdx @@ -0,0 +1,1029 @@ +--- +title: Gusto +description: Manage employees, contractors, and payroll in Gusto +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Gusto](https://gusto.com) is a payroll, benefits, and HR platform that helps small and medium-sized businesses run payroll, manage employees and contractors, and stay compliant with tax filings. + +With the Gusto integration in Sim, you can: + +- **Read company data**: Retrieve company details, locations, and pay schedules +- **Manage employees**: List, get, and create employees, including self-onboarding invites +- **Manage contractors**: List contractors with filters for onboarding status and search +- **Access payroll**: List payrolls and retrieve a specific payroll with calculation details, taxes, benefits, and deductions +- **Track time off**: List time off requests across the company within a date range + +In Sim, the Gusto integration enables your agents to read and act on payroll and HR data as part of automated workflows. Agents can sync employee records, monitor payroll runs, surface time off activity, and trigger downstream actions in other systems—bringing payroll operations into your agent automations. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Run payroll end-to-end, manage employees and contractors (create, update, terminate, rehire), pay contractors, view time off activities and benefits, and access pay stubs, forms, and onboarding status. + + + +## Tools + +### `gusto_get_company` + +Retrieve a Gusto company by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `company` | object | Gusto company | +| ↳ `uuid` | string | Company UUID | +| ↳ `name` | string | Legal entity name | +| ↳ `trade_name` | string | Trade name | +| ↳ `ein` | string | Federal Employer Identification Number | +| ↳ `entity_type` | string | Entity type \(LLC, Corporation, etc.\) | +| ↳ `company_status` | string | Company status \(Approved, Not Approved, Suspended\) | +| ↳ `locations` | array | Company locations | +| ↳ `compensations` | object | Compensation classifications | +| ↳ `primary_signatory` | object | Primary signatory | +| ↳ `primary_payroll_admin` | object | Primary payroll admin | +| ↳ `tier` | string | Company tier | + +### `gusto_list_employees` + +List employees for a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `terminated` | boolean | No | Include only terminated employees when true | +| `page` | number | No | Page number | +| `per` | number | No | Items per page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `employees` | array | List of employees | +| ↳ `uuid` | string | Employee UUID | +| ↳ `first_name` | string | First name | +| ↳ `middle_initial` | string | Middle initial | +| ↳ `last_name` | string | Last name | +| ↳ `preferred_first_name` | string | Preferred first name | +| ↳ `email` | string | Personal email | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `manager_uuid` | string | Manager UUID | +| ↳ `department` | string | Department name | +| ↳ `department_uuid` | string | Department UUID | +| ↳ `date_of_birth` | string | Date of birth | +| ↳ `has_ssn` | boolean | Whether SSN is on file | +| ↳ `ssn` | string | Social security number \(masked\) | +| ↳ `phone` | string | Phone number | +| ↳ `terminated` | boolean | Whether the employee is terminated | +| ↳ `terminations` | array | Termination records | +| ↳ `onboarded` | boolean | Whether the employee is onboarded | +| ↳ `onboarding_status` | string | Onboarding status | +| ↳ `jobs` | array | Employee jobs | +| ↳ `version` | string | Record version | + +### `gusto_get_employee` + +Retrieve a Gusto employee by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `employee` | object | Gusto employee | +| ↳ `uuid` | string | Employee UUID | +| ↳ `first_name` | string | First name | +| ↳ `middle_initial` | string | Middle initial | +| ↳ `last_name` | string | Last name | +| ↳ `preferred_first_name` | string | Preferred first name | +| ↳ `email` | string | Personal email | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `manager_uuid` | string | Manager UUID | +| ↳ `department` | string | Department name | +| ↳ `department_uuid` | string | Department UUID | +| ↳ `date_of_birth` | string | Date of birth | +| ↳ `has_ssn` | boolean | Whether SSN is on file | +| ↳ `ssn` | string | Social security number \(masked\) | +| ↳ `phone` | string | Phone number | +| ↳ `terminated` | boolean | Whether the employee is terminated | +| ↳ `terminations` | array | Termination records | +| ↳ `onboarded` | boolean | Whether the employee is onboarded | +| ↳ `onboarding_status` | string | Onboarding status | +| ↳ `jobs` | array | Employee jobs | +| ↳ `version` | string | Record version | + +### `gusto_create_employee` + +Create a new employee in a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `firstName` | string | Yes | Employee first name | +| `lastName` | string | Yes | Employee last name | +| `email` | string | No | Employee personal email address | +| `middleInitial` | string | No | Middle initial | +| `dateOfBirth` | string | No | Date of birth \(YYYY-MM-DD\) | +| `ssn` | string | No | Social security number \(digits only\) | +| `selfOnboarding` | boolean | No | Send self-onboarding invite to employee | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `employee` | object | Created Gusto employee | +| ↳ `uuid` | string | Employee UUID | +| ↳ `first_name` | string | First name | +| ↳ `middle_initial` | string | Middle initial | +| ↳ `last_name` | string | Last name | +| ↳ `preferred_first_name` | string | Preferred first name | +| ↳ `email` | string | Personal email | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `manager_uuid` | string | Manager UUID | +| ↳ `department` | string | Department name | +| ↳ `department_uuid` | string | Department UUID | +| ↳ `date_of_birth` | string | Date of birth | +| ↳ `has_ssn` | boolean | Whether SSN is on file | +| ↳ `ssn` | string | Social security number \(masked\) | +| ↳ `phone` | string | Phone number | +| ↳ `terminated` | boolean | Whether the employee is terminated | +| ↳ `terminations` | array | Termination records | +| ↳ `onboarded` | boolean | Whether the employee is onboarded | +| ↳ `onboarding_status` | string | Onboarding status | +| ↳ `jobs` | array | Employee jobs | +| ↳ `version` | string | Record version | + +### `gusto_update_employee` + +Update an existing Gusto employee + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | +| `version` | string | Yes | Current version of the employee record \(required for updates\) | +| `firstName` | string | No | Employee first name | +| `lastName` | string | No | Employee last name | +| `middleInitial` | string | No | Middle initial | +| `email` | string | No | Work email | +| `dateOfBirth` | string | No | Date of birth \(YYYY-MM-DD\) | +| `ssn` | string | No | Social security number \(digits only\) | +| `preferredFirstName` | string | No | Preferred first name | +| `twoPercentShareholder` | boolean | No | Whether the employee is a 2% shareholder | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `employee` | object | Updated Gusto employee | +| ↳ `uuid` | string | Employee UUID | +| ↳ `first_name` | string | First name | +| ↳ `middle_initial` | string | Middle initial | +| ↳ `last_name` | string | Last name | +| ↳ `preferred_first_name` | string | Preferred first name | +| ↳ `email` | string | Personal email | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `manager_uuid` | string | Manager UUID | +| ↳ `department` | string | Department name | +| ↳ `department_uuid` | string | Department UUID | +| ↳ `date_of_birth` | string | Date of birth | +| ↳ `has_ssn` | boolean | Whether SSN is on file | +| ↳ `ssn` | string | Social security number \(masked\) | +| ↳ `phone` | string | Phone number | +| ↳ `terminated` | boolean | Whether the employee is terminated | +| ↳ `terminations` | array | Termination records | +| ↳ `onboarded` | boolean | Whether the employee is onboarded | +| ↳ `onboarding_status` | string | Onboarding status | +| ↳ `jobs` | array | Employee jobs | +| ↳ `version` | string | Record version | + +### `gusto_terminate_employee` + +Create a termination for a Gusto employee + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | +| `effectiveDate` | string | Yes | Termination effective date \(YYYY-MM-DD\) | +| `runTerminationPayroll` | boolean | No | If true, the employee receives final wages via off-cycle payroll. If false, on their current pay schedule. | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `termination` | object | Employee termination record | +| ↳ `uuid` | string | Termination UUID | +| ↳ `employee_uuid` | string | Employee UUID | +| ↳ `active` | boolean | Whether the termination is active | +| ↳ `effective_date` | string | Effective date \(YYYY-MM-DD\) | +| ↳ `run_termination_payroll` | boolean | Whether to run a termination payroll | + +### `gusto_rehire_employee` + +Schedule a rehire for a Gusto employee + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | +| `effectiveDate` | string | Yes | The day when the employee returns to work \(YYYY-MM-DD\) | +| `fileNewHireReport` | boolean | Yes | Whether Gusto will file a new hire report for the employee | +| `workLocationUuid` | string | Yes | UUID of the employee's work location | +| `employmentStatus` | string | No | Employment status \(part_time, full_time, part_time_eligible, variable, seasonal, not_set\) | +| `twoPercentShareholder` | boolean | No | Whether the employee is a 2% shareholder \(S-Corp companies only\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `rehire` | object | Employee rehire record | +| ↳ `employee_uuid` | string | Employee UUID | +| ↳ `effective_date` | string | Effective date \(YYYY-MM-DD\) | +| ↳ `work_location_uuid` | string | Work location UUID | +| ↳ `file_new_hire_report` | boolean | Whether to file a new hire report | +| ↳ `employment_status` | string | Employment status | +| ↳ `two_percent_shareholder` | boolean | Whether the employee is a 2% shareholder | + +### `gusto_list_employee_jobs` + +List jobs (compensations and titles) for a Gusto employee + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `jobs` | array | Employee jobs | +| ↳ `uuid` | string | Job UUID | +| ↳ `employee_uuid` | string | Employee UUID | +| ↳ `title` | string | Job title | +| ↳ `hire_date` | string | Hire date \(YYYY-MM-DD\) | +| ↳ `primary` | boolean | Whether this is the primary job | +| ↳ `current_compensation_uuid` | string | Current compensation UUID | +| ↳ `rate` | string | Pay rate | +| ↳ `payment_unit` | string | Payment unit | +| ↳ `compensations` | array | Job compensation history | +| ↳ `state_wc_covered` | boolean | Whether the job is covered by state workers' comp | +| ↳ `state_wc_class_code` | string | State workers' comp class code | +| ↳ `two_percent_shareholder` | boolean | Whether this job is for a 2% shareholder | +| ↳ `version` | string | Record version | + +### `gusto_list_pay_stubs` + +List pay stubs for a Gusto employee + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | +| `page` | number | No | Page number | +| `per` | number | No | Items per page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `payStubs` | array | Pay stubs | +| ↳ `uuid` | string | Pay stub identifier | +| ↳ `payroll_uuid` | string | Payroll UUID | +| ↳ `check_date` | string | Check date | +| ↳ `gross_pay` | string | Gross pay amount | +| ↳ `net_pay` | string | Net pay amount | +| ↳ `check_amount` | string | Check amount | + +### `gusto_get_employee_onboarding_status` + +Get the onboarding status for a Gusto employee + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `onboardingStatus` | object | Employee onboarding status | +| ↳ `uuid` | string | Employee UUID | +| ↳ `onboarding_status` | string | Onboarding status | +| ↳ `onboarding_steps` | array | Onboarding step details | + +### `gusto_list_contractors` + +List contractors for a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `searchTerm` | string | No | Search term to filter contractors by name | +| `page` | number | No | Page number | +| `per` | number | No | Items per page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `contractors` | array | List of contractors | +| ↳ `uuid` | string | Contractor UUID | +| ↳ `type` | string | Contractor type \(Individual or Business\) | +| ↳ `wage_type` | string | Wage type \(Fixed or Hourly\) | +| ↳ `is_active` | boolean | Whether the contractor is active | +| ↳ `first_name` | string | First name | +| ↳ `last_name` | string | Last name | +| ↳ `business_name` | string | Business name | +| ↳ `email` | string | Email address | +| ↳ `start_date` | string | Start date \(YYYY-MM-DD\) | +| ↳ `hourly_rate` | string | Hourly rate | +| ↳ `company_uuid` | string | Company UUID | + +### `gusto_get_contractor` + +Retrieve a Gusto contractor by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contractorId` | string | Yes | Gusto contractor UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `contractor` | object | Gusto contractor | +| ↳ `uuid` | string | Contractor UUID | +| ↳ `type` | string | Contractor type \(Individual or Business\) | +| ↳ `wage_type` | string | Wage type \(Fixed or Hourly\) | +| ↳ `is_active` | boolean | Whether the contractor is active | +| ↳ `first_name` | string | First name | +| ↳ `last_name` | string | Last name | +| ↳ `business_name` | string | Business name | +| ↳ `email` | string | Email address | +| ↳ `start_date` | string | Start date \(YYYY-MM-DD\) | +| ↳ `hourly_rate` | string | Hourly rate | +| ↳ `company_uuid` | string | Company UUID | + +### `gusto_create_contractor` + +Create a new contractor in a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `type` | string | Yes | Contractor type \(Individual or Business\) | +| `wageType` | string | Yes | Wage type \(Fixed or Hourly\) | +| `startDate` | string | Yes | Contractor start date \(YYYY-MM-DD\) | +| `firstName` | string | No | First name \(required for Individual contractors\) | +| `lastName` | string | No | Last name \(required for Individual contractors\) | +| `middleInitial` | string | No | Middle initial | +| `businessName` | string | No | Business name \(required for Business contractors\) | +| `email` | string | No | Contractor email | +| `selfOnboarding` | boolean | No | Send self-onboarding invite to the contractor | +| `ein` | string | No | Employer Identification Number \(Business contractors\) | +| `hourlyRate` | string | No | Hourly rate \(when wage type is Hourly\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `contractor` | object | Created contractor | +| ↳ `uuid` | string | Contractor UUID | +| ↳ `type` | string | Contractor type \(Individual or Business\) | +| ↳ `wage_type` | string | Wage type \(Fixed or Hourly\) | +| ↳ `is_active` | boolean | Whether the contractor is active | +| ↳ `first_name` | string | First name | +| ↳ `last_name` | string | Last name | +| ↳ `business_name` | string | Business name | +| ↳ `email` | string | Email address | +| ↳ `start_date` | string | Start date \(YYYY-MM-DD\) | +| ↳ `hourly_rate` | string | Hourly rate | +| ↳ `company_uuid` | string | Company UUID | + +### `gusto_update_contractor` + +Update an existing Gusto contractor + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contractorId` | string | Yes | Gusto contractor UUID | +| `version` | string | Yes | Current version of the contractor record \(required for updates\) | +| `firstName` | string | No | First name | +| `lastName` | string | No | Last name | +| `middleInitial` | string | No | Middle initial | +| `businessName` | string | No | Business name | +| `email` | string | No | Contractor email | +| `startDate` | string | No | Start date \(YYYY-MM-DD\) | +| `hourlyRate` | string | No | Hourly rate | +| `wageType` | string | No | Wage type \(Fixed or Hourly\) | +| `ein` | string | No | Employer Identification Number | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `contractor` | object | Updated contractor | +| ↳ `uuid` | string | Contractor UUID | +| ↳ `type` | string | Contractor type \(Individual or Business\) | +| ↳ `wage_type` | string | Wage type \(Fixed or Hourly\) | +| ↳ `is_active` | boolean | Whether the contractor is active | +| ↳ `first_name` | string | First name | +| ↳ `last_name` | string | Last name | +| ↳ `business_name` | string | Business name | +| ↳ `email` | string | Email address | +| ↳ `start_date` | string | Start date \(YYYY-MM-DD\) | +| ↳ `hourly_rate` | string | Hourly rate | +| ↳ `company_uuid` | string | Company UUID | + +### `gusto_list_payrolls` + +List payrolls for a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `startDate` | string | No | Start date filter \(YYYY-MM-DD\) | +| `endDate` | string | No | End date filter \(YYYY-MM-DD\) | +| `processingStatuses` | string | No | Comma-separated statuses \(processed, unprocessed\). Defaults to "processed" if omitted | +| `payrollTypes` | string | No | Comma-separated payroll types \(regular, off_cycle, external\). Defaults to "regular" if omitted | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `payrolls` | array | List of payrolls | +| ↳ `uuid` | string | Payroll UUID | +| ↳ `payroll_uuid` | string | Payroll UUID \(legacy alias\) | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `payroll_deadline` | string | Payroll submission deadline | +| ↳ `check_date` | string | Check date | +| ↳ `processed` | boolean | Whether the payroll has been processed | +| ↳ `processed_date` | string | Date the payroll was processed | +| ↳ `calculated_at` | string | When the payroll was calculated | +| ↳ `off_cycle` | boolean | Whether this is an off-cycle payroll | +| ↳ `off_cycle_reason` | string | Off-cycle payroll reason | +| ↳ `external` | boolean | Whether this is an external payroll | +| ↳ `auto_pilot` | boolean | Whether autopilot is enabled | +| ↳ `pay_period` | object | Pay period details | +| ↳ `totals` | object | Payroll totals | +| ↳ `payroll_status_meta` | object | Status metadata \(cancellable, expected_check_date, etc.\) | +| ↳ `employee_compensations` | array | Per-employee compensation breakdown | + +### `gusto_get_payroll` + +Retrieve a single payroll by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `payrollId` | string | Yes | Gusto payroll UUID | +| `include` | string | No | Comma-separated extra fields to include \(e.g. "taxes,benefits,deductions"\). Required to retrieve calculated payroll details. | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `payroll` | object | Gusto payroll | +| ↳ `uuid` | string | Payroll UUID | +| ↳ `payroll_uuid` | string | Payroll UUID \(legacy alias\) | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `payroll_deadline` | string | Payroll submission deadline | +| ↳ `check_date` | string | Check date | +| ↳ `processed` | boolean | Whether the payroll has been processed | +| ↳ `processed_date` | string | Date the payroll was processed | +| ↳ `calculated_at` | string | When the payroll was calculated | +| ↳ `off_cycle` | boolean | Whether this is an off-cycle payroll | +| ↳ `off_cycle_reason` | string | Off-cycle payroll reason | +| ↳ `external` | boolean | Whether this is an external payroll | +| ↳ `auto_pilot` | boolean | Whether autopilot is enabled | +| ↳ `pay_period` | object | Pay period details | +| ↳ `totals` | object | Payroll totals | +| ↳ `payroll_status_meta` | object | Status metadata \(cancellable, expected_check_date, etc.\) | +| ↳ `employee_compensations` | array | Per-employee compensation breakdown | + +### `gusto_create_off_cycle_payroll` + +Create an off-cycle payroll for a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `startDate` | string | Yes | Pay period start date \(YYYY-MM-DD\) | +| `endDate` | string | Yes | Pay period end date \(YYYY-MM-DD\) | +| `checkDate` | string | No | Check date \(YYYY-MM-DD\). Defaults to the next available payday | +| `payScheduleUuid` | string | No | Pay schedule UUID to associate with this off-cycle payroll | +| `fixedWithholdingRate` | boolean | No | Use fixed supplemental withholding rate \(e.g. for bonuses\) | +| `offCycleReason` | string | Yes | Off-cycle reason. One of: Benefit reversal, Bonus, Correction, Dismissed employee, Hired employee, Wage correction, Tax reconciliation, Reversal, Disability insurance distribution, Transition from old pay schedule | +| `employeeUuids` | string | No | Comma-separated employee UUIDs to include | +| `withholdingPayPeriod` | string | No | Withholding pay period override | +| `skipRegularDeductions` | boolean | No | Skip regular deductions | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `payroll` | object | Created off-cycle payroll | +| ↳ `uuid` | string | Payroll UUID | +| ↳ `payroll_uuid` | string | Payroll UUID \(legacy alias\) | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `payroll_deadline` | string | Payroll submission deadline | +| ↳ `check_date` | string | Check date | +| ↳ `processed` | boolean | Whether the payroll has been processed | +| ↳ `processed_date` | string | Date the payroll was processed | +| ↳ `calculated_at` | string | When the payroll was calculated | +| ↳ `off_cycle` | boolean | Whether this is an off-cycle payroll | +| ↳ `off_cycle_reason` | string | Off-cycle payroll reason | +| ↳ `external` | boolean | Whether this is an external payroll | +| ↳ `auto_pilot` | boolean | Whether autopilot is enabled | +| ↳ `pay_period` | object | Pay period details | +| ↳ `totals` | object | Payroll totals | +| ↳ `payroll_status_meta` | object | Status metadata \(cancellable, expected_check_date, etc.\) | +| ↳ `employee_compensations` | array | Per-employee compensation breakdown | + +### `gusto_calculate_payroll` + +Calculate a Gusto payroll (preview totals before submission) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `payrollId` | string | Yes | Gusto payroll UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `payroll` | object | Calculated payroll | +| ↳ `uuid` | string | Payroll UUID | +| ↳ `payroll_uuid` | string | Payroll UUID \(legacy alias\) | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `payroll_deadline` | string | Payroll submission deadline | +| ↳ `check_date` | string | Check date | +| ↳ `processed` | boolean | Whether the payroll has been processed | +| ↳ `processed_date` | string | Date the payroll was processed | +| ↳ `calculated_at` | string | When the payroll was calculated | +| ↳ `off_cycle` | boolean | Whether this is an off-cycle payroll | +| ↳ `off_cycle_reason` | string | Off-cycle payroll reason | +| ↳ `external` | boolean | Whether this is an external payroll | +| ↳ `auto_pilot` | boolean | Whether autopilot is enabled | +| ↳ `pay_period` | object | Pay period details | +| ↳ `totals` | object | Payroll totals | +| ↳ `payroll_status_meta` | object | Status metadata \(cancellable, expected_check_date, etc.\) | +| ↳ `employee_compensations` | array | Per-employee compensation breakdown | + +### `gusto_submit_payroll` + +Submit a calculated Gusto payroll for processing + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `payrollId` | string | Yes | Gusto payroll UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `payroll` | object | Submitted payroll | +| ↳ `uuid` | string | Payroll UUID | +| ↳ `payroll_uuid` | string | Payroll UUID \(legacy alias\) | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `payroll_deadline` | string | Payroll submission deadline | +| ↳ `check_date` | string | Check date | +| ↳ `processed` | boolean | Whether the payroll has been processed | +| ↳ `processed_date` | string | Date the payroll was processed | +| ↳ `calculated_at` | string | When the payroll was calculated | +| ↳ `off_cycle` | boolean | Whether this is an off-cycle payroll | +| ↳ `off_cycle_reason` | string | Off-cycle payroll reason | +| ↳ `external` | boolean | Whether this is an external payroll | +| ↳ `auto_pilot` | boolean | Whether autopilot is enabled | +| ↳ `pay_period` | object | Pay period details | +| ↳ `totals` | object | Payroll totals | +| ↳ `payroll_status_meta` | object | Status metadata \(cancellable, expected_check_date, etc.\) | +| ↳ `employee_compensations` | array | Per-employee compensation breakdown | + +### `gusto_cancel_payroll` + +Cancel a submitted Gusto payroll + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `payrollId` | string | Yes | Gusto payroll UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `payroll` | object | Cancelled payroll | +| ↳ `uuid` | string | Payroll UUID | +| ↳ `payroll_uuid` | string | Payroll UUID \(legacy alias\) | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `payroll_deadline` | string | Payroll submission deadline | +| ↳ `check_date` | string | Check date | +| ↳ `processed` | boolean | Whether the payroll has been processed | +| ↳ `processed_date` | string | Date the payroll was processed | +| ↳ `calculated_at` | string | When the payroll was calculated | +| ↳ `off_cycle` | boolean | Whether this is an off-cycle payroll | +| ↳ `off_cycle_reason` | string | Off-cycle payroll reason | +| ↳ `external` | boolean | Whether this is an external payroll | +| ↳ `auto_pilot` | boolean | Whether autopilot is enabled | +| ↳ `pay_period` | object | Pay period details | +| ↳ `totals` | object | Payroll totals | +| ↳ `payroll_status_meta` | object | Status metadata \(cancellable, expected_check_date, etc.\) | +| ↳ `employee_compensations` | array | Per-employee compensation breakdown | + +### `gusto_list_contractor_payments` + +List contractor payments for a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `startDate` | string | Yes | Start date filter \(YYYY-MM-DD\) | +| `endDate` | string | Yes | End date filter \(YYYY-MM-DD\) | +| `page` | number | No | Page number | +| `per` | number | No | Items per page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `contractorPayments` | array | Contractor payments | +| ↳ `uuid` | string | Contractor payment UUID | +| ↳ `contractor_uuid` | string | Contractor UUID | +| ↳ `bonus` | string | Bonus amount | +| ↳ `date` | string | Payment date \(YYYY-MM-DD\) | +| ↳ `hours` | string | Hours worked | +| ↳ `reimbursement` | string | Reimbursement amount | +| ↳ `wage` | string | Fixed wage amount | +| ↳ `wage_type` | string | Wage type \(Fixed or Hourly\) | +| ↳ `wage_total` | string | Total wage amount | +| ↳ `payment_method` | string | Payment method | +| ↳ `status` | string | Payment status | +| ↳ `may_cancel` | boolean | Whether the payment may be canceled | +| ↳ `check_number` | string | Check number | +| ↳ `debit_date` | string | Date funds will be debited | + +### `gusto_get_contractor_payment` + +Retrieve a single Gusto contractor payment by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `contractorPaymentId` | string | Yes | Gusto contractor payment UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `contractorPayment` | object | Contractor payment | +| ↳ `uuid` | string | Contractor payment UUID | +| ↳ `contractor_uuid` | string | Contractor UUID | +| ↳ `bonus` | string | Bonus amount | +| ↳ `date` | string | Payment date \(YYYY-MM-DD\) | +| ↳ `hours` | string | Hours worked | +| ↳ `reimbursement` | string | Reimbursement amount | +| ↳ `wage` | string | Fixed wage amount | +| ↳ `wage_type` | string | Wage type \(Fixed or Hourly\) | +| ↳ `wage_total` | string | Total wage amount | +| ↳ `payment_method` | string | Payment method | +| ↳ `status` | string | Payment status | +| ↳ `may_cancel` | boolean | Whether the payment may be canceled | +| ↳ `check_number` | string | Check number | +| ↳ `debit_date` | string | Date funds will be debited | + +### `gusto_create_contractor_payment` + +Pay a Gusto contractor (one-off payment) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `contractorUuid` | string | Yes | Gusto contractor UUID | +| `date` | string | Yes | Payment date \(YYYY-MM-DD\) | +| `wage` | number | No | Fixed wage amount \(for Fixed contractors\) | +| `hours` | number | No | Hours worked \(for Hourly contractors\) | +| `bonus` | number | No | Bonus amount | +| `reimbursement` | number | No | Reimbursement amount | +| `paymentMethod` | string | No | Payment method \(Direct Deposit or Check\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `contractorPayment` | object | Created contractor payment | +| ↳ `uuid` | string | Contractor payment UUID | +| ↳ `contractor_uuid` | string | Contractor UUID | +| ↳ `bonus` | string | Bonus amount | +| ↳ `date` | string | Payment date \(YYYY-MM-DD\) | +| ↳ `hours` | string | Hours worked | +| ↳ `reimbursement` | string | Reimbursement amount | +| ↳ `wage` | string | Fixed wage amount | +| ↳ `wage_type` | string | Wage type \(Fixed or Hourly\) | +| ↳ `wage_total` | string | Total wage amount | +| ↳ `payment_method` | string | Payment method | +| ↳ `status` | string | Payment status | +| ↳ `may_cancel` | boolean | Whether the payment may be canceled | +| ↳ `check_number` | string | Check number | +| ↳ `debit_date` | string | Date funds will be debited | + +### `gusto_list_employee_time_off_activities` + +List time off activities for a Gusto employee by time off type + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | +| `timeOffType` | string | Yes | Time off type to query \(e.g. 'sick' or 'vacation'\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `timeOffActivities` | array | Time off activities | +| ↳ `policy_uuid` | string | Time off policy UUID | +| ↳ `time_off_type` | string | Time off type \(vacation or sick\) | +| ↳ `policy_name` | string | Time off policy name | +| ↳ `event_type` | string | Type of the time off event | +| ↳ `event_description` | string | Event description | +| ↳ `effective_time` | string | Datetime of the activity | +| ↳ `balance` | string | Balance at the time of the activity | +| ↳ `balance_change` | string | Balance change amount | + +### `gusto_list_pay_schedules` + +List pay schedules for a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `paySchedules` | array | List of pay schedules | +| ↳ `uuid` | string | Pay schedule UUID | +| ↳ `frequency` | string | Pay frequency | +| ↳ `anchor_pay_date` | string | Anchor pay date | +| ↳ `anchor_end_of_pay_period` | string | Anchor end of pay period | +| ↳ `day_1` | number | First pay day of period \(semimonthly\) | +| ↳ `day_2` | number | Second pay day of period \(semimonthly\) | +| ↳ `name` | string | Pay schedule name | +| ↳ `auto_pilot` | boolean | Whether autopilot is enabled | +| ↳ `active` | boolean | Whether the schedule is active | +| ↳ `custom_name` | string | Custom name | +| ↳ `version` | string | Record version | + +### `gusto_list_locations` + +List locations for a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `locations` | array | List of locations | +| ↳ `uuid` | string | Location UUID | +| ↳ `street_1` | string | Street address line 1 | +| ↳ `street_2` | string | Street address line 2 | +| ↳ `city` | string | City | +| ↳ `state` | string | State | +| ↳ `zip` | string | ZIP code | +| ↳ `country` | string | Country | +| ↳ `phone_number` | string | Phone number | +| ↳ `mailing_address` | boolean | Whether this is the mailing address | +| ↳ `filing_address` | boolean | Whether this is the filing address | +| ↳ `active` | boolean | Whether the location is active | + +### `gusto_list_departments` + +List all departments for a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `departments` | array | Company departments | +| ↳ `uuid` | string | Department UUID | +| ↳ `title` | string | Department title | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `version` | string | Record version | +| ↳ `employees` | array | Employees in the department | +| ↳ `contractors` | array | Contractors in the department | + +### `gusto_create_department` + +Create a department in a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | +| `title` | string | Yes | Department title | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `department` | object | Created department | +| ↳ `uuid` | string | Department UUID | +| ↳ `title` | string | Department title | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `version` | string | Record version | +| ↳ `employees` | array | Employees in the department | +| ↳ `contractors` | array | Contractors in the department | + +### `gusto_list_company_benefits` + +List all benefits configured for a Gusto company + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Gusto company UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `companyBenefits` | array | Company benefits | +| ↳ `uuid` | string | Company benefit UUID | +| ↳ `company_uuid` | string | Company UUID | +| ↳ `benefit_type` | number | Benefit type ID | +| ↳ `active` | boolean | Whether active | +| ↳ `description` | string | Benefit description | +| ↳ `source` | string | Benefit source \(Gusto, partner, etc.\) | +| ↳ `partner_name` | string | Partner name \(if external\) | +| ↳ `enrollment_count` | number | Number of employees enrolled | +| ↳ `deletable` | boolean | Whether the benefit can be deleted | +| ↳ `responsible_for_employer_taxes` | boolean | Whether company is responsible for employer taxes | +| ↳ `responsible_for_employee_w2` | boolean | Whether benefit appears on employee W2 | +| ↳ `supports_percentage_amounts` | boolean | Whether the benefit supports percentage-based amounts | +| ↳ `version` | string | Record version | + +### `gusto_list_employee_benefits` + +List all benefits enrolled for a Gusto employee + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `employeeBenefits` | array | Employee benefits | +| ↳ `uuid` | string | Employee benefit UUID | +| ↳ `employee_uuid` | string | Employee UUID | +| ↳ `company_benefit_uuid` | string | Company benefit UUID | +| ↳ `active` | boolean | Whether active | +| ↳ `employee_deduction` | string | Employee deduction | +| ↳ `company_contribution` | string | Company contribution | +| ↳ `employee_deduction_annual_maximum` | string | Annual maximum employee deduction | +| ↳ `company_contribution_annual_maximum` | string | Annual maximum company contribution | +| ↳ `deduct_as_percentage` | boolean | Whether deduction is calculated as a percentage | +| ↳ `contribute_as_percentage` | boolean | Whether contribution is calculated as a percentage | +| ↳ `contribution` | object | Contribution config | +| ↳ `elective` | boolean | Whether the benefit is elective | +| ↳ `catch_up` | boolean | Whether catch-up contributions apply | +| ↳ `coverage_amount` | string | Coverage amount | +| ↳ `coverage_salary_multiplier` | string | Coverage as a multiplier of salary | +| ↳ `deduction_reduces_taxable_income` | string | Whether deduction reduces taxable income \(unset, true, false\) | +| ↳ `deduction_type` | string | Deduction type | +| ↳ `version` | string | Record version | + +### `gusto_list_employee_forms` + +List forms for a Gusto employee (W-2, I-9, etc.) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `employeeId` | string | Yes | Gusto employee UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `forms` | array | Employee forms | +| ↳ `uuid` | string | Form UUID | +| ↳ `name` | string | Form name | +| ↳ `title` | string | Form title | +| ↳ `description` | string | Form description | +| ↳ `year` | number | Tax year | +| ↳ `quarter` | number | Quarter | +| ↳ `requires_signing` | boolean | Whether the form requires signing | +| ↳ `draft` | boolean | Whether the form is a draft | +| ↳ `document_content_type` | string | Form document MIME type | + +### `gusto_list_contractor_forms` + +List forms for a Gusto contractor (1099, etc.) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contractorId` | string | Yes | Gusto contractor UUID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `forms` | array | Contractor forms | +| ↳ `uuid` | string | Form UUID | +| ↳ `name` | string | Form name | +| ↳ `title` | string | Form title | +| ↳ `description` | string | Form description | +| ↳ `year` | number | Tax year | +| ↳ `quarter` | number | Quarter | +| ↳ `requires_signing` | boolean | Whether the form requires signing | +| ↳ `draft` | boolean | Whether the form is a draft | +| ↳ `document_content_type` | string | Form document MIME type | + + diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index 1f780cff3d2..fd503ab6ab3 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -82,6 +82,7 @@ "granola", "greenhouse", "greptile", + "gusto", "hex", "hubspot", "huggingface", diff --git a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts index 9d3280bd825..8ffa6149129 100644 --- a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts +++ b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts @@ -87,6 +87,7 @@ import { GranolaIcon, GreenhouseIcon, GreptileIcon, + GustoIcon, HexIcon, HubspotIcon, HuggingFaceIcon, @@ -284,6 +285,7 @@ export const blockTypeToIconMap: Record = { granola: GranolaIcon, greenhouse: GreenhouseIcon, greptile: GreptileIcon, + gusto: GustoIcon, hex: HexIcon, hubspot: HubspotIcon, huggingface: HuggingFaceIcon, diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index 4239620a845..dd924ec11ae 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -6056,6 +6056,153 @@ "integrationTypes": ["developer-tools", "documents", "search"], "tags": ["version-control", "knowledge-base"] }, + { + "type": "gusto", + "slug": "gusto", + "name": "Gusto", + "description": "Manage employees, contractors, and payroll in Gusto", + "longDescription": "Run payroll end-to-end, manage employees and contractors (create, update, terminate, rehire), pay contractors, view time off activities and benefits, and access pay stubs, forms, and onboarding status.", + "bgColor": "#F45D48", + "iconName": "GustoIcon", + "docsUrl": "https://docs.sim.ai/tools/gusto", + "operations": [ + { + "name": "Get Company", + "description": "Retrieve a Gusto company by ID" + }, + { + "name": "List Employees", + "description": "List employees for a Gusto company" + }, + { + "name": "Get Employee", + "description": "Retrieve a Gusto employee by ID" + }, + { + "name": "Create Employee", + "description": "Create a new employee in a Gusto company" + }, + { + "name": "Update Employee", + "description": "Update an existing Gusto employee" + }, + { + "name": "Terminate Employee", + "description": "Create a termination for a Gusto employee" + }, + { + "name": "Rehire Employee", + "description": "Schedule a rehire for a Gusto employee" + }, + { + "name": "List Employee Jobs", + "description": "List jobs (compensations and titles) for a Gusto employee" + }, + { + "name": "List Pay Stubs", + "description": "List pay stubs for a Gusto employee" + }, + { + "name": "Get Employee Onboarding Status", + "description": "Get the onboarding status for a Gusto employee" + }, + { + "name": "List Contractors", + "description": "List contractors for a Gusto company" + }, + { + "name": "Get Contractor", + "description": "Retrieve a Gusto contractor by ID" + }, + { + "name": "Create Contractor", + "description": "Create a new contractor in a Gusto company" + }, + { + "name": "Update Contractor", + "description": "Update an existing Gusto contractor" + }, + { + "name": "List Payrolls", + "description": "List payrolls for a Gusto company" + }, + { + "name": "Get Payroll", + "description": "Retrieve a single payroll by ID" + }, + { + "name": "Create Off-Cycle Payroll", + "description": "Create an off-cycle payroll for a Gusto company" + }, + { + "name": "Calculate Payroll", + "description": "Calculate a Gusto payroll (preview totals before submission)" + }, + { + "name": "Submit Payroll", + "description": "Submit a calculated Gusto payroll for processing" + }, + { + "name": "Cancel Payroll", + "description": "Cancel a submitted Gusto payroll" + }, + { + "name": "List Contractor Payments", + "description": "List contractor payments for a Gusto company" + }, + { + "name": "Get Contractor Payment", + "description": "Retrieve a single Gusto contractor payment by ID" + }, + { + "name": "Create Contractor Payment", + "description": "Pay a Gusto contractor (one-off payment)" + }, + { + "name": "List Employee Time Off Activities", + "description": "List time off activities for a Gusto employee by time off type" + }, + { + "name": "List Pay Schedules", + "description": "List pay schedules for a Gusto company" + }, + { + "name": "List Locations", + "description": "List locations for a Gusto company" + }, + { + "name": "List Departments", + "description": "List all departments for a Gusto company" + }, + { + "name": "Create Department", + "description": "Create a department in a Gusto company" + }, + { + "name": "List Company Benefits", + "description": "List all benefits configured for a Gusto company" + }, + { + "name": "List Employee Benefits", + "description": "List all benefits enrolled for a Gusto employee" + }, + { + "name": "List Employee Forms", + "description": "List forms for a Gusto employee (W-2, I-9, etc.)" + }, + { + "name": "List Contractor Forms", + "description": "List forms for a Gusto contractor (1099, etc.)" + } + ], + "operationCount": 32, + "triggers": [], + "triggerCount": 0, + "authType": "oauth", + "category": "tools", + "integrationTypes": ["hr"], + "tags": ["hiring"] + }, { "type": "hex", "slug": "hex", diff --git a/apps/sim/blocks/blocks/gusto.ts b/apps/sim/blocks/blocks/gusto.ts new file mode 100644 index 00000000000..37e104a06a7 --- /dev/null +++ b/apps/sim/blocks/blocks/gusto.ts @@ -0,0 +1,1023 @@ +import { GustoIcon } from '@/components/icons' +import { getScopesForService } from '@/lib/oauth/utils' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode, IntegrationType } from '@/blocks/types' +import type { GustoResponse } from '@/tools/gusto/types' + +const COMPANY_ID_OPS = [ + 'gusto_get_company', + 'gusto_list_employees', + 'gusto_create_employee', + 'gusto_list_contractors', + 'gusto_create_contractor', + 'gusto_list_payrolls', + 'gusto_get_payroll', + 'gusto_create_off_cycle_payroll', + 'gusto_calculate_payroll', + 'gusto_submit_payroll', + 'gusto_cancel_payroll', + 'gusto_list_contractor_payments', + 'gusto_get_contractor_payment', + 'gusto_create_contractor_payment', + 'gusto_list_pay_schedules', + 'gusto_list_locations', + 'gusto_list_departments', + 'gusto_create_department', + 'gusto_list_company_benefits', +] as const + +const EMPLOYEE_ID_OPS = [ + 'gusto_get_employee', + 'gusto_update_employee', + 'gusto_terminate_employee', + 'gusto_rehire_employee', + 'gusto_list_employee_jobs', + 'gusto_list_pay_stubs', + 'gusto_get_employee_onboarding_status', + 'gusto_list_employee_benefits', + 'gusto_list_employee_forms', + 'gusto_list_employee_time_off_activities', +] as const + +const CONTRACTOR_ID_OPS = [ + 'gusto_get_contractor', + 'gusto_update_contractor', + 'gusto_list_contractor_forms', +] as const + +const PAYROLL_ID_OPS = [ + 'gusto_get_payroll', + 'gusto_calculate_payroll', + 'gusto_submit_payroll', + 'gusto_cancel_payroll', +] as const + +const PERSON_NAME_OPS = ['gusto_create_employee', 'gusto_create_contractor'] as const +const VERSION_OPS = ['gusto_update_employee', 'gusto_update_contractor'] as const +const EFFECTIVE_DATE_OPS = ['gusto_terminate_employee', 'gusto_rehire_employee'] as const +const START_END_DATE_OPS = [ + 'gusto_list_payrolls', + 'gusto_list_contractor_payments', + 'gusto_create_off_cycle_payroll', +] as const + +const START_DATE_OPS = [...START_END_DATE_OPS, 'gusto_create_contractor'] as const + +export const GustoBlock: BlockConfig = { + type: 'gusto', + name: 'Gusto', + description: 'Manage employees, contractors, and payroll in Gusto', + longDescription: + 'Run payroll end-to-end, manage employees and contractors (create, update, terminate, rehire), pay contractors, view time off activities and benefits, and access pay stubs, forms, and onboarding status.', + docsLink: 'https://docs.sim.ai/tools/gusto', + category: 'tools', + authMode: AuthMode.OAuth, + integrationType: IntegrationType.HR, + tags: ['hiring'], + icon: GustoIcon, + bgColor: '#F45D48', + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Get Company', id: 'gusto_get_company' }, + { label: 'List Employees', id: 'gusto_list_employees' }, + { label: 'Get Employee', id: 'gusto_get_employee' }, + { label: 'Create Employee', id: 'gusto_create_employee' }, + { label: 'Update Employee', id: 'gusto_update_employee' }, + { label: 'Terminate Employee', id: 'gusto_terminate_employee' }, + { label: 'Rehire Employee', id: 'gusto_rehire_employee' }, + { label: 'List Employee Jobs', id: 'gusto_list_employee_jobs' }, + { label: 'List Pay Stubs', id: 'gusto_list_pay_stubs' }, + { label: 'Get Employee Onboarding Status', id: 'gusto_get_employee_onboarding_status' }, + { label: 'List Contractors', id: 'gusto_list_contractors' }, + { label: 'Get Contractor', id: 'gusto_get_contractor' }, + { label: 'Create Contractor', id: 'gusto_create_contractor' }, + { label: 'Update Contractor', id: 'gusto_update_contractor' }, + { label: 'List Payrolls', id: 'gusto_list_payrolls' }, + { label: 'Get Payroll', id: 'gusto_get_payroll' }, + { label: 'Create Off-Cycle Payroll', id: 'gusto_create_off_cycle_payroll' }, + { label: 'Calculate Payroll', id: 'gusto_calculate_payroll' }, + { label: 'Submit Payroll', id: 'gusto_submit_payroll' }, + { label: 'Cancel Payroll', id: 'gusto_cancel_payroll' }, + { label: 'List Contractor Payments', id: 'gusto_list_contractor_payments' }, + { label: 'Get Contractor Payment', id: 'gusto_get_contractor_payment' }, + { label: 'Create Contractor Payment', id: 'gusto_create_contractor_payment' }, + { + label: 'List Employee Time Off Activities', + id: 'gusto_list_employee_time_off_activities', + }, + { label: 'List Pay Schedules', id: 'gusto_list_pay_schedules' }, + { label: 'List Locations', id: 'gusto_list_locations' }, + { label: 'List Departments', id: 'gusto_list_departments' }, + { label: 'Create Department', id: 'gusto_create_department' }, + { label: 'List Company Benefits', id: 'gusto_list_company_benefits' }, + { label: 'List Employee Benefits', id: 'gusto_list_employee_benefits' }, + { label: 'List Employee Forms', id: 'gusto_list_employee_forms' }, + { label: 'List Contractor Forms', id: 'gusto_list_contractor_forms' }, + ], + value: () => 'gusto_list_employees', + }, + { + id: 'credential', + title: 'Gusto Account', + type: 'oauth-input', + canonicalParamId: 'oauthCredential', + mode: 'basic', + serviceId: 'gusto', + requiredScopes: getScopesForService('gusto'), + placeholder: 'Select Gusto account', + required: true, + }, + { + id: 'manualCredential', + title: 'Gusto Account', + type: 'short-input', + canonicalParamId: 'oauthCredential', + mode: 'advanced', + placeholder: 'Enter credential ID', + required: true, + }, + { + id: 'companyId', + title: 'Company ID', + type: 'short-input', + placeholder: 'Enter Gusto company UUID', + required: { field: 'operation', value: [...COMPANY_ID_OPS] }, + condition: { field: 'operation', value: [...COMPANY_ID_OPS] }, + }, + { + id: 'employeeId', + title: 'Employee ID', + type: 'short-input', + placeholder: 'Enter Gusto employee UUID', + required: { field: 'operation', value: [...EMPLOYEE_ID_OPS] }, + condition: { field: 'operation', value: [...EMPLOYEE_ID_OPS] }, + }, + { + id: 'contractorId', + title: 'Contractor ID', + type: 'short-input', + placeholder: 'Enter Gusto contractor UUID', + required: { field: 'operation', value: [...CONTRACTOR_ID_OPS] }, + condition: { field: 'operation', value: [...CONTRACTOR_ID_OPS] }, + }, + { + id: 'payrollId', + title: 'Payroll ID', + type: 'short-input', + placeholder: 'Enter Gusto payroll UUID', + required: { field: 'operation', value: [...PAYROLL_ID_OPS] }, + condition: { field: 'operation', value: [...PAYROLL_ID_OPS] }, + }, + { + id: 'contractorPaymentId', + title: 'Contractor Payment ID', + type: 'short-input', + placeholder: 'Enter contractor payment UUID', + required: { field: 'operation', value: 'gusto_get_contractor_payment' }, + condition: { field: 'operation', value: 'gusto_get_contractor_payment' }, + }, + { + id: 'version', + title: 'Version', + type: 'short-input', + placeholder: 'Current record version (required for updates)', + required: { field: 'operation', value: [...VERSION_OPS] }, + condition: { field: 'operation', value: [...VERSION_OPS] }, + }, + { + id: 'firstName', + title: 'First Name', + type: 'short-input', + placeholder: 'First name', + required: { field: 'operation', value: 'gusto_create_employee' }, + condition: { + field: 'operation', + value: [...PERSON_NAME_OPS, 'gusto_update_employee', 'gusto_update_contractor'], + }, + }, + { + id: 'lastName', + title: 'Last Name', + type: 'short-input', + placeholder: 'Last name', + required: { field: 'operation', value: 'gusto_create_employee' }, + condition: { + field: 'operation', + value: [...PERSON_NAME_OPS, 'gusto_update_employee', 'gusto_update_contractor'], + }, + }, + { + id: 'email', + title: 'Personal Email', + type: 'short-input', + placeholder: 'Personal email address', + condition: { + field: 'operation', + value: [...PERSON_NAME_OPS, 'gusto_update_employee', 'gusto_update_contractor'], + }, + }, + { + id: 'middleInitial', + title: 'Middle Initial', + type: 'short-input', + placeholder: 'Middle initial', + mode: 'advanced', + condition: { + field: 'operation', + value: [...PERSON_NAME_OPS, 'gusto_update_employee', 'gusto_update_contractor'], + }, + }, + { + id: 'dateOfBirth', + title: 'Date of Birth', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + mode: 'advanced', + condition: { field: 'operation', value: ['gusto_create_employee', 'gusto_update_employee'] }, + }, + { + id: 'ssn', + title: 'SSN', + type: 'short-input', + placeholder: 'Social security number (digits only)', + password: true, + mode: 'advanced', + condition: { field: 'operation', value: ['gusto_create_employee', 'gusto_update_employee'] }, + }, + { + id: 'preferredFirstName', + title: 'Preferred First Name', + type: 'short-input', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_update_employee' }, + }, + { + id: 'twoPercentShareholder', + title: '2% Shareholder', + type: 'switch', + mode: 'advanced', + condition: { + field: 'operation', + value: ['gusto_update_employee', 'gusto_rehire_employee'], + }, + }, + { + id: 'selfOnboarding', + title: 'Self Onboarding', + type: 'switch', + mode: 'advanced', + condition: { field: 'operation', value: [...PERSON_NAME_OPS] }, + }, + { + id: 'type', + title: 'Contractor Type', + type: 'dropdown', + options: [ + { label: 'Individual', id: 'Individual' }, + { label: 'Business', id: 'Business' }, + ], + value: () => 'Individual', + required: { field: 'operation', value: 'gusto_create_contractor' }, + condition: { field: 'operation', value: 'gusto_create_contractor' }, + }, + { + id: 'wageType', + title: 'Wage Type', + type: 'dropdown', + options: [ + { label: 'Fixed', id: 'Fixed' }, + { label: 'Hourly', id: 'Hourly' }, + ], + value: () => 'Fixed', + required: { field: 'operation', value: 'gusto_create_contractor' }, + condition: { + field: 'operation', + value: ['gusto_create_contractor', 'gusto_update_contractor'], + }, + }, + { + id: 'businessName', + title: 'Business Name', + type: 'short-input', + placeholder: 'Business name (Business contractors only)', + condition: { + field: 'operation', + value: ['gusto_create_contractor', 'gusto_update_contractor'], + }, + }, + { + id: 'ein', + title: 'EIN', + type: 'short-input', + placeholder: 'Employer Identification Number', + mode: 'advanced', + condition: { + field: 'operation', + value: ['gusto_create_contractor', 'gusto_update_contractor'], + }, + }, + { + id: 'hourlyRate', + title: 'Hourly Rate', + type: 'short-input', + placeholder: 'e.g. 25.00', + condition: { + field: 'operation', + value: ['gusto_create_contractor', 'gusto_update_contractor'], + }, + }, + { + id: 'effectiveDate', + title: 'Effective Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + required: { field: 'operation', value: [...EFFECTIVE_DATE_OPS] }, + condition: { field: 'operation', value: [...EFFECTIVE_DATE_OPS] }, + }, + { + id: 'runTerminationPayroll', + title: 'Run Termination Payroll', + type: 'switch', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_terminate_employee' }, + }, + { + id: 'fileNewHireReport', + title: 'File New Hire Report', + type: 'switch', + required: { field: 'operation', value: 'gusto_rehire_employee' }, + condition: { field: 'operation', value: 'gusto_rehire_employee' }, + }, + { + id: 'workLocationUuid', + title: 'Work Location UUID', + type: 'short-input', + placeholder: 'UUID of the rehired employee work location', + required: { field: 'operation', value: 'gusto_rehire_employee' }, + condition: { field: 'operation', value: 'gusto_rehire_employee' }, + }, + { + id: 'employmentStatus', + title: 'Employment Status', + type: 'dropdown', + options: [ + { label: 'Full Time', id: 'full_time' }, + { label: 'Part Time', id: 'part_time' }, + { label: 'Part Time Eligible', id: 'part_time_eligible' }, + { label: 'Variable', id: 'variable' }, + { label: 'Seasonal', id: 'seasonal' }, + { label: 'Not Set', id: 'not_set' }, + ], + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_rehire_employee' }, + }, + { + id: 'checkDate', + title: 'Check Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + required: { field: 'operation', value: 'gusto_create_off_cycle_payroll' }, + condition: { field: 'operation', value: 'gusto_create_off_cycle_payroll' }, + }, + { + id: 'offCycleReason', + title: 'Off-Cycle Reason', + type: 'dropdown', + options: [ + { label: 'Benefit reversal', id: 'Benefit reversal' }, + { label: 'Bonus', id: 'Bonus' }, + { label: 'Correction', id: 'Correction' }, + { label: 'Disability insurance distribution', id: 'Disability insurance distribution' }, + { label: 'Dismissed employee', id: 'Dismissed employee' }, + { label: 'Hired employee', id: 'Hired employee' }, + { label: 'Reversal', id: 'Reversal' }, + { label: 'Tax reconciliation', id: 'Tax reconciliation' }, + { label: 'Transition from old pay schedule', id: 'Transition from old pay schedule' }, + { label: 'Wage correction', id: 'Wage correction' }, + ], + required: { field: 'operation', value: 'gusto_create_off_cycle_payroll' }, + condition: { field: 'operation', value: 'gusto_create_off_cycle_payroll' }, + }, + { + id: 'payScheduleUuid', + title: 'Pay Schedule UUID', + type: 'short-input', + placeholder: 'Pay schedule to associate (optional)', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_create_off_cycle_payroll' }, + }, + { + id: 'fixedWithholdingRate', + title: 'Fixed Withholding Rate', + type: 'switch', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_create_off_cycle_payroll' }, + }, + { + id: 'employeeUuids', + title: 'Employee UUIDs', + type: 'short-input', + placeholder: 'Comma-separated employee UUIDs', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_create_off_cycle_payroll' }, + }, + { + id: 'withholdingPayPeriod', + title: 'Withholding Pay Period', + type: 'short-input', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_create_off_cycle_payroll' }, + }, + { + id: 'skipRegularDeductions', + title: 'Skip Regular Deductions', + type: 'switch', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_create_off_cycle_payroll' }, + }, + { + id: 'contractorUuid', + title: 'Contractor UUID', + type: 'short-input', + placeholder: 'Contractor UUID to pay', + required: { field: 'operation', value: 'gusto_create_contractor_payment' }, + condition: { field: 'operation', value: 'gusto_create_contractor_payment' }, + }, + { + id: 'date', + title: 'Payment Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + required: { field: 'operation', value: 'gusto_create_contractor_payment' }, + condition: { field: 'operation', value: 'gusto_create_contractor_payment' }, + }, + { + id: 'wage', + title: 'Wage', + type: 'short-input', + placeholder: 'Fixed wage amount', + condition: { field: 'operation', value: 'gusto_create_contractor_payment' }, + }, + { + id: 'hours', + title: 'Hours', + type: 'short-input', + placeholder: 'Hours worked', + condition: { field: 'operation', value: 'gusto_create_contractor_payment' }, + }, + { + id: 'bonus', + title: 'Bonus', + type: 'short-input', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_create_contractor_payment' }, + }, + { + id: 'reimbursement', + title: 'Reimbursement', + type: 'short-input', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_create_contractor_payment' }, + }, + { + id: 'paymentMethod', + title: 'Payment Method', + type: 'dropdown', + options: [ + { label: 'Direct Deposit', id: 'Direct Deposit' }, + { label: 'Check', id: 'Check' }, + ], + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_create_contractor_payment' }, + }, + { + id: 'timeOffType', + title: 'Time Off Type', + type: 'dropdown', + options: [ + { label: 'Vacation', id: 'vacation' }, + { label: 'Sick', id: 'sick' }, + ], + required: { field: 'operation', value: 'gusto_list_employee_time_off_activities' }, + condition: { field: 'operation', value: 'gusto_list_employee_time_off_activities' }, + }, + { + id: 'title', + title: 'Department Title', + type: 'short-input', + placeholder: 'Department title', + required: { field: 'operation', value: 'gusto_create_department' }, + condition: { field: 'operation', value: 'gusto_create_department' }, + }, + { + id: 'include', + title: 'Include', + type: 'short-input', + placeholder: 'taxes,benefits,deductions', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_get_payroll' }, + }, + { + id: 'terminated', + title: 'Terminated Only', + type: 'switch', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_list_employees' }, + }, + { + id: 'searchTerm', + title: 'Search Term', + type: 'short-input', + placeholder: 'Filter contractors by search term', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_list_contractors' }, + }, + { + id: 'page', + title: 'Page', + type: 'short-input', + placeholder: 'Page number', + mode: 'advanced', + condition: { + field: 'operation', + value: [ + 'gusto_list_employees', + 'gusto_list_pay_stubs', + 'gusto_list_contractors', + 'gusto_list_contractor_payments', + ], + }, + }, + { + id: 'per', + title: 'Per Page', + type: 'short-input', + placeholder: 'Items per page', + mode: 'advanced', + condition: { + field: 'operation', + value: [ + 'gusto_list_employees', + 'gusto_list_pay_stubs', + 'gusto_list_contractors', + 'gusto_list_contractor_payments', + ], + }, + }, + { + id: 'startDate', + title: 'Start Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + required: { + field: 'operation', + value: [ + 'gusto_create_off_cycle_payroll', + 'gusto_list_contractor_payments', + 'gusto_create_contractor', + ], + }, + condition: { field: 'operation', value: [...START_DATE_OPS] }, + }, + { + id: 'endDate', + title: 'End Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + required: { + field: 'operation', + value: ['gusto_create_off_cycle_payroll', 'gusto_list_contractor_payments'], + }, + condition: { field: 'operation', value: [...START_END_DATE_OPS] }, + }, + { + id: 'processingStatuses', + title: 'Processing Statuses', + type: 'short-input', + placeholder: 'processed,unprocessed', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_list_payrolls' }, + }, + { + id: 'payrollTypes', + title: 'Payroll Types', + type: 'short-input', + placeholder: 'regular,off_cycle', + mode: 'advanced', + condition: { field: 'operation', value: 'gusto_list_payrolls' }, + }, + ], + tools: { + access: [ + 'gusto_get_company', + 'gusto_list_employees', + 'gusto_get_employee', + 'gusto_create_employee', + 'gusto_update_employee', + 'gusto_terminate_employee', + 'gusto_rehire_employee', + 'gusto_list_employee_jobs', + 'gusto_list_pay_stubs', + 'gusto_get_employee_onboarding_status', + 'gusto_list_contractors', + 'gusto_get_contractor', + 'gusto_create_contractor', + 'gusto_update_contractor', + 'gusto_list_payrolls', + 'gusto_get_payroll', + 'gusto_create_off_cycle_payroll', + 'gusto_calculate_payroll', + 'gusto_submit_payroll', + 'gusto_cancel_payroll', + 'gusto_list_contractor_payments', + 'gusto_get_contractor_payment', + 'gusto_create_contractor_payment', + 'gusto_list_employee_time_off_activities', + 'gusto_list_pay_schedules', + 'gusto_list_locations', + 'gusto_list_departments', + 'gusto_create_department', + 'gusto_list_company_benefits', + 'gusto_list_employee_benefits', + 'gusto_list_employee_forms', + 'gusto_list_contractor_forms', + ], + config: { + tool: (params) => params.operation || 'gusto_list_employees', + params: (params) => { + const base: Record = { oauthCredential: params.oauthCredential } + const op = params.operation as string + const trimmed = (v: unknown) => + typeof v === 'string' ? v.trim() || undefined : (v as undefined) + + switch (op) { + case 'gusto_get_company': + case 'gusto_list_pay_schedules': + case 'gusto_list_locations': + case 'gusto_list_departments': + case 'gusto_list_company_benefits': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + return { ...base, companyId: params.companyId.trim() } + + case 'gusto_list_employees': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + return { + ...base, + companyId: params.companyId.trim(), + terminated: params.terminated, + page: params.page ? Number(params.page) : undefined, + per: params.per ? Number(params.per) : undefined, + } + + case 'gusto_get_employee': + case 'gusto_list_employee_jobs': + case 'gusto_get_employee_onboarding_status': + case 'gusto_list_employee_benefits': + case 'gusto_list_employee_forms': + if (!params.employeeId?.trim()) throw new Error('Employee ID is required.') + return { ...base, employeeId: params.employeeId.trim() } + + case 'gusto_create_employee': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + if (!params.firstName?.trim() || !params.lastName?.trim()) { + throw new Error('First name and last name are required.') + } + return { + ...base, + companyId: params.companyId.trim(), + firstName: params.firstName.trim(), + lastName: params.lastName.trim(), + email: trimmed(params.email), + middleInitial: trimmed(params.middleInitial), + dateOfBirth: trimmed(params.dateOfBirth), + ssn: trimmed(params.ssn), + selfOnboarding: params.selfOnboarding, + } + + case 'gusto_update_employee': + if (!params.employeeId?.trim()) throw new Error('Employee ID is required.') + if (!params.version?.trim()) throw new Error('Version is required.') + return { + ...base, + employeeId: params.employeeId.trim(), + version: params.version.trim(), + firstName: trimmed(params.firstName), + lastName: trimmed(params.lastName), + middleInitial: trimmed(params.middleInitial), + email: trimmed(params.email), + dateOfBirth: trimmed(params.dateOfBirth), + ssn: trimmed(params.ssn), + preferredFirstName: trimmed(params.preferredFirstName), + twoPercentShareholder: params.twoPercentShareholder, + } + + case 'gusto_terminate_employee': + if (!params.employeeId?.trim()) throw new Error('Employee ID is required.') + if (!params.effectiveDate?.trim()) throw new Error('Effective date is required.') + return { + ...base, + employeeId: params.employeeId.trim(), + effectiveDate: params.effectiveDate.trim(), + runTerminationPayroll: params.runTerminationPayroll, + } + + case 'gusto_rehire_employee': + if (!params.employeeId?.trim()) throw new Error('Employee ID is required.') + if (!params.effectiveDate?.trim()) throw new Error('Effective date is required.') + if (!params.workLocationUuid?.trim()) { + throw new Error('Work location UUID is required.') + } + if (params.fileNewHireReport === undefined || params.fileNewHireReport === null) { + throw new Error('File new hire report is required.') + } + return { + ...base, + employeeId: params.employeeId.trim(), + effectiveDate: params.effectiveDate.trim(), + fileNewHireReport: params.fileNewHireReport, + workLocationUuid: params.workLocationUuid.trim(), + employmentStatus: trimmed(params.employmentStatus), + twoPercentShareholder: params.twoPercentShareholder, + } + + case 'gusto_list_pay_stubs': + if (!params.employeeId?.trim()) throw new Error('Employee ID is required.') + return { + ...base, + employeeId: params.employeeId.trim(), + page: params.page ? Number(params.page) : undefined, + per: params.per ? Number(params.per) : undefined, + } + + case 'gusto_list_contractors': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + return { + ...base, + companyId: params.companyId.trim(), + searchTerm: trimmed(params.searchTerm), + page: params.page ? Number(params.page) : undefined, + per: params.per ? Number(params.per) : undefined, + } + + case 'gusto_get_contractor': + case 'gusto_list_contractor_forms': + if (!params.contractorId?.trim()) throw new Error('Contractor ID is required.') + return { ...base, contractorId: params.contractorId.trim() } + + case 'gusto_create_contractor': { + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + if (!params.type) throw new Error('Contractor type is required.') + if (!params.wageType) throw new Error('Wage type is required.') + if (!params.startDate?.trim()) throw new Error('Start date is required.') + return { + ...base, + companyId: params.companyId.trim(), + type: params.type, + wageType: params.wageType, + startDate: params.startDate.trim(), + firstName: trimmed(params.firstName), + lastName: trimmed(params.lastName), + middleInitial: trimmed(params.middleInitial), + businessName: trimmed(params.businessName), + email: trimmed(params.email), + selfOnboarding: params.selfOnboarding, + ein: trimmed(params.ein), + hourlyRate: trimmed(params.hourlyRate), + } + } + + case 'gusto_update_contractor': + if (!params.contractorId?.trim()) throw new Error('Contractor ID is required.') + if (!params.version?.trim()) throw new Error('Version is required.') + return { + ...base, + contractorId: params.contractorId.trim(), + version: params.version.trim(), + firstName: trimmed(params.firstName), + lastName: trimmed(params.lastName), + middleInitial: trimmed(params.middleInitial), + businessName: trimmed(params.businessName), + email: trimmed(params.email), + startDate: trimmed(params.startDate), + hourlyRate: trimmed(params.hourlyRate), + wageType: trimmed(params.wageType), + ein: trimmed(params.ein), + } + + case 'gusto_list_payrolls': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + return { + ...base, + companyId: params.companyId.trim(), + startDate: trimmed(params.startDate), + endDate: trimmed(params.endDate), + processingStatuses: trimmed(params.processingStatuses), + payrollTypes: trimmed(params.payrollTypes), + } + + case 'gusto_get_payroll': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + if (!params.payrollId?.trim()) throw new Error('Payroll ID is required.') + return { + ...base, + companyId: params.companyId.trim(), + payrollId: params.payrollId.trim(), + include: trimmed(params.include), + } + + case 'gusto_create_off_cycle_payroll': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + if (!params.startDate?.trim() || !params.endDate?.trim()) { + throw new Error('Start date and end date are required.') + } + if (!params.offCycleReason?.trim()) { + throw new Error('Off-cycle reason is required.') + } + return { + ...base, + companyId: params.companyId.trim(), + startDate: params.startDate.trim(), + endDate: params.endDate.trim(), + checkDate: trimmed(params.checkDate), + offCycleReason: params.offCycleReason.trim(), + payScheduleUuid: trimmed(params.payScheduleUuid), + fixedWithholdingRate: params.fixedWithholdingRate, + employeeUuids: trimmed(params.employeeUuids), + withholdingPayPeriod: trimmed(params.withholdingPayPeriod), + skipRegularDeductions: params.skipRegularDeductions, + } + + case 'gusto_calculate_payroll': + case 'gusto_submit_payroll': + case 'gusto_cancel_payroll': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + if (!params.payrollId?.trim()) throw new Error('Payroll ID is required.') + return { + ...base, + companyId: params.companyId.trim(), + payrollId: params.payrollId.trim(), + } + + case 'gusto_list_contractor_payments': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + if (!params.startDate?.trim()) throw new Error('Start date is required.') + if (!params.endDate?.trim()) throw new Error('End date is required.') + return { + ...base, + companyId: params.companyId.trim(), + startDate: params.startDate.trim(), + endDate: params.endDate.trim(), + page: params.page ? Number(params.page) : undefined, + per: params.per ? Number(params.per) : undefined, + } + + case 'gusto_get_contractor_payment': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + if (!params.contractorPaymentId?.trim()) { + throw new Error('Contractor payment ID is required.') + } + return { + ...base, + companyId: params.companyId.trim(), + contractorPaymentId: params.contractorPaymentId.trim(), + } + + case 'gusto_create_contractor_payment': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + if (!params.contractorUuid?.trim()) throw new Error('Contractor UUID is required.') + if (!params.date?.trim()) throw new Error('Date is required.') + return { + ...base, + companyId: params.companyId.trim(), + contractorUuid: params.contractorUuid.trim(), + date: params.date.trim(), + wage: + params.wage !== undefined && params.wage !== '' ? Number(params.wage) : undefined, + hours: + params.hours !== undefined && params.hours !== '' + ? Number(params.hours) + : undefined, + bonus: + params.bonus !== undefined && params.bonus !== '' + ? Number(params.bonus) + : undefined, + reimbursement: + params.reimbursement !== undefined && params.reimbursement !== '' + ? Number(params.reimbursement) + : undefined, + paymentMethod: trimmed(params.paymentMethod), + } + + case 'gusto_list_employee_time_off_activities': + if (!params.employeeId?.trim()) throw new Error('Employee ID is required.') + if (!params.timeOffType?.trim()) throw new Error('Time off type is required.') + return { + ...base, + employeeId: params.employeeId.trim(), + timeOffType: params.timeOffType.trim(), + } + + case 'gusto_create_department': + if (!params.companyId?.trim()) throw new Error('Company ID is required.') + if (!params.title?.trim()) throw new Error('Department title is required.') + return { + ...base, + companyId: params.companyId.trim(), + title: params.title.trim(), + } + + default: + return base + } + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + oauthCredential: { type: 'string', description: 'Gusto OAuth credential' }, + companyId: { type: 'string', description: 'Gusto company UUID' }, + employeeId: { type: 'string', description: 'Gusto employee UUID' }, + contractorId: { type: 'string', description: 'Gusto contractor UUID' }, + payrollId: { type: 'string', description: 'Gusto payroll UUID' }, + contractorPaymentId: { type: 'string', description: 'Contractor payment UUID' }, + version: { type: 'string', description: 'Record version (for updates)' }, + firstName: { type: 'string', description: 'First name' }, + lastName: { type: 'string', description: 'Last name' }, + email: { type: 'string', description: 'Email address' }, + middleInitial: { type: 'string', description: 'Middle initial' }, + dateOfBirth: { type: 'string', description: 'Date of birth (YYYY-MM-DD)' }, + ssn: { type: 'string', description: 'Social security number' }, + preferredFirstName: { type: 'string', description: 'Preferred first name' }, + twoPercentShareholder: { + type: 'boolean', + description: 'Whether the employee is a 2% shareholder', + }, + selfOnboarding: { type: 'boolean', description: 'Send self-onboarding invite' }, + type: { type: 'string', description: 'Contractor type (Individual or Business)' }, + wageType: { type: 'string', description: 'Wage type (Fixed or Hourly)' }, + businessName: { type: 'string', description: 'Business name' }, + ein: { type: 'string', description: 'Employer Identification Number' }, + hourlyRate: { type: 'string', description: 'Hourly rate' }, + effectiveDate: { type: 'string', description: 'Effective date (YYYY-MM-DD)' }, + runTerminationPayroll: { type: 'boolean', description: 'Run a termination payroll' }, + fileNewHireReport: { type: 'boolean', description: 'File a new hire report' }, + workLocationUuid: { type: 'string', description: 'Work location UUID' }, + employmentStatus: { type: 'string', description: 'Employment status' }, + checkDate: { type: 'string', description: 'Check date (YYYY-MM-DD)' }, + payScheduleUuid: { type: 'string', description: 'Pay schedule UUID for off-cycle payroll' }, + fixedWithholdingRate: { + type: 'boolean', + description: 'Use fixed supplemental withholding rate', + }, + offCycleReason: { type: 'string', description: 'Off-cycle payroll reason' }, + employeeUuids: { type: 'string', description: 'Comma-separated employee UUIDs' }, + withholdingPayPeriod: { type: 'string', description: 'Withholding pay period override' }, + skipRegularDeductions: { type: 'boolean', description: 'Skip regular deductions' }, + contractorUuid: { type: 'string', description: 'Contractor UUID' }, + date: { type: 'string', description: 'Payment date (YYYY-MM-DD)' }, + wage: { type: 'number', description: 'Wage amount' }, + hours: { type: 'number', description: 'Hours worked' }, + bonus: { type: 'number', description: 'Bonus amount' }, + reimbursement: { type: 'number', description: 'Reimbursement amount' }, + paymentMethod: { type: 'string', description: 'Payment method' }, + timeOffType: { type: 'string', description: 'Time off type (e.g. vacation or sick)' }, + title: { type: 'string', description: 'Department title' }, + include: { type: 'string', description: 'Comma-separated include fields for payroll detail' }, + terminated: { type: 'boolean', description: 'Filter for terminated employees' }, + searchTerm: { type: 'string', description: 'Search term for contractors' }, + page: { type: 'number', description: 'Pagination page number' }, + per: { type: 'number', description: 'Items per page' }, + startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)' }, + endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' }, + processingStatuses: { type: 'string', description: 'Comma-separated processing statuses' }, + payrollTypes: { type: 'string', description: 'Comma-separated payroll types' }, + }, + outputs: { + company: { type: 'json', description: 'Gusto company (uuid, name, ein, entity_type, ...)' }, + employee: { type: 'json', description: 'Gusto employee record' }, + employees: { type: 'json', description: 'List of employees' }, + contractor: { type: 'json', description: 'Gusto contractor record' }, + contractors: { type: 'json', description: 'List of contractors' }, + payroll: { type: 'json', description: 'Gusto payroll' }, + payrolls: { type: 'json', description: 'List of payrolls' }, + contractorPayment: { type: 'json', description: 'Contractor payment' }, + contractorPayments: { type: 'json', description: 'List of contractor payments' }, + timeOffActivities: { type: 'json', description: 'Time off activities' }, + paySchedules: { type: 'json', description: 'List of pay schedules' }, + locations: { type: 'json', description: 'List of locations' }, + department: { type: 'json', description: 'Department record' }, + departments: { type: 'json', description: 'List of departments' }, + companyBenefits: { type: 'json', description: 'Company benefits' }, + employeeBenefits: { type: 'json', description: 'Employee benefits' }, + forms: { type: 'json', description: 'Forms (W-2, 1099, etc.)' }, + jobs: { type: 'json', description: 'Employee jobs' }, + payStubs: { type: 'json', description: 'Pay stubs' }, + onboardingStatus: { type: 'json', description: 'Employee onboarding status' }, + termination: { type: 'json', description: 'Termination record' }, + rehire: { type: 'json', description: 'Rehire record' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index e7ca943af3c..97885fa069c 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -88,6 +88,7 @@ import { GranolaBlock } from '@/blocks/blocks/granola' import { GreenhouseBlock } from '@/blocks/blocks/greenhouse' import { GreptileBlock } from '@/blocks/blocks/greptile' import { GuardrailsBlock } from '@/blocks/blocks/guardrails' +import { GustoBlock } from '@/blocks/blocks/gusto' import { HexBlock } from '@/blocks/blocks/hex' import { HubSpotBlock } from '@/blocks/blocks/hubspot' import { HuggingFaceBlock } from '@/blocks/blocks/huggingface' @@ -333,6 +334,7 @@ export const registry: Record = { greenhouse: GreenhouseBlock, greptile: GreptileBlock, guardrails: GuardrailsBlock, + gusto: GustoBlock, hex: HexBlock, hubspot: HubSpotBlock, huggingface: HuggingFaceBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index dae53828ccb..6399bdf2192 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -2279,6 +2279,18 @@ export function JiraIcon(props: SVGProps) { ) } +export function GustoIcon(props: React.SVGProps) { + return ( + + + + + + + + ) +} + export function LinearIcon(props: React.SVGProps) { return ( { + try { + const response = await fetch('https://api.gusto.com/v1/me', { + headers: { + Authorization: `Bearer ${tokens.accessToken}`, + Accept: 'application/json', + 'X-Gusto-API-Version': '2026-02-01', + }, + }) + + if (!response.ok) { + const errorText = await response.text() + logger.error('Gusto API error:', { + status: response.status, + statusText: response.statusText, + body: errorText, + }) + throw new Error(`Gusto API error: ${response.status} ${response.statusText}`) + } + + const data = await response.json() + const email = data.email || data.user?.email || '' + const firstName = data.first_name || data.user?.first_name || '' + const lastName = data.last_name || data.user?.last_name || '' + const userId = data.uuid || data.user?.uuid || data.id?.toString() || email + + return { + id: `${userId}-${generateId()}`, + email, + name: [firstName, lastName].filter(Boolean).join(' ') || email, + emailVerified: true, + createdAt: new Date(), + updatedAt: new Date(), + } + } catch (error) { + logger.error('Error in Gusto getUserInfo:', error) + throw error + } + }, + }, + { providerId: 'attio', clientId: env.ATTIO_CLIENT_ID as string, diff --git a/apps/sim/lib/core/config/env.ts b/apps/sim/lib/core/config/env.ts index 0acd2c4e9e3..f4f0f7418dd 100644 --- a/apps/sim/lib/core/config/env.ts +++ b/apps/sim/lib/core/config/env.ts @@ -293,6 +293,8 @@ export const env = createEnv({ PIPEDRIVE_CLIENT_SECRET: z.string().optional(), // Pipedrive OAuth client secret LINEAR_CLIENT_ID: z.string().optional(), // Linear OAuth client ID LINEAR_CLIENT_SECRET: z.string().optional(), // Linear OAuth client secret + GUSTO_CLIENT_ID: z.string().optional(), // Gusto OAuth client ID + GUSTO_CLIENT_SECRET: z.string().optional(), // Gusto OAuth client secret BOX_CLIENT_ID: z.string().optional(), // Box OAuth client ID BOX_CLIENT_SECRET: z.string().optional(), // Box OAuth client secret DROPBOX_CLIENT_ID: z.string().optional(), // Dropbox OAuth client ID diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index 9f12b4c3f60..d142bffeafc 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -23,6 +23,7 @@ import { GoogleMeetIcon, GoogleSheetsIcon, GoogleTasksIcon, + GustoIcon, HubspotIcon, JiraIcon, LinearIcon, @@ -900,6 +901,21 @@ export const OAUTH_PROVIDERS: Record = { }, defaultService: 'pipedrive', }, + gusto: { + name: 'Gusto', + icon: GustoIcon, + services: { + gusto: { + name: 'Gusto', + description: 'Manage employees, contractors, and payroll in Gusto.', + providerId: 'gusto', + icon: GustoIcon, + baseProviderIcon: GustoIcon, + scopes: [], + }, + }, + defaultService: 'gusto', + }, hubspot: { name: 'HubSpot', icon: HubspotIcon, @@ -1193,6 +1209,19 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig { supportsRefreshTokenRotation: true, } } + case 'gusto': { + const { clientId, clientSecret } = getCredentials( + env.GUSTO_CLIENT_ID, + env.GUSTO_CLIENT_SECRET + ) + return { + tokenEndpoint: 'https://api.gusto.com/oauth/token', + clientId, + clientSecret, + useBasicAuth: false, + supportsRefreshTokenRotation: true, + } + } case 'attio': { const { clientId, clientSecret } = getCredentials( env.ATTIO_CLIENT_ID, diff --git a/apps/sim/lib/oauth/types.ts b/apps/sim/lib/oauth/types.ts index 5c39f53440a..f3d8abccf62 100644 --- a/apps/sim/lib/oauth/types.ts +++ b/apps/sim/lib/oauth/types.ts @@ -50,6 +50,7 @@ export type OAuthProvider = | 'spotify' | 'calcom' | 'docusign' + | 'gusto' export type OAuthService = | 'google' @@ -101,6 +102,7 @@ export type OAuthService = | 'calcom' | 'docusign' | 'github' + | 'gusto' | 'monday' export interface OAuthProviderConfig { diff --git a/apps/sim/tools/gusto/calculate_payroll.ts b/apps/sim/tools/gusto/calculate_payroll.ts new file mode 100644 index 00000000000..140a37c8a02 --- /dev/null +++ b/apps/sim/tools/gusto/calculate_payroll.ts @@ -0,0 +1,70 @@ +import type { GustoCalculatePayrollParams, GustoPayrollRecordResponse } from '@/tools/gusto/types' +import { PAYROLL_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoCalculatePayrollTool: ToolConfig< + GustoCalculatePayrollParams, + GustoPayrollRecordResponse +> = { + id: 'gusto_calculate_payroll', + name: 'Gusto Calculate Payroll', + description: 'Calculate a Gusto payroll (preview totals before submission)', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + payrollId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto payroll UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent( + params.companyId.trim() + )}/payrolls/${encodeURIComponent(params.payrollId.trim())}/calculate`, + method: 'PUT', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + if (!response.ok) { + const data = await response.json().catch(() => ({})) + return { + success: false, + error: gustoErrorMessage(data, 'Failed to calculate payroll'), + output: {}, + } + } + if (response.status === 202 || response.status === 204) { + return { success: true, output: { payroll: { status: 'calculating' } } } + } + const data = await response.json().catch(() => ({})) + return { success: true, output: { payroll: data } } + }, + + outputs: { + payroll: { + type: 'object', + description: 'Calculated payroll', + properties: PAYROLL_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/cancel_payroll.ts b/apps/sim/tools/gusto/cancel_payroll.ts new file mode 100644 index 00000000000..ad62877388d --- /dev/null +++ b/apps/sim/tools/gusto/cancel_payroll.ts @@ -0,0 +1,70 @@ +import type { GustoCancelPayrollParams, GustoPayrollRecordResponse } from '@/tools/gusto/types' +import { PAYROLL_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoCancelPayrollTool: ToolConfig< + GustoCancelPayrollParams, + GustoPayrollRecordResponse +> = { + id: 'gusto_cancel_payroll', + name: 'Gusto Cancel Payroll', + description: 'Cancel a submitted Gusto payroll', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + payrollId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto payroll UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent( + params.companyId.trim() + )}/payrolls/${encodeURIComponent(params.payrollId.trim())}/cancel`, + method: 'PUT', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + if (!response.ok) { + const data = await response.json().catch(() => ({})) + return { + success: false, + error: gustoErrorMessage(data, 'Failed to cancel payroll'), + output: {}, + } + } + if (response.status === 202 || response.status === 204) { + return { success: true, output: { payroll: { status: 'cancelled' } } } + } + const data = await response.json().catch(() => ({})) + return { success: true, output: { payroll: data } } + }, + + outputs: { + payroll: { + type: 'object', + description: 'Cancelled payroll', + properties: PAYROLL_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/create_contractor.ts b/apps/sim/tools/gusto/create_contractor.ts new file mode 100644 index 00000000000..274147574d6 --- /dev/null +++ b/apps/sim/tools/gusto/create_contractor.ts @@ -0,0 +1,143 @@ +import type { + GustoContractorRecordResponse, + GustoCreateContractorParams, +} from '@/tools/gusto/types' +import { CONTRACTOR_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoCreateContractorTool: ToolConfig< + GustoCreateContractorParams, + GustoContractorRecordResponse +> = { + id: 'gusto_create_contractor', + name: 'Gusto Create Contractor', + description: 'Create a new contractor in a Gusto company', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + type: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Contractor type (Individual or Business)', + }, + wageType: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Wage type (Fixed or Hourly)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Contractor start date (YYYY-MM-DD)', + }, + firstName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'First name (required for Individual contractors)', + }, + lastName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Last name (required for Individual contractors)', + }, + middleInitial: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Middle initial', + }, + businessName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Business name (required for Business contractors)', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Contractor email', + }, + selfOnboarding: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Send self-onboarding invite to the contractor', + }, + ein: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Employer Identification Number (Business contractors)', + }, + hourlyRate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Hourly rate (when wage type is Hourly)', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent(params.companyId.trim())}/contractors`, + method: 'POST', + headers: (params) => gustoHeaders(params.accessToken), + body: (params) => { + const body: Record = { + type: params.type, + wage_type: params.wageType, + start_date: params.startDate, + } + if (params.firstName) body.first_name = params.firstName + if (params.lastName) body.last_name = params.lastName + if (params.middleInitial) body.middle_initial = params.middleInitial + if (params.businessName) body.business_name = params.businessName + if (params.email) body.email = params.email + if (params.selfOnboarding !== undefined) body.self_onboarding = params.selfOnboarding + if (params.ein) body.ein = params.ein + if (params.hourlyRate) body.hourly_rate = params.hourlyRate + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to create contractor'), + output: {}, + } + } + return { success: true, output: { contractor: data } } + }, + + outputs: { + contractor: { + type: 'object', + description: 'Created contractor', + properties: CONTRACTOR_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/create_contractor_payment.ts b/apps/sim/tools/gusto/create_contractor_payment.ts new file mode 100644 index 00000000000..ccc5c9129f3 --- /dev/null +++ b/apps/sim/tools/gusto/create_contractor_payment.ts @@ -0,0 +1,117 @@ +import type { + GustoContractorPaymentRecordResponse, + GustoCreateContractorPaymentParams, +} from '@/tools/gusto/types' +import { CONTRACTOR_PAYMENT_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoCreateContractorPaymentTool: ToolConfig< + GustoCreateContractorPaymentParams, + GustoContractorPaymentRecordResponse +> = { + id: 'gusto_create_contractor_payment', + name: 'Gusto Create Contractor Payment', + description: 'Pay a Gusto contractor (one-off payment)', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + contractorUuid: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto contractor UUID', + }, + date: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Payment date (YYYY-MM-DD)', + }, + wage: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Fixed wage amount (for Fixed contractors)', + }, + hours: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Hours worked (for Hourly contractors)', + }, + bonus: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Bonus amount', + }, + reimbursement: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Reimbursement amount', + }, + paymentMethod: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Payment method (Direct Deposit or Check)', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent( + params.companyId.trim() + )}/contractor_payments`, + method: 'POST', + headers: (params) => gustoHeaders(params.accessToken), + body: (params) => { + const body: Record = { + contractor_uuid: params.contractorUuid, + date: params.date, + } + if (params.wage !== undefined) body.wage = params.wage + if (params.hours !== undefined) body.hours = params.hours + if (params.bonus !== undefined) body.bonus = params.bonus + if (params.reimbursement !== undefined) body.reimbursement = params.reimbursement + if (params.paymentMethod) body.payment_method = params.paymentMethod + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to create contractor payment'), + output: {}, + } + } + return { success: true, output: { contractorPayment: data } } + }, + + outputs: { + contractorPayment: { + type: 'object', + description: 'Created contractor payment', + properties: CONTRACTOR_PAYMENT_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/create_department.ts b/apps/sim/tools/gusto/create_department.ts new file mode 100644 index 00000000000..5d66fcdb2a6 --- /dev/null +++ b/apps/sim/tools/gusto/create_department.ts @@ -0,0 +1,68 @@ +import type { + GustoCreateDepartmentParams, + GustoDepartmentRecordResponse, +} from '@/tools/gusto/types' +import { DEPARTMENT_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoCreateDepartmentTool: ToolConfig< + GustoCreateDepartmentParams, + GustoDepartmentRecordResponse +> = { + id: 'gusto_create_department', + name: 'Gusto Create Department', + description: 'Create a department in a Gusto company', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + title: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Department title', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent(params.companyId.trim())}/departments`, + method: 'POST', + headers: (params) => gustoHeaders(params.accessToken), + body: (params) => ({ title: params.title }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to create department'), + output: {}, + } + } + return { success: true, output: { department: data } } + }, + + outputs: { + department: { + type: 'object', + description: 'Created department', + properties: DEPARTMENT_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/create_employee.ts b/apps/sim/tools/gusto/create_employee.ts new file mode 100644 index 00000000000..de713f81e3d --- /dev/null +++ b/apps/sim/tools/gusto/create_employee.ts @@ -0,0 +1,118 @@ +import type { GustoCreateEmployeeParams, GustoCreateEmployeeResponse } from '@/tools/gusto/types' +import { EMPLOYEE_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoCreateEmployeeTool: ToolConfig< + GustoCreateEmployeeParams, + GustoCreateEmployeeResponse +> = { + id: 'gusto_create_employee', + name: 'Gusto Create Employee', + description: 'Create a new employee in a Gusto company', + version: '1.0.0', + + oauth: { + required: true, + provider: 'gusto', + }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + firstName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Employee first name', + }, + lastName: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Employee last name', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Employee personal email address', + }, + middleInitial: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Middle initial', + }, + dateOfBirth: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date of birth (YYYY-MM-DD)', + }, + ssn: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Social security number (digits only)', + }, + selfOnboarding: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Send self-onboarding invite to employee', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent(params.companyId.trim())}/employees`, + method: 'POST', + headers: (params) => gustoHeaders(params.accessToken), + body: (params) => { + const body: Record = { + first_name: params.firstName, + last_name: params.lastName, + } + if (params.email) body.email = params.email + if (params.middleInitial) body.middle_initial = params.middleInitial + if (params.dateOfBirth) body.date_of_birth = params.dateOfBirth + if (params.ssn) body.ssn = params.ssn + if (params.selfOnboarding !== undefined) body.self_onboarding = params.selfOnboarding + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to create employee'), + output: {}, + } + } + return { + success: true, + output: { employee: data }, + } + }, + + outputs: { + employee: { + type: 'object', + description: 'Created Gusto employee', + properties: EMPLOYEE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/create_off_cycle_payroll.ts b/apps/sim/tools/gusto/create_off_cycle_payroll.ts new file mode 100644 index 00000000000..d247dc196bf --- /dev/null +++ b/apps/sim/tools/gusto/create_off_cycle_payroll.ts @@ -0,0 +1,140 @@ +import type { + GustoCreateOffCyclePayrollParams, + GustoPayrollRecordResponse, +} from '@/tools/gusto/types' +import { PAYROLL_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoCreateOffCyclePayrollTool: ToolConfig< + GustoCreateOffCyclePayrollParams, + GustoPayrollRecordResponse +> = { + id: 'gusto_create_off_cycle_payroll', + name: 'Gusto Create Off-Cycle Payroll', + description: 'Create an off-cycle payroll for a Gusto company', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Pay period start date (YYYY-MM-DD)', + }, + endDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Pay period end date (YYYY-MM-DD)', + }, + checkDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Check date (YYYY-MM-DD). Defaults to the next available payday', + }, + payScheduleUuid: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pay schedule UUID to associate with this off-cycle payroll', + }, + fixedWithholdingRate: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Use fixed supplemental withholding rate (e.g. for bonuses)', + }, + offCycleReason: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'Off-cycle reason. One of: Benefit reversal, Bonus, Correction, Dismissed employee, Hired employee, Wage correction, Tax reconciliation, Reversal, Disability insurance distribution, Transition from old pay schedule', + }, + employeeUuids: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated employee UUIDs to include', + }, + withholdingPayPeriod: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Withholding pay period override', + }, + skipRegularDeductions: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Skip regular deductions', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent(params.companyId.trim())}/payrolls`, + method: 'POST', + headers: (params) => gustoHeaders(params.accessToken), + body: (params) => { + const body: Record = { + off_cycle: true, + start_date: params.startDate, + end_date: params.endDate, + off_cycle_reason: params.offCycleReason, + } + if (params.checkDate) body.check_date = params.checkDate + if (params.payScheduleUuid) body.pay_schedule_uuid = params.payScheduleUuid + if (params.fixedWithholdingRate !== undefined) { + body.fixed_withholding_rate = params.fixedWithholdingRate + } + if (params.employeeUuids) { + body.employee_uuids = params.employeeUuids + .split(',') + .map((s) => s.trim()) + .filter(Boolean) + } + if (params.withholdingPayPeriod) body.withholding_pay_period = params.withholdingPayPeriod + if (params.skipRegularDeductions !== undefined) { + body.skip_regular_deductions = params.skipRegularDeductions + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to create off-cycle payroll'), + output: {}, + } + } + return { success: true, output: { payroll: data } } + }, + + outputs: { + payroll: { + type: 'object', + description: 'Created off-cycle payroll', + properties: PAYROLL_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/get_company.ts b/apps/sim/tools/gusto/get_company.ts new file mode 100644 index 00000000000..237c75f3d44 --- /dev/null +++ b/apps/sim/tools/gusto/get_company.ts @@ -0,0 +1,69 @@ +import type { GustoGetCompanyParams, GustoGetCompanyResponse } from '@/tools/gusto/types' +import { COMPANY_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import type { ToolConfig } from '@/tools/types' + +export const gustoGetCompanyTool: ToolConfig = { + id: 'gusto_get_company', + name: 'Gusto Get Company', + description: 'Retrieve a Gusto company by ID', + version: '1.0.0', + + oauth: { + required: true, + provider: 'gusto', + }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => `https://api.gusto.com/v1/companies/${encodeURIComponent(params.companyId)}`, + method: 'GET', + headers: (params) => { + if (!params.accessToken) { + throw new Error('Missing access token for Gusto API request') + } + return { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-Gusto-API-Version': '2026-02-01', + Authorization: `Bearer ${params.accessToken}`, + } + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: data.error_description || data.message || 'Failed to fetch company', + output: {}, + } + } + return { + success: true, + output: { company: data }, + } + }, + + outputs: { + company: { + type: 'object', + description: 'Gusto company', + properties: COMPANY_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/get_contractor.ts b/apps/sim/tools/gusto/get_contractor.ts new file mode 100644 index 00000000000..a0906d5c324 --- /dev/null +++ b/apps/sim/tools/gusto/get_contractor.ts @@ -0,0 +1,58 @@ +import type { GustoContractorRecordResponse, GustoGetContractorParams } from '@/tools/gusto/types' +import { CONTRACTOR_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoGetContractorTool: ToolConfig< + GustoGetContractorParams, + GustoContractorRecordResponse +> = { + id: 'gusto_get_contractor', + name: 'Gusto Get Contractor', + description: 'Retrieve a Gusto contractor by ID', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + contractorId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto contractor UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/contractors/${encodeURIComponent(params.contractorId.trim())}`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to get contractor'), + output: {}, + } + } + return { success: true, output: { contractor: data } } + }, + + outputs: { + contractor: { + type: 'object', + description: 'Gusto contractor', + properties: CONTRACTOR_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/get_contractor_payment.ts b/apps/sim/tools/gusto/get_contractor_payment.ts new file mode 100644 index 00000000000..97a76a100b6 --- /dev/null +++ b/apps/sim/tools/gusto/get_contractor_payment.ts @@ -0,0 +1,69 @@ +import type { + GustoContractorPaymentRecordResponse, + GustoGetContractorPaymentParams, +} from '@/tools/gusto/types' +import { CONTRACTOR_PAYMENT_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoGetContractorPaymentTool: ToolConfig< + GustoGetContractorPaymentParams, + GustoContractorPaymentRecordResponse +> = { + id: 'gusto_get_contractor_payment', + name: 'Gusto Get Contractor Payment', + description: 'Retrieve a single Gusto contractor payment by ID', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + contractorPaymentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto contractor payment UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent( + params.companyId.trim() + )}/contractor_payments/${encodeURIComponent(params.contractorPaymentId.trim())}`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to get contractor payment'), + output: {}, + } + } + return { success: true, output: { contractorPayment: data } } + }, + + outputs: { + contractorPayment: { + type: 'object', + description: 'Contractor payment', + properties: CONTRACTOR_PAYMENT_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/get_employee.ts b/apps/sim/tools/gusto/get_employee.ts new file mode 100644 index 00000000000..70c50edfa89 --- /dev/null +++ b/apps/sim/tools/gusto/get_employee.ts @@ -0,0 +1,69 @@ +import type { GustoGetEmployeeParams, GustoGetEmployeeResponse } from '@/tools/gusto/types' +import { EMPLOYEE_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import type { ToolConfig } from '@/tools/types' + +export const gustoGetEmployeeTool: ToolConfig = { + id: 'gusto_get_employee', + name: 'Gusto Get Employee', + description: 'Retrieve a Gusto employee by ID', + version: '1.0.0', + + oauth: { + required: true, + provider: 'gusto', + }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => `https://api.gusto.com/v1/employees/${encodeURIComponent(params.employeeId)}`, + method: 'GET', + headers: (params) => { + if (!params.accessToken) { + throw new Error('Missing access token for Gusto API request') + } + return { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-Gusto-API-Version': '2026-02-01', + Authorization: `Bearer ${params.accessToken}`, + } + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: data.error_description || data.message || 'Failed to fetch employee', + output: {}, + } + } + return { + success: true, + output: { employee: data }, + } + }, + + outputs: { + employee: { + type: 'object', + description: 'Gusto employee', + properties: EMPLOYEE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/get_employee_onboarding_status.ts b/apps/sim/tools/gusto/get_employee_onboarding_status.ts new file mode 100644 index 00000000000..d85ccfe7e4f --- /dev/null +++ b/apps/sim/tools/gusto/get_employee_onboarding_status.ts @@ -0,0 +1,63 @@ +import type { + GustoGetEmployeeOnboardingStatusParams, + GustoOnboardingStatusResponse, +} from '@/tools/gusto/types' +import { ONBOARDING_STATUS_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoGetEmployeeOnboardingStatusTool: ToolConfig< + GustoGetEmployeeOnboardingStatusParams, + GustoOnboardingStatusResponse +> = { + id: 'gusto_get_employee_onboarding_status', + name: 'Gusto Get Employee Onboarding Status', + description: 'Get the onboarding status for a Gusto employee', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/employees/${encodeURIComponent( + params.employeeId.trim() + )}/onboarding_status`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to get onboarding status'), + output: {}, + } + } + return { success: true, output: { onboardingStatus: data } } + }, + + outputs: { + onboardingStatus: { + type: 'object', + description: 'Employee onboarding status', + properties: ONBOARDING_STATUS_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/get_payroll.ts b/apps/sim/tools/gusto/get_payroll.ts new file mode 100644 index 00000000000..e71b7635145 --- /dev/null +++ b/apps/sim/tools/gusto/get_payroll.ts @@ -0,0 +1,80 @@ +import type { GustoGetPayrollParams, GustoGetPayrollResponse } from '@/tools/gusto/types' +import { PAYROLL_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoGetPayrollTool: ToolConfig = { + id: 'gusto_get_payroll', + name: 'Gusto Get Payroll', + description: 'Retrieve a single payroll by ID', + version: '1.0.0', + + oauth: { + required: true, + provider: 'gusto', + }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + payrollId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto payroll UUID', + }, + include: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated extra fields to include (e.g. "taxes,benefits,deductions"). Required to retrieve calculated payroll details.', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => { + const search = new URLSearchParams() + if (params.include) search.set('include', params.include) + const qs = search.toString() + return `${GUSTO_API_BASE}/companies/${encodeURIComponent( + params.companyId.trim() + )}/payrolls/${encodeURIComponent(params.payrollId.trim())}${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to fetch payroll'), + output: {}, + } + } + return { + success: true, + output: { payroll: data }, + } + }, + + outputs: { + payroll: { + type: 'object', + description: 'Gusto payroll', + properties: PAYROLL_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/index.ts b/apps/sim/tools/gusto/index.ts new file mode 100644 index 00000000000..5ba568664ac --- /dev/null +++ b/apps/sim/tools/gusto/index.ts @@ -0,0 +1,67 @@ +import { gustoCalculatePayrollTool } from '@/tools/gusto/calculate_payroll' +import { gustoCancelPayrollTool } from '@/tools/gusto/cancel_payroll' +import { gustoCreateContractorTool } from '@/tools/gusto/create_contractor' +import { gustoCreateContractorPaymentTool } from '@/tools/gusto/create_contractor_payment' +import { gustoCreateDepartmentTool } from '@/tools/gusto/create_department' +import { gustoCreateEmployeeTool } from '@/tools/gusto/create_employee' +import { gustoCreateOffCyclePayrollTool } from '@/tools/gusto/create_off_cycle_payroll' +import { gustoGetCompanyTool } from '@/tools/gusto/get_company' +import { gustoGetContractorTool } from '@/tools/gusto/get_contractor' +import { gustoGetContractorPaymentTool } from '@/tools/gusto/get_contractor_payment' +import { gustoGetEmployeeTool } from '@/tools/gusto/get_employee' +import { gustoGetEmployeeOnboardingStatusTool } from '@/tools/gusto/get_employee_onboarding_status' +import { gustoGetPayrollTool } from '@/tools/gusto/get_payroll' +import { gustoListCompanyBenefitsTool } from '@/tools/gusto/list_company_benefits' +import { gustoListContractorFormsTool } from '@/tools/gusto/list_contractor_forms' +import { gustoListContractorPaymentsTool } from '@/tools/gusto/list_contractor_payments' +import { gustoListContractorsTool } from '@/tools/gusto/list_contractors' +import { gustoListDepartmentsTool } from '@/tools/gusto/list_departments' +import { gustoListEmployeeBenefitsTool } from '@/tools/gusto/list_employee_benefits' +import { gustoListEmployeeFormsTool } from '@/tools/gusto/list_employee_forms' +import { gustoListEmployeeJobsTool } from '@/tools/gusto/list_employee_jobs' +import { gustoListEmployeeTimeOffActivitiesTool } from '@/tools/gusto/list_employee_time_off_activities' +import { gustoListEmployeesTool } from '@/tools/gusto/list_employees' +import { gustoListLocationsTool } from '@/tools/gusto/list_locations' +import { gustoListPaySchedulesTool } from '@/tools/gusto/list_pay_schedules' +import { gustoListPayStubsTool } from '@/tools/gusto/list_pay_stubs' +import { gustoListPayrollsTool } from '@/tools/gusto/list_payrolls' +import { gustoRehireEmployeeTool } from '@/tools/gusto/rehire_employee' +import { gustoSubmitPayrollTool } from '@/tools/gusto/submit_payroll' +import { gustoTerminateEmployeeTool } from '@/tools/gusto/terminate_employee' +import { gustoUpdateContractorTool } from '@/tools/gusto/update_contractor' +import { gustoUpdateEmployeeTool } from '@/tools/gusto/update_employee' + +export { + gustoCalculatePayrollTool, + gustoCancelPayrollTool, + gustoCreateContractorTool, + gustoCreateContractorPaymentTool, + gustoCreateDepartmentTool, + gustoCreateEmployeeTool, + gustoCreateOffCyclePayrollTool, + gustoGetCompanyTool, + gustoGetContractorTool, + gustoGetContractorPaymentTool, + gustoGetEmployeeTool, + gustoGetEmployeeOnboardingStatusTool, + gustoGetPayrollTool, + gustoListCompanyBenefitsTool, + gustoListContractorFormsTool, + gustoListContractorPaymentsTool, + gustoListContractorsTool, + gustoListDepartmentsTool, + gustoListEmployeeBenefitsTool, + gustoListEmployeeFormsTool, + gustoListEmployeeJobsTool, + gustoListEmployeeTimeOffActivitiesTool, + gustoListEmployeesTool, + gustoListLocationsTool, + gustoListPaySchedulesTool, + gustoListPayStubsTool, + gustoListPayrollsTool, + gustoRehireEmployeeTool, + gustoSubmitPayrollTool, + gustoTerminateEmployeeTool, + gustoUpdateContractorTool, + gustoUpdateEmployeeTool, +} diff --git a/apps/sim/tools/gusto/list_company_benefits.ts b/apps/sim/tools/gusto/list_company_benefits.ts new file mode 100644 index 00000000000..e7929f5b026 --- /dev/null +++ b/apps/sim/tools/gusto/list_company_benefits.ts @@ -0,0 +1,68 @@ +import type { + GustoCompanyBenefitsListResponse, + GustoListCompanyBenefitsParams, +} from '@/tools/gusto/types' +import { COMPANY_BENEFIT_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListCompanyBenefitsTool: ToolConfig< + GustoListCompanyBenefitsParams, + GustoCompanyBenefitsListResponse +> = { + id: 'gusto_list_company_benefits', + name: 'Gusto List Company Benefits', + description: 'List all benefits configured for a Gusto company', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent(params.companyId.trim())}/company_benefits`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list company benefits'), + output: {}, + } + } + return { + success: true, + output: { + companyBenefits: Array.isArray(data) + ? data + : (data.company_benefits ?? data.companyBenefits ?? []), + }, + } + }, + + outputs: { + companyBenefits: { + type: 'array', + description: 'Company benefits', + items: { type: 'object', properties: COMPANY_BENEFIT_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_contractor_forms.ts b/apps/sim/tools/gusto/list_contractor_forms.ts new file mode 100644 index 00000000000..793abfcf287 --- /dev/null +++ b/apps/sim/tools/gusto/list_contractor_forms.ts @@ -0,0 +1,58 @@ +import type { GustoFormsListResponse, GustoListContractorFormsParams } from '@/tools/gusto/types' +import { FORM_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListContractorFormsTool: ToolConfig< + GustoListContractorFormsParams, + GustoFormsListResponse +> = { + id: 'gusto_list_contractor_forms', + name: 'Gusto List Contractor Forms', + description: 'List forms for a Gusto contractor (1099, etc.)', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + contractorId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto contractor UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/contractors/${encodeURIComponent(params.contractorId.trim())}/forms`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list contractor forms'), + output: {}, + } + } + return { success: true, output: { forms: Array.isArray(data) ? data : (data.forms ?? []) } } + }, + + outputs: { + forms: { + type: 'array', + description: 'Contractor forms', + items: { type: 'object', properties: FORM_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_contractor_payments.ts b/apps/sim/tools/gusto/list_contractor_payments.ts new file mode 100644 index 00000000000..520808f5784 --- /dev/null +++ b/apps/sim/tools/gusto/list_contractor_payments.ts @@ -0,0 +1,95 @@ +import type { + GustoContractorPaymentsListResponse, + GustoListContractorPaymentsParams, +} from '@/tools/gusto/types' +import { CONTRACTOR_PAYMENT_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListContractorPaymentsTool: ToolConfig< + GustoListContractorPaymentsParams, + GustoContractorPaymentsListResponse +> = { + id: 'gusto_list_contractor_payments', + name: 'Gusto List Contractor Payments', + description: 'List contractor payments for a Gusto company', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date filter (YYYY-MM-DD)', + }, + endDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'End date filter (YYYY-MM-DD)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number', + }, + per: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Items per page', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => { + const search = new URLSearchParams() + search.set('start_date', params.startDate) + search.set('end_date', params.endDate) + if (params.page !== undefined) search.set('page', String(params.page)) + if (params.per !== undefined) search.set('per', String(params.per)) + const qs = search.toString() + return `${GUSTO_API_BASE}/companies/${encodeURIComponent( + params.companyId.trim() + )}/contractor_payments${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list contractor payments'), + output: {}, + } + } + const payments = Array.isArray(data) ? data : (data.contractor_payments ?? []) + return { success: true, output: { contractorPayments: payments } } + }, + + outputs: { + contractorPayments: { + type: 'array', + description: 'Contractor payments', + items: { type: 'object', properties: CONTRACTOR_PAYMENT_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_contractors.ts b/apps/sim/tools/gusto/list_contractors.ts new file mode 100644 index 00000000000..bc03460c7d4 --- /dev/null +++ b/apps/sim/tools/gusto/list_contractors.ts @@ -0,0 +1,93 @@ +import type { GustoListContractorsParams, GustoListContractorsResponse } from '@/tools/gusto/types' +import { CONTRACTOR_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListContractorsTool: ToolConfig< + GustoListContractorsParams, + GustoListContractorsResponse +> = { + id: 'gusto_list_contractors', + name: 'Gusto List Contractors', + description: 'List contractors for a Gusto company', + version: '1.0.0', + + oauth: { + required: true, + provider: 'gusto', + }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + searchTerm: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter contractors by name', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number', + }, + per: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Items per page', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => { + const search = new URLSearchParams() + if (params.searchTerm) search.set('search_term', params.searchTerm) + if (params.page !== undefined) search.set('page', String(params.page)) + if (params.per !== undefined) search.set('per', String(params.per)) + const qs = search.toString() + return `${GUSTO_API_BASE}/companies/${encodeURIComponent( + params.companyId.trim() + )}/contractors${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list contractors'), + output: {}, + } + } + return { + success: true, + output: { contractors: Array.isArray(data) ? data : (data.contractors ?? []) }, + } + }, + + outputs: { + contractors: { + type: 'array', + description: 'List of contractors', + items: { + type: 'object', + properties: CONTRACTOR_OUTPUT_PROPERTIES, + }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_departments.ts b/apps/sim/tools/gusto/list_departments.ts new file mode 100644 index 00000000000..d3d317b6e3d --- /dev/null +++ b/apps/sim/tools/gusto/list_departments.ts @@ -0,0 +1,61 @@ +import type { GustoDepartmentsListResponse, GustoListDepartmentsParams } from '@/tools/gusto/types' +import { DEPARTMENT_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListDepartmentsTool: ToolConfig< + GustoListDepartmentsParams, + GustoDepartmentsListResponse +> = { + id: 'gusto_list_departments', + name: 'Gusto List Departments', + description: 'List all departments for a Gusto company', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent(params.companyId.trim())}/departments`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list departments'), + output: {}, + } + } + return { + success: true, + output: { departments: Array.isArray(data) ? data : (data.departments ?? []) }, + } + }, + + outputs: { + departments: { + type: 'array', + description: 'Company departments', + items: { type: 'object', properties: DEPARTMENT_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_employee_benefits.ts b/apps/sim/tools/gusto/list_employee_benefits.ts new file mode 100644 index 00000000000..3aaf184ec60 --- /dev/null +++ b/apps/sim/tools/gusto/list_employee_benefits.ts @@ -0,0 +1,70 @@ +import type { + GustoEmployeeBenefitsListResponse, + GustoListEmployeeBenefitsParams, +} from '@/tools/gusto/types' +import { EMPLOYEE_BENEFIT_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListEmployeeBenefitsTool: ToolConfig< + GustoListEmployeeBenefitsParams, + GustoEmployeeBenefitsListResponse +> = { + id: 'gusto_list_employee_benefits', + name: 'Gusto List Employee Benefits', + description: 'List all benefits enrolled for a Gusto employee', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/employees/${encodeURIComponent( + params.employeeId.trim() + )}/employee_benefits`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list employee benefits'), + output: {}, + } + } + return { + success: true, + output: { + employeeBenefits: Array.isArray(data) + ? data + : (data.employee_benefits ?? data.employeeBenefits ?? []), + }, + } + }, + + outputs: { + employeeBenefits: { + type: 'array', + description: 'Employee benefits', + items: { type: 'object', properties: EMPLOYEE_BENEFIT_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_employee_forms.ts b/apps/sim/tools/gusto/list_employee_forms.ts new file mode 100644 index 00000000000..924f886836b --- /dev/null +++ b/apps/sim/tools/gusto/list_employee_forms.ts @@ -0,0 +1,58 @@ +import type { GustoFormsListResponse, GustoListEmployeeFormsParams } from '@/tools/gusto/types' +import { FORM_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListEmployeeFormsTool: ToolConfig< + GustoListEmployeeFormsParams, + GustoFormsListResponse +> = { + id: 'gusto_list_employee_forms', + name: 'Gusto List Employee Forms', + description: 'List forms for a Gusto employee (W-2, I-9, etc.)', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/employees/${encodeURIComponent(params.employeeId.trim())}/forms`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list employee forms'), + output: {}, + } + } + return { success: true, output: { forms: Array.isArray(data) ? data : (data.forms ?? []) } } + }, + + outputs: { + forms: { + type: 'array', + description: 'Employee forms', + items: { type: 'object', properties: FORM_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_employee_jobs.ts b/apps/sim/tools/gusto/list_employee_jobs.ts new file mode 100644 index 00000000000..9b76dec2757 --- /dev/null +++ b/apps/sim/tools/gusto/list_employee_jobs.ts @@ -0,0 +1,58 @@ +import type { GustoJobsListResponse, GustoListEmployeeJobsParams } from '@/tools/gusto/types' +import { JOB_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListEmployeeJobsTool: ToolConfig< + GustoListEmployeeJobsParams, + GustoJobsListResponse +> = { + id: 'gusto_list_employee_jobs', + name: 'Gusto List Employee Jobs', + description: 'List jobs (compensations and titles) for a Gusto employee', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/employees/${encodeURIComponent(params.employeeId.trim())}/jobs`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list employee jobs'), + output: {}, + } + } + return { success: true, output: { jobs: Array.isArray(data) ? data : (data.jobs ?? []) } } + }, + + outputs: { + jobs: { + type: 'array', + description: 'Employee jobs', + items: { type: 'object', properties: JOB_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_employee_time_off_activities.ts b/apps/sim/tools/gusto/list_employee_time_off_activities.ts new file mode 100644 index 00000000000..7e814412f7e --- /dev/null +++ b/apps/sim/tools/gusto/list_employee_time_off_activities.ts @@ -0,0 +1,72 @@ +import type { + GustoListEmployeeTimeOffActivitiesParams, + GustoTimeOffActivitiesResponse, +} from '@/tools/gusto/types' +import { TIME_OFF_ACTIVITY_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListEmployeeTimeOffActivitiesTool: ToolConfig< + GustoListEmployeeTimeOffActivitiesParams, + GustoTimeOffActivitiesResponse +> = { + id: 'gusto_list_employee_time_off_activities', + name: 'Gusto List Employee Time Off Activities', + description: 'List time off activities for a Gusto employee by time off type', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + timeOffType: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: "Time off type to query (e.g. 'sick' or 'vacation')", + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => { + const search = new URLSearchParams({ time_off_type: params.timeOffType.trim() }) + return `${GUSTO_API_BASE}/employees/${encodeURIComponent( + params.employeeId.trim() + )}/time_off_activities?${search.toString()}` + }, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list time off activities'), + output: {}, + } + } + const activities = Array.isArray(data) ? data : (data.time_off_activities ?? []) + return { success: true, output: { timeOffActivities: activities } } + }, + + outputs: { + timeOffActivities: { + type: 'array', + description: 'Time off activities', + items: { type: 'object', properties: TIME_OFF_ACTIVITY_OUTPUT_PROPERTIES }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_employees.ts b/apps/sim/tools/gusto/list_employees.ts new file mode 100644 index 00000000000..9b2d2e954be --- /dev/null +++ b/apps/sim/tools/gusto/list_employees.ts @@ -0,0 +1,102 @@ +import type { GustoListEmployeesParams, GustoListEmployeesResponse } from '@/tools/gusto/types' +import { EMPLOYEE_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import type { ToolConfig } from '@/tools/types' + +export const gustoListEmployeesTool: ToolConfig< + GustoListEmployeesParams, + GustoListEmployeesResponse +> = { + id: 'gusto_list_employees', + name: 'Gusto List Employees', + description: 'List employees for a Gusto company', + version: '1.0.0', + + oauth: { + required: true, + provider: 'gusto', + }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + terminated: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Include only terminated employees when true', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number', + }, + per: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Items per page', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => { + const search = new URLSearchParams() + if (params.terminated !== undefined) search.set('terminated', String(params.terminated)) + if (params.page) search.set('page', String(params.page)) + if (params.per) search.set('per', String(params.per)) + const qs = search.toString() + return `https://api.gusto.com/v1/companies/${encodeURIComponent(params.companyId)}/employees${ + qs ? `?${qs}` : '' + }` + }, + method: 'GET', + headers: (params) => { + if (!params.accessToken) { + throw new Error('Missing access token for Gusto API request') + } + return { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-Gusto-API-Version': '2026-02-01', + Authorization: `Bearer ${params.accessToken}`, + } + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: data.error_description || data.message || 'Failed to list employees', + output: {}, + } + } + return { + success: true, + output: { employees: Array.isArray(data) ? data : (data.employees ?? []) }, + } + }, + + outputs: { + employees: { + type: 'array', + description: 'List of employees', + items: { + type: 'object', + properties: EMPLOYEE_OUTPUT_PROPERTIES, + }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_locations.ts b/apps/sim/tools/gusto/list_locations.ts new file mode 100644 index 00000000000..502a9dec24b --- /dev/null +++ b/apps/sim/tools/gusto/list_locations.ts @@ -0,0 +1,67 @@ +import type { GustoListLocationsParams, GustoListLocationsResponse } from '@/tools/gusto/types' +import { LOCATION_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListLocationsTool: ToolConfig< + GustoListLocationsParams, + GustoListLocationsResponse +> = { + id: 'gusto_list_locations', + name: 'Gusto List Locations', + description: 'List locations for a Gusto company', + version: '1.0.0', + + oauth: { + required: true, + provider: 'gusto', + }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent(params.companyId.trim())}/locations`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => ({})) + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list locations'), + output: {}, + } + } + return { + success: true, + output: { locations: Array.isArray(data) ? data : (data.locations ?? []) }, + } + }, + + outputs: { + locations: { + type: 'array', + description: 'List of locations', + items: { + type: 'object', + properties: LOCATION_OUTPUT_PROPERTIES, + }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_pay_schedules.ts b/apps/sim/tools/gusto/list_pay_schedules.ts new file mode 100644 index 00000000000..4a8a1d72f89 --- /dev/null +++ b/apps/sim/tools/gusto/list_pay_schedules.ts @@ -0,0 +1,70 @@ +import type { + GustoListPaySchedulesParams, + GustoListPaySchedulesResponse, +} from '@/tools/gusto/types' +import { PAY_SCHEDULE_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListPaySchedulesTool: ToolConfig< + GustoListPaySchedulesParams, + GustoListPaySchedulesResponse +> = { + id: 'gusto_list_pay_schedules', + name: 'Gusto List Pay Schedules', + description: 'List pay schedules for a Gusto company', + version: '1.0.0', + + oauth: { + required: true, + provider: 'gusto', + }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent(params.companyId.trim())}/pay_schedules`, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => ({})) + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list pay schedules'), + output: {}, + } + } + return { + success: true, + output: { paySchedules: Array.isArray(data) ? data : (data.pay_schedules ?? []) }, + } + }, + + outputs: { + paySchedules: { + type: 'array', + description: 'List of pay schedules', + items: { + type: 'object', + properties: PAY_SCHEDULE_OUTPUT_PROPERTIES, + }, + }, + }, +} diff --git a/apps/sim/tools/gusto/list_pay_stubs.ts b/apps/sim/tools/gusto/list_pay_stubs.ts new file mode 100644 index 00000000000..d9278ec2df9 --- /dev/null +++ b/apps/sim/tools/gusto/list_pay_stubs.ts @@ -0,0 +1,78 @@ +import type { GustoListPayStubsParams, GustoPayStubsListResponse } from '@/tools/gusto/types' +import { PAY_STUB_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListPayStubsTool: ToolConfig = + { + id: 'gusto_list_pay_stubs', + name: 'Gusto List Pay Stubs', + description: 'List pay stubs for a Gusto employee', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number', + }, + per: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Items per page', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => { + const search = new URLSearchParams() + if (params.page !== undefined) search.set('page', String(params.page)) + if (params.per !== undefined) search.set('per', String(params.per)) + const qs = search.toString() + return `${GUSTO_API_BASE}/employees/${encodeURIComponent( + params.employeeId.trim() + )}/pay_stubs${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list pay stubs'), + output: {}, + } + } + return { + success: true, + output: { payStubs: Array.isArray(data) ? data : (data.pay_stubs ?? []) }, + } + }, + + outputs: { + payStubs: { + type: 'array', + description: 'Pay stubs', + items: { type: 'object', properties: PAY_STUB_OUTPUT_PROPERTIES }, + }, + }, + } diff --git a/apps/sim/tools/gusto/list_payrolls.ts b/apps/sim/tools/gusto/list_payrolls.ts new file mode 100644 index 00000000000..edf43deda47 --- /dev/null +++ b/apps/sim/tools/gusto/list_payrolls.ts @@ -0,0 +1,100 @@ +import type { GustoListPayrollsParams, GustoListPayrollsResponse } from '@/tools/gusto/types' +import { PAYROLL_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoListPayrollsTool: ToolConfig = + { + id: 'gusto_list_payrolls', + name: 'Gusto List Payrolls', + description: 'List payrolls for a Gusto company', + version: '1.0.0', + + oauth: { + required: true, + provider: 'gusto', + }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date filter (YYYY-MM-DD)', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date filter (YYYY-MM-DD)', + }, + processingStatuses: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated statuses (processed, unprocessed). Defaults to "processed" if omitted', + }, + payrollTypes: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated payroll types (regular, off_cycle, external). Defaults to "regular" if omitted', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => { + const search = new URLSearchParams() + if (params.startDate) search.set('start_date', params.startDate) + if (params.endDate) search.set('end_date', params.endDate) + if (params.processingStatuses) search.set('processing_statuses', params.processingStatuses) + if (params.payrollTypes) search.set('payroll_types', params.payrollTypes) + const qs = search.toString() + return `${GUSTO_API_BASE}/companies/${encodeURIComponent( + params.companyId.trim() + )}/payrolls${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => gustoHeaders(params.accessToken), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to list payrolls'), + output: {}, + } + } + return { + success: true, + output: { payrolls: Array.isArray(data) ? data : (data.payrolls ?? []) }, + } + }, + + outputs: { + payrolls: { + type: 'array', + description: 'List of payrolls', + items: { + type: 'object', + properties: PAYROLL_OUTPUT_PROPERTIES, + }, + }, + }, + } diff --git a/apps/sim/tools/gusto/rehire_employee.ts b/apps/sim/tools/gusto/rehire_employee.ts new file mode 100644 index 00000000000..c0af57799fb --- /dev/null +++ b/apps/sim/tools/gusto/rehire_employee.ts @@ -0,0 +1,98 @@ +import type { GustoRehireEmployeeParams, GustoRehireResponse } from '@/tools/gusto/types' +import { REHIRE_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoRehireEmployeeTool: ToolConfig = { + id: 'gusto_rehire_employee', + name: 'Gusto Rehire Employee', + description: 'Schedule a rehire for a Gusto employee', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + effectiveDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The day when the employee returns to work (YYYY-MM-DD)', + }, + fileNewHireReport: { + type: 'boolean', + required: true, + visibility: 'user-or-llm', + description: 'Whether Gusto will file a new hire report for the employee', + }, + workLocationUuid: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: "UUID of the employee's work location", + }, + employmentStatus: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Employment status (part_time, full_time, part_time_eligible, variable, seasonal, not_set)', + }, + twoPercentShareholder: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether the employee is a 2% shareholder (S-Corp companies only)', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/employees/${encodeURIComponent(params.employeeId.trim())}/rehire`, + method: 'POST', + headers: (params) => gustoHeaders(params.accessToken), + body: (params) => { + const body: Record = { + effective_date: params.effectiveDate, + file_new_hire_report: params.fileNewHireReport, + work_location_uuid: params.workLocationUuid, + } + if (params.employmentStatus) body.employment_status = params.employmentStatus + if (params.twoPercentShareholder !== undefined) { + body.two_percent_shareholder = params.twoPercentShareholder + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to rehire employee'), + output: {}, + } + } + return { success: true, output: { rehire: data } } + }, + + outputs: { + rehire: { + type: 'object', + description: 'Employee rehire record', + properties: REHIRE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/submit_payroll.ts b/apps/sim/tools/gusto/submit_payroll.ts new file mode 100644 index 00000000000..8c9251f3502 --- /dev/null +++ b/apps/sim/tools/gusto/submit_payroll.ts @@ -0,0 +1,71 @@ +import type { GustoPayrollRecordResponse, GustoSubmitPayrollParams } from '@/tools/gusto/types' +import { PAYROLL_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoSubmitPayrollTool: ToolConfig< + GustoSubmitPayrollParams, + GustoPayrollRecordResponse +> = { + id: 'gusto_submit_payroll', + name: 'Gusto Submit Payroll', + description: 'Submit a calculated Gusto payroll for processing', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto company UUID', + }, + payrollId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto payroll UUID', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/companies/${encodeURIComponent( + params.companyId.trim() + )}/payrolls/${encodeURIComponent(params.payrollId.trim())}/submit`, + method: 'PUT', + headers: (params) => gustoHeaders(params.accessToken), + body: () => ({}), + }, + + transformResponse: async (response) => { + if (!response.ok) { + const data = await response.json().catch(() => ({})) + return { + success: false, + error: gustoErrorMessage(data, 'Failed to submit payroll'), + output: {}, + } + } + if (response.status === 202 || response.status === 204) { + return { success: true, output: { payroll: { status: 'submitting' } } } + } + const data = await response.json().catch(() => ({})) + return { success: true, output: { payroll: data } } + }, + + outputs: { + payroll: { + type: 'object', + description: 'Submitted payroll', + properties: PAYROLL_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/terminate_employee.ts b/apps/sim/tools/gusto/terminate_employee.ts new file mode 100644 index 00000000000..bfc3df6fb78 --- /dev/null +++ b/apps/sim/tools/gusto/terminate_employee.ts @@ -0,0 +1,78 @@ +import type { GustoTerminateEmployeeParams, GustoTerminationResponse } from '@/tools/gusto/types' +import { TERMINATION_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoTerminateEmployeeTool: ToolConfig< + GustoTerminateEmployeeParams, + GustoTerminationResponse +> = { + id: 'gusto_terminate_employee', + name: 'Gusto Terminate Employee', + description: 'Create a termination for a Gusto employee', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + effectiveDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Termination effective date (YYYY-MM-DD)', + }, + runTerminationPayroll: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: + 'If true, the employee receives final wages via off-cycle payroll. If false, on their current pay schedule.', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/employees/${encodeURIComponent(params.employeeId.trim())}/terminations`, + method: 'POST', + headers: (params) => gustoHeaders(params.accessToken), + body: (params) => { + const body: Record = { effective_date: params.effectiveDate } + if (params.runTerminationPayroll !== undefined) { + body.run_termination_payroll = params.runTerminationPayroll + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to terminate employee'), + output: {}, + } + } + return { success: true, output: { termination: data } } + }, + + outputs: { + termination: { + type: 'object', + description: 'Employee termination record', + properties: TERMINATION_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/types.ts b/apps/sim/tools/gusto/types.ts new file mode 100644 index 00000000000..a18a06955c7 --- /dev/null +++ b/apps/sim/tools/gusto/types.ts @@ -0,0 +1,771 @@ +import type { OutputProperty, ToolResponse } from '@/tools/types' + +/** + * Shared output property definitions for Gusto API responses. + * Fields based on Gusto Embedded Payroll API. + */ + +export const COMPANY_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Company UUID' }, + name: { type: 'string', description: 'Legal entity name' }, + trade_name: { type: 'string', description: 'Trade name', optional: true }, + ein: { type: 'string', description: 'Federal Employer Identification Number', optional: true }, + entity_type: { + type: 'string', + description: 'Entity type (LLC, Corporation, etc.)', + optional: true, + }, + company_status: { + type: 'string', + description: 'Company status (Approved, Not Approved, Suspended)', + optional: true, + }, + locations: { type: 'array', description: 'Company locations', optional: true }, + compensations: { type: 'object', description: 'Compensation classifications', optional: true }, + primary_signatory: { type: 'object', description: 'Primary signatory', optional: true }, + primary_payroll_admin: { type: 'object', description: 'Primary payroll admin', optional: true }, + tier: { type: 'string', description: 'Company tier', optional: true }, +} as const satisfies Record + +export const EMPLOYEE_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Employee UUID' }, + first_name: { type: 'string', description: 'First name' }, + middle_initial: { type: 'string', description: 'Middle initial', optional: true }, + last_name: { type: 'string', description: 'Last name' }, + preferred_first_name: { type: 'string', description: 'Preferred first name', optional: true }, + email: { type: 'string', description: 'Personal email', optional: true }, + company_uuid: { type: 'string', description: 'Company UUID' }, + manager_uuid: { type: 'string', description: 'Manager UUID', optional: true }, + department: { type: 'string', description: 'Department name', optional: true }, + department_uuid: { type: 'string', description: 'Department UUID', optional: true }, + date_of_birth: { type: 'string', description: 'Date of birth', optional: true }, + has_ssn: { type: 'boolean', description: 'Whether SSN is on file', optional: true }, + ssn: { type: 'string', description: 'Social security number (masked)', optional: true }, + phone: { type: 'string', description: 'Phone number', optional: true }, + terminated: { type: 'boolean', description: 'Whether the employee is terminated' }, + terminations: { type: 'array', description: 'Termination records', optional: true }, + onboarded: { type: 'boolean', description: 'Whether the employee is onboarded' }, + onboarding_status: { type: 'string', description: 'Onboarding status', optional: true }, + jobs: { type: 'array', description: 'Employee jobs', optional: true }, + version: { type: 'string', description: 'Record version', optional: true }, +} as const satisfies Record + +export const CONTRACTOR_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Contractor UUID' }, + type: { type: 'string', description: 'Contractor type (Individual or Business)' }, + wage_type: { type: 'string', description: 'Wage type (Fixed or Hourly)' }, + is_active: { type: 'boolean', description: 'Whether the contractor is active' }, + first_name: { type: 'string', description: 'First name', optional: true }, + last_name: { type: 'string', description: 'Last name', optional: true }, + business_name: { type: 'string', description: 'Business name', optional: true }, + email: { type: 'string', description: 'Email address', optional: true }, + start_date: { type: 'string', description: 'Start date (YYYY-MM-DD)', optional: true }, + hourly_rate: { type: 'string', description: 'Hourly rate', optional: true }, + company_uuid: { type: 'string', description: 'Company UUID' }, +} as const satisfies Record + +export const PAYROLL_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Payroll UUID' }, + payroll_uuid: { type: 'string', description: 'Payroll UUID (legacy alias)', optional: true }, + company_uuid: { type: 'string', description: 'Company UUID', optional: true }, + payroll_deadline: { type: 'string', description: 'Payroll submission deadline', optional: true }, + check_date: { type: 'string', description: 'Check date', optional: true }, + processed: { type: 'boolean', description: 'Whether the payroll has been processed' }, + processed_date: { + type: 'string', + description: 'Date the payroll was processed', + optional: true, + }, + calculated_at: { type: 'string', description: 'When the payroll was calculated', optional: true }, + off_cycle: { type: 'boolean', description: 'Whether this is an off-cycle payroll' }, + off_cycle_reason: { type: 'string', description: 'Off-cycle payroll reason', optional: true }, + external: { type: 'boolean', description: 'Whether this is an external payroll', optional: true }, + auto_pilot: { type: 'boolean', description: 'Whether autopilot is enabled', optional: true }, + pay_period: { type: 'object', description: 'Pay period details', optional: true }, + totals: { type: 'object', description: 'Payroll totals', optional: true }, + payroll_status_meta: { + type: 'object', + description: 'Status metadata (cancellable, expected_check_date, etc.)', + optional: true, + }, + employee_compensations: { + type: 'array', + description: 'Per-employee compensation breakdown', + optional: true, + }, +} as const satisfies Record + +export const PAY_SCHEDULE_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Pay schedule UUID', optional: true }, + frequency: { type: 'string', description: 'Pay frequency', optional: true }, + anchor_pay_date: { type: 'string', description: 'Anchor pay date', optional: true }, + anchor_end_of_pay_period: { + type: 'string', + description: 'Anchor end of pay period', + optional: true, + }, + day_1: { type: 'number', description: 'First pay day of period (semimonthly)', optional: true }, + day_2: { type: 'number', description: 'Second pay day of period (semimonthly)', optional: true }, + name: { type: 'string', description: 'Pay schedule name', optional: true }, + auto_pilot: { type: 'boolean', description: 'Whether autopilot is enabled', optional: true }, + active: { type: 'boolean', description: 'Whether the schedule is active', optional: true }, + custom_name: { type: 'string', description: 'Custom name', optional: true }, + version: { type: 'string', description: 'Record version', optional: true }, +} as const satisfies Record + +export const DEPARTMENT_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Department UUID', optional: true }, + title: { type: 'string', description: 'Department title', optional: true }, + company_uuid: { type: 'string', description: 'Company UUID', optional: true }, + version: { type: 'string', description: 'Record version', optional: true }, + employees: { + type: 'array', + description: 'Employees in the department', + optional: true, + }, + contractors: { + type: 'array', + description: 'Contractors in the department', + optional: true, + }, +} as const satisfies Record + +export const JOB_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Job UUID' }, + employee_uuid: { type: 'string', description: 'Employee UUID' }, + title: { type: 'string', description: 'Job title', optional: true }, + hire_date: { type: 'string', description: 'Hire date (YYYY-MM-DD)', optional: true }, + primary: { type: 'boolean', description: 'Whether this is the primary job', optional: true }, + current_compensation_uuid: { + type: 'string', + description: 'Current compensation UUID', + optional: true, + }, + rate: { type: 'string', description: 'Pay rate', optional: true }, + payment_unit: { type: 'string', description: 'Payment unit', optional: true }, + compensations: { type: 'array', description: 'Job compensation history', optional: true }, + state_wc_covered: { + type: 'boolean', + description: "Whether the job is covered by state workers' comp", + optional: true, + }, + state_wc_class_code: { + type: 'string', + description: "State workers' comp class code", + optional: true, + }, + two_percent_shareholder: { + type: 'boolean', + description: 'Whether this job is for a 2% shareholder', + optional: true, + }, + version: { type: 'string', description: 'Record version', optional: true }, +} as const satisfies Record + +export const PAY_STUB_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Pay stub identifier' }, + payroll_uuid: { type: 'string', description: 'Payroll UUID', optional: true }, + check_date: { type: 'string', description: 'Check date', optional: true }, + gross_pay: { type: 'string', description: 'Gross pay amount', optional: true }, + net_pay: { type: 'string', description: 'Net pay amount', optional: true }, + check_amount: { type: 'string', description: 'Check amount', optional: true }, +} as const satisfies Record + +export const TIME_OFF_ACTIVITY_OUTPUT_PROPERTIES = { + policy_uuid: { type: 'string', description: 'Time off policy UUID', optional: true }, + time_off_type: { + type: 'string', + description: 'Time off type (vacation or sick)', + optional: true, + }, + policy_name: { type: 'string', description: 'Time off policy name', optional: true }, + event_type: { type: 'string', description: 'Type of the time off event', optional: true }, + event_description: { type: 'string', description: 'Event description', optional: true }, + effective_time: { type: 'string', description: 'Datetime of the activity', optional: true }, + balance: { type: 'string', description: 'Balance at the time of the activity', optional: true }, + balance_change: { type: 'string', description: 'Balance change amount', optional: true }, +} as const satisfies Record + +export const CONTRACTOR_PAYMENT_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Contractor payment UUID' }, + contractor_uuid: { type: 'string', description: 'Contractor UUID' }, + bonus: { type: 'string', description: 'Bonus amount', optional: true }, + date: { type: 'string', description: 'Payment date (YYYY-MM-DD)', optional: true }, + hours: { type: 'string', description: 'Hours worked', optional: true }, + reimbursement: { type: 'string', description: 'Reimbursement amount', optional: true }, + wage: { type: 'string', description: 'Fixed wage amount', optional: true }, + wage_type: { type: 'string', description: 'Wage type (Fixed or Hourly)', optional: true }, + wage_total: { type: 'string', description: 'Total wage amount', optional: true }, + payment_method: { type: 'string', description: 'Payment method', optional: true }, + status: { type: 'string', description: 'Payment status', optional: true }, + may_cancel: { + type: 'boolean', + description: 'Whether the payment may be canceled', + optional: true, + }, + check_number: { type: 'string', description: 'Check number', optional: true }, + debit_date: { type: 'string', description: 'Date funds will be debited', optional: true }, +} as const satisfies Record + +export const COMPANY_BENEFIT_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Company benefit UUID' }, + company_uuid: { type: 'string', description: 'Company UUID', optional: true }, + benefit_type: { type: 'number', description: 'Benefit type ID', optional: true }, + active: { type: 'boolean', description: 'Whether active', optional: true }, + description: { type: 'string', description: 'Benefit description', optional: true }, + source: { type: 'string', description: 'Benefit source (Gusto, partner, etc.)', optional: true }, + partner_name: { type: 'string', description: 'Partner name (if external)', optional: true }, + enrollment_count: { + type: 'number', + description: 'Number of employees enrolled', + optional: true, + }, + deletable: { type: 'boolean', description: 'Whether the benefit can be deleted', optional: true }, + responsible_for_employer_taxes: { + type: 'boolean', + description: 'Whether company is responsible for employer taxes', + optional: true, + }, + responsible_for_employee_w2: { + type: 'boolean', + description: 'Whether benefit appears on employee W2', + optional: true, + }, + supports_percentage_amounts: { + type: 'boolean', + description: 'Whether the benefit supports percentage-based amounts', + optional: true, + }, + version: { type: 'string', description: 'Record version', optional: true }, +} as const satisfies Record + +export const EMPLOYEE_BENEFIT_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Employee benefit UUID' }, + employee_uuid: { type: 'string', description: 'Employee UUID' }, + company_benefit_uuid: { type: 'string', description: 'Company benefit UUID' }, + active: { type: 'boolean', description: 'Whether active', optional: true }, + employee_deduction: { type: 'string', description: 'Employee deduction', optional: true }, + company_contribution: { type: 'string', description: 'Company contribution', optional: true }, + employee_deduction_annual_maximum: { + type: 'string', + description: 'Annual maximum employee deduction', + optional: true, + }, + company_contribution_annual_maximum: { + type: 'string', + description: 'Annual maximum company contribution', + optional: true, + }, + deduct_as_percentage: { + type: 'boolean', + description: 'Whether deduction is calculated as a percentage', + optional: true, + }, + contribute_as_percentage: { + type: 'boolean', + description: 'Whether contribution is calculated as a percentage', + optional: true, + }, + contribution: { type: 'object', description: 'Contribution config', optional: true }, + elective: { type: 'boolean', description: 'Whether the benefit is elective', optional: true }, + catch_up: { + type: 'boolean', + description: 'Whether catch-up contributions apply', + optional: true, + }, + coverage_amount: { type: 'string', description: 'Coverage amount', optional: true }, + coverage_salary_multiplier: { + type: 'string', + description: 'Coverage as a multiplier of salary', + optional: true, + }, + deduction_reduces_taxable_income: { + type: 'string', + description: 'Whether deduction reduces taxable income (unset, true, false)', + optional: true, + }, + deduction_type: { type: 'string', description: 'Deduction type', optional: true }, + version: { type: 'string', description: 'Record version', optional: true }, +} as const satisfies Record + +export const FORM_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Form UUID' }, + name: { type: 'string', description: 'Form name', optional: true }, + title: { type: 'string', description: 'Form title', optional: true }, + description: { type: 'string', description: 'Form description', optional: true }, + year: { type: 'number', description: 'Tax year', optional: true }, + quarter: { type: 'number', description: 'Quarter', optional: true }, + requires_signing: { + type: 'boolean', + description: 'Whether the form requires signing', + optional: true, + }, + draft: { type: 'boolean', description: 'Whether the form is a draft', optional: true }, + document_content_type: { + type: 'string', + description: 'Form document MIME type', + optional: true, + }, +} as const satisfies Record + +export const ONBOARDING_STATUS_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Employee UUID', optional: true }, + onboarding_status: { type: 'string', description: 'Onboarding status' }, + onboarding_steps: { + type: 'array', + description: 'Onboarding step details', + optional: true, + }, +} as const satisfies Record + +export const TERMINATION_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Termination UUID' }, + employee_uuid: { type: 'string', description: 'Employee UUID' }, + active: { type: 'boolean', description: 'Whether the termination is active', optional: true }, + effective_date: { + type: 'string', + description: 'Effective date (YYYY-MM-DD)', + optional: true, + }, + run_termination_payroll: { + type: 'boolean', + description: 'Whether to run a termination payroll', + optional: true, + }, +} as const satisfies Record + +export const REHIRE_OUTPUT_PROPERTIES = { + employee_uuid: { type: 'string', description: 'Employee UUID' }, + effective_date: { type: 'string', description: 'Effective date (YYYY-MM-DD)', optional: true }, + work_location_uuid: { + type: 'string', + description: 'Work location UUID', + optional: true, + }, + file_new_hire_report: { + type: 'boolean', + description: 'Whether to file a new hire report', + optional: true, + }, + employment_status: { + type: 'string', + description: 'Employment status', + optional: true, + }, + two_percent_shareholder: { + type: 'boolean', + description: 'Whether the employee is a 2% shareholder', + optional: true, + }, +} as const satisfies Record + +export const LOCATION_OUTPUT_PROPERTIES = { + uuid: { type: 'string', description: 'Location UUID' }, + street_1: { type: 'string', description: 'Street address line 1' }, + street_2: { type: 'string', description: 'Street address line 2', optional: true }, + city: { type: 'string', description: 'City' }, + state: { type: 'string', description: 'State' }, + zip: { type: 'string', description: 'ZIP code' }, + country: { type: 'string', description: 'Country' }, + phone_number: { type: 'string', description: 'Phone number', optional: true }, + mailing_address: { type: 'boolean', description: 'Whether this is the mailing address' }, + filing_address: { type: 'boolean', description: 'Whether this is the filing address' }, + active: { type: 'boolean', description: 'Whether the location is active' }, +} as const satisfies Record + +/* ========== Param Types ========== */ + +export interface GustoGetCompanyParams { + companyId: string + accessToken?: string +} + +export interface GustoListEmployeesParams { + companyId: string + terminated?: boolean + page?: number + per?: number + accessToken?: string +} + +export interface GustoGetEmployeeParams { + employeeId: string + accessToken?: string +} + +export interface GustoCreateEmployeeParams { + companyId: string + firstName: string + lastName: string + email?: string + middleInitial?: string + dateOfBirth?: string + ssn?: string + selfOnboarding?: boolean + accessToken?: string +} + +export interface GustoListContractorsParams { + companyId: string + searchTerm?: string + page?: number + per?: number + accessToken?: string +} + +export interface GustoListPayrollsParams { + companyId: string + startDate?: string + endDate?: string + processingStatuses?: string + payrollTypes?: string + accessToken?: string +} + +export interface GustoGetPayrollParams { + companyId: string + payrollId: string + include?: string + accessToken?: string +} + +export interface GustoListPaySchedulesParams { + companyId: string + accessToken?: string +} + +export interface GustoListLocationsParams { + companyId: string + accessToken?: string +} + +/* ========== Response Types ========== */ + +export interface GustoGetCompanyResponse extends ToolResponse { + output: { + company?: Record + } +} + +export interface GustoListEmployeesResponse extends ToolResponse { + output: { + employees?: Record[] + } +} + +export interface GustoGetEmployeeResponse extends ToolResponse { + output: { + employee?: Record + } +} + +export interface GustoCreateEmployeeResponse extends ToolResponse { + output: { + employee?: Record + } +} + +export interface GustoListContractorsResponse extends ToolResponse { + output: { + contractors?: Record[] + } +} + +export interface GustoListPayrollsResponse extends ToolResponse { + output: { + payrolls?: Record[] + } +} + +export interface GustoGetPayrollResponse extends ToolResponse { + output: { + payroll?: Record + } +} + +export interface GustoListPaySchedulesResponse extends ToolResponse { + output: { + paySchedules?: Record[] + } +} + +export interface GustoListLocationsResponse extends ToolResponse { + output: { + locations?: Record[] + } +} + +/* ========== New Param Types ========== */ + +export interface GustoUpdateEmployeeParams { + employeeId: string + version: string + firstName?: string + lastName?: string + middleInitial?: string + email?: string + dateOfBirth?: string + ssn?: string + preferredFirstName?: string + twoPercentShareholder?: boolean + accessToken?: string +} + +export interface GustoTerminateEmployeeParams { + employeeId: string + effectiveDate: string + runTerminationPayroll?: boolean + accessToken?: string +} + +export interface GustoRehireEmployeeParams { + employeeId: string + effectiveDate: string + fileNewHireReport: boolean + workLocationUuid: string + employmentStatus?: string + twoPercentShareholder?: boolean + accessToken?: string +} + +export interface GustoListEmployeeJobsParams { + employeeId: string + accessToken?: string +} + +export interface GustoListPayStubsParams { + employeeId: string + page?: number + per?: number + accessToken?: string +} + +export interface GustoGetEmployeeOnboardingStatusParams { + employeeId: string + accessToken?: string +} + +export interface GustoCreateContractorParams { + companyId: string + type: 'Individual' | 'Business' + wageType: 'Fixed' | 'Hourly' + startDate: string + firstName?: string + lastName?: string + middleInitial?: string + businessName?: string + email?: string + selfOnboarding?: boolean + ein?: string + hourlyRate?: string + accessToken?: string +} + +export interface GustoGetContractorParams { + contractorId: string + accessToken?: string +} + +export interface GustoUpdateContractorParams { + contractorId: string + version: string + firstName?: string + lastName?: string + middleInitial?: string + businessName?: string + email?: string + startDate?: string + hourlyRate?: string + wageType?: 'Fixed' | 'Hourly' + ein?: string + accessToken?: string +} + +export interface GustoCreateOffCyclePayrollParams { + companyId: string + startDate: string + endDate: string + offCycleReason: string + checkDate?: string + payScheduleUuid?: string + fixedWithholdingRate?: boolean + employeeUuids?: string + withholdingPayPeriod?: string + skipRegularDeductions?: boolean + accessToken?: string +} + +export interface GustoCalculatePayrollParams { + companyId: string + payrollId: string + accessToken?: string +} + +export interface GustoSubmitPayrollParams { + companyId: string + payrollId: string + accessToken?: string +} + +export interface GustoCancelPayrollParams { + companyId: string + payrollId: string + accessToken?: string +} + +export interface GustoListContractorPaymentsParams { + companyId: string + startDate: string + endDate: string + page?: number + per?: number + accessToken?: string +} + +export interface GustoGetContractorPaymentParams { + companyId: string + contractorPaymentId: string + accessToken?: string +} + +export interface GustoCreateContractorPaymentParams { + companyId: string + contractorUuid: string + date: string + wage?: number + hours?: number + bonus?: number + reimbursement?: number + paymentMethod?: 'Direct Deposit' | 'Check' + accessToken?: string +} + +export interface GustoListEmployeeTimeOffActivitiesParams { + employeeId: string + timeOffType: string + accessToken?: string +} + +export interface GustoListDepartmentsParams { + companyId: string + accessToken?: string +} + +export interface GustoCreateDepartmentParams { + companyId: string + title: string + accessToken?: string +} + +export interface GustoListCompanyBenefitsParams { + companyId: string + accessToken?: string +} + +export interface GustoListEmployeeBenefitsParams { + employeeId: string + accessToken?: string +} + +export interface GustoListEmployeeFormsParams { + employeeId: string + accessToken?: string +} + +export interface GustoListContractorFormsParams { + contractorId: string + accessToken?: string +} + +/* ========== New Response Types ========== */ + +export interface GustoEmployeeRecordResponse extends ToolResponse { + output: { employee?: Record } +} + +export interface GustoTerminationResponse extends ToolResponse { + output: { termination?: Record } +} + +export interface GustoRehireResponse extends ToolResponse { + output: { rehire?: Record } +} + +export interface GustoJobsListResponse extends ToolResponse { + output: { jobs?: Record[] } +} + +export interface GustoPayStubsListResponse extends ToolResponse { + output: { payStubs?: Record[] } +} + +export interface GustoOnboardingStatusResponse extends ToolResponse { + output: { onboardingStatus?: Record } +} + +export interface GustoContractorRecordResponse extends ToolResponse { + output: { contractor?: Record } +} + +export interface GustoPayrollRecordResponse extends ToolResponse { + output: { payroll?: Record } +} + +export interface GustoContractorPaymentsListResponse extends ToolResponse { + output: { contractorPayments?: Record[] } +} + +export interface GustoContractorPaymentRecordResponse extends ToolResponse { + output: { contractorPayment?: Record } +} + +export interface GustoTimeOffActivitiesResponse extends ToolResponse { + output: { timeOffActivities?: Record[] } +} + +export interface GustoDepartmentsListResponse extends ToolResponse { + output: { departments?: Record[] } +} + +export interface GustoDepartmentRecordResponse extends ToolResponse { + output: { department?: Record } +} + +export interface GustoCompanyBenefitsListResponse extends ToolResponse { + output: { companyBenefits?: Record[] } +} + +export interface GustoEmployeeBenefitsListResponse extends ToolResponse { + output: { employeeBenefits?: Record[] } +} + +export interface GustoFormsListResponse extends ToolResponse { + output: { forms?: Record[] } +} + +export type GustoResponse = + | GustoGetCompanyResponse + | GustoListEmployeesResponse + | GustoGetEmployeeResponse + | GustoCreateEmployeeResponse + | GustoListContractorsResponse + | GustoListPayrollsResponse + | GustoGetPayrollResponse + | GustoListPaySchedulesResponse + | GustoListLocationsResponse + | GustoEmployeeRecordResponse + | GustoTerminationResponse + | GustoRehireResponse + | GustoJobsListResponse + | GustoPayStubsListResponse + | GustoOnboardingStatusResponse + | GustoContractorRecordResponse + | GustoPayrollRecordResponse + | GustoContractorPaymentsListResponse + | GustoContractorPaymentRecordResponse + | GustoTimeOffActivitiesResponse + | GustoDepartmentsListResponse + | GustoDepartmentRecordResponse + | GustoCompanyBenefitsListResponse + | GustoEmployeeBenefitsListResponse + | GustoFormsListResponse diff --git a/apps/sim/tools/gusto/update_contractor.ts b/apps/sim/tools/gusto/update_contractor.ts new file mode 100644 index 00000000000..24f34651561 --- /dev/null +++ b/apps/sim/tools/gusto/update_contractor.ts @@ -0,0 +1,134 @@ +import type { + GustoContractorRecordResponse, + GustoUpdateContractorParams, +} from '@/tools/gusto/types' +import { CONTRACTOR_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoUpdateContractorTool: ToolConfig< + GustoUpdateContractorParams, + GustoContractorRecordResponse +> = { + id: 'gusto_update_contractor', + name: 'Gusto Update Contractor', + description: 'Update an existing Gusto contractor', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + contractorId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto contractor UUID', + }, + version: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Current version of the contractor record (required for updates)', + }, + firstName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'First name', + }, + lastName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Last name', + }, + middleInitial: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Middle initial', + }, + businessName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Business name', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Contractor email', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD)', + }, + hourlyRate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Hourly rate', + }, + wageType: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Wage type (Fixed or Hourly)', + }, + ein: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Employer Identification Number', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => + `${GUSTO_API_BASE}/contractors/${encodeURIComponent(params.contractorId.trim())}`, + method: 'PUT', + headers: (params) => gustoHeaders(params.accessToken), + body: (params) => { + const body: Record = { version: params.version } + if (params.firstName) body.first_name = params.firstName + if (params.lastName) body.last_name = params.lastName + if (params.middleInitial) body.middle_initial = params.middleInitial + if (params.businessName) body.business_name = params.businessName + if (params.email) body.email = params.email + if (params.startDate) body.start_date = params.startDate + if (params.hourlyRate) body.hourly_rate = params.hourlyRate + if (params.wageType) body.wage_type = params.wageType + if (params.ein) body.ein = params.ein + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to update contractor'), + output: {}, + } + } + return { success: true, output: { contractor: data } } + }, + + outputs: { + contractor: { + type: 'object', + description: 'Updated contractor', + properties: CONTRACTOR_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/update_employee.ts b/apps/sim/tools/gusto/update_employee.ts new file mode 100644 index 00000000000..31bfd229149 --- /dev/null +++ b/apps/sim/tools/gusto/update_employee.ts @@ -0,0 +1,125 @@ +import type { GustoEmployeeRecordResponse, GustoUpdateEmployeeParams } from '@/tools/gusto/types' +import { EMPLOYEE_OUTPUT_PROPERTIES } from '@/tools/gusto/types' +import { GUSTO_API_BASE, gustoErrorMessage, gustoHeaders } from '@/tools/gusto/utils' +import type { ToolConfig } from '@/tools/types' + +export const gustoUpdateEmployeeTool: ToolConfig< + GustoUpdateEmployeeParams, + GustoEmployeeRecordResponse +> = { + id: 'gusto_update_employee', + name: 'Gusto Update Employee', + description: 'Update an existing Gusto employee', + version: '1.0.0', + + oauth: { required: true, provider: 'gusto' }, + + params: { + employeeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Gusto employee UUID', + }, + version: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Current version of the employee record (required for updates)', + }, + firstName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Employee first name', + }, + lastName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Employee last name', + }, + middleInitial: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Middle initial', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Work email', + }, + dateOfBirth: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date of birth (YYYY-MM-DD)', + }, + ssn: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Social security number (digits only)', + }, + preferredFirstName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Preferred first name', + }, + twoPercentShareholder: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether the employee is a 2% shareholder', + }, + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token', + }, + }, + + request: { + url: (params) => `${GUSTO_API_BASE}/employees/${encodeURIComponent(params.employeeId.trim())}`, + method: 'PUT', + headers: (params) => gustoHeaders(params.accessToken), + body: (params) => { + const body: Record = { version: params.version } + if (params.firstName) body.first_name = params.firstName + if (params.lastName) body.last_name = params.lastName + if (params.middleInitial) body.middle_initial = params.middleInitial + if (params.email) body.email = params.email + if (params.dateOfBirth) body.date_of_birth = params.dateOfBirth + if (params.ssn) body.ssn = params.ssn + if (params.preferredFirstName) body.preferred_first_name = params.preferredFirstName + if (params.twoPercentShareholder !== undefined) { + body.two_percent_shareholder = params.twoPercentShareholder + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + return { + success: false, + error: gustoErrorMessage(data, 'Failed to update employee'), + output: {}, + } + } + return { success: true, output: { employee: data } } + }, + + outputs: { + employee: { + type: 'object', + description: 'Updated Gusto employee', + properties: EMPLOYEE_OUTPUT_PROPERTIES, + }, + }, +} diff --git a/apps/sim/tools/gusto/utils.ts b/apps/sim/tools/gusto/utils.ts new file mode 100644 index 00000000000..f4cd60a02bb --- /dev/null +++ b/apps/sim/tools/gusto/utils.ts @@ -0,0 +1,28 @@ +export const GUSTO_API_BASE = 'https://api.gusto.com/v1' +export const GUSTO_API_VERSION = '2026-02-01' + +export function gustoHeaders(accessToken: string | undefined): Record { + if (!accessToken) { + throw new Error('Missing access token for Gusto API request') + } + return { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-Gusto-API-Version': GUSTO_API_VERSION, + Authorization: `Bearer ${accessToken}`, + } +} + +export function gustoErrorMessage(data: unknown, fallback: string): string { + if (data && typeof data === 'object') { + const obj = data as Record + if (typeof obj.error_description === 'string') return obj.error_description + if (typeof obj.message === 'string') return obj.message + if (Array.isArray(obj.errors) && obj.errors.length > 0) { + const first = obj.errors[0] as Record + const msg = typeof first.message === 'string' ? first.message : undefined + if (msg) return msg + } + } + return fallback +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 8ee2f4f45f7..6638ab1c21d 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -1135,6 +1135,40 @@ import { greptileStatusTool, } from '@/tools/greptile' import { guardrailsValidateTool } from '@/tools/guardrails' +import { + gustoCalculatePayrollTool, + gustoCancelPayrollTool, + gustoCreateContractorPaymentTool, + gustoCreateContractorTool, + gustoCreateDepartmentTool, + gustoCreateEmployeeTool, + gustoCreateOffCyclePayrollTool, + gustoGetCompanyTool, + gustoGetContractorPaymentTool, + gustoGetContractorTool, + gustoGetEmployeeOnboardingStatusTool, + gustoGetEmployeeTool, + gustoGetPayrollTool, + gustoListCompanyBenefitsTool, + gustoListContractorFormsTool, + gustoListContractorPaymentsTool, + gustoListContractorsTool, + gustoListDepartmentsTool, + gustoListEmployeeBenefitsTool, + gustoListEmployeeFormsTool, + gustoListEmployeeJobsTool, + gustoListEmployeesTool, + gustoListEmployeeTimeOffActivitiesTool, + gustoListLocationsTool, + gustoListPayrollsTool, + gustoListPaySchedulesTool, + gustoListPayStubsTool, + gustoRehireEmployeeTool, + gustoSubmitPayrollTool, + gustoTerminateEmployeeTool, + gustoUpdateContractorTool, + gustoUpdateEmployeeTool, +} from '@/tools/gusto' import { hexCancelRunTool, hexCreateCollectionTool, @@ -3178,6 +3212,38 @@ export const tools: Record = { granola_list_notes: granolaListNotesTool, granola_get_note: granolaGetNoteTool, guardrails_validate: guardrailsValidateTool, + gusto_calculate_payroll: gustoCalculatePayrollTool, + gusto_cancel_payroll: gustoCancelPayrollTool, + gusto_create_contractor: gustoCreateContractorTool, + gusto_create_contractor_payment: gustoCreateContractorPaymentTool, + gusto_create_department: gustoCreateDepartmentTool, + gusto_create_employee: gustoCreateEmployeeTool, + gusto_create_off_cycle_payroll: gustoCreateOffCyclePayrollTool, + gusto_get_company: gustoGetCompanyTool, + gusto_get_contractor: gustoGetContractorTool, + gusto_get_contractor_payment: gustoGetContractorPaymentTool, + gusto_get_employee: gustoGetEmployeeTool, + gusto_get_employee_onboarding_status: gustoGetEmployeeOnboardingStatusTool, + gusto_get_payroll: gustoGetPayrollTool, + gusto_list_company_benefits: gustoListCompanyBenefitsTool, + gusto_list_contractor_forms: gustoListContractorFormsTool, + gusto_list_contractor_payments: gustoListContractorPaymentsTool, + gusto_list_contractors: gustoListContractorsTool, + gusto_list_departments: gustoListDepartmentsTool, + gusto_list_employee_benefits: gustoListEmployeeBenefitsTool, + gusto_list_employee_forms: gustoListEmployeeFormsTool, + gusto_list_employee_jobs: gustoListEmployeeJobsTool, + gusto_list_employee_time_off_activities: gustoListEmployeeTimeOffActivitiesTool, + gusto_list_employees: gustoListEmployeesTool, + gusto_list_locations: gustoListLocationsTool, + gusto_list_pay_schedules: gustoListPaySchedulesTool, + gusto_list_pay_stubs: gustoListPayStubsTool, + gusto_list_payrolls: gustoListPayrollsTool, + gusto_rehire_employee: gustoRehireEmployeeTool, + gusto_submit_payroll: gustoSubmitPayrollTool, + gusto_terminate_employee: gustoTerminateEmployeeTool, + gusto_update_contractor: gustoUpdateContractorTool, + gusto_update_employee: gustoUpdateEmployeeTool, hex_cancel_run: hexCancelRunTool, hex_create_collection: hexCreateCollectionTool, hex_get_collection: hexGetCollectionTool,