Stitchflow
Greenhouse logo

Greenhouse User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 9, 2026

Summary and recommendation

The Greenhouse Harvest API exposes user management under `https://harvest.greenhouse.io/v1/users` using HTTP Basic Auth - the API key is Base64-encoded as the username with an empty password. All write operations (POST, PATCH) require an `On-Behalf-Of: <greenhouse_user_id>` header referencing an active Site Admin; omitting it returns a 403.

There is no official SDK; all integrations are raw HTTP. API keys are displayed only once at creation and must be stored immediately.

Rate limits are enforced at 50 requests per 10 seconds per key. Exceeding the limit returns HTTP 429 with `X-RateLimit-Limit` and `X-RateLimit-Remaining` headers; no `Retry-After` header is provided, so callers must implement their own backoff.

Pagination uses `page` + `per_page` (max 500); Link headers are absent - check whether the returned array length equals `per_page` to determine if additional pages exist.

API quick reference

Has user APIYes
Auth methodHTTP Basic Auth (API key as username, empty password); key is Base64-encoded in Authorization header
Base URLOfficial docs
SCIM availableYes
SCIM plan requiredAdvanced or Expert (custom/quote-based pricing)

Authentication

Auth method: HTTP Basic Auth (API key as username, empty password); key is Base64-encoded in Authorization header

Setup steps

  1. Log in to Greenhouse as a Site Admin.
  2. Navigate to Configure > Dev Center > API Credential Management.
  3. Click 'Create New API Key', select 'Harvest' as the API type, and assign permissions.
  4. Copy the generated API key immediately (shown only once).
  5. Encode the key as Base64 (key + ':') and pass as the Authorization header: 'Authorization: Basic '.

Required scopes

Scope Description Required for
Users – View Read access to user records (GET /users, GET /users/{id}) Listing and retrieving users
Users – Edit Write access to update user records (PATCH /users/{id}) Updating user attributes such as name or employee ID
Users – Disable Permission to disable a user account Deactivating users via PATCH /users/{id}

User object / data model

Field Type Description On create On update Notes
id integer Unique Greenhouse user ID system-assigned immutable Used as path parameter in user-specific endpoints
name string Full name of the user required optional
first_name string User's first name required optional
last_name string User's last name required optional
primary_email_address string Primary email address used for login required optional Must be unique across the organization
emails array All email addresses associated with the user optional optional
employee_id string External employee identifier optional optional Useful for HRIS correlation
updated_at datetime (ISO 8601) Timestamp of last update system-assigned system-assigned Useful for delta sync filtering via updated_after query param
created_at datetime (ISO 8601) Timestamp of user creation system-assigned immutable
disabled boolean Whether the user account is disabled defaults to false optional Set to true to deactivate; cannot delete users via API
site_admin boolean Whether the user has Site Admin privileges optional optional Requires elevated API key permissions to modify
linked_candidate_ids array of integers Candidate records linked to this user system-assigned read-only
offices array of objects Offices the user is associated with optional optional
departments array of objects Departments the user is associated with optional optional
timezone string User's timezone (IANA format) optional optional

Core endpoints

List Users

  • Method: GET
  • URL: https://harvest.greenhouse.io/v1/users
  • Watch out for: Pagination is required for large orgs; max per_page is 500. Use updated_after for incremental sync.

Request example

GET /v1/users?per_page=100&page=1&updated_after=2024-01-01T00:00:00Z
Authorization: Basic <base64_api_key>

Response example

[
  {
    "id": 112233,
    "name": "Jane Doe",
    "primary_email_address": "jane@example.com",
    "disabled": false,
    "site_admin": false
  }
]

Get Single User

  • Method: GET
  • URL: https://harvest.greenhouse.io/v1/users/{id}
  • Watch out for: Can also look up by email using GET /v1/users?email=jane@example.com

Request example

GET /v1/users/112233
Authorization: Basic <base64_api_key>

Response example

{
  "id": 112233,
  "name": "Jane Doe",
  "primary_email_address": "jane@example.com",
  "employee_id": "EMP-001",
  "disabled": false
}

Get User by Email

  • Method: GET
  • URL: https://harvest.greenhouse.io/v1/users?email={email}
  • Watch out for: Returns an array; check for empty array if user not found.

Request example

GET /v1/users?email=jane%40example.com
Authorization: Basic <base64_api_key>

Response example

[
  {
    "id": 112233,
    "name": "Jane Doe",
    "primary_email_address": "jane@example.com"
  }
]

Update User

  • Method: PATCH
  • URL: https://harvest.greenhouse.io/v1/users/{id}
  • Watch out for: Only fields included in the body are updated. Cannot change primary_email_address via this endpoint.

Request example

PATCH /v1/users/112233
Content-Type: application/json

{"first_name":"Jane","last_name":"Smith","employee_id":"EMP-002"}

Response example

{
  "id": 112233,
  "name": "Jane Smith",
  "employee_id": "EMP-002",
  "disabled": false
}

Disable User

  • Method: PATCH
  • URL: https://harvest.greenhouse.io/v1/users/{id}/disable
  • Watch out for: There is no DELETE endpoint for users; disabling is the only deactivation method via the Harvest API.

Request example

PATCH /v1/users/112233/disable
Authorization: Basic <base64_api_key>

Response example

{
  "id": 112233,
  "name": "Jane Smith",
  "disabled": true
}

Enable User

  • Method: PATCH
  • URL: https://harvest.greenhouse.io/v1/users/{id}/enable
  • Watch out for: Re-enabling a user restores their access but does not restore removed job permissions.

Request example

PATCH /v1/users/112233/enable
Authorization: Basic <base64_api_key>

Response example

{
  "id": 112233,
  "name": "Jane Smith",
  "disabled": false
}

List User Permissions (Job)

  • Method: GET
  • URL: https://harvest.greenhouse.io/v1/users/{id}/permissions/jobs
  • Watch out for: Job-level permissions are separate from site-level roles; both must be managed independently.

Request example

GET /v1/users/112233/permissions/jobs
Authorization: Basic <base64_api_key>

Response example

[
  {
    "id": 9988,
    "job_id": 5544,
    "user_role_id": 2
  }
]

Add User (via Job Board / Invite)

  • Method: POST
  • URL: https://harvest.greenhouse.io/v1/users
  • Watch out for: The On-Behalf-Of header (On-Behalf-Of: ) is required to attribute the action to a specific admin user.

Request example

POST /v1/users
Content-Type: application/json

{"first_name":"John","last_name":"Doe","email":"john@example.com","send_email_invite":true}

Response example

{
  "id": 223344,
  "name": "John Doe",
  "primary_email_address": "john@example.com",
  "disabled": false
}

Rate limits, pagination, and events

  • Rate limits: Greenhouse enforces rate limits on the Harvest API. The documented limit is 50 requests per 10 seconds per API key.
  • Rate-limit headers: Yes
  • Retry-After header: No
  • Rate-limit notes: When the limit is exceeded, the API returns HTTP 429. Response headers include X-RateLimit-Limit and X-RateLimit-Remaining. Retry after a brief backoff; no Retry-After header is documented.
  • Pagination method: offset
  • Default page size: 100
  • Max page size: 500
  • Pagination pointer: page / per_page
Plan Limit Concurrent
All plans (Harvest API) 50 requests per 10 seconds 0
  • Webhooks available: Yes
  • Webhook notes: Greenhouse supports webhooks for key recruiting events. User-specific webhook events are limited; most webhooks cover candidate and job lifecycle events.
  • Alternative event strategy: For user change detection, poll GET /v1/users with the updated_after query parameter for incremental sync.
  • Webhook events: candidate_hired, candidate_stage_change, job_post_updated, offer_created, offer_updated, application_submitted, prospect_created

SCIM API status

  • SCIM available: Yes

  • SCIM version: 2.0

  • Plan required: Advanced or Expert (custom/quote-based pricing)

  • Endpoint: https://harvest.greenhouse.io/scim/v2

  • Supported operations: Create user (POST /Users), Read user (GET /Users/{id}), List users (GET /Users), Update user (PUT /Users/{id}), Deactivate user (PATCH /Users/{id} with active=false)

Limitations:

  • Group provisioning is not supported; only user provisioning.
  • SSO must be configured before SCIM can be enabled.
  • Azure Entra ID sync interval is approximately 40 minutes.
  • SCIM does not provision job-level permissions; those must be managed manually or via Harvest API.
  • Supported IdPs: Okta, Azure Entra ID, Google Workspace, OneLogin.
  • Deprovisioning sets the user to disabled; it does not delete the user record.

Common scenarios

Three integration patterns cover the primary use cases against the Harvest API, each with a distinct identity graph consideration.

Onboarding with job permission assignment: POST /v1/users creates the account (include send_email_invite=true and the On-Behalf-Of header). Capture the returned id, then POST /v1/users/{id}/permissions/jobs with the target job_id and user_role_id. SCIM alone cannot complete this flow - job-level permissions sit outside the SCIM schema and must be set via Harvest API.

Incremental sync to an external identity graph: GET /v1/users?updated_after=<ISO8601_UTC>&per_page=500 drives delta pulls. Use primary_email_address or employee_id as the correlation key when upserting into a downstream identity graph. Subtract a 5-minute buffer from the last sync timestamp to guard against clock skew causing missed updates.

SCIM offboarding (Okta): Unassigning the user in Okta triggers a SCIM PATCH /Users/{id} with active=false, setting disabled=true in Greenhouse. Confirm via GET /v1/users/{id}. Note that group membership changes in Okta do not propagate - Greenhouse SCIM does not support group provisioning.

Onboard a new employee and assign job permissions

  1. POST /v1/users with first_name, last_name, email, and send_email_invite=true (include On-Behalf-Of header).
  2. Capture the returned user id.
  3. POST /v1/users/{id}/permissions/jobs with the target job_id and user_role_id to grant job-level access.

Watch out for: The On-Behalf-Of header must reference an active Site Admin user ID, or the request returns 403.

Incremental sync of updated users to an external system

  1. Store the timestamp of the last successful sync.
  2. GET /v1/users?updated_after=&per_page=500&page=1.
  3. Iterate pages until the returned array has fewer items than per_page.
  4. Upsert each user record into the external system using primary_email_address or employee_id as the correlation key.
  5. Update the stored timestamp to the current time.

Watch out for: updated_after uses ISO 8601 UTC; ensure the timestamp is URL-encoded. Clock skew can cause missed updates - subtract a small buffer (e.g., 5 minutes) from the last sync time.

Offboard a departing employee via SCIM (Okta)

  1. In Okta, unassign the user from the Greenhouse SCIM application or deactivate the Okta user.
  2. Okta sends a SCIM PATCH /Users/{id} with active=false to Greenhouse.
  3. Greenhouse sets the user's disabled flag to true, revoking login access.
  4. Verify via GET /v1/users/{id} that disabled=true.

Watch out for: SCIM deprovisioning only disables the user; job-level permissions and historical records are retained. Group membership changes in Okta do not propagate to Greenhouse (groups not supported).

Why building this yourself is a trap

Several API behaviors are non-obvious and will cause silent failures if not handled explicitly.

  • No delete endpoint exists. PATCH /v1/users/{id}/disable is the only deactivation path; disabled users retain all historical data and can be re-enabled, restoring access but not removed job permissions.
  • primary_email_address is immutable via API. The PATCH /users/{id} endpoint cannot change the primary email; that requires a manual admin action in the UI, breaking any automated identity graph update that relies on email as a mutable key.
  • SCIM scope is user-only. Group provisioning is not supported. Role and job-level permission assignment falls entirely outside the SCIM 2.0 implementation at https://harvest.greenhouse.io/scim/v2, requiring a parallel Harvest API call for every provisioned user.
  • SCIM requires Advanced or Expert tier plus a fully configured SSO. Attempting to enable SCIM before SSO is active will fail without a clear error surfaced to the end user.

Automate Greenhouse workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 9, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

15Five logo

15Five

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

15Five uses a fixed role-based permission model with six predefined roles: Account Admin, HR Admin, Billing Admin, Group Admin, Manager, and Employee. No custom roles can be constructed. User management lives at Settings gear → People → Manage people p

1Password logo

1Password

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

1Password's admin console at my.1password.com covers the full user lifecycle — invitations, group assignments, vault access, suspension, and deletion — without any third-party tooling. Like every app that mixes role-based and resource-level permissions

8x8 logo

8x8

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

8x8 Admin Console supports full lifecycle user management — create, deactivate, and delete — across its X Series unified communications platform. Every app a user can access (8x8 Work desktop, mobile, web, Agent Workspace) is gated by license assignmen