> ## Documentation Index
> Fetch the complete documentation index at: https://trygradient.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Roles

> Manage the roles your organization is hiring for

# Roles

A role is a job opening you're hiring for. Roles are stored independently of
assessments, and an assessment can be linked to one or more roles. A role holds
a title, an optional job description (markdown) and/or posting URL, and up to
five custom priority skills selected from the JD.

## List Roles

```
GET /api/roles
```

Returns the org's roles (newest first), each with a count of linked assessments.

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://app.trygradient.ai/api/roles" \
    -H "Authorization: Bearer gai_your_key"
  ```

  ```javascript Node.js theme={null}
  const res = await fetch('https://app.trygradient.ai/api/roles', {
    headers: { Authorization: `Bearer ${apiKey}` },
  });
  const { roles } = await res.json();
  ```
</CodeGroup>

## Create Role

```
POST /api/roles
```

<ParamField body="title" type="string" required>
  The role title, e.g. "Senior Product Manager".
</ParamField>

<ParamField body="jdText" type="string">
  The job description body (markdown). Optional.
</ParamField>

<ParamField body="jdLink" type="string">
  A URL to the job posting. Optional.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://app.trygradient.ai/api/roles" \
    -H "Authorization: Bearer gai_your_key" \
    -H "Content-Type: application/json" \
    -d '{"title":"Senior Product Manager","jdText":"# About the role..."}'
  ```
</CodeGroup>

Returns `{ role }` with a `201` status.

## Import Roles from ATS

Bulk-create roles from open jobs in a connected ATS. One role is created per
selected job, with its title and job description filled in from the ATS. Jobs
already linked to a role (same provider and job id) are auto-skipped, so the
call is idempotent — re-running it never creates duplicate roles. Requires the
ATS integrations feature to be enabled for the org.

```
POST /api/roles/import-ats
```

<ParamField body="provider" type="string" required>
  The ATS provider to import from, e.g. `ashby` or `greenhouse`.
</ParamField>

<ParamField body="jobs" type="array" required>
  The jobs to import. Each item is `{ jobId, title? }`, where `jobId` is the
  ATS job's external id. `title` is optional and used only for skip/failure
  display.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://app.trygradient.ai/api/roles/import-ats" \
    -H "Authorization: Bearer gai_your_key" \
    -H "Content-Type: application/json" \
    -d '{"provider":"ashby","jobs":[{"jobId":"09d9fe97","title":"Forward Deployed PM"}]}'
  ```
</CodeGroup>

Returns `{ imported, skipped, failed }`, where `imported` is the list of created
roles (`{ jobId, roleId, title }`), `skipped` covers jobs already imported, and
`failed` covers jobs whose detail fetch or role creation errored.

## Get Role

```
GET /api/roles/:id
```

Returns a single role scoped to your organization. `404` if it does not exist or
has been deleted.

## Update Role

```
PATCH /api/roles/:id
```

<ParamField body="title" type="string">
  New role title. Cannot be set to empty.
</ParamField>

<ParamField body="jdText" type="string">
  Replace the job description body.
</ParamField>

<ParamField body="jdLink" type="string">
  Replace the job posting URL.
</ParamField>

<ParamField body="hiringManagerId" type="string">
  The workspace member who owns this role. Pass `null` to unassign.
</ParamField>

<ParamField body="recruiterId" type="string">
  The workspace member acting as recruiter for this role. Pass `null` to unassign. (Update-only — not accepted by Create Role.)
</ParamField>

## Extract Skills From The JD

```
POST /api/roles/:id/extract-skills
```

Returns the skills the role's job description calls for, drawn from your
organization's skill taxonomy. The JD and the taxonomy are both read
server-side from the role.

The result is cached on the role and reused while the job description and the
taxonomy are unchanged, so a repeat call returns immediately instead of waiting
on a model. Editing either one invalidates the cache and the next call
re-extracts. A `fallback` result is never cached.

<ParamField body="refresh" type="boolean" default="false">
  Ignore any cached result and extract again.
</ParamField>

Returns `{ skills, source, cached }`. Each skill is
`{ name, evidencePhrases, suggestedPriority }` — `name` is always an exact
taxonomy entry, `evidencePhrases` are verbatim JD substrings supporting it, and
`suggestedPriority` marks the two to four most role-defining skills. `source` is
`"ai"` for a model extraction or `"fallback"` for the deterministic keyword
matcher, which is used automatically whenever the model is unavailable, so this
endpoint does not fail on an extraction error.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://app.trygradient.ai/api/roles/role_123/extract-skills" \
    -H "Authorization: Bearer gai_your_key"
  ```
</CodeGroup>

## Delete Role

```
DELETE /api/roles/:id
```

Soft-deletes the role (sets `deleted_at`). Linked assessments are unaffected.
