---
title: "Create approval flow"
method: POST
path: "/v3/approval_flows"
tags: ["Approval Flows"]
---

# Create approval flow

`POST /v3/approval_flows`

Create a multi-step approval flow on a job (and optionally a specific offer), seeded with the supplied approver groups. Each job allows at most one flow per `approval_type`: one `open_job` flow gating job opening, one `offer_job` flow gating offer creation, and one `offer_candidate` prototype on the job plus one `offer_candidate` flow per offer (set `offer_id` for the latter). The new flow is created in `pending` status with no emails sent; call `POST /v3/approval_flows/{id}/request_approvals` to actually start it. Each approver entry may identify a user by `user_id`, `email`, or `employee_id`, but the same user cannot appear in more than one group of the same flow.

## Request body

- object
  - `job_id` integer, required — Id of the job (hiring plan) this approval flow will gate. References a `/v3/jobs` row.
  - `offer_id` integer — Id of the specific offer this approval flow will gate. Only valid (and required) when `approval_type` is `offer_candidate`; omit for `open_job` and `offer_job` flows.
  - `approval_type` 'open_job' | 'offer_job' | 'offer_candidate', required — What this flow approves. `open_job` gates opening the job for recruiting, `offer_job` gates allowing offers to be created on the job, and `offer_candidate` gates extending an individual offer to a candidate. Each job allows at most one `open_job` and one `offer_job` flow; `offer_candidate` flows exist either as a job-level prototype (no `offer_id`) or one per offer.
  - `sequential` boolean, required — When `true`, approver groups resolve one at a time in submitted order — only after the current group resolves as approved are the next group's approvers asked. When `false`, all groups are activated in parallel once the flow is started.
  - `approver_groups` object[], required — Ordered tiers of approvers to seed into the new flow. The array's index becomes each group's `sort_order` (zero-based), so earlier entries are asked first in sequential flows.
    - `approvals_required` integer, required — Number of approvers in this group whose approvals are needed to resolve the group as approved (the M in "M of N"). Must be at least 1 and no greater than the number of `approvers` in the group.
    - `approvers` union[], required — Users to add as approvers in this group. Each entry must identify exactly one user via `user_id`, `email`, or `employee_id`; the service resolves the identifier to a user in the caller's organization and returns 422 if any entry cannot be matched or is duplicated across groups in the same flow.
      - union
        - object
          - `email` string — Email of an existing, non-disabled user in the caller's organization.
          - `employee_id` string — External employee id (from employee data sync) of an existing, non-disabled user in the caller's organization. Useful when an HRIS holds employee ids but not Greenhouse user ids.
          - `user_id` integer, required — Id of an existing, non-disabled user in the caller's organization. References a `/v3/users` row.
        - object
          - `email` string, required — Email of an existing, non-disabled user in the caller's organization.
          - `employee_id` string — External employee id (from employee data sync) of an existing, non-disabled user in the caller's organization. Useful when an HRIS holds employee ids but not Greenhouse user ids.
          - `user_id` integer — Id of an existing, non-disabled user in the caller's organization. References a `/v3/users` row.
        - object
          - `email` string — Email of an existing, non-disabled user in the caller's organization.
          - `employee_id` string, required — External employee id (from employee data sync) of an existing, non-disabled user in the caller's organization. Useful when an HRIS holds employee ids but not Greenhouse user ids.
          - `user_id` integer — Id of an existing, non-disabled user in the caller's organization. References a `/v3/users` row.

## Response `201`

Successful

- object
  - `id` integer
  - `created_at` string, date-time
  - `updated_at` string, date-time
  - `job_id` integer — Id of the job (hiring plan) this approval flow belongs to. References a `/v3/jobs` row.
  - `offer_id` integer, nullable — Id of the specific offer this approval flow gates. Set only for `offer_candidate` flows attached to a real offer; null on the `open_job` and `offer_job` flows (which gate the job itself), and null on the `offer_candidate` prototype flow that lives on the job before any offer is created.
  - `approval_type` 'open_job' | 'offer_job' | 'offer_candidate' — What this flow approves. `open_job` gates opening the job for recruiting, `offer_job` gates allowing offers to be created on the job, and `offer_candidate` gates extending an individual offer to a candidate (one flow per offer, plus a prototype on the job).
  - `sequential` boolean — When `true`, approver groups resolve one at a time in `sort_order` — only after the current group resolves as approved are the next group's approvers asked. When `false`, all groups are activated in parallel as soon as the flow is started.
  - `version` integer — Monotonically increasing revision counter for this approval flow. Job approval flows increment when system fields (e.g., department, requisition id, openings) or custom fields marked as triggering re-approval change after the flow has started. Use this together with an approver's `version_sent` to tell whether a pending approver is responding to the current version or a stale request. Offer approval flows attached to a specific `offer_id` are always version 1.
  - `requested_by_id` integer, nullable — Id of the user who started the flow by requesting approvals (the V3 `request_approvals` endpoint, or the in-app "Request Approval" button). References a `/v3/users` row. Null until the flow has been started — approver groups exist but no emails have been sent.
  - `approval_status` 'pending' | 'rejected' | 'approved' | 'null', nullable — Denormalized terminal state of the flow, computed from the underlying approvers. `pending` while any group is still unresolved, `approved` once every group has reached its `approvals_required` threshold, `rejected` once any group has accumulated enough rejections to be unrecoverable. Updates to `sequential` and `replace_approver_groups` are only allowed while `pending`.
  - `approver_groups` object[] — Approver groups created on this flow, ordered by `sort_order`. Returned only on create/replace responses as a convenience; the canonical way to read groups for an existing flow is `/v3/approver_groups?approval_flow_ids=` and the people in each group via `/v3/approvers?approver_group_ids=`.
    - `id` integer — Id of the created approver group. References a `/v3/approver_groups` row.
    - `approvals_required` integer — Number of approvers in this group whose `approve` actions are needed to resolve the group as approved (i.e., the M in "M of N").
    - `sort_order` integer — Zero-based position of this group within the flow. When the flow is `sequential`, lower `sort_order` groups must resolve as approved before later groups become actionable.
    - `approvers` object[] — Users seeded into this group, in the order they were submitted in the request.
      - `id` integer — Id of the created approver row. References a `/v3/approvers` row.
      - `user_id` integer — Id of the user designated as an approver. References a `/v3/users` row.
      - `email` string — Email address of the designated user, echoed back for client-side display so partners do not have to round-trip to `/v3/users` after a create.

## Other responses

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

---

[API](https://skmtc.dev/greenhouse/apis/auth-api.md) · [All operations](https://skmtc.dev/greenhouse/apis/auth-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/greenhouse/auth-api/revisions/9517a2e54640/schema)
