greenhouse

$npx mdskill add vm0-ai/vm0-skills/greenhouse

Fetch candidate data from Greenhouse ATS via API.

  • Retrieves applicant information for recruiting pipelines.
  • Integrates with Greenhouse Harvest API using HTTP Basic Auth.
  • Selects actions based on keywords like candidate or job application.
  • Returns paginated lists of records via standard HTTP responses.

SKILL.md

.github/skills/greenhouseView on GitHub ↗
---
name: greenhouse
description: Greenhouse Harvest API for applicant tracking and recruiting. Use when user mentions "Greenhouse", "ATS", "applicant tracking", "candidate", "job application", "recruiting pipeline", "job post", "scheduled interview", or "offer".
---

## Troubleshooting

If requests fail, run `zero doctor check-connector --env-name GREENHOUSE_TOKEN` or `zero doctor check-connector --url https://harvest.greenhouse.io/v1/candidates --method GET`

## Authentication

Greenhouse Harvest uses **HTTP Basic Auth** with the API token as the **username** and a **blank password**. The token is sent in the `Authorization` header as `Basic base64(TOKEN:)` (note the trailing colon — blank password).

Encode the header on the fly with `printf` and `base64`:

```bash
curl -s "https://harvest.greenhouse.io/v1/candidates?per_page=5" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

All examples below reuse this header. Write POST bodies to `/tmp/*.json` and pass with `-d @/tmp/file.json` per house style.

## Environment Variables

| Variable | Description |
|---|---|
| `GREENHOUSE_TOKEN` | Harvest API key (v1/v2) |

## Key Endpoints

Base URL: `https://harvest.greenhouse.io`

### 1. List Candidates

Paginated. `per_page` max is 500 (default 100). Pagination uses RFC-5988 `Link` response headers (`rel="next"`, `rel="prev"`, `rel="last"`).

```bash
curl -s "https://harvest.greenhouse.io/v1/candidates?per_page=100&page=1" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

Useful query parameters:

- `per_page` — 1 to 500
- `page` — 1-based pagination cursor
- `created_after` / `updated_after` — ISO-8601 timestamp
- `email` — exact email match
- `job_id` — filter by associated job

To follow pagination, read the `Link` header. Show it by adding `-D -` (dump headers):

```bash
curl -s -D - "https://harvest.greenhouse.io/v1/candidates?per_page=100&page=1" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)" -o /tmp/greenhouse_candidates.json
```

The `Link` header looks like: `<https://harvest.greenhouse.io/v1/candidates?page=2&per_page=100>; rel="next", ...`.

### 2. Get a Single Candidate

Replace `<candidate-id>` with the actual candidate ID:

```bash
curl -s "https://harvest.greenhouse.io/v1/candidates/<candidate-id>" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

### 3. Create a Candidate

**Required fields:** `first_name`, `last_name`. Also commonly set: `company`, `title`, `emails`, `phone_numbers`, `applications`.

**Required header:** `On-Behalf-Of: <greenhouse-user-id>` — the Greenhouse user ID the request is made on behalf of (used for auditing). Find user IDs with `GET /v1/users`.

Write to `/tmp/greenhouse_candidate.json`:

```json
{
  "first_name": "Jane",
  "last_name": "Doe",
  "company": "Acme Corp",
  "title": "Staff Engineer",
  "emails": [
    { "value": "jane.doe@example.com", "type": "personal" }
  ],
  "phone_numbers": [
    { "value": "+1-555-123-4567", "type": "mobile" }
  ]
}
```

Then run. Replace `<greenhouse-user-id>` with the actual user ID:

```bash
curl -s -X POST "https://harvest.greenhouse.io/v1/candidates" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)" --header "On-Behalf-Of: <greenhouse-user-id>" --header "Content-Type: application/json" -d @/tmp/greenhouse_candidate.json
```

### 4. List Applications

An application is a candidate's submission to a specific job. Paginated the same way as candidates.

```bash
curl -s "https://harvest.greenhouse.io/v1/applications?per_page=100&page=1&status=active" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

Useful query parameters:

- `status` — `active`, `converted`, `hired`, `rejected`
- `job_id` — filter by job
- `created_after` / `created_before` / `last_activity_after` — ISO-8601 timestamps
- `skip_count=true` — faster; drops `rel="last"` from the Link header

### 5. List Jobs

```bash
curl -s "https://harvest.greenhouse.io/v1/jobs?status=open&per_page=100" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

Useful query parameters:

- `status` — `open`, `closed`, `draft`
- `department_id`, `office_id`
- `created_after` / `updated_after` — ISO-8601 timestamps

### 6. List Job Posts

Job posts are the public-facing postings attached to jobs.

```bash
curl -s "https://harvest.greenhouse.io/v1/job_posts?active=true&live=true&per_page=100" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

### 7. List Offers

```bash
curl -s "https://harvest.greenhouse.io/v1/offers?status=sent&per_page=100" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

Useful query parameters:

- `status` — `draft`, `approval_sent`, `approved`, `sent`, `sent_manually`, `accepted`, `rejected`, `deprecated`
- `created_after` / `updated_after` — ISO-8601 timestamps

### 8. List Scheduled Interviews

```bash
curl -s "https://harvest.greenhouse.io/v1/scheduled_interviews?per_page=100" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

Useful query parameters:

- `application_id` — filter to a single application
- `updated_after`, `starts_after`, `ends_before` — ISO-8601 timestamps
- `actionable` — `true` to return only interviews awaiting scorecard feedback

### 9. Add a Note to a Candidate's Activity Feed

**Required header:** `On-Behalf-Of: <greenhouse-user-id>`.

Write to `/tmp/greenhouse_note.json`:

```json
{
  "user_id": 158108,
  "body": "Reached out via LinkedIn on 2026-04-18. Awaiting response.",
  "visibility": "admin_only"
}
```

`visibility` is one of `admin_only`, `private`, `public`. Then run. Replace `<candidate-id>` and `<greenhouse-user-id>`:

```bash
curl -s -X POST "https://harvest.greenhouse.io/v1/candidates/<candidate-id>/activity_feed/notes" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)" --header "On-Behalf-Of: <greenhouse-user-id>" --header "Content-Type: application/json" -d @/tmp/greenhouse_note.json
```

## Common Workflows

### Find a Candidate by Email, Then Read Their Activity Feed

```bash
curl -s "https://harvest.greenhouse.io/v1/candidates?email=jane.doe@example.com" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

Replace `<candidate-id>` with the `id` from the previous response:

```bash
curl -s "https://harvest.greenhouse.io/v1/candidates/<candidate-id>/activity_feed" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

### Pull All Applications for an Open Job

Replace `<job-id>` with the actual job ID:

```bash
curl -s "https://harvest.greenhouse.io/v1/applications?job_id=<job-id>&status=active&per_page=500" --header "Authorization: Basic $(printf "%s:" "$GREENHOUSE_TOKEN" | base64 -w 0)"
```

## Notes

- **Harvest v1/v2 will be deprecated on August 31, 2026.** Greenhouse is migrating to Harvest v3 (OAuth-based). Plan to migrate before the deprecation date. Until then, v1 continues to work. This skill targets v1 endpoints.
- **Basic Auth encoding** uses a trailing colon — `base64(TOKEN:)` — because the password is blank.
- **Rate limits:** Responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers; limits are per 10-second window. On HTTP 429, honour the `Retry-After` header.
- **`On-Behalf-Of` header** is required for all POST/PATCH/DELETE requests. Pass the integer ID of the acting Greenhouse user.
- **Pagination** uses the RFC-5988 `Link` header. The maximum `per_page` value is 500.
- **Permission scoping:** each API key has endpoint-level permissions configured at creation time in the Greenhouse Dev Center. A 403 response usually means the key is missing a scope — not an auth failure.

More from vm0-ai/vm0-skills

SkillDescription
account-reconciliationPerform account reconciliations comparing general ledger balances against subledgers, bank statements, or external records. Use for bank reconciliation, GL-to-subledger reconciliation, intercompany reconciliation, balance sheet reconciliation, reconciling item analysis, outstanding item aging, or clearing open items.
agentphoneBuild AI phone agents with AgentPhone API. Use when the user wants to make phone calls, send/receive SMS, manage phone numbers, create voice agents, set up webhooks, or check usage — anything related to telephony, phone numbers, or voice AI.
ahrefsAhrefs SEO API for backlink and keyword analysis. Use when user mentions
amplitudeAmplitude product analytics API. Use when user mentions "Amplitude",
analysis-qaQuality-check a data analysis before sharing — verify joins, aggregations, denominators, time ranges, and metric definitions. Detect pitfalls like survivorship bias, average-of-averages, join explosion, timezone mismatches, incomplete periods, and selection bias. Includes documentation templates for reproducible analyses.
anthropic-managed-agentsAnthropic Managed Agents API for programmatically creating, running, and streaming AI agents on Anthropic's cloud infrastructure. Use when the user mentions "Managed Agents", "Anthropic agent sessions", or needs to create/run/stream an Anthropic agent with tool use (bash, git, web), attach GitHub repositories, or inject secrets via Vault. Do NOT use for standard Claude Messages API — use the Claude API skill instead.
apifyApify web scraping platform. Use when user mentions "scrape website",
asanaAsana API for tasks and projects. Use when user mentions "Asana", "asana.com",
atlassianAtlassian API for Confluence and Jira. Use when user mentions "Confluence
attioAttio REST API for AI-native CRM operations — manage companies, people, deals, and custom objects, plus notes, tasks, lists, and comments. Use when the user mentions "Attio", "CRM record", "create company", "add person", "list entry", "CRM note", or "CRM task".