---
title: "Create offer"
method: POST
path: "/v3/offers"
tags: ["Offers"]
---

# Create offer

`POST /v3/offers`

## Request

- Base URL: `https://harvest.greenhouse.io`
- URL: `https://harvest.greenhouse.io/v3/offers`
- Auth: one of:
  - OAuth 2
    - Authorization code: `https://auth.greenhouse.io/authorize`
    - Client credentials
    - Token URL: `https://auth.greenhouse.io/token` scopes: `harvest:offers:create`
  - HTTP bearer

Create a new offer on an existing application. `application_id` is always required; `starts_on` and `custom_fields` (compensation components plus any offer custom fields configured on the hiring plan) are optional in the schema, but organization configuration may require more: offer custom fields marked as required must be provided in `custom_fields` (a 422 response names the missing fields), `starts_on` must be provided when the Start Date field is marked required, and `opening_id` must be provided when your organization requires an opening on offers. `opening_id` must be an open opening on the application's job that no other unresolved offer holds. Discover the organization's offer fields — including `required` and `name_key` — via `GET /v3/custom_fields?field_type=offer&active=true`. The new offer is created with `status=Created` at `version=1`. If the application already has an offer, the submission is applied to the same offer chain rather than creating a competing record: a new version is created — and the prior version marked `Deprecated` — only when a version-triggering field changes, namely `starts_on` (when your organization enables offer versioning on start-date changes) or a compensation/offer custom field configured to trigger a new version; otherwise the current offer is updated in place. Approval flows configured for the hiring plan are attached automatically — partners must drive the approval through `/v3/approval_flows` before the offer can be sent to the candidate. Offers carry private compensation data, so writing them is gated separately from the `harvest:offers:create` scope: when the access token includes a `sub` claim, the Site Admin it names must also hold the "See private notes, private fields, salary info, manage offers, request approval, and approve jobs/offers" advanced permission, or the request is rejected with `403 Forbidden`. Tokens issued without a `sub` claim act as your integration user and are not subject to it.

## Request body

- object
  - `application_id` integer, required: Id of the application this offer is being extended on. Required.
  - `starts_on` string, date: Candidate's proposed start date, in ISO 8601 (YYYY-MM-DD). Required when the organization marks the Start Date offer field as required.
  - `opening_id` integer: Id of the opening this offer is for. Must be an open opening on the application's job that no other unresolved offer holds; otherwise the request fails with a 422. Required when the organization requires an opening on offers. List the job's openings via `GET /v3/openings?job_ids={id}&open=true`.
  - `custom_fields` union[]: Values for the offer's custom fields, including compensation components (base pay, equity, bonus, etc.). Each entry sets one custom field by name or id; only fields configured on the application's hiring plan apply. Offer custom fields the organization marks as required must be included, otherwise the request fails with a 422 naming the missing fields. List the organization's offer fields, including `required` and `name_key`, via `GET /v3/custom_fields?field_type=offer&active=true`. Harvest V3 does not evaluate calculated custom fields; if the organization hasn't enabled overriding that field, the request fails with a 422 naming it as not overridable.
    - union
      - object
        - `name_key` string, required: Required if custom_field_id is not provided. Exactly one of name_key or custom_field_id must be specified.
        - `custom_field_id` integer: Required if name_key is not provided. Exactly one of name_key or custom_field_id must be specified.
        - `value` union
          - string
          - string
          - string
          - string
          - string
          - boolean
          - number: User ID
          - union
            - number
            - string
          - union
            - string[]
            - number[]
          - number
          - object
            - `amount` number, required
            - `currency_code` string, required
            - `rationale` string
            - `frequency` string
          - object
            - `min_amount` number, required
            - `max_amount` number, required
            - `currency_code` string, required
          - object
            - `min_value` number, required
            - `max_value` number, required
      - object
        - `name_key` string: Required if custom_field_id is not provided. Exactly one of name_key or custom_field_id must be specified.
        - `custom_field_id` integer, required: Required if name_key is not provided. Exactly one of name_key or custom_field_id must be specified.
        - `value` union
          - string
          - string
          - string
          - string
          - string
          - boolean
          - number: User ID
          - union
            - number
            - string
          - union
            - string[]
            - number[]
          - number
          - object
            - `amount` number, required
            - `currency_code` string, required
            - `rationale` string
            - `frequency` string
          - object
            - `min_amount` number, required
            - `max_amount` number, required
            - `currency_code` string, required
          - object
            - `min_value` number, required
            - `max_value` number, required

## Response `201`

Successful

- object
  - `id` integer
  - `created_at` string, date-time
  - `updated_at` string, date-time
  - `version` integer: Revision number of this offer within its application. Greenhouse creates a new offer row (incrementing `version`) whenever a tracked field on an existing offer changes — typically `starts_on`, `opening_id`, or a custom field configured to trigger a new version. Pair with `current_only=true` to filter the list endpoint down to the latest version per application.
  - `application_id` integer: Id of the application this offer is extended on. Every offer belongs to exactly one application; the offer is voided if the application is rejected or deleted.
  - `job_id` integer: Id of the job this offer's application is on.
  - `candidate_id` integer: Id of the candidate (person) receiving this offer. Resolved through the offer's application.
  - `opening_id` integer, nullable: Id of the specific opening this offer is being extended for. `null` when the offer has not yet been linked to an opening.
  - `status` 'Created' | 'Accepted' | 'Rejected' | 'Deprecated': Lifecycle status of the offer. `Created` for offers still being drafted or pending approval, `Accepted` once the candidate accepts, `Rejected` if declined or withdrawn, and `Deprecated` for superseded prior versions (a new offer version replaces an earlier one with this status).
  - `starts_on` string, date, nullable: Candidate's proposed start date, in ISO 8601 (YYYY-MM-DD). `null` when no start date has been set on the offer.
  - `sent_on` string, date, nullable: Date the offer was sent to the candidate, in ISO 8601 (YYYY-MM-DD). `null` until the offer has been sent.
  - `resolved_at` string, date-time, nullable: Timestamp the offer was resolved (`Accepted` or `Rejected`), in ISO 8601. Date updates submitted through `PATCH /v3/offers/{id}` are normalized to noon UTC on the supplied date. `null` while the offer is still `Created` or has been superseded as `Deprecated` without a resolution.
  - `custom_fields` object, nullable

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `422`: Unprocessable Content

---

[API](https://skmtc.dev/greenhouse/apis/harvest-api.md) - [All operations](https://skmtc.dev/greenhouse/apis/harvest-api/llms.txt) - [OpenAPI document](https://skmtc.dev/greenhouse/apis/harvest-api/revisions/822822e3f771?raw)
