feat(rippling): add Rippling HR integration with 19 tools (#3764)

* feat(rippling): add Rippling HR integration with 19 tools

* fix(rippling): address PR review feedback

- Fix lint:check import ordering in icon-mapping.ts
- Build clean params object instead of spreading all UI fields to API
- Add try/catch around JSON.parse for users field
- Use != null guard for limit/offset to not drop 0 values
- Add missing tags to block config and integrations.json

* fix(rippling): guard startDate by operation and clarify totalCount descriptions

- Guard startDate/endDate with operation check to prevent candidateStartDate
  from clobbering date filters on leave/activity operations
- Update totalCount output descriptions on paginated tools to clarify it
  reflects page size, not total record count

* fix(rippling): use null-safe guard for groupVersion param

* fix(rippling): remove operation field from tool params payload

* fix(rippling): add input validation for action param and empty group update body
This commit is contained in:
Waleed
2026-03-25 12:02:19 -07:00
committed by GitHub
parent 8caaf01371
commit 2691c12747
32 changed files with 3318 additions and 4 deletions
+11
View File
@@ -6270,6 +6270,17 @@ export function RedisIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function RipplingIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 145.3 109.9' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M17.5,30.5C17.5,18.4,11.4,8.4,0,0h26.5c9.3,7.2,15,18.2,15,30.5c0,12.3-5.7,23.3-15,30.5 c8.6,3.6,13.5,12.4,13.5,25v24H16v-24c0-12-5.7-20.4-16-25C11.4,52.5,17.5,42.6,17.5,30.5 M69.4,30.5c0-12.1-6.1-22.1-17.5-30.5 h26.5c9.3,7.2,15,18.2,15,30.5c0,12.3-5.7,23.3-15,30.5c8.6,3.6,13.5,12.4,13.5,25v24h-24v-24c0-12-5.7-20.4-16-25 C63.3,52.5,69.4,42.6,69.4,30.5 M121.4,30.5c0-12.1-6.1-22.1-17.5-30.5h26.5c9.3,7.2,15,18.2,15,30.5c0,12.3-5.7,23.3-15,30.5 c8.6,3.6,13.5,12.4,13.5,25v24h-24v-24c0-12-5.7-20.4-16-25C115.3,52.5,121.4,42.6,121.4,30.5'
fill='#502D3C'
/>
</svg>
)
}
export function HexIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1450.3 600'>
+2
View File
@@ -133,6 +133,7 @@ import {
ReductoIcon,
ResendIcon,
RevenueCatIcon,
RipplingIcon,
S3Icon,
SalesforceIcon,
SearchIcon,
@@ -306,6 +307,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
reducto_v2: ReductoIcon,
resend: ResendIcon,
revenuecat: RevenueCatIcon,
rippling: RipplingIcon,
s3: S3Icon,
salesforce: SalesforceIcon,
search: SearchIcon,
@@ -128,6 +128,7 @@
"reducto",
"resend",
"revenuecat",
"rippling",
"s3",
"salesforce",
"search",
+3 -2
View File
@@ -59,8 +59,9 @@ Generate SVG images from text prompts using QuiverAI
| --------- | ---- | ----------- |
| `success` | boolean | Whether the SVG generation succeeded |
| `output` | object | Generated SVG output |
| ↳ `file` | file | Generated SVG file |
| ↳ `svgContent` | string | Raw SVG markup content |
| ↳ `file` | file | First generated SVG file |
| ↳ `files` | json | All generated SVG files \(when n &gt; 1\) |
| ↳ `svgContent` | string | Raw SVG markup content of the first result |
| ↳ `id` | string | Generation request ID |
| ↳ `usage` | json | Token usage statistics |
| ↳ `totalTokens` | number | Total tokens used |
@@ -0,0 +1,506 @@
---
title: Rippling
description: Manage employees, leave, departments, and company data in Rippling
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="rippling"
color="#FFCC1C"
/>
{/* MANUAL-CONTENT-START:intro */}
[Rippling](https://www.rippling.com/) is a unified workforce management platform that brings together HR, IT, and Finance into a single system. Rippling lets companies manage payroll, benefits, devices, apps, and more — all from one place — while automating the tedious manual work that typically bogs down HR teams. Its robust API provides programmatic access to employee data, organizational structure, leave management, and onboarding workflows.
**Why Rippling?**
- **Unified Employee System of Record:** A single source of truth for employee profiles, departments, teams, levels, and work locations — no more syncing data across disconnected tools.
- **Leave Management:** Full visibility into leave requests, balances, and types with the ability to approve or decline requests programmatically.
- **Company Insights:** Access company activity events, custom fields, and organizational hierarchy to power reporting and compliance workflows.
- **Onboarding Automation:** Push candidates directly into Rippling's onboarding flow, eliminating manual data entry when bringing on new hires.
- **Group Management:** Create and update groups for third-party app provisioning via SCIM-compatible endpoints.
**Using Rippling in Sim**
Sim's Rippling integration connects your agentic workflows directly to your Rippling account using an API key. With 19 operations spanning employees, departments, teams, leave, groups, and candidates, you can build powerful HR automations without writing backend code.
**Key benefits of using Rippling in Sim:**
- **Employee directory automation:** List, search, and retrieve employee details — including terminated employees — to power onboarding checklists, offboarding workflows, and org chart updates.
- **Leave workflow automation:** Monitor leave requests, check balances, and programmatically approve or decline requests based on custom business rules.
- **Organizational intelligence:** Query departments, teams, levels, work locations, and custom fields to build dynamic org reports or trigger actions based on structural changes.
- **Candidate onboarding:** Push candidates from your ATS or recruiting pipeline directly into Rippling's onboarding flow, complete with job title, department, and start date.
- **Activity monitoring:** Track company activity events to build audit trails, compliance dashboards, or alert workflows when key changes occur.
Whether you're automating new hire onboarding, building leave approval workflows, or syncing employee data across your tool stack, Rippling in Sim gives you direct, secure access to your HR platform — no middleware required. Simply configure your API key, select the operation you need, and let Sim handle the rest.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Integrate Rippling into your workflow. Manage employees, departments, teams, leave requests, work locations, groups, candidates, and company information.
## Tools
### `rippling_list_employees`
List all employees in Rippling with optional pagination
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `limit` | number | No | Maximum number of employees to return \(default 100, max 100\) |
| `offset` | number | No | Offset for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `employees` | array | List of employees |
| ↳ `id` | string | Employee ID |
| ↳ `firstName` | string | First name |
| ↳ `lastName` | string | Last name |
| ↳ `workEmail` | string | Work email address |
| ↳ `personalEmail` | string | Personal email address |
| ↳ `roleState` | string | Employment status |
| ↳ `department` | string | Department name or ID |
| ↳ `title` | string | Job title |
| ↳ `startDate` | string | Employment start date |
| ↳ `endDate` | string | Employment end date |
| ↳ `manager` | string | Manager ID or name |
| ↳ `phone` | string | Phone number |
| `totalCount` | number | Total number of employees returned |
### `rippling_get_employee`
Get details for a specific employee by ID
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `employeeId` | string | Yes | The ID of the employee to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Employee ID |
| `firstName` | string | First name |
| `lastName` | string | Last name |
| `workEmail` | string | Work email address |
| `personalEmail` | string | Personal email address |
| `roleState` | string | Employment status |
| `department` | string | Department name or ID |
| `title` | string | Job title |
| `startDate` | string | Employment start date |
| `endDate` | string | Employment end date |
| `manager` | string | Manager ID or name |
| `phone` | string | Phone number |
### `rippling_list_employees_with_terminated`
List all employees in Rippling including terminated employees with optional pagination
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `limit` | number | No | Maximum number of employees to return \(default 100, max 100\) |
| `offset` | number | No | Offset for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `employees` | array | List of employees including terminated |
| ↳ `id` | string | Employee ID |
| ↳ `firstName` | string | First name |
| ↳ `lastName` | string | Last name |
| ↳ `workEmail` | string | Work email address |
| ↳ `personalEmail` | string | Personal email address |
| ↳ `roleState` | string | Employment status |
| ↳ `department` | string | Department name or ID |
| ↳ `title` | string | Job title |
| ↳ `startDate` | string | Employment start date |
| ↳ `endDate` | string | Employment end date |
| ↳ `manager` | string | Manager ID or name |
| ↳ `phone` | string | Phone number |
| `totalCount` | number | Total number of employees returned |
### `rippling_list_departments`
List all departments in the Rippling organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `limit` | number | No | Maximum number of departments to return |
| `offset` | number | No | Offset for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `departments` | array | List of departments |
| ↳ `id` | string | Department ID |
| ↳ `name` | string | Department name |
| ↳ `parent` | string | Parent department ID |
| `totalCount` | number | Total number of departments returned |
### `rippling_list_teams`
List all teams in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `limit` | number | No | Maximum number of teams to return |
| `offset` | number | No | Offset for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `teams` | array | List of teams |
| ↳ `id` | string | Team ID |
| ↳ `name` | string | Team name |
| ↳ `parent` | string | Parent team ID |
| `totalCount` | number | Total number of teams returned |
### `rippling_list_levels`
List all position levels in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `limit` | number | No | Maximum number of levels to return |
| `offset` | number | No | Offset for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `levels` | array | List of position levels |
| ↳ `id` | string | Level ID |
| ↳ `name` | string | Level name |
| ↳ `parent` | string | Parent level ID |
| `totalCount` | number | Total number of levels returned |
### `rippling_list_work_locations`
List all work locations in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `limit` | number | No | Maximum number of work locations to return |
| `offset` | number | No | Offset for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `workLocations` | array | List of work locations |
| ↳ `id` | string | Work location ID |
| ↳ `nickname` | string | Location nickname |
| ↳ `street` | string | Street address |
| ↳ `city` | string | City |
| ↳ `state` | string | State or province |
| ↳ `zip` | string | ZIP or postal code |
| ↳ `country` | string | Country |
| `totalCount` | number | Total number of work locations returned |
### `rippling_get_company`
Get details for the current company in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Company ID |
| `name` | string | Company name |
| `address` | json | Company address with street, city, state, zip, country |
| `email` | string | Company email address |
| `phone` | string | Company phone number |
| `workLocations` | array | List of work location IDs |
### `rippling_get_company_activity`
Get activity events for the current company in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `startDate` | string | No | Start date filter in ISO format \(e.g. 2024-01-01\) |
| `endDate` | string | No | End date filter in ISO format \(e.g. 2024-12-31\) |
| `limit` | number | No | Maximum number of activity events to return |
| `next` | string | No | Cursor for fetching the next page of results |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `events` | array | List of company activity events |
| ↳ `id` | string | Event ID |
| ↳ `type` | string | Event type |
| ↳ `description` | string | Event description |
| ↳ `createdAt` | string | Event creation timestamp |
| ↳ `actor` | json | Actor who triggered the event \(id, name\) |
| `totalCount` | number | Total number of activity events returned |
| `nextCursor` | string | Cursor for fetching the next page of results |
### `rippling_list_custom_fields`
List all custom fields defined in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `limit` | number | No | Maximum number of custom fields to return |
| `offset` | number | No | Offset for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `customFields` | array | List of custom fields |
| ↳ `id` | string | Custom field ID |
| ↳ `type` | string | Field type |
| ↳ `title` | string | Field title |
| ↳ `mandatory` | boolean | Whether the field is mandatory |
| `totalCount` | number | Total number of custom fields returned |
### `rippling_get_current_user`
Get the current authenticated user details
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | User ID |
| `workEmail` | string | Work email address |
| `company` | string | Company ID |
### `rippling_list_leave_requests`
List leave requests in Rippling with optional filtering by date range and status
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `startDate` | string | No | Filter by start date \(ISO date string\) |
| `endDate` | string | No | Filter by end date \(ISO date string\) |
| `status` | string | No | Filter by status \(e.g. pending, approved, declined\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leaveRequests` | array | List of leave requests |
| ↳ `id` | string | Leave request ID |
| ↳ `requestedBy` | string | Employee ID who requested leave |
| ↳ `status` | string | Request status \(pending/approved/declined\) |
| ↳ `startDate` | string | Leave start date |
| ↳ `endDate` | string | Leave end date |
| ↳ `reason` | string | Reason for leave |
| ↳ `leaveType` | string | Type of leave |
| ↳ `createdAt` | string | When the request was created |
| `totalCount` | number | Total number of leave requests returned |
### `rippling_process_leave_request`
Approve or decline a leave request in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `leaveRequestId` | string | Yes | The ID of the leave request to process |
| `action` | string | Yes | Action to take on the leave request \(approve or decline\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Leave request ID |
| `status` | string | Updated status of the leave request |
| `requestedBy` | string | Employee ID who requested leave |
| `startDate` | string | Leave start date |
| `endDate` | string | Leave end date |
### `rippling_list_leave_balances`
List leave balances for all employees in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `limit` | number | No | Maximum number of leave balances to return |
| `offset` | number | No | Offset for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leaveBalances` | array | List of employee leave balances |
| ↳ `employeeId` | string | Employee ID |
| ↳ `balances` | array | Leave balance entries |
| ↳ `leaveType` | string | Type of leave |
| ↳ `minutesRemaining` | number | Minutes of leave remaining |
| `totalCount` | number | Total number of leave balances returned |
### `rippling_get_leave_balance`
Get leave balance for a specific employee by role ID
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `roleId` | string | Yes | The employee/role ID to retrieve leave balance for |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `employeeId` | string | Employee ID |
| `balances` | array | Leave balance entries |
| ↳ `leaveType` | string | Type of leave |
| ↳ `minutesRemaining` | number | Minutes of leave remaining |
### `rippling_list_leave_types`
List company leave types configured in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `managedBy` | string | No | Filter leave types by manager |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leaveTypes` | array | List of company leave types |
| ↳ `id` | string | Leave type ID |
| ↳ `name` | string | Leave type name |
| ↳ `managedBy` | string | Manager of this leave type |
| `totalCount` | number | Total number of leave types returned |
### `rippling_create_group`
Create a new group in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `name` | string | Yes | Name of the group |
| `spokeId` | string | Yes | Third-party app identifier |
| `users` | json | No | Array of user ID strings to add to the group |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Group ID |
| `name` | string | Group name |
| `spokeId` | string | Third-party app identifier |
| `users` | array | Array of user IDs in the group |
| `version` | number | Group version number |
### `rippling_update_group`
Update an existing group in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `groupId` | string | Yes | The ID of the group to update |
| `name` | string | No | New name for the group |
| `spokeId` | string | No | Third-party app identifier |
| `users` | json | No | Array of user ID strings to set for the group |
| `version` | number | No | Group version number for optimistic concurrency |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Group ID |
| `name` | string | Group name |
| `spokeId` | string | Third-party app identifier |
| `users` | array | Array of user IDs in the group |
| `version` | number | Group version number |
### `rippling_push_candidate`
Push a candidate to onboarding in Rippling
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Rippling API key |
| `firstName` | string | Yes | Candidate first name |
| `lastName` | string | Yes | Candidate last name |
| `email` | string | Yes | Candidate email address |
| `phone` | string | No | Candidate phone number |
| `jobTitle` | string | No | Job title for the candidate |
| `department` | string | No | Department for the candidate |
| `startDate` | string | No | Start date in ISO 8601 format \(e.g., 2025-01-15\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Candidate ID |
| `firstName` | string | Candidate first name |
| `lastName` | string | Candidate last name |
| `email` | string | Candidate email address |
| `status` | string | Candidate onboarding status |
@@ -133,6 +133,7 @@ import {
ReductoIcon,
ResendIcon,
RevenueCatIcon,
RipplingIcon,
S3Icon,
SalesforceIcon,
SearchIcon,
@@ -306,6 +307,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
reducto_v2: ReductoIcon,
resend: ResendIcon,
revenuecat: RevenueCatIcon,
rippling: RipplingIcon,
s3: S3Icon,
salesforce: SalesforceIcon,
search: SearchIcon,
@@ -904,7 +904,7 @@
},
{
"name": "Assert Record (Upsert)",
"description": "Upsert a record in Attio — creates it if no match is found, updates it if a match exists"
"description": "Upsert a record in Attio \u2014 creates it if no match is found, updates it if a match exists"
},
{
"name": "List Notes",
@@ -2504,7 +2504,7 @@
"slug": "dspy",
"name": "DSPy",
"description": "Run predictions using self-hosted DSPy programs",
"longDescription": "Integrate with your self-hosted DSPy programs for LLM-powered predictions. Supports Predict, Chain of Thought, and ReAct agents. DSPy is the framework for programming—not prompting—language models.",
"longDescription": "Integrate with your self-hosted DSPy programs for LLM-powered predictions. Supports Predict, Chain of Thought, and ReAct agents. DSPy is the framework for programming\u2014not prompting\u2014language models.",
"bgColor": "#E0E0E0",
"iconName": "DsPyIcon",
"docsUrl": "https://docs.sim.ai/tools/dspy",
@@ -8871,6 +8871,101 @@
"integrationType": "ecommerce",
"tags": ["payments", "subscriptions"]
},
{
"type": "rippling",
"slug": "rippling",
"name": "Rippling",
"description": "Manage employees, leave, departments, and company data in Rippling",
"longDescription": "Integrate Rippling into your workflow. Manage employees, departments, teams, leave requests, work locations, groups, candidates, and company information.",
"bgColor": "#FFCC1C",
"iconName": "RipplingIcon",
"docsUrl": "https://docs.sim.ai/tools/rippling",
"operations": [
{
"name": "List Employees",
"description": "List all employees in Rippling with optional pagination"
},
{
"name": "Get Employee",
"description": "Get details for a specific employee by ID"
},
{
"name": "List Employees (Including Terminated)",
"description": "List all employees in Rippling including terminated employees with optional pagination"
},
{
"name": "List Departments",
"description": "List all departments in the Rippling organization"
},
{
"name": "List Teams",
"description": "List all teams in Rippling"
},
{
"name": "List Levels",
"description": "List all position levels in Rippling"
},
{
"name": "List Work Locations",
"description": "List all work locations in Rippling"
},
{
"name": "Get Company",
"description": "Get details for the current company in Rippling"
},
{
"name": "Get Company Activity",
"description": "Get activity events for the current company in Rippling"
},
{
"name": "List Custom Fields",
"description": "List all custom fields defined in Rippling"
},
{
"name": "Get Current User",
"description": "Get the current authenticated user details"
},
{
"name": "List Leave Requests",
"description": "List leave requests in Rippling with optional filtering by date range and status"
},
{
"name": "Approve/Decline Leave Request",
"description": "Approve or decline a leave request in Rippling"
},
{
"name": "List Leave Balances",
"description": "List leave balances for all employees in Rippling"
},
{
"name": "Get Leave Balance",
"description": "Get leave balance for a specific employee by role ID"
},
{
"name": "List Leave Types",
"description": ""
},
{
"name": "Create Group",
"description": "Create a new group in Rippling"
},
{
"name": "Update Group",
"description": "Update an existing group in Rippling"
},
{
"name": "Push Candidate",
"description": "Push a candidate to onboarding in Rippling"
}
],
"operationCount": 19,
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
"category": "tools",
"integrationType": "hr",
"tags": ["hiring"]
},
{
"type": "s3",
"slug": "s3",
+437
View File
@@ -0,0 +1,437 @@
import { RipplingIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode, IntegrationType } from '@/blocks/types'
export const RipplingBlock: BlockConfig = {
type: 'rippling',
name: 'Rippling',
description: 'Manage employees, leave, departments, and company data in Rippling',
longDescription:
'Integrate Rippling into your workflow. Manage employees, departments, teams, leave requests, work locations, groups, candidates, and company information.',
docsLink: 'https://docs.sim.ai/tools/rippling',
category: 'tools',
integrationType: IntegrationType.HR,
tags: ['hiring'],
bgColor: '#FFCC1C',
icon: RipplingIcon,
authMode: AuthMode.ApiKey,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'List Employees', id: 'list_employees' },
{ label: 'Get Employee', id: 'get_employee' },
{ label: 'List Employees (Including Terminated)', id: 'list_employees_with_terminated' },
{ label: 'List Departments', id: 'list_departments' },
{ label: 'List Teams', id: 'list_teams' },
{ label: 'List Levels', id: 'list_levels' },
{ label: 'List Work Locations', id: 'list_work_locations' },
{ label: 'Get Company', id: 'get_company' },
{ label: 'Get Company Activity', id: 'get_company_activity' },
{ label: 'List Custom Fields', id: 'list_custom_fields' },
{ label: 'Get Current User', id: 'get_current_user' },
{ label: 'List Leave Requests', id: 'list_leave_requests' },
{ label: 'Approve/Decline Leave Request', id: 'process_leave_request' },
{ label: 'List Leave Balances', id: 'list_leave_balances' },
{ label: 'Get Leave Balance', id: 'get_leave_balance' },
{ label: 'List Leave Types', id: 'list_leave_types' },
{ label: 'Create Group', id: 'create_group' },
{ label: 'Update Group', id: 'update_group' },
{ label: 'Push Candidate', id: 'push_candidate' },
],
value: () => 'list_employees',
},
// Employee ID - for get_employee
{
id: 'employeeId',
title: 'Employee ID',
type: 'short-input',
placeholder: 'Enter the employee ID',
required: { field: 'operation', value: 'get_employee' },
condition: { field: 'operation', value: 'get_employee' },
},
// Leave Request fields
{
id: 'leaveRequestId',
title: 'Leave Request ID',
type: 'short-input',
placeholder: 'Enter the leave request ID',
required: { field: 'operation', value: 'process_leave_request' },
condition: { field: 'operation', value: 'process_leave_request' },
},
{
id: 'action',
title: 'Action',
type: 'dropdown',
options: [
{ label: 'Approve', id: 'approve' },
{ label: 'Decline', id: 'decline' },
],
value: () => 'approve',
required: { field: 'operation', value: 'process_leave_request' },
condition: { field: 'operation', value: 'process_leave_request' },
},
// Leave balance - role ID
{
id: 'roleId',
title: 'Employee/Role ID',
type: 'short-input',
placeholder: 'Enter the employee or role ID',
required: { field: 'operation', value: 'get_leave_balance' },
condition: { field: 'operation', value: 'get_leave_balance' },
},
// Group fields
{
id: 'groupName',
title: 'Group Name',
type: 'short-input',
placeholder: 'Enter group name',
required: { field: 'operation', value: 'create_group' },
condition: { field: 'operation', value: ['create_group', 'update_group'] },
},
{
id: 'spokeId',
title: 'Spoke ID',
type: 'short-input',
placeholder: 'Third-party app identifier',
required: { field: 'operation', value: 'create_group' },
condition: { field: 'operation', value: ['create_group', 'update_group'] },
},
{
id: 'groupId',
title: 'Group ID',
type: 'short-input',
placeholder: 'Enter the group ID to update',
required: { field: 'operation', value: 'update_group' },
condition: { field: 'operation', value: 'update_group' },
},
{
id: 'users',
title: 'User IDs',
type: 'long-input',
placeholder: '["user-id-1", "user-id-2"]',
mode: 'advanced',
condition: { field: 'operation', value: ['create_group', 'update_group'] },
},
{
id: 'groupVersion',
title: 'Version',
type: 'short-input',
placeholder: 'Group version number',
mode: 'advanced',
condition: { field: 'operation', value: 'update_group' },
},
// Candidate fields
{
id: 'firstName',
title: 'First Name',
type: 'short-input',
placeholder: 'Candidate first name',
required: { field: 'operation', value: 'push_candidate' },
condition: { field: 'operation', value: 'push_candidate' },
},
{
id: 'lastName',
title: 'Last Name',
type: 'short-input',
placeholder: 'Candidate last name',
required: { field: 'operation', value: 'push_candidate' },
condition: { field: 'operation', value: 'push_candidate' },
},
{
id: 'email',
title: 'Email',
type: 'short-input',
placeholder: 'Candidate email address',
required: { field: 'operation', value: 'push_candidate' },
condition: { field: 'operation', value: 'push_candidate' },
},
{
id: 'candidatePhone',
title: 'Phone',
type: 'short-input',
placeholder: 'Candidate phone number',
mode: 'advanced',
condition: { field: 'operation', value: 'push_candidate' },
},
{
id: 'jobTitle',
title: 'Job Title',
type: 'short-input',
placeholder: 'Job title for the candidate',
mode: 'advanced',
condition: { field: 'operation', value: 'push_candidate' },
},
{
id: 'candidateDepartment',
title: 'Department',
type: 'short-input',
placeholder: 'Department for the candidate',
mode: 'advanced',
condition: { field: 'operation', value: 'push_candidate' },
},
{
id: 'candidateStartDate',
title: 'Start Date',
type: 'short-input',
placeholder: 'YYYY-MM-DD',
mode: 'advanced',
condition: { field: 'operation', value: 'push_candidate' },
wandConfig: {
enabled: true,
prompt:
'Generate a date in YYYY-MM-DD format for a candidate start date. Return ONLY the date string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
// Date filters for leave requests and company activity
{
id: 'startDate',
title: 'Start Date',
type: 'short-input',
placeholder: 'YYYY-MM-DD',
mode: 'advanced',
condition: { field: 'operation', value: ['list_leave_requests', 'get_company_activity'] },
wandConfig: {
enabled: true,
prompt:
'Generate a date in YYYY-MM-DD format for filtering by start date. Return ONLY the date string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'endDate',
title: 'End Date',
type: 'short-input',
placeholder: 'YYYY-MM-DD',
mode: 'advanced',
condition: { field: 'operation', value: ['list_leave_requests', 'get_company_activity'] },
wandConfig: {
enabled: true,
prompt:
'Generate a date in YYYY-MM-DD format for filtering by end date. Return ONLY the date string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'status',
title: 'Status Filter',
type: 'short-input',
placeholder: 'e.g., pending, approved, declined',
mode: 'advanced',
condition: { field: 'operation', value: 'list_leave_requests' },
},
{
id: 'managedBy',
title: 'Managed By',
type: 'short-input',
placeholder: 'Filter by manager',
mode: 'advanced',
condition: { field: 'operation', value: 'list_leave_types' },
},
// Pagination - shared across list operations (offset-based)
{
id: 'limit',
title: 'Limit',
type: 'short-input',
placeholder: '100',
mode: 'advanced',
condition: {
field: 'operation',
value: [
'list_employees',
'list_employees_with_terminated',
'list_departments',
'list_teams',
'list_levels',
'list_work_locations',
'list_custom_fields',
'list_leave_balances',
'get_company_activity',
],
},
},
{
id: 'offset',
title: 'Offset',
type: 'short-input',
placeholder: '0',
mode: 'advanced',
condition: {
field: 'operation',
value: [
'list_employees',
'list_employees_with_terminated',
'list_departments',
'list_teams',
'list_levels',
'list_work_locations',
'list_custom_fields',
'list_leave_balances',
],
},
},
// Cursor-based pagination for company activity
{
id: 'nextCursor',
title: 'Next Page Cursor',
type: 'short-input',
placeholder: 'Cursor from previous response',
mode: 'advanced',
condition: { field: 'operation', value: 'get_company_activity' },
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your Rippling API key',
required: true,
password: true,
},
],
tools: {
access: [
'rippling_list_employees',
'rippling_get_employee',
'rippling_list_employees_with_terminated',
'rippling_list_departments',
'rippling_list_teams',
'rippling_list_levels',
'rippling_list_work_locations',
'rippling_get_company',
'rippling_get_company_activity',
'rippling_list_custom_fields',
'rippling_get_current_user',
'rippling_list_leave_requests',
'rippling_process_leave_request',
'rippling_list_leave_balances',
'rippling_get_leave_balance',
'rippling_list_leave_types',
'rippling_create_group',
'rippling_update_group',
'rippling_push_candidate',
],
config: {
tool: (params) => `rippling_${params.operation}`,
params: (params) => {
const mapped: Record<string, unknown> = {
apiKey: params.apiKey,
}
if (params.employeeId) mapped.employeeId = params.employeeId
if (params.leaveRequestId) mapped.leaveRequestId = params.leaveRequestId
if (params.action) mapped.action = params.action
if (params.roleId) mapped.roleId = params.roleId
if (params.spokeId) mapped.spokeId = params.spokeId
if (params.groupId) mapped.groupId = params.groupId
if (params.firstName) mapped.firstName = params.firstName
if (params.lastName) mapped.lastName = params.lastName
if (params.email) mapped.email = params.email
if (params.jobTitle) mapped.jobTitle = params.jobTitle
if (params.startDate && params.operation !== 'push_candidate')
mapped.startDate = params.startDate
if (params.endDate && params.operation !== 'push_candidate') mapped.endDate = params.endDate
if (params.status) mapped.status = params.status
if (params.managedBy) mapped.managedBy = params.managedBy
if (params.limit != null && params.limit !== '') mapped.limit = Number(params.limit)
if (params.offset != null && params.offset !== '') mapped.offset = Number(params.offset)
if (params.groupVersion != null && params.groupVersion !== '')
mapped.version = Number(params.groupVersion)
if (params.groupName) mapped.name = params.groupName
if (params.candidatePhone) mapped.phone = params.candidatePhone
if (params.candidateDepartment) mapped.department = params.candidateDepartment
if (params.candidateStartDate && params.operation === 'push_candidate')
mapped.startDate = params.candidateStartDate
if (params.nextCursor) mapped.next = params.nextCursor
if (params.users) {
try {
mapped.users =
typeof params.users === 'string' ? JSON.parse(params.users) : params.users
} catch {
throw new Error(
'Invalid JSON for "User IDs" field. Expected an array like ["user-id-1", "user-id-2"].'
)
}
}
return mapped
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
employeeId: { type: 'string', description: 'Employee ID' },
leaveRequestId: { type: 'string', description: 'Leave request ID' },
action: { type: 'string', description: 'Action to take (approve or decline)' },
roleId: { type: 'string', description: 'Employee/role ID for leave balance' },
groupName: { type: 'string', description: 'Group name' },
spokeId: { type: 'string', description: 'Third-party app identifier' },
groupId: { type: 'string', description: 'Group ID to update' },
users: { type: 'json', description: 'Array of user IDs' },
firstName: { type: 'string', description: 'Candidate first name' },
lastName: { type: 'string', description: 'Candidate last name' },
email: { type: 'string', description: 'Candidate email' },
candidatePhone: { type: 'string', description: 'Candidate phone number' },
jobTitle: { type: 'string', description: 'Job title' },
candidateDepartment: { type: 'string', description: 'Department' },
candidateStartDate: { type: 'string', description: 'Start date (ISO format)' },
startDate: { type: 'string', description: 'Filter start date' },
endDate: { type: 'string', description: 'Filter end date' },
status: { type: 'string', description: 'Leave request status filter' },
managedBy: { type: 'string', description: 'Filter leave types by manager' },
limit: { type: 'number', description: 'Maximum number of results' },
offset: { type: 'number', description: 'Pagination offset' },
nextCursor: { type: 'string', description: 'Cursor for next page (company activity)' },
apiKey: { type: 'string', description: 'Rippling API key' },
},
outputs: {
employees: {
type: 'array',
description:
'List of employees (id, firstName, lastName, workEmail, roleState, department, title)',
},
departments: { type: 'array', description: 'List of departments (id, name, parent)' },
teams: { type: 'array', description: 'List of teams (id, name, parent)' },
levels: { type: 'array', description: 'List of position levels (id, name, parent)' },
workLocations: {
type: 'array',
description: 'List of work locations (id, nickname, street, city, state, zip, country)',
},
customFields: {
type: 'array',
description: 'List of custom fields (id, type, title, mandatory)',
},
events: {
type: 'array',
description: 'List of company activity events (id, type, description, createdAt, actor)',
},
leaveRequests: {
type: 'array',
description: 'List of leave requests (id, requestedBy, status, startDate, endDate)',
},
leaveBalances: { type: 'array', description: 'List of leave balances (employeeId, balances)' },
leaveTypes: { type: 'array', description: 'List of leave types (id, name, managedBy)' },
totalCount: { type: 'number', description: 'Total number of items returned' },
id: { type: 'string', description: 'Resource ID' },
name: { type: 'string', description: 'Resource name' },
workEmail: { type: 'string', description: 'Work email address' },
company: { type: 'string', description: 'Company ID' },
status: { type: 'string', description: 'Status of the resource' },
users: { type: 'array', description: 'Array of user IDs in a group' },
version: { type: 'number', description: 'Group version number' },
address: { type: 'json', description: 'Company address (street, city, state, zip, country)' },
email: { type: 'string', description: 'Email address' },
phone: { type: 'string', description: 'Phone number' },
balances: { type: 'array', description: 'Leave balance entries (leaveType, minutesRemaining)' },
employeeId: { type: 'string', description: 'Employee ID' },
nextCursor: { type: 'string', description: 'Cursor for fetching the next page of results' },
},
}
+2
View File
@@ -145,6 +145,7 @@ import { ReductoBlock, ReductoV2Block } from '@/blocks/blocks/reducto'
import { ResendBlock } from '@/blocks/blocks/resend'
import { ResponseBlock } from '@/blocks/blocks/response'
import { RevenueCatBlock } from '@/blocks/blocks/revenuecat'
import { RipplingBlock } from '@/blocks/blocks/rippling'
import { RouterBlock, RouterV2Block } from '@/blocks/blocks/router'
import { RssBlock } from '@/blocks/blocks/rss'
import { S3Block } from '@/blocks/blocks/s3'
@@ -367,6 +368,7 @@ export const registry: Record<string, BlockConfig> = {
resend: ResendBlock,
response: ResponseBlock,
revenuecat: RevenueCatBlock,
rippling: RipplingBlock,
router: RouterBlock,
router_v2: RouterV2Block,
rss: RssBlock,
+11
View File
@@ -6270,6 +6270,17 @@ export function RedisIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function RipplingIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 145.3 109.9' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M17.5,30.5C17.5,18.4,11.4,8.4,0,0h26.5c9.3,7.2,15,18.2,15,30.5c0,12.3-5.7,23.3-15,30.5 c8.6,3.6,13.5,12.4,13.5,25v24H16v-24c0-12-5.7-20.4-16-25C11.4,52.5,17.5,42.6,17.5,30.5 M69.4,30.5c0-12.1-6.1-22.1-17.5-30.5 h26.5c9.3,7.2,15,18.2,15,30.5c0,12.3-5.7,23.3-15,30.5c8.6,3.6,13.5,12.4,13.5,25v24h-24v-24c0-12-5.7-20.4-16-25 C63.3,52.5,69.4,42.6,69.4,30.5 M121.4,30.5c0-12.1-6.1-22.1-17.5-30.5h26.5c9.3,7.2,15,18.2,15,30.5c0,12.3-5.7,23.3-15,30.5 c8.6,3.6,13.5,12.4,13.5,25v24h-24v-24c0-12-5.7-20.4-16-25C115.3,52.5,121.4,42.6,121.4,30.5'
fill='#502D3C'
/>
</svg>
)
}
export function HexIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1450.3 600'>
+40
View File
@@ -1814,6 +1814,27 @@ import {
revenuecatRevokeGoogleSubscriptionTool,
revenuecatUpdateSubscriberAttributesTool,
} from '@/tools/revenuecat'
import {
ripplingCreateGroupTool,
ripplingGetCompanyActivityTool,
ripplingGetCompanyTool,
ripplingGetCurrentUserTool,
ripplingGetEmployeeTool,
ripplingGetLeaveBalanceTool,
ripplingListCustomFieldsTool,
ripplingListDepartmentsTool,
ripplingListEmployeesTool,
ripplingListEmployeesWithTerminatedTool,
ripplingListLeaveBalancesTool,
ripplingListLeaveRequestsTool,
ripplingListLeaveTypesTool,
ripplingListLevelsTool,
ripplingListTeamsTool,
ripplingListWorkLocationsTool,
ripplingProcessLeaveRequestTool,
ripplingPushCandidateTool,
ripplingUpdateGroupTool,
} from '@/tools/rippling'
import {
s3CopyObjectTool,
s3DeleteObjectTool,
@@ -3457,6 +3478,25 @@ export const tools: Record<string, ToolConfig> = {
revenuecat_defer_google_subscription: revenuecatDeferGoogleSubscriptionTool,
revenuecat_refund_google_subscription: revenuecatRefundGoogleSubscriptionTool,
revenuecat_revoke_google_subscription: revenuecatRevokeGoogleSubscriptionTool,
rippling_create_group: ripplingCreateGroupTool,
rippling_get_company: ripplingGetCompanyTool,
rippling_get_company_activity: ripplingGetCompanyActivityTool,
rippling_get_current_user: ripplingGetCurrentUserTool,
rippling_get_employee: ripplingGetEmployeeTool,
rippling_get_leave_balance: ripplingGetLeaveBalanceTool,
rippling_list_custom_fields: ripplingListCustomFieldsTool,
rippling_list_departments: ripplingListDepartmentsTool,
rippling_list_employees: ripplingListEmployeesTool,
rippling_list_employees_with_terminated: ripplingListEmployeesWithTerminatedTool,
rippling_list_leave_balances: ripplingListLeaveBalancesTool,
rippling_list_leave_requests: ripplingListLeaveRequestsTool,
rippling_list_leave_types: ripplingListLeaveTypesTool,
rippling_list_levels: ripplingListLevelsTool,
rippling_list_teams: ripplingListTeamsTool,
rippling_list_work_locations: ripplingListWorkLocationsTool,
rippling_process_leave_request: ripplingProcessLeaveRequestTool,
rippling_push_candidate: ripplingPushCandidateTool,
rippling_update_group: ripplingUpdateGroupTool,
google_drive_copy: googleDriveCopyTool,
google_drive_create_folder: googleDriveCreateFolderTool,
google_drive_delete: googleDriveDeleteTool,
+91
View File
@@ -0,0 +1,91 @@
import type { RipplingCreateGroupParams, RipplingCreateGroupResponse } from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingCreateGroupTool: ToolConfig<
RipplingCreateGroupParams,
RipplingCreateGroupResponse
> = {
id: 'rippling_create_group',
name: 'Rippling Create Group',
description: 'Create a new group in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the group',
},
spokeId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Third-party app identifier',
},
users: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Array of user ID strings to add to the group',
},
},
request: {
url: 'https://api.rippling.com/platform/api/groups',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
name: params.name,
spokeId: params.spokeId,
}
if (params.users !== undefined) {
body.users = params.users
}
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
return {
success: true,
output: {
id: (data.id as string) ?? '',
name: (data.name as string) ?? null,
spokeId: (data.spokeId as string) ?? null,
users: (data.users as string[]) ?? [],
version: (data.version as number) ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Group ID' },
name: { type: 'string', description: 'Group name' },
spokeId: { type: 'string', description: 'Third-party app identifier' },
users: {
type: 'array',
description: 'Array of user IDs in the group',
items: { type: 'string' },
},
version: { type: 'number', description: 'Group version number' },
},
}
+75
View File
@@ -0,0 +1,75 @@
import type { RipplingGetCompanyParams, RipplingGetCompanyResponse } from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingGetCompanyTool: ToolConfig<
RipplingGetCompanyParams,
RipplingGetCompanyResponse
> = {
id: 'rippling_get_company',
name: 'Rippling Get Company',
description: 'Get details for the current company in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
},
request: {
url: 'https://api.rippling.com/platform/api/companies/current',
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const address = data.address ?? {}
return {
success: true,
output: {
id: data.id ?? '',
name: data.name ?? null,
address: {
street: address.street ?? null,
city: address.city ?? null,
state: address.state ?? null,
zip: address.zip ?? null,
country: address.country ?? null,
},
email: data.email ?? null,
phone: data.phone ?? null,
workLocations: data.workLocations ?? [],
},
}
},
outputs: {
id: { type: 'string', description: 'Company ID' },
name: { type: 'string', description: 'Company name', optional: true },
address: {
type: 'json',
description: 'Company address with street, city, state, zip, country',
},
email: { type: 'string', description: 'Company email address', optional: true },
phone: { type: 'string', description: 'Company phone number', optional: true },
workLocations: {
type: 'array',
description: 'List of work location IDs',
items: { type: 'string' },
},
},
}
@@ -0,0 +1,125 @@
import type {
RipplingGetCompanyActivityParams,
RipplingGetCompanyActivityResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingGetCompanyActivityTool: ToolConfig<
RipplingGetCompanyActivityParams,
RipplingGetCompanyActivityResponse
> = {
id: 'rippling_get_company_activity',
name: 'Rippling Get Company Activity',
description: 'Get activity events for the current company in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
startDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Start date filter in ISO format (e.g. 2024-01-01)',
},
endDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'End date filter in ISO format (e.g. 2024-12-31)',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of activity events to return',
},
next: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor for fetching the next page of results',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.startDate) query.set('startDate', params.startDate)
if (params.endDate) query.set('endDate', params.endDate)
if (params.limit != null) query.set('limit', String(params.limit))
if (params.next) query.set('next', params.next)
const qs = query.toString()
return `https://api.rippling.com/platform/api/company_activity${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const nextCursor = Array.isArray(data) ? null : ((data.next as string) ?? null)
const events = results.map((event: Record<string, unknown>) => {
const actor = (event.actor as Record<string, unknown>) ?? {}
return {
id: (event.id as string) ?? '',
type: (event.type as string) ?? null,
description: (event.description as string) ?? null,
createdAt: (event.createdAt as string) ?? null,
actor: {
id: (actor.id as string) ?? null,
name: (actor.name as string) ?? null,
},
}
})
return {
success: true,
output: {
events,
totalCount: events.length,
nextCursor,
},
}
},
outputs: {
events: {
type: 'array',
description: 'List of company activity events',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Event ID' },
type: { type: 'string', description: 'Event type' },
description: { type: 'string', description: 'Event description' },
createdAt: { type: 'string', description: 'Event creation timestamp' },
actor: { type: 'json', description: 'Actor who triggered the event (id, name)' },
},
},
},
totalCount: {
type: 'number',
description: 'Number of activity events returned on this page',
},
nextCursor: {
type: 'string',
description: 'Cursor for fetching the next page of results',
optional: true,
},
},
}
@@ -0,0 +1,57 @@
import type {
RipplingGetCurrentUserParams,
RipplingGetCurrentUserResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingGetCurrentUserTool: ToolConfig<
RipplingGetCurrentUserParams,
RipplingGetCurrentUserResponse
> = {
id: 'rippling_get_current_user',
name: 'Rippling Get Current User',
description: 'Get the current authenticated user details',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
},
request: {
url: 'https://api.rippling.com/platform/api/me',
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? '',
workEmail: data.workEmail ?? null,
company: data.company ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'User ID' },
workEmail: { type: 'string', description: 'Work email address', optional: true },
company: { type: 'string', description: 'Company ID', optional: true },
},
}
+79
View File
@@ -0,0 +1,79 @@
import type { RipplingGetEmployeeParams, RipplingGetEmployeeResponse } from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingGetEmployeeTool: ToolConfig<
RipplingGetEmployeeParams,
RipplingGetEmployeeResponse
> = {
id: 'rippling_get_employee',
name: 'Rippling Get Employee',
description: 'Get details for a specific employee by ID',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
employeeId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the employee to retrieve',
},
},
request: {
url: (params) =>
`https://api.rippling.com/platform/api/employees/${encodeURIComponent(params.employeeId.trim())}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const emp = await response.json()
return {
success: true,
output: {
id: emp.id ?? '',
firstName: emp.firstName ?? null,
lastName: emp.lastName ?? null,
workEmail: emp.workEmail ?? null,
personalEmail: emp.personalEmail ?? null,
roleState: emp.roleState ?? null,
department: emp.department ?? null,
title: emp.title ?? null,
startDate: emp.startDate ?? null,
endDate: emp.endDate ?? null,
manager: emp.manager ?? null,
phone: emp.phone ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Employee ID' },
firstName: { type: 'string', description: 'First name', optional: true },
lastName: { type: 'string', description: 'Last name', optional: true },
workEmail: { type: 'string', description: 'Work email address', optional: true },
personalEmail: { type: 'string', description: 'Personal email address', optional: true },
roleState: { type: 'string', description: 'Employment status', optional: true },
department: { type: 'string', description: 'Department name or ID', optional: true },
title: { type: 'string', description: 'Job title', optional: true },
startDate: { type: 'string', description: 'Employment start date', optional: true },
endDate: { type: 'string', description: 'Employment end date', optional: true },
manager: { type: 'string', description: 'Manager ID or name', optional: true },
phone: { type: 'string', description: 'Phone number', optional: true },
},
}
@@ -0,0 +1,77 @@
import type {
RipplingGetLeaveBalanceParams,
RipplingGetLeaveBalanceResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingGetLeaveBalanceTool: ToolConfig<
RipplingGetLeaveBalanceParams,
RipplingGetLeaveBalanceResponse
> = {
id: 'rippling_get_leave_balance',
name: 'Rippling Get Leave Balance',
description: 'Get leave balance for a specific employee by role ID',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
roleId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The employee/role ID to retrieve leave balance for',
},
},
request: {
url: (params) =>
`https://api.rippling.com/platform/api/leave_balances/${encodeURIComponent(params.roleId.trim())}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
return {
success: true,
output: {
employeeId: data.employeeId ?? '',
balances: (Array.isArray(data.balances) ? data.balances : []).map(
(b: Record<string, unknown>) => ({
leaveType: (b.leaveType as string) ?? '',
minutesRemaining: (b.minutesRemaining as number) ?? 0,
})
),
},
}
},
outputs: {
employeeId: { type: 'string', description: 'Employee ID' },
balances: {
type: 'array',
description: 'Leave balance entries',
items: {
type: 'json',
properties: {
leaveType: { type: 'string', description: 'Type of leave' },
minutesRemaining: { type: 'number', description: 'Minutes of leave remaining' },
},
},
},
},
}
+20
View File
@@ -0,0 +1,20 @@
export { ripplingCreateGroupTool } from '@/tools/rippling/create_group'
export { ripplingGetCompanyTool } from '@/tools/rippling/get_company'
export { ripplingGetCompanyActivityTool } from '@/tools/rippling/get_company_activity'
export { ripplingGetCurrentUserTool } from '@/tools/rippling/get_current_user'
export { ripplingGetEmployeeTool } from '@/tools/rippling/get_employee'
export { ripplingGetLeaveBalanceTool } from '@/tools/rippling/get_leave_balance'
export { ripplingListCustomFieldsTool } from '@/tools/rippling/list_custom_fields'
export { ripplingListDepartmentsTool } from '@/tools/rippling/list_departments'
export { ripplingListEmployeesTool } from '@/tools/rippling/list_employees'
export { ripplingListEmployeesWithTerminatedTool } from '@/tools/rippling/list_employees_with_terminated'
export { ripplingListLeaveBalancesTool } from '@/tools/rippling/list_leave_balances'
export { ripplingListLeaveRequestsTool } from '@/tools/rippling/list_leave_requests'
export { ripplingListLeaveTypesTool } from '@/tools/rippling/list_leave_types'
export { ripplingListLevelsTool } from '@/tools/rippling/list_levels'
export { ripplingListTeamsTool } from '@/tools/rippling/list_teams'
export { ripplingListWorkLocationsTool } from '@/tools/rippling/list_work_locations'
export { ripplingProcessLeaveRequestTool } from '@/tools/rippling/process_leave_request'
export { ripplingPushCandidateTool } from '@/tools/rippling/push_candidate'
export * from '@/tools/rippling/types'
export { ripplingUpdateGroupTool } from '@/tools/rippling/update_group'
@@ -0,0 +1,96 @@
import type {
RipplingListCustomFieldsParams,
RipplingListCustomFieldsResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListCustomFieldsTool: ToolConfig<
RipplingListCustomFieldsParams,
RipplingListCustomFieldsResponse
> = {
id: 'rippling_list_custom_fields',
name: 'Rippling List Custom Fields',
description: 'List all custom fields defined in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of custom fields to return',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.limit != null) query.set('limit', String(params.limit))
if (params.offset != null) query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.rippling.com/platform/api/custom_fields${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const customFields = results.map((field: Record<string, unknown>) => ({
id: (field.id as string) ?? '',
type: (field.type as string) ?? null,
title: (field.title as string) ?? null,
mandatory: Boolean(field.mandatory),
}))
return {
success: true,
output: {
customFields,
totalCount: customFields.length,
},
}
},
outputs: {
customFields: {
type: 'array',
description: 'List of custom fields',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Custom field ID' },
type: { type: 'string', description: 'Field type' },
title: { type: 'string', description: 'Field title' },
mandatory: { type: 'boolean', description: 'Whether the field is mandatory' },
},
},
},
totalCount: {
type: 'number',
description: 'Number of custom fields returned on this page',
},
},
}
@@ -0,0 +1,94 @@
import type {
RipplingListDepartmentsParams,
RipplingListDepartmentsResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListDepartmentsTool: ToolConfig<
RipplingListDepartmentsParams,
RipplingListDepartmentsResponse
> = {
id: 'rippling_list_departments',
name: 'Rippling List Departments',
description: 'List all departments in the Rippling organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of departments to return',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.limit != null) query.set('limit', String(params.limit))
if (params.offset != null) query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.rippling.com/platform/api/departments${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const departments = results.map((dept: Record<string, unknown>) => ({
id: (dept.id as string) ?? '',
name: (dept.name as string) ?? null,
parent: (dept.parent as string) ?? null,
}))
return {
success: true,
output: {
departments,
totalCount: departments.length,
},
}
},
outputs: {
departments: {
type: 'array',
description: 'List of departments',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Department ID' },
name: { type: 'string', description: 'Department name' },
parent: { type: 'string', description: 'Parent department ID' },
},
},
},
totalCount: {
type: 'number',
description: 'Number of departments returned on this page',
},
},
}
+112
View File
@@ -0,0 +1,112 @@
import type {
RipplingListEmployeesParams,
RipplingListEmployeesResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListEmployeesTool: ToolConfig<
RipplingListEmployeesParams,
RipplingListEmployeesResponse
> = {
id: 'rippling_list_employees',
name: 'Rippling List Employees',
description: 'List all employees in Rippling with optional pagination',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of employees to return (default 100, max 100)',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.limit != null) query.set('limit', String(params.limit))
if (params.offset != null) query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.rippling.com/platform/api/employees${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const employees = results.map((emp: Record<string, unknown>) => ({
id: (emp.id as string) ?? '',
firstName: (emp.firstName as string) ?? null,
lastName: (emp.lastName as string) ?? null,
workEmail: (emp.workEmail as string) ?? null,
personalEmail: (emp.personalEmail as string) ?? null,
roleState: (emp.roleState as string) ?? null,
department: (emp.department as string) ?? null,
title: (emp.title as string) ?? null,
startDate: (emp.startDate as string) ?? null,
endDate: (emp.endDate as string) ?? null,
manager: (emp.manager as string) ?? null,
phone: (emp.phone as string) ?? null,
}))
return {
success: true,
output: {
employees,
totalCount: employees.length,
},
}
},
outputs: {
employees: {
type: 'array',
description: 'List of employees',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Employee ID' },
firstName: { type: 'string', description: 'First name' },
lastName: { type: 'string', description: 'Last name' },
workEmail: { type: 'string', description: 'Work email address' },
personalEmail: { type: 'string', description: 'Personal email address' },
roleState: { type: 'string', description: 'Employment status' },
department: { type: 'string', description: 'Department name or ID' },
title: { type: 'string', description: 'Job title' },
startDate: { type: 'string', description: 'Employment start date' },
endDate: { type: 'string', description: 'Employment end date' },
manager: { type: 'string', description: 'Manager ID or name' },
phone: { type: 'string', description: 'Phone number' },
},
},
},
totalCount: {
type: 'number',
description: 'Number of employees returned on this page',
},
},
}
@@ -0,0 +1,113 @@
import type {
RipplingListEmployeesWithTerminatedParams,
RipplingListEmployeesWithTerminatedResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListEmployeesWithTerminatedTool: ToolConfig<
RipplingListEmployeesWithTerminatedParams,
RipplingListEmployeesWithTerminatedResponse
> = {
id: 'rippling_list_employees_with_terminated',
name: 'Rippling List Employees Including Terminated',
description:
'List all employees in Rippling including terminated employees with optional pagination',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of employees to return (default 100, max 100)',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.limit != null) query.set('limit', String(params.limit))
if (params.offset != null) query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.rippling.com/platform/api/employees/include_terminated${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const employees = results.map((emp: Record<string, unknown>) => ({
id: (emp.id as string) ?? '',
firstName: (emp.firstName as string) ?? null,
lastName: (emp.lastName as string) ?? null,
workEmail: (emp.workEmail as string) ?? null,
personalEmail: (emp.personalEmail as string) ?? null,
roleState: (emp.roleState as string) ?? null,
department: (emp.department as string) ?? null,
title: (emp.title as string) ?? null,
startDate: (emp.startDate as string) ?? null,
endDate: (emp.endDate as string) ?? null,
manager: (emp.manager as string) ?? null,
phone: (emp.phone as string) ?? null,
}))
return {
success: true,
output: {
employees,
totalCount: employees.length,
},
}
},
outputs: {
employees: {
type: 'array',
description: 'List of employees including terminated',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Employee ID' },
firstName: { type: 'string', description: 'First name' },
lastName: { type: 'string', description: 'Last name' },
workEmail: { type: 'string', description: 'Work email address' },
personalEmail: { type: 'string', description: 'Personal email address' },
roleState: { type: 'string', description: 'Employment status' },
department: { type: 'string', description: 'Department name or ID' },
title: { type: 'string', description: 'Job title' },
startDate: { type: 'string', description: 'Employment start date' },
endDate: { type: 'string', description: 'Employment end date' },
manager: { type: 'string', description: 'Manager ID or name' },
phone: { type: 'string', description: 'Phone number' },
},
},
},
totalCount: {
type: 'number',
description: 'Number of employees returned on this page',
},
},
}
@@ -0,0 +1,107 @@
import type {
RipplingListLeaveBalancesParams,
RipplingListLeaveBalancesResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListLeaveBalancesTool: ToolConfig<
RipplingListLeaveBalancesParams,
RipplingListLeaveBalancesResponse
> = {
id: 'rippling_list_leave_balances',
name: 'Rippling List Leave Balances',
description: 'List leave balances for all employees in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of leave balances to return',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.limit != null) query.set('limit', String(params.limit))
if (params.offset != null) query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.rippling.com/platform/api/leave_balances${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const leaveBalances = results.map((bal: Record<string, unknown>) => ({
employeeId: (bal.employeeId as string) ?? '',
balances: (Array.isArray(bal.balances) ? bal.balances : []).map(
(b: Record<string, unknown>) => ({
leaveType: (b.leaveType as string) ?? '',
minutesRemaining: (b.minutesRemaining as number) ?? 0,
})
),
}))
return {
success: true,
output: {
leaveBalances,
totalCount: leaveBalances.length,
},
}
},
outputs: {
leaveBalances: {
type: 'array',
description: 'List of employee leave balances',
items: {
type: 'json',
properties: {
employeeId: { type: 'string', description: 'Employee ID' },
balances: {
type: 'array',
description: 'Leave balance entries',
items: {
type: 'json',
properties: {
leaveType: { type: 'string', description: 'Type of leave' },
minutesRemaining: { type: 'number', description: 'Minutes of leave remaining' },
},
},
},
},
},
},
totalCount: {
type: 'number',
description: 'Number of leave balances returned on this page',
},
},
}
@@ -0,0 +1,111 @@
import type {
RipplingListLeaveRequestsParams,
RipplingListLeaveRequestsResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListLeaveRequestsTool: ToolConfig<
RipplingListLeaveRequestsParams,
RipplingListLeaveRequestsResponse
> = {
id: 'rippling_list_leave_requests',
name: 'Rippling List Leave Requests',
description: 'List leave requests in Rippling with optional filtering by date range and status',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
startDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by start date (ISO date string)',
},
endDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by end date (ISO date string)',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (e.g. pending, approved, declined)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.startDate) query.set('startDate', params.startDate)
if (params.endDate) query.set('endDate', params.endDate)
if (params.status) query.set('status', params.status)
const qs = query.toString()
return `https://api.rippling.com/platform/api/leave_requests${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const leaveRequests = results.map((req: Record<string, unknown>) => ({
id: (req.id as string) ?? '',
requestedBy: (req.requestedBy as string) ?? '',
status: (req.status as string) ?? '',
startDate: (req.startDate as string) ?? '',
endDate: (req.endDate as string) ?? '',
reason: (req.reason as string) ?? null,
leaveType: (req.leaveType as string) ?? null,
createdAt: (req.createdAt as string) ?? null,
}))
return {
success: true,
output: {
leaveRequests,
totalCount: leaveRequests.length,
},
}
},
outputs: {
leaveRequests: {
type: 'array',
description: 'List of leave requests',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Leave request ID' },
requestedBy: { type: 'string', description: 'Employee ID who requested leave' },
status: { type: 'string', description: 'Request status (pending/approved/declined)' },
startDate: { type: 'string', description: 'Leave start date' },
endDate: { type: 'string', description: 'Leave end date' },
reason: { type: 'string', description: 'Reason for leave' },
leaveType: { type: 'string', description: 'Type of leave' },
createdAt: { type: 'string', description: 'When the request was created' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of leave requests returned',
},
},
}
@@ -0,0 +1,87 @@
import type {
RipplingListLeaveTypesParams,
RipplingListLeaveTypesResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListLeaveTypesTool: ToolConfig<
RipplingListLeaveTypesParams,
RipplingListLeaveTypesResponse
> = {
id: 'rippling_list_leave_types',
name: 'Rippling List Leave Types',
description: 'List company leave types configured in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
managedBy: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter leave types by manager',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.managedBy) query.set('managedBy', params.managedBy)
const qs = query.toString()
return `https://api.rippling.com/platform/api/company_leave_types${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const leaveTypes = results.map((lt: Record<string, unknown>) => ({
id: (lt.id as string) ?? '',
name: (lt.name as string) ?? '',
managedBy: (lt.managedBy as string) ?? null,
}))
return {
success: true,
output: {
leaveTypes,
totalCount: leaveTypes.length,
},
}
},
outputs: {
leaveTypes: {
type: 'array',
description: 'List of company leave types',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Leave type ID' },
name: { type: 'string', description: 'Leave type name' },
managedBy: { type: 'string', description: 'Manager of this leave type' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of leave types returned',
},
},
}
+91
View File
@@ -0,0 +1,91 @@
import type { RipplingListLevelsParams, RipplingListLevelsResponse } from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListLevelsTool: ToolConfig<
RipplingListLevelsParams,
RipplingListLevelsResponse
> = {
id: 'rippling_list_levels',
name: 'Rippling List Levels',
description: 'List all position levels in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of levels to return',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.limit != null) query.set('limit', String(params.limit))
if (params.offset != null) query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.rippling.com/platform/api/levels${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const levels = results.map((level: Record<string, unknown>) => ({
id: (level.id as string) ?? '',
name: (level.name as string) ?? null,
parent: (level.parent as string) ?? null,
}))
return {
success: true,
output: {
levels,
totalCount: levels.length,
},
}
},
outputs: {
levels: {
type: 'array',
description: 'List of position levels',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Level ID' },
name: { type: 'string', description: 'Level name' },
parent: { type: 'string', description: 'Parent level ID' },
},
},
},
totalCount: {
type: 'number',
description: 'Number of levels returned on this page',
},
},
}
+89
View File
@@ -0,0 +1,89 @@
import type { RipplingListTeamsParams, RipplingListTeamsResponse } from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListTeamsTool: ToolConfig<RipplingListTeamsParams, RipplingListTeamsResponse> =
{
id: 'rippling_list_teams',
name: 'Rippling List Teams',
description: 'List all teams in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of teams to return',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.limit != null) query.set('limit', String(params.limit))
if (params.offset != null) query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.rippling.com/platform/api/teams${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const teams = results.map((team: Record<string, unknown>) => ({
id: (team.id as string) ?? '',
name: (team.name as string) ?? null,
parent: (team.parent as string) ?? null,
}))
return {
success: true,
output: {
teams,
totalCount: teams.length,
},
}
},
outputs: {
teams: {
type: 'array',
description: 'List of teams',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Team ID' },
name: { type: 'string', description: 'Team name' },
parent: { type: 'string', description: 'Parent team ID' },
},
},
},
totalCount: {
type: 'number',
description: 'Number of teams returned on this page',
},
},
}
@@ -0,0 +1,102 @@
import type {
RipplingListWorkLocationsParams,
RipplingListWorkLocationsResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingListWorkLocationsTool: ToolConfig<
RipplingListWorkLocationsParams,
RipplingListWorkLocationsResponse
> = {
id: 'rippling_list_work_locations',
name: 'Rippling List Work Locations',
description: 'List all work locations in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of work locations to return',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.limit != null) query.set('limit', String(params.limit))
if (params.offset != null) query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.rippling.com/platform/api/work_locations${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
const results = Array.isArray(data) ? data : (data.results ?? [])
const workLocations = results.map((loc: Record<string, unknown>) => ({
id: (loc.id as string) ?? '',
nickname: (loc.nickname as string) ?? null,
street: (loc.street as string) ?? null,
city: (loc.city as string) ?? null,
state: (loc.state as string) ?? null,
zip: (loc.zip as string) ?? null,
country: (loc.country as string) ?? null,
}))
return {
success: true,
output: {
workLocations,
totalCount: workLocations.length,
},
}
},
outputs: {
workLocations: {
type: 'array',
description: 'List of work locations',
items: {
type: 'json',
properties: {
id: { type: 'string', description: 'Work location ID' },
nickname: { type: 'string', description: 'Location nickname' },
street: { type: 'string', description: 'Street address' },
city: { type: 'string', description: 'City' },
state: { type: 'string', description: 'State or province' },
zip: { type: 'string', description: 'ZIP or postal code' },
country: { type: 'string', description: 'Country' },
},
},
},
totalCount: {
type: 'number',
description: 'Number of work locations returned on this page',
},
},
}
@@ -0,0 +1,79 @@
import type {
RipplingProcessLeaveRequestParams,
RipplingProcessLeaveRequestResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingProcessLeaveRequestTool: ToolConfig<
RipplingProcessLeaveRequestParams,
RipplingProcessLeaveRequestResponse
> = {
id: 'rippling_process_leave_request',
name: 'Rippling Process Leave Request',
description: 'Approve or decline a leave request in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
leaveRequestId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the leave request to process',
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Action to take on the leave request (approve or decline)',
},
},
request: {
url: (params) => {
const action = params.action.trim()
if (action !== 'approve' && action !== 'decline') {
throw new Error(`Invalid action "${action}". Must be "approve" or "decline".`)
}
return `https://api.rippling.com/platform/api/leave_requests/${encodeURIComponent(params.leaveRequestId.trim())}/process?action=${encodeURIComponent(action)}`
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? '',
status: data.status ?? '',
requestedBy: data.requestedBy ?? '',
startDate: data.startDate ?? '',
endDate: data.endDate ?? '',
},
}
},
outputs: {
id: { type: 'string', description: 'Leave request ID' },
status: { type: 'string', description: 'Updated status of the leave request' },
requestedBy: { type: 'string', description: 'Employee ID who requested leave' },
startDate: { type: 'string', description: 'Leave start date' },
endDate: { type: 'string', description: 'Leave end date' },
},
}
+124
View File
@@ -0,0 +1,124 @@
import type {
RipplingPushCandidateParams,
RipplingPushCandidateResponse,
} from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingPushCandidateTool: ToolConfig<
RipplingPushCandidateParams,
RipplingPushCandidateResponse
> = {
id: 'rippling_push_candidate',
name: 'Rippling Push Candidate',
description: 'Push a candidate to onboarding in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
firstName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Candidate first name',
},
lastName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Candidate last name',
},
email: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Candidate email address',
},
phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Candidate phone number',
},
jobTitle: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Job title for the candidate',
},
department: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Department for the candidate',
},
startDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Start date in ISO 8601 format (e.g., 2025-01-15)',
},
},
request: {
url: 'https://api.rippling.com/platform/api/ats_candidates/push_candidate',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
firstName: params.firstName,
lastName: params.lastName,
email: params.email,
}
if (params.phone !== undefined) {
body.phone = params.phone
}
if (params.jobTitle !== undefined) {
body.jobTitle = params.jobTitle
}
if (params.department !== undefined) {
body.department = params.department
}
if (params.startDate !== undefined) {
body.startDate = params.startDate
}
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
return {
success: true,
output: {
id: (data.id as string) ?? '',
firstName: (data.firstName as string) ?? null,
lastName: (data.lastName as string) ?? null,
email: (data.email as string) ?? null,
status: (data.status as string) ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Candidate ID' },
firstName: { type: 'string', description: 'Candidate first name' },
lastName: { type: 'string', description: 'Candidate last name' },
email: { type: 'string', description: 'Candidate email address' },
status: { type: 'string', description: 'Candidate onboarding status', optional: true },
},
}
+362
View File
@@ -0,0 +1,362 @@
import type { ToolResponse } from '@/tools/types'
export interface RipplingListEmployeesParams {
apiKey: string
limit?: number
offset?: number
}
export interface RipplingGetEmployeeParams {
apiKey: string
employeeId: string
}
export interface RipplingListDepartmentsParams {
apiKey: string
limit?: number
offset?: number
}
export interface RipplingGetCurrentUserParams {
apiKey: string
}
export interface RipplingEmployee {
id: string
firstName: string | null
lastName: string | null
workEmail: string | null
personalEmail: string | null
roleState: string | null
department: string | null
title: string | null
startDate: string | null
endDate: string | null
manager: string | null
phone: string | null
}
export interface RipplingDepartment {
id: string
name: string | null
parent: string | null
}
export interface RipplingListEmployeesResponse extends ToolResponse {
output: {
employees: RipplingEmployee[]
totalCount: number
}
}
export interface RipplingGetEmployeeResponse extends ToolResponse {
output: RipplingEmployee
}
export interface RipplingListDepartmentsResponse extends ToolResponse {
output: {
departments: RipplingDepartment[]
totalCount: number
}
}
export interface RipplingGetCurrentUserResponse extends ToolResponse {
output: {
id: string
workEmail: string | null
company: string | null
}
}
export interface RipplingGetCompanyParams {
apiKey: string
}
export interface RipplingCompanyAddress {
street: string | null
city: string | null
state: string | null
zip: string | null
country: string | null
}
export interface RipplingGetCompanyResponse extends ToolResponse {
output: {
id: string
name: string | null
address: RipplingCompanyAddress
email: string | null
phone: string | null
workLocations: string[]
}
}
export interface RipplingListCustomFieldsParams {
apiKey: string
limit?: number
offset?: number
}
export interface RipplingCustomField {
id: string
type: string | null
title: string | null
mandatory: boolean
}
export interface RipplingListCustomFieldsResponse extends ToolResponse {
output: {
customFields: RipplingCustomField[]
totalCount: number
}
}
export interface RipplingListLevelsParams {
apiKey: string
limit?: number
offset?: number
}
export interface RipplingLevel {
id: string
name: string | null
parent: string | null
}
export interface RipplingListLevelsResponse extends ToolResponse {
output: {
levels: RipplingLevel[]
totalCount: number
}
}
export interface RipplingListTeamsParams {
apiKey: string
limit?: number
offset?: number
}
export interface RipplingTeam {
id: string
name: string | null
parent: string | null
}
export interface RipplingListTeamsResponse extends ToolResponse {
output: {
teams: RipplingTeam[]
totalCount: number
}
}
export interface RipplingListWorkLocationsParams {
apiKey: string
limit?: number
offset?: number
}
export interface RipplingWorkLocation {
id: string
nickname: string | null
street: string | null
city: string | null
state: string | null
zip: string | null
country: string | null
}
export interface RipplingListWorkLocationsResponse extends ToolResponse {
output: {
workLocations: RipplingWorkLocation[]
totalCount: number
}
}
export interface RipplingGetCompanyActivityParams {
apiKey: string
startDate?: string
endDate?: string
limit?: number
next?: string
}
export interface RipplingActivityActor {
id: string | null
name: string | null
}
export interface RipplingActivityEvent {
id: string
type: string | null
description: string | null
createdAt: string | null
actor: RipplingActivityActor
}
export interface RipplingGetCompanyActivityResponse extends ToolResponse {
output: {
events: RipplingActivityEvent[]
totalCount: number
nextCursor: string | null
}
}
export interface RipplingListEmployeesWithTerminatedParams {
apiKey: string
limit?: number
offset?: number
}
export interface RipplingListEmployeesWithTerminatedResponse extends ToolResponse {
output: {
employees: RipplingEmployee[]
totalCount: number
}
}
export interface RipplingCreateGroupParams {
apiKey: string
name: string
spokeId: string
users?: string[]
}
export interface RipplingGroup {
id: string
name: string | null
spokeId: string | null
users: string[]
version: number | null
}
export interface RipplingCreateGroupResponse extends ToolResponse {
output: RipplingGroup
}
export interface RipplingUpdateGroupParams {
apiKey: string
groupId: string
name?: string
spokeId?: string
users?: string[]
version?: number
}
export interface RipplingUpdateGroupResponse extends ToolResponse {
output: RipplingGroup
}
export interface RipplingPushCandidateParams {
apiKey: string
firstName: string
lastName: string
email: string
phone?: string
jobTitle?: string
department?: string
startDate?: string
}
export interface RipplingPushCandidateResponse extends ToolResponse {
output: {
id: string
firstName: string | null
lastName: string | null
email: string | null
status: string | null
}
}
export interface RipplingListLeaveRequestsParams {
apiKey: string
startDate?: string
endDate?: string
status?: string
}
export interface RipplingLeaveRequest {
id: string
requestedBy: string
status: string
startDate: string
endDate: string
reason: string | null
leaveType: string | null
createdAt: string | null
}
export interface RipplingListLeaveRequestsResponse extends ToolResponse {
output: {
leaveRequests: RipplingLeaveRequest[]
totalCount: number
}
}
export interface RipplingProcessLeaveRequestParams {
apiKey: string
leaveRequestId: string
action: string
}
export interface RipplingProcessLeaveRequestResponse extends ToolResponse {
output: {
id: string
status: string
requestedBy: string
startDate: string
endDate: string
}
}
export interface RipplingListLeaveBalancesParams {
apiKey: string
limit?: number
offset?: number
}
export interface RipplingLeaveBalanceEntry {
leaveType: string
minutesRemaining: number
}
export interface RipplingLeaveBalance {
employeeId: string
balances: RipplingLeaveBalanceEntry[]
}
export interface RipplingListLeaveBalancesResponse extends ToolResponse {
output: {
leaveBalances: RipplingLeaveBalance[]
totalCount: number
}
}
export interface RipplingGetLeaveBalanceParams {
apiKey: string
roleId: string
}
export interface RipplingGetLeaveBalanceResponse extends ToolResponse {
output: {
employeeId: string
balances: RipplingLeaveBalanceEntry[]
}
}
export interface RipplingListLeaveTypesParams {
apiKey: string
managedBy?: string
}
export interface RipplingLeaveType {
id: string
name: string
managedBy: string | null
}
export interface RipplingListLeaveTypesResponse extends ToolResponse {
output: {
leaveTypes: RipplingLeaveType[]
totalCount: number
}
}
+115
View File
@@ -0,0 +1,115 @@
import type { RipplingUpdateGroupParams, RipplingUpdateGroupResponse } from '@/tools/rippling/types'
import type { ToolConfig } from '@/tools/types'
export const ripplingUpdateGroupTool: ToolConfig<
RipplingUpdateGroupParams,
RipplingUpdateGroupResponse
> = {
id: 'rippling_update_group',
name: 'Rippling Update Group',
description: 'Update an existing group in Rippling',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rippling API key',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the group to update',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New name for the group',
},
spokeId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Third-party app identifier',
},
users: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Array of user ID strings to set for the group',
},
version: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Group version number for optimistic concurrency',
},
},
request: {
url: (params) =>
`https://api.rippling.com/platform/api/groups/${encodeURIComponent(params.groupId.trim())}`,
method: 'PUT',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.name !== undefined) {
body.name = params.name
}
if (params.spokeId !== undefined) {
body.spokeId = params.spokeId
}
if (params.users !== undefined) {
body.users = params.users
}
if (params.version !== undefined) {
body.version = params.version
}
if (Object.keys(body).length === 0) {
throw new Error(
'At least one field (name, spokeId, users, or version) must be provided to update a group.'
)
}
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Rippling API error (${response.status}): ${errorText}`)
}
const data = await response.json()
return {
success: true,
output: {
id: (data.id as string) ?? '',
name: (data.name as string) ?? null,
spokeId: (data.spokeId as string) ?? null,
users: (data.users as string[]) ?? [],
version: (data.version as number) ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Group ID' },
name: { type: 'string', description: 'Group name' },
spokeId: { type: 'string', description: 'Third-party app identifier' },
users: {
type: 'array',
description: 'Array of user IDs in the group',
items: { type: 'string' },
},
version: { type: 'number', description: 'Group version number' },
},
}