diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 9e68974089..c44e9714fb 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -4825,6 +4825,17 @@ export function CirclebackIcon(props: SVGProps) { ) } +export function GreenhouseIcon(props: SVGProps) { + return ( + + + + ) +} + export function GreptileIcon(props: SVGProps) { return ( diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index 822ce48aeb..51d16b3ba9 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -59,6 +59,7 @@ import { GoogleVaultIcon, GrafanaIcon, GrainIcon, + GreenhouseIcon, GreptileIcon, HexIcon, HubspotIcon, @@ -208,6 +209,7 @@ export const blockTypeToIconMap: Record = { google_vault: GoogleVaultIcon, grafana: GrafanaIcon, grain: GrainIcon, + greenhouse: GreenhouseIcon, greptile: GreptileIcon, hex: HexIcon, hubspot: HubspotIcon, diff --git a/apps/docs/content/docs/en/tools/greenhouse.mdx b/apps/docs/content/docs/en/tools/greenhouse.mdx new file mode 100644 index 0000000000..10b54ea49d --- /dev/null +++ b/apps/docs/content/docs/en/tools/greenhouse.mdx @@ -0,0 +1,560 @@ +--- +title: Greenhouse +description: Manage candidates, jobs, and applications in Greenhouse +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +## Usage Instructions + +Integrate Greenhouse into the workflow. List and retrieve candidates, jobs, applications, users, departments, offices, and job stages from your Greenhouse ATS account. + + + +## Tools + +### `greenhouse_list_candidates` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_get_candidate` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_list_jobs` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_get_job` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_list_applications` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_get_application` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_list_users` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_get_user` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_list_departments` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_list_offices` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + +### `greenhouse_list_job_stages` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `candidates` | json | List of candidates | +| `jobs` | json | List of jobs | +| `applications` | json | List of applications | +| `users` | json | List of users | +| `departments` | json | List of departments | +| `offices` | json | List of offices | +| `stages` | json | List of job stages | +| `count` | number | Number of results returned | +| `id` | number | Resource ID | +| `first_name` | string | First name | +| `last_name` | string | Last name | +| `name` | string | Resource name | +| `status` | string | Status | +| `email_addresses` | json | Email addresses | +| `phone_numbers` | json | Phone numbers | +| `tags` | json | Tags | +| `application_ids` | json | Associated application IDs | +| `recruiter` | json | Assigned recruiter | +| `coordinator` | json | Assigned coordinator | +| `current_stage` | json | Current interview stage | +| `source` | json | Application source | +| `hiring_team` | json | Hiring team members | +| `openings` | json | Job openings | +| `custom_fields` | json | Custom field values | +| `attachments` | json | File attachments | +| `educations` | json | Education history | +| `employments` | json | Employment history | +| `answers` | json | Application question answers | +| `prospect` | boolean | Whether this is a prospect | +| `confidential` | boolean | Whether the job is confidential | +| `is_private` | boolean | Whether the candidate is private | +| `can_email` | boolean | Whether the candidate can be emailed | +| `disabled` | boolean | Whether the user is disabled | +| `site_admin` | boolean | Whether the user is a site admin | +| `primary_email_address` | string | Primary email address | +| `created_at` | string | Creation timestamp \(ISO 8601\) | +| `updated_at` | string | Last updated timestamp \(ISO 8601\) | + + diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index a089247e2e..a34d5f3bcd 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -54,6 +54,7 @@ "google_vault", "grafana", "grain", + "greenhouse", "greptile", "hex", "hubspot", diff --git a/apps/sim/blocks/blocks/greenhouse.ts b/apps/sim/blocks/blocks/greenhouse.ts new file mode 100644 index 0000000000..5478756faa --- /dev/null +++ b/apps/sim/blocks/blocks/greenhouse.ts @@ -0,0 +1,394 @@ +import { GreenhouseIcon } from '@/components/icons' +import { AuthMode, type BlockConfig } from '@/blocks/types' +import type { GreenhouseResponse } from '@/tools/greenhouse/types' + +export const GreenhouseBlock: BlockConfig = { + type: 'greenhouse', + name: 'Greenhouse', + description: 'Manage candidates, jobs, and applications in Greenhouse', + longDescription: + 'Integrate Greenhouse into the workflow. List and retrieve candidates, jobs, applications, users, departments, offices, and job stages from your Greenhouse ATS account.', + docsLink: 'https://docs.sim.ai/tools/greenhouse', + category: 'tools', + bgColor: '#469776', + icon: GreenhouseIcon, + authMode: AuthMode.ApiKey, + + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'List Candidates', id: 'greenhouse_list_candidates' }, + { label: 'Get Candidate', id: 'greenhouse_get_candidate' }, + { label: 'List Jobs', id: 'greenhouse_list_jobs' }, + { label: 'Get Job', id: 'greenhouse_get_job' }, + { label: 'List Applications', id: 'greenhouse_list_applications' }, + { label: 'Get Application', id: 'greenhouse_get_application' }, + { label: 'List Users', id: 'greenhouse_list_users' }, + { label: 'Get User', id: 'greenhouse_get_user' }, + { label: 'List Departments', id: 'greenhouse_list_departments' }, + { label: 'List Offices', id: 'greenhouse_list_offices' }, + { label: 'List Job Stages', id: 'greenhouse_list_job_stages' }, + ], + value: () => 'greenhouse_list_candidates', + }, + + // ── Get by ID fields ── + + { + id: 'candidateId', + title: 'Candidate ID', + type: 'short-input', + placeholder: 'Enter candidate ID', + required: { field: 'operation', value: 'greenhouse_get_candidate' }, + condition: { field: 'operation', value: 'greenhouse_get_candidate' }, + }, + { + id: 'jobId', + title: 'Job ID', + type: 'short-input', + placeholder: 'Enter job ID', + required: { + field: 'operation', + value: ['greenhouse_get_job', 'greenhouse_list_job_stages'], + }, + condition: { + field: 'operation', + value: ['greenhouse_get_job', 'greenhouse_list_job_stages'], + }, + }, + { + id: 'applicationId', + title: 'Application ID', + type: 'short-input', + placeholder: 'Enter application ID', + required: { field: 'operation', value: 'greenhouse_get_application' }, + condition: { field: 'operation', value: 'greenhouse_get_application' }, + }, + { + id: 'userId', + title: 'User ID', + type: 'short-input', + placeholder: 'Enter user ID', + required: { field: 'operation', value: 'greenhouse_get_user' }, + condition: { field: 'operation', value: 'greenhouse_get_user' }, + }, + + // ── List Candidates filters ── + + { + id: 'email', + title: 'Email Filter', + type: 'short-input', + placeholder: 'Filter by email address', + condition: { + field: 'operation', + value: ['greenhouse_list_candidates', 'greenhouse_list_users'], + }, + mode: 'advanced', + }, + { + id: 'job_id', + title: 'Job ID Filter', + type: 'short-input', + placeholder: 'Filter by job ID', + condition: { + field: 'operation', + value: ['greenhouse_list_candidates', 'greenhouse_list_applications'], + }, + mode: 'advanced', + }, + { + id: 'candidate_ids', + title: 'Candidate IDs', + type: 'short-input', + placeholder: 'Comma-separated IDs (max 50)', + condition: { field: 'operation', value: 'greenhouse_list_candidates' }, + mode: 'advanced', + }, + + // ── List Jobs filters ── + + { + id: 'status', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'All', id: '' }, + { label: 'Open', id: 'open' }, + { label: 'Closed', id: 'closed' }, + { label: 'Draft', id: 'draft' }, + ], + value: () => '', + condition: { field: 'operation', value: 'greenhouse_list_jobs' }, + }, + { + id: 'department_id', + title: 'Department ID', + type: 'short-input', + placeholder: 'Filter by department ID', + condition: { field: 'operation', value: 'greenhouse_list_jobs' }, + mode: 'advanced', + }, + { + id: 'office_id', + title: 'Office ID', + type: 'short-input', + placeholder: 'Filter by office ID', + condition: { field: 'operation', value: 'greenhouse_list_jobs' }, + mode: 'advanced', + }, + + // ── List Applications filters ── + + { + id: 'applicationStatus', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'All', id: '' }, + { label: 'Active', id: 'active' }, + { label: 'Converted', id: 'converted' }, + { label: 'Hired', id: 'hired' }, + { label: 'Rejected', id: 'rejected' }, + ], + value: () => '', + condition: { field: 'operation', value: 'greenhouse_list_applications' }, + }, + { + id: 'last_activity_after', + title: 'Activity After', + type: 'short-input', + placeholder: 'ISO 8601 timestamp (e.g., 2024-01-01T00:00:00Z)', + condition: { field: 'operation', value: 'greenhouse_list_applications' }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: `Generate an ISO 8601 timestamp for the Greenhouse API based on the user's description. + +Examples: +- "last 7 days" -> Calculate 7 days ago from today in ISO 8601 format +- "last 30 days" -> Calculate 30 days ago from today in ISO 8601 format +- "since January 1st 2024" -> 2024-01-01T00:00:00Z +- "beginning of this month" -> First day of current month at 00:00:00Z +- "yesterday" -> Yesterday's date at 00:00:00Z + +Return ONLY the ISO 8601 timestamp - no explanations, no extra text.`, + placeholder: 'Describe the time filter (e.g., "last 7 days", "since January 1st")...', + generationType: 'timestamp', + }, + }, + + // ── Shared date filters (advanced) ── + + { + id: 'created_after', + title: 'Created After', + type: 'short-input', + placeholder: 'ISO 8601 timestamp', + condition: { + field: 'operation', + value: [ + 'greenhouse_list_candidates', + 'greenhouse_list_jobs', + 'greenhouse_list_applications', + 'greenhouse_list_users', + ], + }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: `Generate an ISO 8601 timestamp for the Greenhouse API based on the user's description. + +Examples: +- "last 7 days" -> Calculate 7 days ago from today in ISO 8601 format +- "last 30 days" -> Calculate 30 days ago from today in ISO 8601 format +- "since January 1st 2024" -> 2024-01-01T00:00:00Z +- "beginning of this month" -> First day of current month at 00:00:00Z + +Return ONLY the ISO 8601 timestamp - no explanations, no extra text.`, + placeholder: 'Describe the start date (e.g., "last 30 days", "since January 1st")...', + generationType: 'timestamp', + }, + }, + { + id: 'updated_after', + title: 'Updated After', + type: 'short-input', + placeholder: 'ISO 8601 timestamp', + condition: { + field: 'operation', + value: ['greenhouse_list_candidates', 'greenhouse_list_jobs', 'greenhouse_list_users'], + }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: `Generate an ISO 8601 timestamp for the Greenhouse API based on the user's description. + +Examples: +- "last 24 hours" -> Calculate 24 hours ago in ISO 8601 format +- "last week" -> Calculate 7 days ago in ISO 8601 format +- "since March 2024" -> 2024-03-01T00:00:00Z + +Return ONLY the ISO 8601 timestamp - no explanations, no extra text.`, + placeholder: 'Describe the update date filter (e.g., "last 24 hours")...', + generationType: 'timestamp', + }, + }, + + // ── Pagination (advanced, shared across list operations) ── + + { + id: 'per_page', + title: 'Results Per Page', + type: 'short-input', + placeholder: '100 (max 500)', + condition: { + field: 'operation', + value: [ + 'greenhouse_list_candidates', + 'greenhouse_list_jobs', + 'greenhouse_list_applications', + 'greenhouse_list_users', + 'greenhouse_list_departments', + 'greenhouse_list_offices', + 'greenhouse_list_job_stages', + ], + }, + mode: 'advanced', + }, + { + id: 'page', + title: 'Page', + type: 'short-input', + placeholder: 'Page number (default: 1)', + condition: { + field: 'operation', + value: [ + 'greenhouse_list_candidates', + 'greenhouse_list_jobs', + 'greenhouse_list_applications', + 'greenhouse_list_users', + 'greenhouse_list_departments', + 'greenhouse_list_offices', + 'greenhouse_list_job_stages', + ], + }, + mode: 'advanced', + }, + + // ── API Key (common) ── + + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + placeholder: 'Enter your Greenhouse Harvest API key', + required: true, + password: true, + }, + ], + + tools: { + access: [ + 'greenhouse_list_candidates', + 'greenhouse_get_candidate', + 'greenhouse_list_jobs', + 'greenhouse_get_job', + 'greenhouse_list_applications', + 'greenhouse_get_application', + 'greenhouse_list_users', + 'greenhouse_get_user', + 'greenhouse_list_departments', + 'greenhouse_list_offices', + 'greenhouse_list_job_stages', + ], + config: { + tool: (params) => `${params.operation}`, + params: (params) => { + const result: Record = {} + + if (params.per_page) result.per_page = Number(params.per_page) + if (params.page) result.page = Number(params.page) + + if (params.operation === 'greenhouse_list_applications' && params.applicationStatus) { + result.status = params.applicationStatus + } + if (params.operation === 'greenhouse_list_jobs' && params.status === '') { + result.status = undefined + } + + return result + }, + }, + }, + + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Greenhouse Harvest API key' }, + candidateId: { type: 'string', description: 'Candidate ID' }, + jobId: { type: 'string', description: 'Job ID' }, + applicationId: { type: 'string', description: 'Application ID' }, + userId: { type: 'string', description: 'User ID' }, + status: { type: 'string', description: 'Job status filter (open, closed, draft)' }, + applicationStatus: { + type: 'string', + description: 'Application status filter (active, converted, hired, rejected)', + }, + job_id: { type: 'string', description: 'Job ID filter for candidates/applications' }, + email: { type: 'string', description: 'Email address filter' }, + candidate_ids: { type: 'string', description: 'Comma-separated candidate IDs (max 50)' }, + department_id: { type: 'string', description: 'Department ID filter for jobs' }, + office_id: { type: 'string', description: 'Office ID filter for jobs' }, + created_after: { type: 'string', description: 'Created after date filter (ISO 8601)' }, + updated_after: { type: 'string', description: 'Updated after date filter (ISO 8601)' }, + last_activity_after: { + type: 'string', + description: 'Last activity after date filter (ISO 8601)', + }, + per_page: { type: 'number', description: 'Number of results per page (max 500)' }, + page: { type: 'number', description: 'Page number for pagination' }, + }, + + outputs: { + candidates: { type: 'json', description: 'List of candidates' }, + jobs: { type: 'json', description: 'List of jobs' }, + applications: { type: 'json', description: 'List of applications' }, + users: { type: 'json', description: 'List of users' }, + departments: { type: 'json', description: 'List of departments' }, + offices: { type: 'json', description: 'List of offices' }, + stages: { type: 'json', description: 'List of job stages' }, + count: { type: 'number', description: 'Number of results returned' }, + id: { type: 'number', description: 'Resource ID' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + name: { type: 'string', description: 'Resource name' }, + status: { type: 'string', description: 'Status' }, + email_addresses: { type: 'json', description: 'Email addresses' }, + phone_numbers: { type: 'json', description: 'Phone numbers' }, + tags: { type: 'json', description: 'Tags' }, + application_ids: { type: 'json', description: 'Associated application IDs' }, + recruiter: { type: 'json', description: 'Assigned recruiter' }, + coordinator: { type: 'json', description: 'Assigned coordinator' }, + current_stage: { type: 'json', description: 'Current interview stage' }, + source: { type: 'json', description: 'Application source' }, + hiring_team: { type: 'json', description: 'Hiring team members' }, + openings: { type: 'json', description: 'Job openings' }, + custom_fields: { type: 'json', description: 'Custom field values' }, + attachments: { type: 'json', description: 'File attachments' }, + educations: { type: 'json', description: 'Education history' }, + employments: { type: 'json', description: 'Employment history' }, + answers: { type: 'json', description: 'Application question answers' }, + prospect: { type: 'boolean', description: 'Whether this is a prospect' }, + confidential: { type: 'boolean', description: 'Whether the job is confidential' }, + is_private: { type: 'boolean', description: 'Whether the candidate is private' }, + can_email: { type: 'boolean', description: 'Whether the candidate can be emailed' }, + disabled: { type: 'boolean', description: 'Whether the user is disabled' }, + site_admin: { type: 'boolean', description: 'Whether the user is a site admin' }, + primary_email_address: { type: 'string', description: 'Primary email address' }, + created_at: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updated_at: { type: 'string', description: 'Last updated timestamp (ISO 8601)' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index eff25ffb1d..94cff9faa9 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -59,6 +59,7 @@ import { GoogleTranslateBlock } from '@/blocks/blocks/google_translate' import { GoogleVaultBlock } from '@/blocks/blocks/google_vault' import { GrafanaBlock } from '@/blocks/blocks/grafana' import { GrainBlock } from '@/blocks/blocks/grain' +import { GreenhouseBlock } from '@/blocks/blocks/greenhouse' import { GreptileBlock } from '@/blocks/blocks/greptile' import { GuardrailsBlock } from '@/blocks/blocks/guardrails' import { HexBlock } from '@/blocks/blocks/hex' @@ -251,6 +252,7 @@ export const registry: Record = { google_vault: GoogleVaultBlock, grafana: GrafanaBlock, grain: GrainBlock, + greenhouse: GreenhouseBlock, greptile: GreptileBlock, guardrails: GuardrailsBlock, hex: HexBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 9e68974089..c44e9714fb 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -4825,6 +4825,17 @@ export function CirclebackIcon(props: SVGProps) { ) } +export function GreenhouseIcon(props: SVGProps) { + return ( + + + + ) +} + export function GreptileIcon(props: SVGProps) { return ( diff --git a/apps/sim/tools/greenhouse/get-application.ts b/apps/sim/tools/greenhouse/get-application.ts new file mode 100644 index 0000000000..5d9e9b8369 --- /dev/null +++ b/apps/sim/tools/greenhouse/get-application.ts @@ -0,0 +1,228 @@ +import type { + GreenhouseGetApplicationParams, + GreenhouseGetApplicationResponse, +} from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseGetApplicationTool: ToolConfig< + GreenhouseGetApplicationParams, + GreenhouseGetApplicationResponse +> = { + id: 'greenhouse_get_application', + name: 'Greenhouse Get Application', + description: + 'Retrieves a specific application by ID with full details including source, stage, answers, and attachments', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + applicationId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the application to retrieve', + }, + }, + + request: { + url: (params: GreenhouseGetApplicationParams) => + `https://harvest.greenhouse.io/v1/applications/${params.applicationId}`, + method: 'GET', + headers: (params: GreenhouseGetApplicationParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { + id: 0, + candidate_id: 0, + prospect: false, + status: null, + applied_at: null, + rejected_at: null, + last_activity_at: null, + location: null, + source: null, + credited_to: null, + recruiter: null, + coordinator: null, + current_stage: null, + rejection_reason: null, + jobs: [], + job_post_id: null, + answers: [], + attachments: [], + custom_fields: {}, + }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const a = await response.json() + return { + success: true, + output: { + id: a.id ?? 0, + candidate_id: a.candidate_id ?? 0, + prospect: a.prospect ?? false, + status: a.status ?? null, + applied_at: a.applied_at ?? null, + rejected_at: a.rejected_at ?? null, + last_activity_at: a.last_activity_at ?? null, + location: a.location ?? null, + source: a.source ?? null, + credited_to: a.credited_to ?? null, + recruiter: a.recruiter ?? null, + coordinator: a.coordinator ?? null, + current_stage: a.current_stage ?? null, + rejection_reason: a.rejection_reason ?? null, + jobs: a.jobs ?? [], + job_post_id: a.job_post_id ?? null, + answers: a.answers ?? [], + attachments: (a.attachments ?? []).map((att: Record) => ({ + filename: att.filename ?? '', + url: att.url ?? '', + type: att.type ?? '', + created_at: (att.created_at as string) ?? null, + })), + custom_fields: a.custom_fields ?? {}, + }, + } + }, + + outputs: { + id: { type: 'number', description: 'Application ID' }, + candidate_id: { type: 'number', description: 'Associated candidate ID' }, + prospect: { type: 'boolean', description: 'Whether this is a prospect application' }, + status: { type: 'string', description: 'Status (active, converted, hired, rejected)' }, + applied_at: { type: 'string', description: 'Application date (ISO 8601)' }, + rejected_at: { type: 'string', description: 'Rejection date (ISO 8601)', optional: true }, + last_activity_at: { type: 'string', description: 'Last activity date (ISO 8601)' }, + location: { + type: 'object', + description: 'Candidate location', + optional: true, + properties: { + address: { type: 'string', description: 'Location address', optional: true }, + }, + }, + source: { + type: 'object', + description: 'Application source', + optional: true, + properties: { + id: { type: 'number', description: 'Source ID' }, + public_name: { type: 'string', description: 'Source name' }, + }, + }, + credited_to: { + type: 'object', + description: 'User credited for the application', + optional: true, + properties: { + id: { type: 'number', description: 'User ID' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + name: { type: 'string', description: 'Full name' }, + employee_id: { type: 'string', description: 'Employee ID', optional: true }, + }, + }, + recruiter: { + type: 'object', + description: 'Assigned recruiter', + optional: true, + properties: { + id: { type: 'number', description: 'User ID' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + name: { type: 'string', description: 'Full name' }, + employee_id: { type: 'string', description: 'Employee ID', optional: true }, + }, + }, + coordinator: { + type: 'object', + description: 'Assigned coordinator', + optional: true, + properties: { + id: { type: 'number', description: 'User ID' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + name: { type: 'string', description: 'Full name' }, + employee_id: { type: 'string', description: 'Employee ID', optional: true }, + }, + }, + current_stage: { + type: 'object', + description: 'Current interview stage (null when hired)', + optional: true, + properties: { + id: { type: 'number', description: 'Stage ID' }, + name: { type: 'string', description: 'Stage name' }, + }, + }, + rejection_reason: { + type: 'object', + description: 'Rejection reason', + optional: true, + properties: { + id: { type: 'number', description: 'Rejection reason ID' }, + name: { type: 'string', description: 'Rejection reason name' }, + type: { + type: 'object', + description: 'Rejection reason type', + properties: { + id: { type: 'number', description: 'Type ID' }, + name: { type: 'string', description: 'Type name' }, + }, + }, + }, + }, + jobs: { + type: 'array', + description: 'Associated jobs', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Job ID' }, + name: { type: 'string', description: 'Job name' }, + }, + }, + }, + job_post_id: { type: 'number', description: 'Job post ID', optional: true }, + answers: { + type: 'array', + description: 'Application question answers', + items: { + type: 'object', + properties: { + question: { type: 'string', description: 'Question text' }, + answer: { type: 'string', description: 'Answer text' }, + }, + }, + }, + attachments: { + type: 'array', + description: 'File attachments (URLs expire after 7 days)', + items: { + type: 'object', + properties: { + filename: { type: 'string', description: 'File name' }, + url: { type: 'string', description: 'Download URL (expires after 7 days)' }, + type: { type: 'string', description: 'Type (resume, cover_letter, offer_packet, other)' }, + created_at: { type: 'string', description: 'Upload timestamp', optional: true }, + }, + }, + }, + custom_fields: { type: 'object', description: 'Custom field values' }, + }, +} diff --git a/apps/sim/tools/greenhouse/get-candidate.ts b/apps/sim/tools/greenhouse/get-candidate.ts new file mode 100644 index 0000000000..2dd0585355 --- /dev/null +++ b/apps/sim/tools/greenhouse/get-candidate.ts @@ -0,0 +1,267 @@ +import type { + GreenhouseGetCandidateParams, + GreenhouseGetCandidateResponse, +} from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseGetCandidateTool: ToolConfig< + GreenhouseGetCandidateParams, + GreenhouseGetCandidateResponse +> = { + id: 'greenhouse_get_candidate', + name: 'Greenhouse Get Candidate', + description: + 'Retrieves a specific candidate by ID with full details including contact info, education, and employment history', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + candidateId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the candidate to retrieve', + }, + }, + + request: { + url: (params: GreenhouseGetCandidateParams) => + `https://harvest.greenhouse.io/v1/candidates/${params.candidateId}`, + method: 'GET', + headers: (params: GreenhouseGetCandidateParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { + id: 0, + first_name: null, + last_name: null, + company: null, + title: null, + is_private: false, + can_email: false, + created_at: null, + updated_at: null, + last_activity: null, + email_addresses: [], + phone_numbers: [], + addresses: [], + website_addresses: [], + social_media_addresses: [], + tags: [], + application_ids: [], + recruiter: null, + coordinator: null, + attachments: [], + educations: [], + employments: [], + custom_fields: {}, + }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const c = await response.json() + return { + success: true, + output: { + id: c.id ?? 0, + first_name: c.first_name ?? null, + last_name: c.last_name ?? null, + company: c.company ?? null, + title: c.title ?? null, + is_private: c.is_private ?? false, + can_email: c.can_email ?? false, + created_at: c.created_at ?? null, + updated_at: c.updated_at ?? null, + last_activity: c.last_activity ?? null, + email_addresses: c.email_addresses ?? [], + phone_numbers: c.phone_numbers ?? [], + addresses: c.addresses ?? [], + website_addresses: c.website_addresses ?? [], + social_media_addresses: c.social_media_addresses ?? [], + tags: c.tags ?? [], + application_ids: c.application_ids ?? [], + recruiter: c.recruiter ?? null, + coordinator: c.coordinator ?? null, + attachments: (c.attachments ?? []).map((a: Record) => ({ + filename: a.filename ?? '', + url: a.url ?? '', + type: a.type ?? '', + created_at: (a.created_at as string) ?? null, + })), + educations: (c.educations ?? []).map((e: Record) => ({ + id: e.id ?? 0, + school_name: e.school_name ?? null, + degree: e.degree ?? null, + discipline: e.discipline ?? null, + start_date: (e.start_date as string) ?? null, + end_date: (e.end_date as string) ?? null, + })), + employments: (c.employments ?? []).map((e: Record) => ({ + id: e.id ?? 0, + company_name: e.company_name ?? null, + title: e.title ?? null, + start_date: (e.start_date as string) ?? null, + end_date: (e.end_date as string) ?? null, + })), + custom_fields: c.custom_fields ?? {}, + }, + } + }, + + outputs: { + id: { type: 'number', description: 'Candidate ID' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + company: { type: 'string', description: 'Current employer', optional: true }, + title: { type: 'string', description: 'Current job title', optional: true }, + is_private: { type: 'boolean', description: 'Whether candidate is private' }, + can_email: { type: 'boolean', description: 'Whether candidate can be emailed' }, + created_at: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updated_at: { type: 'string', description: 'Last updated timestamp (ISO 8601)' }, + last_activity: { + type: 'string', + description: 'Last activity timestamp (ISO 8601)', + optional: true, + }, + email_addresses: { + type: 'array', + description: 'Email addresses', + items: { + type: 'object', + properties: { + value: { type: 'string', description: 'Email address' }, + type: { type: 'string', description: 'Type (personal, work, other)' }, + }, + }, + }, + phone_numbers: { + type: 'array', + description: 'Phone numbers', + items: { + type: 'object', + properties: { + value: { type: 'string', description: 'Phone number' }, + type: { type: 'string', description: 'Type (home, work, mobile, skype, other)' }, + }, + }, + }, + addresses: { + type: 'array', + description: 'Addresses', + items: { + type: 'object', + properties: { + value: { type: 'string', description: 'Address' }, + type: { type: 'string', description: 'Type (home, work, other)' }, + }, + }, + }, + website_addresses: { + type: 'array', + description: 'Website addresses', + items: { + type: 'object', + properties: { + value: { type: 'string', description: 'URL' }, + type: { type: 'string', description: 'Type (personal, company, portfolio, blog, other)' }, + }, + }, + }, + social_media_addresses: { + type: 'array', + description: 'Social media profiles', + items: { + type: 'object', + properties: { + value: { type: 'string', description: 'URL or handle' }, + }, + }, + }, + tags: { type: 'array', description: 'Tags', items: { type: 'string', description: 'Tag' } }, + application_ids: { + type: 'array', + description: 'Associated application IDs', + items: { type: 'number', description: 'Application ID' }, + }, + recruiter: { + type: 'object', + description: 'Assigned recruiter', + optional: true, + properties: { + id: { type: 'number', description: 'User ID' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + name: { type: 'string', description: 'Full name' }, + employee_id: { type: 'string', description: 'Employee ID', optional: true }, + }, + }, + coordinator: { + type: 'object', + description: 'Assigned coordinator', + optional: true, + properties: { + id: { type: 'number', description: 'User ID' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + name: { type: 'string', description: 'Full name' }, + employee_id: { type: 'string', description: 'Employee ID', optional: true }, + }, + }, + attachments: { + type: 'array', + description: 'File attachments (URLs expire after 7 days)', + items: { + type: 'object', + properties: { + filename: { type: 'string', description: 'File name' }, + url: { type: 'string', description: 'Download URL (expires after 7 days)' }, + type: { type: 'string', description: 'Type (resume, cover_letter, offer_packet, other)' }, + created_at: { type: 'string', description: 'Upload timestamp', optional: true }, + }, + }, + }, + educations: { + type: 'array', + description: 'Education history', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Education record ID' }, + school_name: { type: 'string', description: 'School name', optional: true }, + degree: { type: 'string', description: 'Degree type', optional: true }, + discipline: { type: 'string', description: 'Field of study', optional: true }, + start_date: { type: 'string', description: 'Start date (ISO 8601)', optional: true }, + end_date: { type: 'string', description: 'End date (ISO 8601)', optional: true }, + }, + }, + }, + employments: { + type: 'array', + description: 'Employment history', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Employment record ID' }, + company_name: { type: 'string', description: 'Company name', optional: true }, + title: { type: 'string', description: 'Job title', optional: true }, + start_date: { type: 'string', description: 'Start date (ISO 8601)', optional: true }, + end_date: { type: 'string', description: 'End date (ISO 8601)', optional: true }, + }, + }, + }, + custom_fields: { type: 'object', description: 'Custom field values' }, + }, +} diff --git a/apps/sim/tools/greenhouse/get-job.ts b/apps/sim/tools/greenhouse/get-job.ts new file mode 100644 index 0000000000..154512267f --- /dev/null +++ b/apps/sim/tools/greenhouse/get-job.ts @@ -0,0 +1,185 @@ +import type { GreenhouseGetJobParams, GreenhouseGetJobResponse } from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseGetJobTool: ToolConfig = { + id: 'greenhouse_get_job', + name: 'Greenhouse Get Job', + description: + 'Retrieves a specific job by ID with full details including hiring team, openings, and custom fields', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + jobId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the job to retrieve', + }, + }, + + request: { + url: (params: GreenhouseGetJobParams) => + `https://harvest.greenhouse.io/v1/jobs/${params.jobId}`, + method: 'GET', + headers: (params: GreenhouseGetJobParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { + id: 0, + name: null, + requisition_id: null, + status: null, + confidential: false, + created_at: null, + opened_at: null, + closed_at: null, + updated_at: null, + is_template: null, + notes: null, + departments: [], + offices: [], + hiring_team: { hiring_managers: [], recruiters: [], coordinators: [], sourcers: [] }, + openings: [], + custom_fields: {}, + }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const j = await response.json() + return { + success: true, + output: { + id: j.id ?? 0, + name: j.name ?? null, + requisition_id: j.requisition_id ?? null, + status: j.status ?? null, + confidential: j.confidential ?? false, + created_at: j.created_at ?? null, + opened_at: j.opened_at ?? null, + closed_at: j.closed_at ?? null, + updated_at: j.updated_at ?? null, + is_template: j.is_template ?? null, + notes: j.notes ?? null, + departments: (j.departments ?? []).map((d: Record) => ({ + id: d.id ?? 0, + name: (d.name as string) ?? '', + parent_id: (d.parent_id as number) ?? null, + })), + offices: (j.offices ?? []).map((o: Record) => ({ + id: o.id ?? 0, + name: (o.name as string) ?? '', + location: { name: ((o.location as Record)?.name as string) ?? null }, + })), + hiring_team: { + hiring_managers: j.hiring_team?.hiring_managers ?? [], + recruiters: j.hiring_team?.recruiters ?? [], + coordinators: j.hiring_team?.coordinators ?? [], + sourcers: j.hiring_team?.sourcers ?? [], + }, + openings: (j.openings ?? []).map((o: Record) => ({ + id: o.id ?? 0, + opening_id: (o.opening_id as string) ?? null, + status: (o.status as string) ?? 'open', + opened_at: (o.opened_at as string) ?? null, + closed_at: (o.closed_at as string) ?? null, + application_id: (o.application_id as number) ?? null, + close_reason: (o.close_reason as { id: number; name: string }) ?? null, + })), + custom_fields: j.custom_fields ?? {}, + }, + } + }, + + outputs: { + id: { type: 'number', description: 'Job ID' }, + name: { type: 'string', description: 'Job title' }, + requisition_id: { type: 'string', description: 'External requisition ID', optional: true }, + status: { type: 'string', description: 'Job status (open, closed, draft)' }, + confidential: { type: 'boolean', description: 'Whether the job is confidential' }, + created_at: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + opened_at: { type: 'string', description: 'Date job was opened (ISO 8601)', optional: true }, + closed_at: { type: 'string', description: 'Date job was closed (ISO 8601)', optional: true }, + updated_at: { type: 'string', description: 'Last updated timestamp (ISO 8601)' }, + is_template: { type: 'boolean', description: 'Whether this is a job template', optional: true }, + notes: { type: 'string', description: 'Hiring plan notes (may contain HTML)', optional: true }, + departments: { + type: 'array', + description: 'Associated departments', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Department ID' }, + name: { type: 'string', description: 'Department name' }, + parent_id: { type: 'number', description: 'Parent department ID', optional: true }, + }, + }, + }, + offices: { + type: 'array', + description: 'Associated offices', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Office ID' }, + name: { type: 'string', description: 'Office name' }, + location: { + type: 'object', + description: 'Office location', + properties: { + name: { type: 'string', description: 'Location name', optional: true }, + }, + }, + }, + }, + }, + hiring_team: { + type: 'object', + description: 'Hiring team members', + properties: { + hiring_managers: { type: 'array', description: 'Hiring managers' }, + recruiters: { type: 'array', description: 'Recruiters (includes responsible flag)' }, + coordinators: { type: 'array', description: 'Coordinators (includes responsible flag)' }, + sourcers: { type: 'array', description: 'Sourcers' }, + }, + }, + openings: { + type: 'array', + description: 'Job openings/slots', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Opening internal ID' }, + opening_id: { type: 'string', description: 'Custom opening identifier', optional: true }, + status: { type: 'string', description: 'Opening status (open, closed)' }, + opened_at: { type: 'string', description: 'Date opened (ISO 8601)', optional: true }, + closed_at: { type: 'string', description: 'Date closed (ISO 8601)', optional: true }, + application_id: { type: 'number', description: 'Hired application ID', optional: true }, + close_reason: { + type: 'object', + description: 'Reason for closing', + optional: true, + properties: { + id: { type: 'number', description: 'Close reason ID' }, + name: { type: 'string', description: 'Close reason name' }, + }, + }, + }, + }, + }, + custom_fields: { type: 'object', description: 'Custom field values' }, + }, +} diff --git a/apps/sim/tools/greenhouse/get-user.ts b/apps/sim/tools/greenhouse/get-user.ts new file mode 100644 index 0000000000..3cbdb29288 --- /dev/null +++ b/apps/sim/tools/greenhouse/get-user.ts @@ -0,0 +1,100 @@ +import type { GreenhouseGetUserParams, GreenhouseGetUserResponse } from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseGetUserTool: ToolConfig = + { + id: 'greenhouse_get_user', + name: 'Greenhouse Get User', + description: 'Retrieves a specific Greenhouse user by ID', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the user to retrieve', + }, + }, + + request: { + url: (params: GreenhouseGetUserParams) => + `https://harvest.greenhouse.io/v1/users/${params.userId}`, + method: 'GET', + headers: (params: GreenhouseGetUserParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { + id: 0, + name: null, + first_name: null, + last_name: null, + primary_email_address: null, + disabled: false, + site_admin: false, + emails: [], + employee_id: null, + linked_candidate_ids: [], + created_at: null, + updated_at: null, + }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const u = await response.json() + return { + success: true, + output: { + id: u.id ?? 0, + name: u.name ?? null, + first_name: u.first_name ?? null, + last_name: u.last_name ?? null, + primary_email_address: u.primary_email_address ?? null, + disabled: u.disabled ?? false, + site_admin: u.site_admin ?? false, + emails: u.emails ?? [], + employee_id: u.employee_id ?? null, + linked_candidate_ids: u.linked_candidate_ids ?? [], + created_at: u.created_at ?? null, + updated_at: u.updated_at ?? null, + }, + } + }, + + outputs: { + id: { type: 'number', description: 'User ID' }, + name: { type: 'string', description: 'Full name' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + primary_email_address: { type: 'string', description: 'Primary email address' }, + disabled: { type: 'boolean', description: 'Whether the user is disabled' }, + site_admin: { type: 'boolean', description: 'Whether the user is a site admin' }, + emails: { + type: 'array', + description: 'All email addresses', + items: { type: 'string', description: 'Email address' }, + }, + employee_id: { type: 'string', description: 'Employee ID', optional: true }, + linked_candidate_ids: { + type: 'array', + description: 'IDs of candidates linked to this user', + items: { type: 'number', description: 'Candidate ID' }, + }, + created_at: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updated_at: { type: 'string', description: 'Last updated timestamp (ISO 8601)' }, + }, + } diff --git a/apps/sim/tools/greenhouse/index.ts b/apps/sim/tools/greenhouse/index.ts new file mode 100644 index 0000000000..e3298b7149 --- /dev/null +++ b/apps/sim/tools/greenhouse/index.ts @@ -0,0 +1,25 @@ +import { greenhouseGetApplicationTool } from '@/tools/greenhouse/get-application' +import { greenhouseGetCandidateTool } from '@/tools/greenhouse/get-candidate' +import { greenhouseGetJobTool } from '@/tools/greenhouse/get-job' +import { greenhouseGetUserTool } from '@/tools/greenhouse/get-user' +import { greenhouseListApplicationsTool } from '@/tools/greenhouse/list-applications' +import { greenhouseListCandidatesTool } from '@/tools/greenhouse/list-candidates' +import { greenhouseListDepartmentsTool } from '@/tools/greenhouse/list-departments' +import { greenhouseListJobStagesTool } from '@/tools/greenhouse/list-job-stages' +import { greenhouseListJobsTool } from '@/tools/greenhouse/list-jobs' +import { greenhouseListOfficesTool } from '@/tools/greenhouse/list-offices' +import { greenhouseListUsersTool } from '@/tools/greenhouse/list-users' + +export { + greenhouseGetApplicationTool, + greenhouseGetCandidateTool, + greenhouseGetJobTool, + greenhouseGetUserTool, + greenhouseListApplicationsTool, + greenhouseListCandidatesTool, + greenhouseListDepartmentsTool, + greenhouseListJobStagesTool, + greenhouseListJobsTool, + greenhouseListOfficesTool, + greenhouseListUsersTool, +} diff --git a/apps/sim/tools/greenhouse/list-applications.ts b/apps/sim/tools/greenhouse/list-applications.ts new file mode 100644 index 0000000000..d1578b721e --- /dev/null +++ b/apps/sim/tools/greenhouse/list-applications.ts @@ -0,0 +1,156 @@ +import type { + GreenhouseApplicationSummary, + GreenhouseListApplicationsParams, + GreenhouseListApplicationsResponse, +} from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseListApplicationsTool: ToolConfig< + GreenhouseListApplicationsParams, + GreenhouseListApplicationsResponse +> = { + id: 'greenhouse_list_applications', + name: 'Greenhouse List Applications', + description: 'Lists applications from Greenhouse with optional filtering by job, status, or date', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-500, default 100)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + job_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter applications by job ID', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by status (active, converted, hired, rejected)', + }, + created_after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only applications created at or after this ISO 8601 timestamp', + }, + created_before: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only applications created before this ISO 8601 timestamp', + }, + last_activity_after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only applications with activity at or after this ISO 8601 timestamp', + }, + }, + + request: { + url: (params: GreenhouseListApplicationsParams) => { + const url = new URL('https://harvest.greenhouse.io/v1/applications') + if (params.per_page) url.searchParams.append('per_page', String(params.per_page)) + if (params.page) url.searchParams.append('page', String(params.page)) + if (params.job_id) url.searchParams.append('job_id', params.job_id) + if (params.status) url.searchParams.append('status', params.status) + if (params.created_after) url.searchParams.append('created_after', params.created_after) + if (params.created_before) url.searchParams.append('created_before', params.created_before) + if (params.last_activity_after) + url.searchParams.append('last_activity_after', params.last_activity_after) + return url.toString() + }, + method: 'GET', + headers: (params: GreenhouseListApplicationsParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { applications: [], count: 0 }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const data = await response.json() + const applications: GreenhouseApplicationSummary[] = (Array.isArray(data) ? data : []).map( + (a: Record) => ({ + id: (a.id as number) ?? 0, + candidate_id: (a.candidate_id as number) ?? null, + prospect: (a.prospect as boolean) ?? false, + status: (a.status as string) ?? null, + current_stage: (a.current_stage as { id: number; name: string }) ?? null, + jobs: (a.jobs as Array<{ id: number; name: string }>) ?? [], + applied_at: (a.applied_at as string) ?? null, + rejected_at: (a.rejected_at as string) ?? null, + last_activity_at: (a.last_activity_at as string) ?? null, + }) + ) + return { + success: true, + output: { applications, count: applications.length }, + } + }, + + outputs: { + applications: { + type: 'array', + description: 'List of applications', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Application ID' }, + candidate_id: { type: 'number', description: 'Associated candidate ID' }, + prospect: { type: 'boolean', description: 'Whether this is a prospect application' }, + status: { type: 'string', description: 'Status (active, converted, hired, rejected)' }, + current_stage: { + type: 'object', + description: 'Current interview stage', + optional: true, + properties: { + id: { type: 'number', description: 'Stage ID' }, + name: { type: 'string', description: 'Stage name' }, + }, + }, + jobs: { + type: 'array', + description: 'Associated jobs', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Job ID' }, + name: { type: 'string', description: 'Job name' }, + }, + }, + }, + applied_at: { type: 'string', description: 'Application date (ISO 8601)' }, + rejected_at: { type: 'string', description: 'Rejection date (ISO 8601)', optional: true }, + last_activity_at: { type: 'string', description: 'Last activity date (ISO 8601)' }, + }, + }, + }, + count: { type: 'number', description: 'Number of applications returned' }, + }, +} diff --git a/apps/sim/tools/greenhouse/list-candidates.ts b/apps/sim/tools/greenhouse/list-candidates.ts new file mode 100644 index 0000000000..8254313392 --- /dev/null +++ b/apps/sim/tools/greenhouse/list-candidates.ts @@ -0,0 +1,181 @@ +import type { + GreenhouseCandidateSummary, + GreenhouseListCandidatesParams, + GreenhouseListCandidatesResponse, +} from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseListCandidatesTool: ToolConfig< + GreenhouseListCandidatesParams, + GreenhouseListCandidatesResponse +> = { + id: 'greenhouse_list_candidates', + name: 'Greenhouse List Candidates', + description: 'Lists candidates from Greenhouse with optional filtering by date, job, or email', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-500, default 100)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + created_after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only candidates created at or after this ISO 8601 timestamp', + }, + created_before: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only candidates created before this ISO 8601 timestamp', + }, + updated_after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only candidates updated at or after this ISO 8601 timestamp', + }, + updated_before: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only candidates updated before this ISO 8601 timestamp', + }, + job_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter to candidates who applied to this job ID (excludes prospects)', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter to candidates with this email address', + }, + candidate_ids: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated candidate IDs to retrieve (max 50)', + }, + }, + + request: { + url: (params: GreenhouseListCandidatesParams) => { + const url = new URL('https://harvest.greenhouse.io/v1/candidates') + if (params.per_page) url.searchParams.append('per_page', String(params.per_page)) + if (params.page) url.searchParams.append('page', String(params.page)) + if (params.created_after) url.searchParams.append('created_after', params.created_after) + if (params.created_before) url.searchParams.append('created_before', params.created_before) + if (params.updated_after) url.searchParams.append('updated_after', params.updated_after) + if (params.updated_before) url.searchParams.append('updated_before', params.updated_before) + if (params.job_id) url.searchParams.append('job_id', params.job_id) + if (params.email) url.searchParams.append('email', params.email) + if (params.candidate_ids) url.searchParams.append('candidate_ids', params.candidate_ids) + return url.toString() + }, + method: 'GET', + headers: (params: GreenhouseListCandidatesParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { candidates: [], count: 0 }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const data = await response.json() + const candidates: GreenhouseCandidateSummary[] = (Array.isArray(data) ? data : []).map( + (c: Record) => ({ + id: (c.id as number) ?? 0, + first_name: (c.first_name as string) ?? null, + last_name: (c.last_name as string) ?? null, + company: (c.company as string) ?? null, + title: (c.title as string) ?? null, + is_private: (c.is_private as boolean) ?? false, + can_email: (c.can_email as boolean) ?? false, + email_addresses: (c.email_addresses as Array<{ value: string; type: string }>) ?? [], + tags: (c.tags as string[]) ?? [], + application_ids: (c.application_ids as number[]) ?? [], + created_at: (c.created_at as string) ?? null, + updated_at: (c.updated_at as string) ?? null, + last_activity: (c.last_activity as string) ?? null, + }) + ) + return { + success: true, + output: { candidates, count: candidates.length }, + } + }, + + outputs: { + candidates: { + type: 'array', + description: 'List of candidates', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Candidate ID' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + company: { type: 'string', description: 'Current employer', optional: true }, + title: { type: 'string', description: 'Current job title', optional: true }, + is_private: { type: 'boolean', description: 'Whether candidate is private' }, + can_email: { type: 'boolean', description: 'Whether candidate can be emailed' }, + email_addresses: { + type: 'array', + description: 'Email addresses', + items: { + type: 'object', + properties: { + value: { type: 'string', description: 'Email address' }, + type: { type: 'string', description: 'Email type (personal, work, other)' }, + }, + }, + }, + tags: { + type: 'array', + description: 'Candidate tags', + items: { type: 'string', description: 'Tag' }, + }, + application_ids: { + type: 'array', + description: 'Associated application IDs', + items: { type: 'number', description: 'Application ID' }, + }, + created_at: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updated_at: { type: 'string', description: 'Last updated timestamp (ISO 8601)' }, + last_activity: { + type: 'string', + description: 'Last activity timestamp (ISO 8601)', + optional: true, + }, + }, + }, + }, + count: { type: 'number', description: 'Number of candidates returned' }, + }, +} diff --git a/apps/sim/tools/greenhouse/list-departments.ts b/apps/sim/tools/greenhouse/list-departments.ts new file mode 100644 index 0000000000..2d4d14d21b --- /dev/null +++ b/apps/sim/tools/greenhouse/list-departments.ts @@ -0,0 +1,98 @@ +import type { + GreenhouseDepartment, + GreenhouseListDepartmentsParams, + GreenhouseListDepartmentsResponse, +} from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseListDepartmentsTool: ToolConfig< + GreenhouseListDepartmentsParams, + GreenhouseListDepartmentsResponse +> = { + id: 'greenhouse_list_departments', + name: 'Greenhouse List Departments', + description: 'Lists all departments configured in Greenhouse', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-500, default 100)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params: GreenhouseListDepartmentsParams) => { + const url = new URL('https://harvest.greenhouse.io/v1/departments') + if (params.per_page) url.searchParams.append('per_page', String(params.per_page)) + if (params.page) url.searchParams.append('page', String(params.page)) + return url.toString() + }, + method: 'GET', + headers: (params: GreenhouseListDepartmentsParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { departments: [], count: 0 }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const data = await response.json() + const departments: GreenhouseDepartment[] = (Array.isArray(data) ? data : []).map( + (d: Record) => ({ + id: (d.id as number) ?? 0, + name: (d.name as string) ?? null, + parent_id: (d.parent_id as number) ?? null, + child_ids: (d.child_ids as number[]) ?? [], + external_id: (d.external_id as string) ?? null, + }) + ) + return { + success: true, + output: { departments, count: departments.length }, + } + }, + + outputs: { + departments: { + type: 'array', + description: 'List of departments', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Department ID' }, + name: { type: 'string', description: 'Department name' }, + parent_id: { type: 'number', description: 'Parent department ID', optional: true }, + child_ids: { + type: 'array', + description: 'Child department IDs', + items: { type: 'number', description: 'Department ID' }, + }, + external_id: { type: 'string', description: 'External system ID', optional: true }, + }, + }, + }, + count: { type: 'number', description: 'Number of departments returned' }, + }, +} diff --git a/apps/sim/tools/greenhouse/list-job-stages.ts b/apps/sim/tools/greenhouse/list-job-stages.ts new file mode 100644 index 0000000000..68a204ef71 --- /dev/null +++ b/apps/sim/tools/greenhouse/list-job-stages.ts @@ -0,0 +1,189 @@ +import type { + GreenhouseJobStage, + GreenhouseJobStageInterview, + GreenhouseListJobStagesParams, + GreenhouseListJobStagesResponse, + GreenhouseUserRef, +} from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseListJobStagesTool: ToolConfig< + GreenhouseListJobStagesParams, + GreenhouseListJobStagesResponse +> = { + id: 'greenhouse_list_job_stages', + name: 'Greenhouse List Job Stages', + description: 'Lists all interview stages for a specific job in Greenhouse', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + jobId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The job ID to list stages for', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-500, default 100)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params: GreenhouseListJobStagesParams) => { + const url = new URL(`https://harvest.greenhouse.io/v1/jobs/${params.jobId}/stages`) + if (params.per_page) url.searchParams.append('per_page', String(params.per_page)) + if (params.page) url.searchParams.append('page', String(params.page)) + return url.toString() + }, + method: 'GET', + headers: (params: GreenhouseListJobStagesParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { stages: [], count: 0 }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const data = await response.json() + const stages: GreenhouseJobStage[] = (Array.isArray(data) ? data : []).map( + (s: Record) => ({ + id: (s.id as number) ?? 0, + name: (s.name as string) ?? null, + created_at: (s.created_at as string) ?? null, + updated_at: (s.updated_at as string) ?? null, + job_id: (s.job_id as number) ?? 0, + priority: (s.priority as number) ?? 0, + active: (s.active as boolean) ?? true, + interviews: (Array.isArray(s.interviews) ? s.interviews : []).map( + (i: Record): GreenhouseJobStageInterview => ({ + id: (i.id as number) ?? 0, + name: (i.name as string) ?? null, + schedulable: (i.schedulable as boolean) ?? false, + estimated_minutes: (i.estimated_minutes as number) ?? null, + default_interviewer_users: (Array.isArray(i.default_interviewer_users) + ? i.default_interviewer_users + : [] + ).map( + (u: Record): GreenhouseUserRef => ({ + id: (u.id as number) ?? 0, + first_name: (u.first_name as string) ?? '', + last_name: (u.last_name as string) ?? '', + name: (u.name as string) ?? '', + employee_id: (u.employee_id as string) ?? null, + }) + ), + interview_kit: i.interview_kit + ? { + id: ((i.interview_kit as Record).id as number) ?? 0, + content: ((i.interview_kit as Record).content as string) ?? null, + questions: (Array.isArray((i.interview_kit as Record).questions) + ? (i.interview_kit as Record).questions + : []) as Array<{ id: number; question: string }>, + } + : null, + }) + ), + }) + ) + return { + success: true, + output: { stages, count: stages.length }, + } + }, + + outputs: { + stages: { + type: 'array', + description: 'List of job stages in order', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Stage ID' }, + name: { type: 'string', description: 'Stage name' }, + created_at: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updated_at: { type: 'string', description: 'Last updated timestamp (ISO 8601)' }, + job_id: { type: 'number', description: 'Associated job ID' }, + priority: { type: 'number', description: 'Stage order priority' }, + active: { type: 'boolean', description: 'Whether the stage is active' }, + interviews: { + type: 'array', + description: 'Interview steps in this stage', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Interview ID' }, + name: { type: 'string', description: 'Interview name' }, + schedulable: { + type: 'boolean', + description: 'Whether the interview is schedulable', + }, + estimated_minutes: { + type: 'number', + description: 'Estimated duration in minutes', + optional: true, + }, + default_interviewer_users: { + type: 'array', + description: 'Default interviewers', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'User ID' }, + name: { type: 'string', description: 'Full name' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + employee_id: { type: 'string', description: 'Employee ID', optional: true }, + }, + }, + }, + interview_kit: { + type: 'object', + description: 'Interview kit details', + optional: true, + properties: { + id: { type: 'number', description: 'Kit ID' }, + content: { type: 'string', description: 'Kit content (HTML)', optional: true }, + questions: { + type: 'array', + description: 'Interview kit questions', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Question ID' }, + question: { type: 'string', description: 'Question text' }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + count: { type: 'number', description: 'Number of stages returned' }, + }, +} diff --git a/apps/sim/tools/greenhouse/list-jobs.ts b/apps/sim/tools/greenhouse/list-jobs.ts new file mode 100644 index 0000000000..3b80df9afa --- /dev/null +++ b/apps/sim/tools/greenhouse/list-jobs.ts @@ -0,0 +1,182 @@ +import type { + GreenhouseJobSummary, + GreenhouseListJobsParams, + GreenhouseListJobsResponse, +} from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseListJobsTool: ToolConfig< + GreenhouseListJobsParams, + GreenhouseListJobsResponse +> = { + id: 'greenhouse_list_jobs', + name: 'Greenhouse List Jobs', + description: + 'Lists jobs from Greenhouse with optional filtering by status, department, or office', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-500, default 100)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by job status (open, closed, draft)', + }, + created_after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only jobs created at or after this ISO 8601 timestamp', + }, + created_before: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only jobs created before this ISO 8601 timestamp', + }, + updated_after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only jobs updated at or after this ISO 8601 timestamp', + }, + updated_before: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only jobs updated before this ISO 8601 timestamp', + }, + department_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter to jobs in this department ID', + }, + office_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter to jobs in this office ID', + }, + }, + + request: { + url: (params: GreenhouseListJobsParams) => { + const url = new URL('https://harvest.greenhouse.io/v1/jobs') + if (params.per_page) url.searchParams.append('per_page', String(params.per_page)) + if (params.page) url.searchParams.append('page', String(params.page)) + if (params.status) url.searchParams.append('status', params.status) + if (params.created_after) url.searchParams.append('created_after', params.created_after) + if (params.created_before) url.searchParams.append('created_before', params.created_before) + if (params.updated_after) url.searchParams.append('updated_after', params.updated_after) + if (params.updated_before) url.searchParams.append('updated_before', params.updated_before) + if (params.department_id) url.searchParams.append('department_id', params.department_id) + if (params.office_id) url.searchParams.append('office_id', params.office_id) + return url.toString() + }, + method: 'GET', + headers: (params: GreenhouseListJobsParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { jobs: [], count: 0 }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const data = await response.json() + const jobs: GreenhouseJobSummary[] = (Array.isArray(data) ? data : []).map( + (j: Record) => ({ + id: (j.id as number) ?? 0, + name: (j.name as string) ?? null, + status: (j.status as string) ?? null, + confidential: (j.confidential as boolean) ?? false, + departments: (j.departments as Array<{ id: number; name: string }>) ?? [], + offices: (j.offices as Array<{ id: number; name: string }>) ?? [], + opened_at: (j.opened_at as string) ?? null, + closed_at: (j.closed_at as string) ?? null, + created_at: (j.created_at as string) ?? null, + updated_at: (j.updated_at as string) ?? null, + }) + ) + return { + success: true, + output: { jobs, count: jobs.length }, + } + }, + + outputs: { + jobs: { + type: 'array', + description: 'List of jobs', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Job ID' }, + name: { type: 'string', description: 'Job title' }, + status: { type: 'string', description: 'Job status (open, closed, draft)' }, + confidential: { type: 'boolean', description: 'Whether the job is confidential' }, + departments: { + type: 'array', + description: 'Associated departments', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Department ID' }, + name: { type: 'string', description: 'Department name' }, + }, + }, + }, + offices: { + type: 'array', + description: 'Associated offices', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Office ID' }, + name: { type: 'string', description: 'Office name' }, + }, + }, + }, + opened_at: { + type: 'string', + description: 'Date job was opened (ISO 8601)', + optional: true, + }, + closed_at: { + type: 'string', + description: 'Date job was closed (ISO 8601)', + optional: true, + }, + created_at: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updated_at: { type: 'string', description: 'Last updated timestamp (ISO 8601)' }, + }, + }, + }, + count: { type: 'number', description: 'Number of jobs returned' }, + }, +} diff --git a/apps/sim/tools/greenhouse/list-offices.ts b/apps/sim/tools/greenhouse/list-offices.ts new file mode 100644 index 0000000000..d27cf1031e --- /dev/null +++ b/apps/sim/tools/greenhouse/list-offices.ts @@ -0,0 +1,114 @@ +import type { + GreenhouseListOfficesParams, + GreenhouseListOfficesResponse, + GreenhouseOffice, +} from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseListOfficesTool: ToolConfig< + GreenhouseListOfficesParams, + GreenhouseListOfficesResponse +> = { + id: 'greenhouse_list_offices', + name: 'Greenhouse List Offices', + description: 'Lists all offices configured in Greenhouse', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-500, default 100)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params: GreenhouseListOfficesParams) => { + const url = new URL('https://harvest.greenhouse.io/v1/offices') + if (params.per_page) url.searchParams.append('per_page', String(params.per_page)) + if (params.page) url.searchParams.append('page', String(params.page)) + return url.toString() + }, + method: 'GET', + headers: (params: GreenhouseListOfficesParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { offices: [], count: 0 }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const data = await response.json() + const offices: GreenhouseOffice[] = (Array.isArray(data) ? data : []).map( + (o: Record) => ({ + id: (o.id as number) ?? 0, + name: (o.name as string) ?? null, + location: { + name: ((o.location as Record)?.name as string) ?? null, + }, + primary_contact_user_id: (o.primary_contact_user_id as number) ?? null, + parent_id: (o.parent_id as number) ?? null, + child_ids: (o.child_ids as number[]) ?? [], + external_id: (o.external_id as string) ?? null, + }) + ) + return { + success: true, + output: { offices, count: offices.length }, + } + }, + + outputs: { + offices: { + type: 'array', + description: 'List of offices', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'Office ID' }, + name: { type: 'string', description: 'Office name' }, + location: { + type: 'object', + description: 'Office location', + properties: { + name: { type: 'string', description: 'Location name', optional: true }, + }, + }, + primary_contact_user_id: { + type: 'number', + description: 'Primary contact user ID', + optional: true, + }, + parent_id: { type: 'number', description: 'Parent office ID', optional: true }, + child_ids: { + type: 'array', + description: 'Child office IDs', + items: { type: 'number', description: 'Office ID' }, + }, + external_id: { type: 'string', description: 'External system ID', optional: true }, + }, + }, + }, + count: { type: 'number', description: 'Number of offices returned' }, + }, +} diff --git a/apps/sim/tools/greenhouse/list-users.ts b/apps/sim/tools/greenhouse/list-users.ts new file mode 100644 index 0000000000..f0f10a9aae --- /dev/null +++ b/apps/sim/tools/greenhouse/list-users.ts @@ -0,0 +1,152 @@ +import type { + GreenhouseListUsersParams, + GreenhouseListUsersResponse, + GreenhouseUser, +} from '@/tools/greenhouse/types' +import type { ToolConfig } from '@/tools/types' + +export const greenhouseListUsersTool: ToolConfig< + GreenhouseListUsersParams, + GreenhouseListUsersResponse +> = { + id: 'greenhouse_list_users', + name: 'Greenhouse List Users', + description: + 'Lists Greenhouse users (recruiters, hiring managers, admins) with optional filtering', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Greenhouse Harvest API key', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of results per page (1-500, default 100)', + }, + page: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + created_after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only users created at or after this ISO 8601 timestamp', + }, + created_before: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only users created before this ISO 8601 timestamp', + }, + updated_after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only users updated at or after this ISO 8601 timestamp', + }, + updated_before: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Return only users updated before this ISO 8601 timestamp', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by email address', + }, + }, + + request: { + url: (params: GreenhouseListUsersParams) => { + const url = new URL('https://harvest.greenhouse.io/v1/users') + if (params.per_page) url.searchParams.append('per_page', String(params.per_page)) + if (params.page) url.searchParams.append('page', String(params.page)) + if (params.created_after) url.searchParams.append('created_after', params.created_after) + if (params.created_before) url.searchParams.append('created_before', params.created_before) + if (params.updated_after) url.searchParams.append('updated_after', params.updated_after) + if (params.updated_before) url.searchParams.append('updated_before', params.updated_before) + if (params.email) url.searchParams.append('email', params.email) + return url.toString() + }, + method: 'GET', + headers: (params: GreenhouseListUsersParams) => ({ + Authorization: `Basic ${btoa(`${params.apiKey}:`)}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + return { + success: false, + output: { users: [], count: 0 }, + error: `Greenhouse API error: ${response.status} ${response.statusText}`, + } + } + + const data = await response.json() + const users: GreenhouseUser[] = (Array.isArray(data) ? data : []).map( + (u: Record) => ({ + id: (u.id as number) ?? 0, + name: (u.name as string) ?? null, + first_name: (u.first_name as string) ?? null, + last_name: (u.last_name as string) ?? null, + primary_email_address: (u.primary_email_address as string) ?? null, + disabled: (u.disabled as boolean) ?? false, + site_admin: (u.site_admin as boolean) ?? false, + emails: (u.emails as string[]) ?? [], + employee_id: (u.employee_id as string) ?? null, + linked_candidate_ids: (u.linked_candidate_ids as number[]) ?? [], + created_at: (u.created_at as string) ?? null, + updated_at: (u.updated_at as string) ?? null, + }) + ) + return { + success: true, + output: { users, count: users.length }, + } + }, + + outputs: { + users: { + type: 'array', + description: 'List of Greenhouse users', + items: { + type: 'object', + properties: { + id: { type: 'number', description: 'User ID' }, + name: { type: 'string', description: 'Full name' }, + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + primary_email_address: { type: 'string', description: 'Primary email' }, + disabled: { type: 'boolean', description: 'Whether the user is disabled' }, + site_admin: { type: 'boolean', description: 'Whether the user is a site admin' }, + emails: { + type: 'array', + description: 'All email addresses', + items: { type: 'string', description: 'Email address' }, + }, + employee_id: { type: 'string', description: 'Employee ID', optional: true }, + linked_candidate_ids: { + type: 'array', + description: 'IDs of candidates linked to this user', + items: { type: 'number', description: 'Candidate ID' }, + }, + created_at: { type: 'string', description: 'Creation timestamp (ISO 8601)' }, + updated_at: { type: 'string', description: 'Last updated timestamp (ISO 8601)' }, + }, + }, + }, + count: { type: 'number', description: 'Number of users returned' }, + }, +} diff --git a/apps/sim/tools/greenhouse/types.ts b/apps/sim/tools/greenhouse/types.ts new file mode 100644 index 0000000000..dce7bdea94 --- /dev/null +++ b/apps/sim/tools/greenhouse/types.ts @@ -0,0 +1,410 @@ +import type { ToolResponse } from '@/tools/types' + +/** + * Base parameters shared by all Greenhouse tools + */ +export interface GreenhouseBaseParams { + apiKey: string +} + +// ── List Candidates ── + +export interface GreenhouseListCandidatesParams extends GreenhouseBaseParams { + per_page?: number + page?: number + created_before?: string + created_after?: string + updated_before?: string + updated_after?: string + job_id?: string + email?: string + candidate_ids?: string +} + +export interface GreenhouseListCandidatesResponse extends ToolResponse { + output: { + candidates: GreenhouseCandidateSummary[] + count: number + } +} + +// ── Get Candidate ── + +export interface GreenhouseGetCandidateParams extends GreenhouseBaseParams { + candidateId: string +} + +export interface GreenhouseGetCandidateResponse extends ToolResponse { + output: { + id: number + first_name: string | null + last_name: string | null + company: string | null + title: string | null + is_private: boolean + can_email: boolean + created_at: string | null + updated_at: string | null + last_activity: string | null + email_addresses: Array<{ value: string; type: string }> + phone_numbers: Array<{ value: string; type: string }> + addresses: Array<{ value: string; type: string }> + website_addresses: Array<{ value: string; type: string }> + social_media_addresses: Array<{ value: string }> + tags: string[] + application_ids: number[] + recruiter: GreenhouseUserRef | null + coordinator: GreenhouseUserRef | null + attachments: GreenhouseAttachment[] + educations: GreenhouseEducation[] + employments: GreenhouseEmployment[] + custom_fields: Record + } +} + +// ── List Jobs ── + +export interface GreenhouseListJobsParams extends GreenhouseBaseParams { + per_page?: number + page?: number + status?: string + created_before?: string + created_after?: string + updated_before?: string + updated_after?: string + department_id?: string + office_id?: string +} + +export interface GreenhouseListJobsResponse extends ToolResponse { + output: { + jobs: GreenhouseJobSummary[] + count: number + } +} + +// ── Get Job ── + +export interface GreenhouseGetJobParams extends GreenhouseBaseParams { + jobId: string +} + +export interface GreenhouseGetJobResponse extends ToolResponse { + output: { + id: number + name: string | null + requisition_id: string | null + status: string | null + confidential: boolean + created_at: string | null + opened_at: string | null + closed_at: string | null + updated_at: string | null + is_template: boolean | null + notes: string | null + departments: Array<{ id: number; name: string; parent_id: number | null }> + offices: Array<{ id: number; name: string; location: { name: string | null } }> + hiring_team: GreenhouseHiringTeam + openings: GreenhouseOpening[] + custom_fields: Record + } +} + +// ── List Applications ── + +export interface GreenhouseListApplicationsParams extends GreenhouseBaseParams { + per_page?: number + page?: number + job_id?: string + status?: string + created_before?: string + created_after?: string + last_activity_after?: string +} + +export interface GreenhouseListApplicationsResponse extends ToolResponse { + output: { + applications: GreenhouseApplicationSummary[] + count: number + } +} + +// ── Get Application ── + +export interface GreenhouseGetApplicationParams extends GreenhouseBaseParams { + applicationId: string +} + +export interface GreenhouseGetApplicationResponse extends ToolResponse { + output: { + id: number + candidate_id: number + prospect: boolean + status: string | null + applied_at: string | null + rejected_at: string | null + last_activity_at: string | null + location: { address: string | null } | null + source: { id: number; public_name: string } | null + credited_to: GreenhouseUserRef | null + recruiter: GreenhouseUserRef | null + coordinator: GreenhouseUserRef | null + current_stage: { id: number; name: string } | null + rejection_reason: { id: number; name: string; type: { id: number; name: string } } | null + jobs: Array<{ id: number; name: string }> + job_post_id: number | null + answers: Array<{ question: string; answer: string }> + attachments: GreenhouseAttachment[] + custom_fields: Record + } +} + +// ── List Users ── + +export interface GreenhouseListUsersParams extends GreenhouseBaseParams { + per_page?: number + page?: number + created_before?: string + created_after?: string + updated_before?: string + updated_after?: string + email?: string +} + +export interface GreenhouseListUsersResponse extends ToolResponse { + output: { + users: GreenhouseUser[] + count: number + } +} + +// ── Get User ── + +export interface GreenhouseGetUserParams extends GreenhouseBaseParams { + userId: string +} + +export interface GreenhouseGetUserResponse extends ToolResponse { + output: { + id: number + name: string | null + first_name: string | null + last_name: string | null + primary_email_address: string | null + disabled: boolean + site_admin: boolean + emails: string[] + employee_id: string | null + linked_candidate_ids: number[] + created_at: string | null + updated_at: string | null + } +} + +// ── List Departments ── + +export interface GreenhouseListDepartmentsParams extends GreenhouseBaseParams { + per_page?: number + page?: number +} + +export interface GreenhouseListDepartmentsResponse extends ToolResponse { + output: { + departments: GreenhouseDepartment[] + count: number + } +} + +// ── List Offices ── + +export interface GreenhouseListOfficesParams extends GreenhouseBaseParams { + per_page?: number + page?: number +} + +export interface GreenhouseListOfficesResponse extends ToolResponse { + output: { + offices: GreenhouseOffice[] + count: number + } +} + +// ── List Job Stages ── + +export interface GreenhouseListJobStagesParams extends GreenhouseBaseParams { + jobId: string + per_page?: number + page?: number +} + +export interface GreenhouseListJobStagesResponse extends ToolResponse { + output: { + stages: GreenhouseJobStage[] + count: number + } +} + +// ── Shared Types ── + +export interface GreenhouseUserRef { + id: number + first_name: string + last_name: string + name: string + employee_id: string | null +} + +export interface GreenhouseAttachment { + filename: string + url: string + type: string + created_at: string | null +} + +export interface GreenhouseEducation { + id: number + school_name: string | null + degree: string | null + discipline: string | null + start_date: string | null + end_date: string | null +} + +export interface GreenhouseEmployment { + id: number + company_name: string | null + title: string | null + start_date: string | null + end_date: string | null +} + +export interface GreenhouseUser { + id: number + name: string | null + first_name: string | null + last_name: string | null + primary_email_address: string | null + disabled: boolean + site_admin: boolean + emails: string[] + employee_id: string | null + linked_candidate_ids: number[] + created_at: string | null + updated_at: string | null +} + +export interface GreenhouseDepartment { + id: number + name: string | null + parent_id: number | null + child_ids: number[] + external_id: string | null +} + +export interface GreenhouseOffice { + id: number + name: string | null + location: { name: string | null } + primary_contact_user_id: number | null + parent_id: number | null + child_ids: number[] + external_id: string | null +} + +export interface GreenhouseJobStageInterview { + id: number + name: string | null + schedulable: boolean + estimated_minutes: number | null + default_interviewer_users: GreenhouseUserRef[] + interview_kit: { + id: number + content: string | null + questions: Array<{ id: number; question: string }> + } | null +} + +export interface GreenhouseJobStage { + id: number + name: string | null + created_at: string | null + updated_at: string | null + job_id: number + priority: number + active: boolean + interviews: GreenhouseJobStageInterview[] +} + +export interface GreenhouseCandidateSummary { + id: number + first_name: string | null + last_name: string | null + company: string | null + title: string | null + is_private: boolean + can_email: boolean + email_addresses: Array<{ value: string; type: string }> + tags: string[] + application_ids: number[] + created_at: string | null + updated_at: string | null + last_activity: string | null +} + +export interface GreenhouseJobSummary { + id: number + name: string | null + status: string | null + confidential: boolean + departments: Array<{ id: number; name: string }> + offices: Array<{ id: number; name: string }> + opened_at: string | null + closed_at: string | null + created_at: string | null + updated_at: string | null +} + +export interface GreenhouseApplicationSummary { + id: number + candidate_id: number | null + prospect: boolean + status: string | null + current_stage: { id: number; name: string } | null + jobs: Array<{ id: number; name: string }> + applied_at: string | null + rejected_at: string | null + last_activity_at: string | null +} + +export interface GreenhouseHiringTeam { + hiring_managers: GreenhouseUserRef[] + recruiters: Array + coordinators: Array + sourcers: GreenhouseUserRef[] +} + +export interface GreenhouseOpening { + id: number + opening_id: string | null + status: string + opened_at: string | null + closed_at: string | null + application_id: number | null + close_reason: { id: number; name: string } | null +} + +/** + * Union type of all Greenhouse responses + */ +export type GreenhouseResponse = + | GreenhouseListCandidatesResponse + | GreenhouseGetCandidateResponse + | GreenhouseListJobsResponse + | GreenhouseGetJobResponse + | GreenhouseListApplicationsResponse + | GreenhouseGetApplicationResponse + | GreenhouseListUsersResponse + | GreenhouseGetUserResponse + | GreenhouseListDepartmentsResponse + | GreenhouseListOfficesResponse + | GreenhouseListJobStagesResponse diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index d8302770c3..1f4899024e 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -831,6 +831,19 @@ import { grainListRecordingsTool, grainListTeamsTool, } from '@/tools/grain' +import { + greenhouseGetApplicationTool, + greenhouseGetCandidateTool, + greenhouseGetJobTool, + greenhouseGetUserTool, + greenhouseListApplicationsTool, + greenhouseListCandidatesTool, + greenhouseListDepartmentsTool, + greenhouseListJobStagesTool, + greenhouseListJobsTool, + greenhouseListOfficesTool, + greenhouseListUsersTool, +} from '@/tools/greenhouse' import { greptileIndexRepoTool, greptileQueryTool, @@ -2208,6 +2221,17 @@ export const tools: Record = { grafana_list_folders: grafanaListFoldersTool, grafana_create_folder: grafanaCreateFolderTool, google_search: googleSearchTool, + greenhouse_list_candidates: greenhouseListCandidatesTool, + greenhouse_get_candidate: greenhouseGetCandidateTool, + greenhouse_list_jobs: greenhouseListJobsTool, + greenhouse_get_job: greenhouseGetJobTool, + greenhouse_list_applications: greenhouseListApplicationsTool, + greenhouse_get_application: greenhouseGetApplicationTool, + greenhouse_list_users: greenhouseListUsersTool, + greenhouse_get_user: greenhouseGetUserTool, + greenhouse_list_departments: greenhouseListDepartmentsTool, + greenhouse_list_offices: greenhouseListOfficesTool, + greenhouse_list_job_stages: greenhouseListJobStagesTool, guardrails_validate: guardrailsValidateTool, hex_cancel_run: hexCancelRunTool, hex_create_collection: hexCreateCollectionTool,