Karat API documentation
The Karat API enables organizations to integrate Karat's interview services directly into their workflows and applicant tracking systems (ATS). With this API, companies can automate tasks such as:
- Creating and deleting users.
- Inviting candidates to interview with Karat.
- Scheduling interviews.
- Updating candidate details.
- Updating candidacies with eventual outcomes for each candidate.
Terminology
Before continuing with this guide, familiarize yourself with the following terms used throughout:
| Term | Description |
| Candidate | Individual being interviewed for a specific role. Typically filtered out from a group with different parameters attached (for example, Interview Results, Latest Activity, Current Status, and others), and can have additional information parsed such as the recruiter, name, e-mail, phone number, and other identifiable data. |
| Role | A role is the definition of a series of one or more assessments. It is generally recommended to have one role per open job opening/requisition. |
| Interview | An interview is a live virtual technical assessment conducted by a professional Interview Engineer from Karat's global network. These interviews are designed to evaluate a candidate's technical skills and abilities for a specific role and consist of code review, discussion, analysis questions, and coding exercises. |
| Quiz | A quiz, known also as "Karat Qualify", is a self-administered, 15 question multiple-choice assessment that tests an applicant's technical knowledge in a particular technology, language, or area of “real world” engineering. |
| Onsite interview | An onsite interview refers to interview conducted by members of a client's organization rather than by a Karat Interview Engineer. Despite their name, "onsite" interviews are conducted remotely facilitated by the Karat Platform. |
| Interview State |
Describes the current state of the interview process. The state can take one of the following values depending on where the candidate is in the process:
|
| Candidacy Status |
Current state of the candidacy. The state can take one of the following values:
|
| Integrity State |
The integrity state tracks the cheating detection status of an interview and has four possible values:
|
| Recruiter | User who invites or sends the invitation to candidate for taking the Karat interview. The recruiter must have a Karat account to perform activities in the Karat platform. |
| Recommendation |
Post-interview recommendations from Karat based on the candidate's performance. Possible outcomes include:
|
| Resume URL | URL link to the candidate's resume. |
| LinkedIn URL | URL link to the candidate's LinkedIn profile. |
| Group | A group in Karat is a sub-division within an organization on the Karat platform. Groups represent teams or business units under an organization. For example, within a company named "Piper," there could be groups like "Integrations" and "Product." They help organize interviews, roles, and other data within an organization. |
| Organization | An organization is the highest-level entity that typically represents a client company using the Karat platform. An organization contains groups, role archetypes, billing information, and other client-specific details. Organizations can have multiple groups under them, with each group often representing departments or business units within the company. The organization structure allows for centralized management, user administration, and control over settings like single sign-on (SSO), permissions, and access. |
Security model
To interact with Karat Platform API servers, you need the following to access and operate on Karat entities:
- Access Token: A valid access token is needed to authenticate and authorize API requests. The token must be included in each API call to verify the identity and permissions of the requester.
- Authorization: The API services will authenticate the token and ensure it has the necessary permissions to execute the requested operation.
- IP allowlist: If configured by your organization, the system checks the source IP before responding to the request.
Access token
The Karat API uses access tokens as a key component of its security model. These tokens are configurable to control varying access levels for specific entities, such as roles, interviews, and candidates.
Key Features:
- Tokens are managed by the API administrator, who can issue, revoke, or modify them.
- Each token is uniquely linked to a specific User ID.
- All in-transit data is encrypted via TLS 1.2, and data at rest is encrypted using AES-256.
Some queries or mutations may require additional permissions linked to your API key. If an "Access Denied" error occurs and you believe it to be a mistake, please contact support for assistance.
Authentication
As long as you provide a valid API key, you can write and execute queries and see the result immediately. Access tokens are attached to HTTP requests via the ’Authorization’ header, which the syntax is:
Authorization: Bearer <token>
Host: api.authorization-server.com
All authentication is handled via Bearer tokens, which are passed as headers of HTTPS requests to the GraphQL https://your-organization.karat.io/api/v1/graphql endpoint:
Setting an Authorization bearer token in Altair GraphQL
The GraphQL endpoint for the fictional Example Organization
API keys
Only organization admins and group admins can generate API keys on their profile pages in Karat's platform. Create your API key with the following steps:
- Sign in to the Karat platform.
- Go to your profile settings by clicking your profile name at the bottom of the left side menu.
- Go to Profile.
- Go to APIs.
- Click Create API key.
The API key listing for a given user
- Set a name and copy the Karat API key. API keys are only displayed once. Make sure to save it securely, as it can't be recovered after this stage.
- Optionally set an expiration lifetime (If your organization has an expiration policy, that will be the default).
A new API key has been created
- Click Save.
You can create a new key by clicking Create API key. You can give each key a meaningful name to help keep track of them, and disable or delete keys that are no longer in use. You can create multiple keys, so if a key is lost you can create a new one and (when ready) retire the old one.
Admin users are able to manage API keys for other users in their organization, by viewing the user's profile page via the Users index.
API Key Expiration Policy
Organization admins can enforce a maximum lifetime for all API keys in their organization.
- Go to the organization settings on the left side panel.
- Then click on the API tab.
- You can select a maximum lifetime for API keys or leave it with no expiration. The options are: No maximum (default), 30, 60, 90, or 366 days (1 year).
- Save settings.
Considerations:
- When creating/editing an API key, if the org policy is 60 days, users can only select 30 or 60 days.
- Shortest expiration wins (between user choice and org policy).
- Retroactive: Applies to all existing keys, not just new ones. ieg. If two keys were created, A. 50 days ago, and B. 60 days ago, and then the org sets a 60-day policy today, the first key A expires in 10 days, while the second one B will expire immediately.
API IP Allowlist
Organization admins can restrict API access to specific IP addresses or CIDR ranges.
How It Works.
- If the allowlist is empty → all IPs are allowed.
- If the allowlist is configured → only listed IPs/ranges are allowed.
- Error response: Returns 401 Unauthorized if IP is not in allowlist.
- Supports both IPv4 and IPv6
- Go to the organization settings on the left side panel.
- Then click on the API tab.
- Add one IP or CIDR range per line.
- Example:
- 180.52.96.43
- 10.0.0.0/8
- 2001:0db8::/32
- Example:
- Save settings.
Roles and permissions
Access to Karat entities is controlled by mapping a User ID (associated with the API bearer token) to specific roles, which in turn define the permissions that govern what actions a user can perform. These permissions determine the scope of what the user, via the access token, can view or modify.
In most cases, permissions fall into two categories:
- View: Grants access to read or retrieve data.
- Update: Allows creation, modification, or deletion of objects.
The API performs permission checks for the following entities:
- Viewing candidates
- Inviting candidates
- Scheduling interviews
- Updating candidate details
- Creating users
To access or modify any of these entities, the User ID linked to the token must have the appropriate permissions assigned.
Status Codes
The Karat API uses standard HTTP status codes to communicate the success or failure of requests:
- 401 Unauthorized: Authentication failed. The supplied token is missing or invalid.
- 403 Forbidden: Authorization failed. The token is valid, but the client lacks the necessary permissions to access the requested resource (for example, access restrictions or time constraints). If you receive a 403, it means authentication checks (401) have already been passed.
- 404 Not Found or 410 Gone: The resource either doesn’t exist or is no longer available. For security reasons, after authentication (401) and authorization (403) checks have been completed, the service may mask the requested resource by returning 404 (resource not found) or 410 (resource intentionally removed). A 410 status specifically indicates the resource has been permanently removed and should be purged.
- 429 Too Many Requests: The user has exceeded the allowed number of requests (more than 100 in a single day). Intended for use with rate-limiting schemes. This adds an upper limit to catch any runaway automation, but also allows for the busiest actual behavior (for example, a recruiter running a batch of invitations at once).
Postman
You can use Postman to test API requests. Find an available Postman collection in the api-docs repository, which can be imported and used to make API requests. You need to provide values for the following two variables:
- token: Your API key.
- subdomain: Your Karat subdomain, found in the leftmost part of the URL when logged into Karat (for example, https://YOUR-SUBDOMAIN.karat.io).
These can be found in the collection properties:
Find a sample Python SDK in the api-sdk repository. The example.py script provides examples of some API calls, and the client can be imported and used from the Python command line thus:
GraphQL queries
GraphQL queries allow you to retrieve specific data from the Karat platform. The process for creating a query depends on the environment you are working in. You can explore this list of GraphQL client libraries to find tools available in multiple languages, including C#/.NET, Go, Java, JavaScript, and Python. These libraries can simplify query creation and often include built-in features like caching.
However, since GraphQL is simply a query language served over HTTP, you can also make requests directly using standard HTTP libraries without relying on GraphQL-specific tools.
Keep in mind that per GraphQL best practices:
- Many fields are optional and will only appear in the response if both populated and explicitly requested.
- Efforts are made to maintain backward compatibility for documented fields (for example, avoiding renaming of existing fields), but new fields may be added at any time. Fields not documented may change without notice.
The following sections describe some examples of queries you can run to retrieve data from a company’s test session.
Pagination
The Karat API uses a cursor-based pagination method. This pagination method allows clients to retrieve large sets of data in smaller, manageable chunks. See the example below and consider the following fields:
- first parameter: This limits the number of records returned in a single query (up to 100). For instance, if you set first: 4(as the example below) the server will return the first 4 records of the result set. In this case, the maximum limit is 100 records.
- endCursor field: When a query is executed, the server responds with an endCursor in the pageInfo object. The cursor represents the position of the last item in the current batch of records. This allows you to know where to start the next query to retrieve additional records.
- after parameter: The endCursor value can be passed as the after argument in a subsequent query to load the next page of records. This ensures the client retrieves the next set of data seamlessly, picking up where the last query left off.
if candidacies(first: 4)
returns
{
data: {
candidacies: {
totalCount: 20,
pageInfo: {
endCursor: "MQ",
hasNextPage: true
},
#... first four candidacies are returned
}
}
...
a subsequent request with candidacies(first: 4, after: "MQ")
returns
{
data: {
candidacies: {
totalCount: 20,
pageInfo: {
endCursor: "nR",
hasNextPage: true
},
#... next four candidacies are returned
}
}
Reading Interview Information
Get the Interview details.
query ($interviewId: ID!){
interviewDetails(interviewId: $interviewId){
id
candidacyStatus
interviewState
integrityState
recommendation
competencies{
id
name
allocation
}
}
}
variables: {
"interviewId": "858952"
}
Reading Role Information
When inviting a Candidate, one of the required parameters is a roleId for the role the invitation is attached to. Note that roleId (and other ids) in the API are GraphQL "ID" types, so while they may appear numeric in some cases they should be treated as strings if persisted externally for future use (and they are not guaranteed to remain formatted as integers over time). These IDs can be retrieved with the following query:
query($filter: RoleFilter, $orderBy: RoleOrderBy, $search: String) {
roles(filter: $filter, orderBy: $orderBy, search: $search) {
totalCount
pageInfo {
endCursor
hasNextPage
}
nodes {
id
name
group {
id
name
}
}
}
}
Several optional variables are supported:
orderBy: ordering. One of the following field names with a suffix of either "_ASC" or "_DESC" (i.e. to sort by name, descending, you'd pass "orderBy": "NAME_DESC"):
"ID"
"NAME"
"GROUP_ID"
"SCHEDULED_INTERVIEW"
"SHIPPED_INTERVIEW"
"UPDATED_AT"
"LAST_UPDATED"
search: a case-insensitive string that the role name must contain.
filter: object, optional subfields as follows:
archived: Array of booleans, see note below.
demo: Array of booleans, see note below.
group: a list of numeric Group IDs, specifying which groups to include roles from
id: a list of numeric Role IDs, identifying a specific set of roles to include
For the demo and archived parameters, the following possible values apply:
[true]: only archived/demo roles.
[false]: only non-archived/demo roles.
[true, false]: both archived/demo and non-archived/demo roles.
filter omitted: For archived, archived and non-archived roles. For demo, respect the group level 'show demos' setting.
Example response for a query similar to above, with "search": "engineer 1":
{
"data": {
"roles": {
"totalCount": 2,
"pageInfo": {
"endCursor": "Mg",
"hasNextPage": false
},
"nodes": [
{
"id": "11",
"name": "Software Engineer 1",
"group": {
"id": "3",
"name": "Group 3"
}
},
{
"id": "16",
"name": "Software Engineer 1",
"group": {
"id": "4",
"name": "Group 4"
}
}
]
}
}
}
The following fields can be included in the object:
createdAt: The timestamps of when the role was created.
updatedAt: The timestamps of when the role was updated.
group: The organizational group the role belongs to.
supportLevel: The level of support associated with the role.
availabilityType: The type of availability required for the role.
description: The description of the role.
notes: Any additional, informal information regarding the role
name: the official name of the role.
customName: This field indicates whether the role has a custom name (i.e., different from the default name). It is a boolean field (true or false).
locations: The geographical locations associated with the role.
experienceLevels: The required experience levels for the role.
roleConfigurationMemberships: The membership of the role in specific configurations.
Reading Group Information
For filtering other queries, it may be useful to provide one or more Group IDs. Information on Groups can be retrieved using the following query:
query($filter: GroupFilter, $search: String) {
groups(filter: $filter, search: $search)
{
totalCount
pageInfo {
endCursor
hasNextPage
}
nodes {
id
name
}
}
}
Example response for a query similar to above, with "search": "up 1":
{
"data": {
"groups": {
"totalCount": 3,
"pageInfo": {
"endCursor": "jC",
"hasNextPage": false
},
"nodes": [
{
"id": "1",
"name": "Group 1"
},
{
"id": "11",
"name": "Group 11"
},
{
"id": "12",
"name": "Group 12"
}
]
}
}
}
The following optional fields can be included in the filter object:
search: a case-insensitive string that the group name must contain.
filter: object, optional subfields as follows:
id: a list of numeric Group IDs, identifying a specific set of groups to include
archived: includes archive groups (boolean).
Reading User Information
You may query the API for information on your organization's users.
query($filter: UserFilter, $orderBy: [UserOrderBy!], $search: String) {
users(filter: $filter, orderBy: $orderBy, search: $search) {
totalCount
pageInfo {
endCursor
hasNextPage
}
nodes {
id
name
phone
timeZone
clientUserType
lastSeen
groups {
nodes {
id
name
}
}
}
}
}
The following fields can be included in the filter object for users (not including candidates):
clientUserType: Array of any of "ADMIN", "TEAM_MEMBER", "LIMITED_TEAM_MEMBER", "EXTERNAL_MEMBER"
group: Array of IDs of the Groups that fetched Users should belong to
disabled: Boolean, signifying whether fetched Users should be disabled
mentor
The following fields can be included in the orderBy object (remember to suffix by "_ASC" or "_DESC"):
"ID"
"CREATED_AT"
"UPDATED_AT"
"LAST_SEEN"
"NAME"
"EMAIL"
"CLIENT_USER_TYPE"
"TYPE" is a field in the orderBy object which can be ignored.
search is an optional, case-insensitive string that the user name or email must contain.
Example response for a query to fetch all non-disabled admin Users in groups 1 or 11, ordered by creation date:
{
"data": {
"users": {
"totalCount": 3,
"pageInfo": {
"endCursor": "Mw",
"hasNextPage": false
},
"nodes": [
{
"id": "125",
"name": "Alice Longabaugh",
"email": "alice@example.com",
"phone": null,
"timeZone": null,
"clientUserType": "ADMIN",
"lastSeen": null,
"groups": {
"nodes": [
{
"id": "11",
"name": "Organization 1 - Demo"
}
]
}
},
{
"id": "74",
"name": "Billy Knapp",
"email": "billy@example.com",
"phone": null,
"timeZone": "America/Los_Angeles",
"clientUserType": "ADMIN",
"lastSeen": "2022-04-19T21:22:10Z",
"groups": {
"nodes": [
{
"id": "1",
"name": "Organization 1"
},
{
"id": "11",
"name": "Organization 1 - Demo"
}
]
}
},
{
"id": "46",
"name": "Buster Scruggs",
"email": "buster@example.com",
"phone": null,
"timeZone": "America/Los_Angeles",
"clientUserType": "ADMIN",
"lastSeen": "2022-04-20T00:05:42Z",
"groups": {
"nodes": [
{
"id": "1",
"name": "Organization 1"
},
{
"id": "11",
"name": "Organization 1 - Demo"
}
]
}
}
]
}
}
}
Variables used:
{"filter": {"clientUserType": "ADMIN", "disabled": false, "group": [1, 11]}, "orderBy": ["CREATED_AT_DESC"]}
Reading Archetype Information
An archetype can be thought of as a template for roles where all roles under the umbrella of an archetype will be required to have the same assessments. You can query archetypes to read the archetype metadata and related role information for your organization by proving the organization ID for your organization.
Sample query:
query Archetypes($orgId: ID!, $search: String) {
archetypes(filter: { archetypeOrganization: [$orgId] }, search: $search) {
nodes {
id
name
state
description
rolesCount
liveRolesCount
updatedAt
}
pageInfo { hasNextPage endCursor }
}
}
query ArchetypesWithMetadata(
$orgIds: [ID!]!,
$search: String,
$hideEmpty: Boolean,
$first: Int = 20,
$after: Int = 0
) {
archetypesWithMetadata(
filter: { archetypeOrganization: $orgIds }
search: $search
hideEmptyArchetypes: $hideEmpty
first: $first
after: $after
) {
archetypes { id name state description rolesCount liveRolesCount updatedAt }
totalArchetypesCount
nonEmptyArchetypesCount
archetypesRolesCount
archetypesCandidatesCount
paginationInfo { currentOffset pageSize nextAvailable hasMore }
}
}
Sample Inputs:
# Query Archetypes for organization with ID 1
{ "orgId": "1" }
# Query Archetypes with Metadata for organization with ID 1
{ "orgIds": "1" }
Reading Vendor Information
You can query the vendors and vendor access grants configured for your organization by providing the organization ID and querying the vendor fields on the organization. Vendor access grants specify permissions for vendors to access groups and roles in your organization.
Sample query:
query Vendors($orgId: ID!) {
organization(id: $orgId) {
vendors {
nodes {
id
name
active
partnerOrganization { id name } # Note: this is only used in rare use cases
}
}
vendorAccessGrants {
nodes {
id
scope {
type # type group or role
id # the role id or group id that the vendor can access
}
vendor { id name }
}
}
}
}
Sample Input:
{ "orgId": "1" }
Reading Job Families information
You may query the API for information on your job families:
query JobFamilies {
jobFamilies {
id
name
}
}
Example response
{
"data": {
"jobFamilies": [
{
"id": "1",
"name": "AI/ML"
},
{
"id": "2",
"name": "Backend"
},
{
"id": "3",
"name": "Data"
}
]
}
}
Reading Role Experience Levels information
You may query the API for information on your role experience levels:
query RoleExperienceLevels ($after: String, $before: String, $first: Int, $last: Int, $orderBy: [RoleExperienceLevelOrderBy!], $filter: RoleExperienceLevelFilter) {
roleExperienceLevels (after: $after, before: $before, first: $first, last: $last, orderBy: $orderBy, filter: $filter) {
pageInfo {
endCursor
startCursor
hasNextPage
hasPreviousPage
}
totalCount
nodes {
id
createdAt
updatedAt
group {
id
name
}
groupId
label
maxYearsExperience
minYearsExperience
resourcePath
roles {
nodes {
id
name
}
}
roleIds
}
}
}
Query inputs
The query takes the following pagination inputs:
$after: String
$before: String
$first: Int
$last: Int
See the Pagination section for more details.
In addition, you can control the sort order by passing the $orderBy input variable
$orderBy: [RoleExperienceLevelOrderBy!]
The RoleExperienceLevelOrderBy enum has the following values:
ID_ASC
ID_DESC
LABEL_ASC
LABEL_DESC
MIN_YEARS_EXPERIENCE_ASC
MIN_YEARS_EXPERIENCE_DESC
MAX_YEARS_EXPERIENCE_ASC
MAX_YEARS_EXPERIENCE_DESC
You can also filter records by passing the $filter input variable. It's a record with the following shape:
group: [ID!]
id: [ID!]
Example response
{
"data": {
"roleExperienceLevels": {
"pageInfo": {
"endCursor": "MQ",
"startCursor": "MQ",
"hasNextPage": true,
"hasPreviousPage": false
},
"totalCount": 12,
"nodes": [
{
"id": "1",
"createdAt": "2025-09-09T19:37:10Z",
"updatedAt": "2025-09-09T19:37:10Z",
"group": {
"id": "1",
"name": "Cirrus"
},
"groupId": 1,
"label": "L1",
"maxYearsExperience": 4,
"minYearsExperience": 0,
"resourcePath": "/admin/roles/experience_levels/1",
"roles": {
"nodes": [
{
"id": "6",
"name": "Software Engineer 6"
},
{
"id": "9",
"name": "Software Engineer System Design"
}
]
},
"roleIds": [
"6",
"9"
]
}
]
}
}
}
Reading Role Locations information
You may query the API for information on your role locations:
query RoleLocations($after: String, $before: String, $first: Int, $last: Int, $orderBy: [RoleLocationOrderBy!], $filter: RoleLocationFilter) {
roleLocations(after: $after, before: $before, first: $first, last: $last, orderBy: $orderBy, filter: $filter) {
nodes {
id
createdAt
updatedAt
address
city
country
countryCode
googlePlaceId
group {
id
name
}
groupId
label
latitude
longitude
postalCode
resourcePath
roles {
nodes {
id
name
}
}
roleIds
state
stateCode
}
}
}
Query inputs
The query takes the following pagination inputs:
$after: String
$before: String
$first: Int
$last: Int
See the Pagination section for more details.
In addition, you can control the sort order by passing the $orderBy input variable
$orderBy: [RoleLocationOrderBy!]
The RoleLocationOrderBy enum has the following values:
ID_ASC
ID_DESC
LABEL_ASC
LABEL_DESC
COUNTRY_ASC
COUNTRY_DESC
You can also filter records by passing the $filter input variable. It's a record with the following shape:
group: [ID!]
id: [ID!]
Example response
{
"data": {
"roleLocations": {
"nodes": [
{
"id": "1",
"createdAt": "2025-09-09T19:37:10Z",
"updatedAt": "2025-09-09T19:37:10Z",
"address": "123 Street St. W",
"city": "Seattle",
"country": "United States",
"countryCode": "USA",
"googlePlaceId": "_unused",
"group": {
"id": "1",
"name": "Cirrus"
},
"groupId": 1,
"label": "Main Office",
"latitude": 0,
"longitude": 0,
"postalCode": "98105",
"resourcePath": "/admin/roles/locations/1",
"roles": {
"nodes": [
{
"id": "2",
"name": "Software Engineer 2"
},
{
"id": "3",
"name": "Software Engineer 3"
}
]
},
"roleIds": [
"2",
"3"
],
"state": "Washington",
"stateCode": "WA"
}
]
}
}
}
Reading organization id
You can use the me query to get your organization id:
query me {
me {
organization {
id
}
}
}
This query returns the authenticated user's object, which contain a lot of information. Contact us for full reference.
Example response
{
"data": {
"me": {
"organization": {
"id": "1"
}
}
}
}
Candidacy Status and Results
You can fetch a list of candidates and their details using the GraphQL endpoint. This query can also includes interview result details via the interview.recommendation and interview.result fields. It retrieves a comprehensive list of candidates along with related information, such as the recruiter, role details, and specific candidate data like candidate ID, name, email, latest activity, group info, and more.
Sample query:
query($filter: CandidacyFilter, $after: String, $first: Int) {
candidacies(filter: $filter, after: $after, first: $first)
{
totalCount
pageInfo { # for pagination information
endCursor
hasNextPage
}
nodes { # The list of Candidacies
id
status
lastStatusUpdate
resultSharingLink
candidate {
name
}
recruiter {
id
name
}
followers {
id
name
}
role {
id
name
group {
id
name
}
}
codeChallenges {
nodes {
id
createdAt
state
endTime
recommendation
}
}
interviews {
nodes {
id
createdAt
state
startTime
endTime
recommendation
isRedo
shippedTime
resultSummary
result {
id
overallThoughts
overallFeedback
wentWellFeedback
workOnFeedback
updatedAt
createdAt
}
nextRedoInterview {
id
createdAt
state
endTime
recommendation
isRedo
}
link
}
}
}
}
}
The following fields can be included in the filter object:
id: Array of numeric Candidacy IDs.
group: Array of numeric Group IDs.
role: Array of numeric Role IDs.
recruiter: Array of numeric Recruiter IDs.
archived: Array of Boolean, see additional note below.
demo: Array of Boolean, see additional note below
codeChallengeState: An array containing any of 'INVITED', 'REGISTERED', 'IN_PROGRESS', 'SUBMITTED', 'SCORING', 'NEEDS_DECISION', 'TO_INVITE', 'INVITE_INTERVIEW', 'SEND_TO_CLIENT', 'DECLINED', 'HOLD', 'RESET'
interviewState: An array containing any of 'SCHEDULING', 'SCHEDULED', 'INTERVIEWING', 'PROCESSING', 'CREATING', 'AWAITING_QC', 'QC', 'HOLD', 'FINAL_CHECK', 'CONTENT_CHECK', 'READY_TO_SHIP', 'SHIPPING', 'SHIPPED', 'CANCELLED', 'RESCHEDULED'
platformAssessmenState: An array containing possible states: 'PLANNED','IN_PROGRESS','READY_TO_REVIEW', and'COMPLETED'.
quizState: An array containing the states related to any quizzes: 'INVITED', 'REGISTERED', 'STARTED', 'SHIPPED', and 'ABANDONED'.
redoneQuizzes: a boolean flag which will exclude redone quizzes when false.
status: One of 'CODE_CHALLENGE_PENDING', 'CODE_CHALLENGE_READY_TO_REVIEW', 'INTERVIEW_PENDING', 'INTERVIEW_READY_TO_REVIEW', 'INVITED_TO_PHONE_SCREEN', 'INVITED_TO_ONSITE_INTERVIEW', 'DECLINED_AT_KARAT_CODE_CHALLENGE', 'DECLINED_AT_KARAT_INTERVIEW','DECLINED_AT_PHONE_SCREEN', 'DECLINED_AT_ONSITE_INTERVIEW', 'OFFER_OUTSTANDING', 'OFFER_ACCEPTED', 'OFFER_DECLINED', 'INACTIVE'
atsUrl: String, the full ATS URL for the candidacy.
atsUrlSubstring: String, a partial ATS URL for the candidacy.
applicationId: String.
createdAfter: ISO8601 DateTime String.
createdBefore: ISO8601 DateTime String.
lastStatusUpdateAfter: ISO8601 DateTime String.
lastStatusUpdateBefore: ISO8601 DateTime String.
Sharing result link
The resultSharingLink allows you to grant specific team members access to view a candidate's interview results, even if they don't normally have access to that role or candidate.
Who can view the shared link?
- Members of the same group that owns the role
- Users must be logged in when they click the link
Once accessed, the team member will continue to have access to that candidacy going forward.
Incremental updates
A key feature to note is the ability to filter using the lastStatusUpdate field (via the lastStatusUpdateAfter and lastStatusUpdateBefore parameters). This field tracks significant changes to a candidacy (such as when an interview is shipped) and can be used to trigger updates to the local information you store for a candidacy after such changes occur.
In the following sample query, all information prior to 2022-03-20 has already been processed in a previous batch run, you can use this filter to retrieve all candidacies that have had a major update since that date.
query($filter: CandidacyFilter, $after: String, $first: Int) {
candidacies(filter: $filter, after: $after, first: $first) {
totalCount
pageInfo {
endCursor
hasNextPage
}
nodes {
lastStatusUpdate # The date and time of the last activity on this candidacy
candidate { … } # Candidate details such as name and email
recruiter { … } # Recruiter details such as name and email
followers { … } # Followers details such as name and email
role { … } # Role details such as name and group
interviews {
nodes {
# other interview fields such as recommendation, isRedo, shippedTime etc.
result {
id
overallThoughts # The Overall Thoughts for this interview
createdAt
updatedAt
}
}
}
}
}
}
variables: {
"filter": {
"lastStatusUpdateAfter": "2022-03-20T00:00:00Z"
}
}
Sample response:
{
"data": {
"candidacies": {
"totalCount": 3,
"pageInfo": {
"endCursor": "...",
"hasNextPage": false
},
"nodes": [
{
"id": "231",
"status": "INTERVIEW_READY_TO_REVIEW",
"lastStatusUpdate": "2022-03-23T12:22:49Z",
"candidate": {
"name": "Candidate Name",
"email": "candidate_email@example.org"
},
"recruiter": {
"name": "Recruiter Name",
"email": "recruiter_email@example.com"
},
"followers" [
{
"name": "Follower Name",
"email": "follower_email@example.com"
}
]
"role": {
"name": "Software Engineer 1",
"group": {
"name": "Group Name"
}
},
},
"interviews": {
"nodes": [
{
"id": "231",
"recommendation": "INVITE_TO_NEXT_ROUND",
"isRedo": false,
"shippedTime": "2022-03-23T12:22:49Z",
"result": {
"id": "3214",
"overallThoughts": "The candidate produced ...",
"createdAt": "2022-03-21T14:30:13Z",
"updatedAt": "2022-03-23T12:19:43Z"
}
}
]
}
},
{
"id": "232",
"status": "INTERVIEW_PENDING",
"lastStatusUpdate": "2022-03-25T16:50:23Z",
# ... other requested fields
"interviews": {
"nodes": [
{
"id": "232",
"recommendation": null,
"isRedo": false,
"shippedTime": null,
"result": null,
}
]
}
},
{
"id": "233",
"status": "INTERVIEW_READY_TO_REVIEW",
"lastStatusUpdate": "2022-03-26T09:32:15Z",
# ... other requested fields
"interviews": {
"nodes": [
{
"id": "233",
"recommendation": "DO_NOT_PURSUE",
"isRedo": false,
"shippedTime": "2022-03-26T09:32:15Z",
"result": {
"id": "3214",
"overallThoughts": "The candidate produced ...",
"createdAt": "2022-03-23T12:22:49Z",
"updatedAt": "2022-03-25T09:15:29Z"
}
}
]
}
}
]
}
}
}
If requested, the response can include the following fields for overallThoughts :
overallThoughts: String with general information about the performace of the candidate interview.
# The following fields apply only for candidates who receive written feedback from IVEs
overallFeedback: String, null: true
wentWellFeedback: String, null: true
workOnFeedback: String, null: true
Expected values for recommendation are as follows:
DO_NOT_PURSUE
REQUIRES_FURTHER_REVIEW
INVITE_TO_NEXT_ROUND
FAST_TRACK
Result Summary
For retrieving interview's results, you can use:
- resultSummary field to obtain a text summary of the interview results and the URL of the results.
Sample query:
query ($id: ID!) {
interview(id: $id) {
uuid
state
recommendation
resultSummary
}
Sample response:
{
"data": {
"interview": {
"uuid": "fc8f388d-ccbd-49a1-b6d6-3d33b8009272",
"state": "SHIPPED",
"recommendation": "INVITE_TO_NEXT_ROUND",
"resultSummary": "Interview: http://example.karat.io/interviews/10009\n\nRecommendation: **** Invite to next round\n\nTop N URLs: Excellent\nQuestion 1 - Single Top URL\n- Completeness: Fully working\n- Optimality: Optimal\n- Handholding: None\n- Debugging: Minimal\n- Time & Space Complexity: Was not asked to identify\nQuestion 2 - Top N URLs\n- Completeness: Fully working\n- Optimality: Optimal\n- Handholding: None\n- Debugging: None\n- Time & Space Complexity: Was not asked to identify\nQuestion 3 - Top N URLs Command Line\n- Completeness: Fully working\n- Optimality: Optimal\n- Handholding: None\n- Debugging: Moderate\n- Time & Space Complexity: Was not asked to identify\n\nCoding Style: Fair\n\nCommunication: Excellent\n- Quality of Explanation: Clear explanations\n- Interaction: Notable interpersonal skills"
}
}
}
With line breaks, the result summery looks like this:
Interview: https://example.karat.io/interviews/10009
Recommendation: **** Fast track to next round
Top N URLs: Excellent
Question 1 - Single Top URL
- Completeness: Fully working
- Optimality: Optimal
- Handholding: None
- Debugging: Minimal
- Time & Space Complexity: Was not asked to identify
Question 2 - Top N URLs
- Completeness: Fully working
- Optimality: Optimal
- Handholding: None
- Debugging: None
- Time & Space Complexity: Was not asked to identify
Question 3 - Top N URLs Command Line
- Completeness: Fully working
- Optimality: Optimal
- Handholding: None
- Debugging: Moderate
- Time & Space Complexity: Was not asked to identify
Coding Style: Fair
Communication: Excellent
- Quality of Explanation: Clear explanations
- Interaction: Notable interpersonal skills
AI Result Summary
You can query an AI-generated result summary from an interview through the InterviewSummary type.
Sample query:
query($id: ID!) {
interview(id: $id) {
id
state
interviewSummary {
id
flags
summary
createdAt
updatedAt
}
}
}
{
"data": {
"interview": {
"id": "2",
"state": "CANCELLED",
"interviewSummary": {
"id": "2",
"flags": "foo",
"summary": "the summary here",
"createdAt": "2024-04-09T22:03:53Z",
"updatedAt": "2024-04-09T22:03:53Z"
}
}
}
}
Sample response:
{
"data": {
"interview": {
"id": "2",
"state": "CANCELLED",
"interviewSummary": {
"id": "2",
"flags": "foo",
"summary": "the summary here",
"createdAt": "2024-04-09T22:03:53Z",
"updatedAt": "2024-04-09T22:03:53Z"
}
}
}
}
Potential plagiarism observations and integrity review
If potential plagiarism is detected, the interview will be flagged with CANDIDATE_CHEATING, and a list of cheating observation types will be populated within the interview data.
Sample query:
query Interview($id: ID!) {
interview(id: $id) {
id
tags
cheatingObservations
}
}
Sample response:
{
"data": {
"interview": {
"id": "143",
"tags": [
"CANDIDATE_CHEATING"
],
"cheatingObservations": [
"pastedSolution",
"looksDown"
]
}
}
}
You may obtain the following potential plagiarism observations:
| Enumeration | Description |
| someoneElseHeard | Someone else can be heard giving the answers or talking to the candidate. |
| pastedSolution | The candidate pastes in a solution. |
| perfectSolutionBadExplanation | The candidate quickly writes a perfect solution line-by-line but provides a glaringly incorrect explanation of the approach. |
| matchesOnlineSolution | Candidate's code matches an online solution or another solution exactly. |
| approachDoesntMatchSolution | Candidate doesn't explain their approach and instead jumps immediately into a completely new and optimal solution. |
| topDownCode | Candidate writes code top-down without jumping around to add if-cases later or move something into a function. |
| looksDown | Candidate appears to look down at other papers or a laptop or switches windows when explaining the approach or optimality, or when writing code. |
| noEdgeCases | Candidate says the code works and cannot come up with any edge cases for a perfectly optimal solution |
GraphQL mutations
In the context of GraphQL, a mutation is a type of operation used to modify data on the server. While queries are used to fetch data, mutations are specifically designed to create, update, or delete data.
All GraphQL fields are accessible based on your user account's permissions. Each mutation includes an errors field that reports any issues encountered during processing. The message subfield provides detailed information about the error.
Invitation
The createInvitation mutation is used to invite a candidate to an interview. The following fields are mandatory:
- name: Candidate's full name.
- email: Email address of the candidate, used to invite.
- roleId: numeric ID of the role being invited for (this also binds the candidacy to the role's Group).
The Assessment type for which the candidate is invited is determined by the specified role. This can include options such as "Interview," "Qualify," or others. By default, the invitation will be sent for the assessment at the initial role stage, unless the optional parameter roleStageId is specified.
Note: The roleId field can be retrieved using the Role query outlined above. Use a valid email addresses when testing the invitation functionality.
Additionally, there are several optional fields you can provide (note that the schedulingType field should not be used):
roleStageId: The specific role stage assessment to invite the candidate to. This specification should be used if trying to skip any initial stages.
recruiterId: The numeric Karat ID of the recruiter.
recruiterEmail: The email of the recruiter.
phone: The candidate's phone number
source: A nullable field, which has the value 'internal' or null. Denotes whether or not a candidate is an internal employee
employee_id: A string denoting the employee_id of an internal candidate, if specified without source, source is automatically set to internal
atsUrl: The URL of the candidate's profile in your Applicant Tracking System
resume: The URL of the candidate's resume
githubUrl: The URL of the candidate's GitHub profile
linkedinUrl: The URL of the candidate's LinkedIn profile
stackoverflowUrl: The URL of the candidate's StackOverflow profile
blogUrl: The URL of the candidate's blog
followerEmails: The email addresses of individuals who are notified or "follow" the candidate's progress.
Note: While the recruiterId field is not mandatory, it's strongly suggested to include it in candidate invitations. Any individual user can hit the API and query the me field to get their own details - this will be the user who owns the API key being used to auth the request.
Basic sample query:
query{
me {
id
name
}
}
Basic sample response:
{
"data": {
"me": {
"id": "196774",
"name": "Recruiter Name"
}
}
}
Sample invitation query with more fields:
mutation CreateInvitation(
$name: String!
$email: String!
$roleId: ID!
$recruiterId: ID
$phone: String
$atsUrl: String
$resume: String
$githubUrl: String
$linkedinUrl: String
) {
createInvitation (
input: {
name: $name
email: $email
roleId: $roleId
recruiterId: $recruiterId
phone: $phone
atsUrl: $atsUrl
resume: $resume
githubUrl: $githubUrl
linkedinUrl: $linkedinUrl
}
)
{
assessment {
id
candidacy {
id
}
link
}
errors {
message
}
}
}
variables: {
"name": "Example Candidate",
"email": "candidate@example.com",
"roleId": "your_numeric_role_id",
}
Sample invitation response:
{
"data": {
"createInvitation": {
"assessment": {
"id": "1334",
"candidacy": {
"id": "1358"
},
"link": "{subdomain}.karat.io/interviews/1334"
},
"errors": []
}
}
}
With the previous sample invitation query, the following candidate is created and invited in the Karat platform. You will see something like this:
Recent Interview Warning
If your organization has the Recent Interview Warning setting enabled, submitting a createInvitation mutation for a candidate who has completed an identical interview within the last 6 months will return an error instead of creating the invite:
"This candidate completed an identical interview within the last 6 months ([date], ID: [interview_id]). Please proceed with this invitation in your Karat Portal."
The invite is not created. To proceed, the recruiter should log into the Karat Portal and re-submit the invite, where they can review the warning and confirm.
To bypass the warning programmatically, pass the following field in your mutation:
confirmRecentInterviewWarning: true
When this field is set to true, the warning is acknowledged and the invite is created without requiring Portal confirmation.
Invitation With Vendor
Sample invitation query with vendor:
mutation CreateInvitation(
$name: String!
$email: String!
$roleId: ID!
$vendorId: ID
) {
createInvitation (
input: {
name: $name
email: $email
roleId: $roleId
vendorId: $vendorId
}
)
{
assessment {
id
candidacy {
id
vendor {
id
name
}
}
link
}
errors {
message
}
}
}
variables: {
"name": "Example Candidate",
"email": "candidate@example.com",
"roleId": "your_numeric_role_id",
"vendorId": "your_uuid_vendor_id",
}
Sample invitation with Vendor response:
{
"data": {
"createInvitation": {
"assessment": {
"id": "1334",
"candidacy": {
"id": "1358"
"vendor": {
"id", "4b0d9f7f-fa20-468b-9e72-1162ee1667cb",
"name", "Averion Demo Vendor"
}
},
"link": "{subdomain}.karat.io/interviews/1334"
},
"errors": []
}
}
}
Cancel Interview
The cancelInterview mutation is used to cancel an interview. The following input variables are mandatory:
- ID: The interview ID
- reason: The reason the interview is being cancelled
Below is a list of cancellation reasons:
- CANDIDATE_NO_SHOW
- CANDIDATE_REQUESTED_CANCELLATION
- CANDIDATE_NOT_ELIGIBLE_FOR_AN_AUTOMATIC_RESCHEDULE_DUE_TO_ROLE_SETTINGS
- RECRUITER_REQUESTED_CANCELLATION
- CANDIDATE_TOOK_OTHER_JOB_OFFER
- CANDIDATE_NO_LONGER_INTERESTED
- CANDIDATE_BROUGHT_ONSITE
- INTERVIEW_ENGINEER_NO_SHOW
- UNABLE_TO_FIND_REPLACEMENT_INTERVIEW_ENGINEER
- EXPIRED_INVITATION
- PREFER_TO_INTERVIEW_DIRECTLY_WITH_HIRING_COMPANY
- REDO_INTERVIEW_NO_LONGER_NEEDED
- CANDIDATE_ACCELERATED_TO_ONSITE
- CANDIDATE_DOES_NOT_BELIEVE_THEY_ARE_READY
- CANDIDATE_HAS_AN_INTERFERING_PRIORITY
- OTHER
- CANDIDATE_SKIPPED_TO_NEXT_CAMPAIGN
Example mutation:
mutation cancelInterview($interviewId: ID!, $reason: CancellationReason!) {
cancelInterview(input:{
id: $interviewId,
reason: $reason
}) {
interview {
id
state
cancellationReason
}
errors {
path
message
}
}
}
Example response:
{
"data": {
"cancelInterview": {
"interview": {
"id": "109",
"state": "CANCELLED",
"cancellationReason": "10"
},
"errors": null
}
}
}
Example error response:
{
"data": {
"cancelInterview": {
"interview": null,
"errors": [
{
"path": [
"input"
],
"message": "Interview is already cancelled."
}
]
}
}
}
Create Client Users
Overview
The createClientUser mutation is used to create a new client user within an organization. The following fields are mandatory:
- name: Client user's full name.
- email: Email address of the client user, used for authentication and notifications.
- clientUserType: The type of client user being created, which determines their permissions and access level.
Client User Types
The client user type determines the level of access and permissions the user will have within the organization. Available types include:
- ORGANIZATION_ADMIN: Organization Admin - Full access to the entire organization
- ADMIN: Client Admin - Access to specific groups they're assigned to
- TEAM_MEMBER: Team Member - Standard access to assigned groups and roles
- LIMITED_TEAM_MEMBER: Limited Team Member - Restricted access to assigned groups and roles
- EXTERNAL_MEMBER: External Member - Limited access for external users
- INTERVIEWER_MEMBER: Interviewer Member - Access specifically for conducting interviews
Optional Fields
Additionally, there are several optional fields you can provide:
- phone: Phone number of the client user (non-numeric characters will be automatically removed)
- timeZone: Time zone for the user (e.g., "America/New_York", "Europe/London")
-
clientTitle: Job title/role within the organization:
- Default: Default title
- Recruiter: Recruiter
- Engineer: Engineer
- ExternalRecruiter: External Recruiter
- Other: Other
- disabled: Boolean flag to disable the user account (defaults to false)
- groupIds: Array of group IDs the user should be assigned to
- accessibleRoleIds: Array of role IDs the user should have access to (roles must belong to the groups provided on the previous field)
Permissions
The ability to create client users is restricted based on the current user's role:
- Staff members can create any type of client user
- Organization admins can create users within their own organization
- Client admins can create users but cannot create organization admins and can only assign groups they have access to
Note: Use valid email addresses when creating client users.
Examples
Basic Sample Query
mutation CreateClientUser($input: CreateClientUserInput!) {
createClientUser(input: $input) {
user {
id
name
phone
timeZone
clientUserType
disabled
organization {
id
}
groups {
nodes {
id
}
}
}
errors {
path
message
}
}
}
Basic Sample Response
{
"data": {
"createClientUser": {
"user": {
"id": "123",
"name": "John Doe",
"email": "john@example.com",
"phone": null,
"timeZone": null,
"clientUserType": "TEAM_MEMBER",
"disabled": false,
"organization": {
"id": "456"
},
"groups": {
"nodes": [
{
"id": "789"
}
]
}
},
"errors": []
}
}
}
Sample Query with More Fields
mutation CreateClientUser($input: CreateClientUserInput!) {
createClientUser(input: $input) {
user {
id
name
phone
timeZone
clientUserType
clientTitle
disabled
organization {
id
}
groups {
nodes {
id
}
}
}
errors {
path
message
}
}
}
Sample Query Variables
{
"input": {
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "1234567890",
"timeZone": "America/New_York",
"clientTitle": "Recruiter",
"clientUserType": "ADMIN",
"disabled": false,
"groupIds": ["789", "101"],
"accessibleRoleIds": ["111", "222"]
}
}
Sample Response with More Fields
{
"data": {
"createClientUser": {
"user": {
"id": "124",
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "1234567890",
"timeZone": "America/New_York",
"clientUserType": "ADMIN",
"clientTitle": "Recruiter",
"disabled": false,
"organization": {
"id": "456"
},
"groups": {
"nodes": [
{
"id": "789"
},
{
"id": "101"
}
]
}
},
"errors": []
}
}
}
Behavior Notes
With the previous sample query, the following client user is created in the Karat platform:
- The user will be automatically confirmed and will receive a welcome email notification
- If role IDs are provided, the user will be granted access to those specific roles
- The user's password is automatically generated and they will receive an email to set their password
- Group memberships are established based on the provided groupIds
- Role access memberships are created based on the provided accessibleRoleIds
Error Handling
The mutation returns validation errors in the errors field if:
- Required fields are missing or invalid
- Email format is invalid
- User type is not valid
- Group IDs don't exist or user doesn't have permission to assign them
- Role IDs don't exist or user doesn't have permission to assign them
Update Client User
The updateClientUser mutation is used to update a client user within an organization:
mutation UpdateClientUser($input: UpdateClientUserInput!) {
updateClientUser(input: $input) {
errors {
message
path
}
user {
id
name
phone
timeZone
clientUserType
disabled
groups {
nodes {
id
}
}
}
}
}
The $input variable has the following shape:
userId: ID!
name: String
phone: String
timeZone: String
clientTitle: ClientTitle
clientUserType: ClientUserType
disabled: Boolean
groupIds: [ID!]
accessibleRoleIds: [ID!]
The clientTitle variable has the following possible values:
Default
Recruiter
Engineer
ExternalRecruiter
Other
The clientUserType variable has the following possible values:
ORGANIZATION_ADMIN
ADMIN
TEAM_MEMBER
LIMITED_TEAM_MEMBER
EXTERNAL_MEMBER
INTERVIEWER_MEMBER
Example successful response
{
"data": {
"updateClientUser": {
"errors": [],
"user": {
"id": "61",
"name": "Peter Gilligan",
"phone": "087616063",
"timeZone": null,
"clientUserType": "LIMITED_TEAM_MEMBER",
"disabled": false,
"groups": {
"nodes": [
{
"id": "1"
}
]
}
}
}
}
}
Candidacy Follower and Recruiter Updates
You can update the followers and recruiter on a candidacy using the following mutations.
Sample Mutation:
mutation FollowCandidacy($candidacyId: ID!) {
followCandidacy(input: { candidacyId: $candidacyId, follow: true }) {
candidacy { id }
follower { id name email }
errors { message }
}
}
Sample Inputs:
# Add current user as follower
{ "candidacyId": "123" }
# Add specific user as follower
{ "candidacyId": "123", "followerId": "456", "follow": true }
# Remove specific user as follower
{ "candidacyId": "123", "followerId": "456", "follow": false }
Sample Mutation:
mutation UpdateRecruiter($candidacyId: ID!, $recruiterId: ID!) {
updateRecruiter(input: { candidacyId: $candidacyId, recruiterId: $recruiterId }) {
candidacy { id recruiter { id name email } }
errors { message }
}
}
Sample Input:
{ "candidacyId": "123", "recruiterId": "10" }
Create Role From Archetype
This mutation creates a new Role by cloning a template role from a given Archetype.
It links the new role to the specified group, experience level, job family, and location.
Optional hiring managers may also be assigned at creation time.
If one or more provided hiring manager IDs cannot be found, the mutation will return an error.
If no state is provided, the role defaults to the CREATING state.
Arguments
- archetype_id (ID, required) — The archetype to clone the template role from.
- group_id (ID, required) — The group that owns the new role.
- name (String, required) — The name of the new role.
- description (String, required) — The description of the new role.
- experience_level (ID, required) — The role’s experience level.
- job_family (ID, required) — The role’s job family.
- location (ID, required) — The role’s location.
- hiring_manager_ids ([ID], optional) — List of user IDs to assign as hiring managers.
- state (RoleStateType, optional) — The initial state of the role. Defaults to CREATING. Allowed values: CREATING , LIVE , ARCHIVED
- hiring_managers ([NewClientUserInput], optional) — List of hiring managers to create and assign to the role. Each entry has email (String, required) and name (String, optional). If a user with the given email already exists in the organization, they are assigned directly; otherwise, a new user is created. Can be used alongside hiring_manager_ids. Use this when the hiring managers may not already exist in the system.
- recruiters [NewClientUserInput], optional) — List of recruiters to create and assign to the role. Each entry has email (String, required) and name (String, optional). If a user with the given email already exists in the organization, they are assigned directly; otherwise, a new user is created.
- follower_emails ([String], optional) — List of email addresses for existing users in the group to add as followers on the role. All emails must belong to existing users in the group. Followers receive notifications about role activity.
- job_requisition_job_id String, optional) — The ATS Job ID / Requisition ID to associate with the role, used for client reporting to map roles to internal tracking systems. When an ATS integration triggers a candidate invite, Karat uses this value to automatically resolve the correct role. Direct API callers creating invitations via createInvitation still need to pass Karat's roleId directly. Format: letters, numbers, periods, and single dashes (max 32 characters). Leading/trailing dashes and consecutive dashes are not allowed. Examples: ABCDXX12345678, 01234567, R12345678, P01234567890.
Returns
- role (RoleType) — The newly created role if successful.
- errors ([ErrorType]) — Errors encountered during creation, or null on success.
Sample Mutation
mutation CreateRoleFromArchetype(
$archetypeId: ID!
$groupId: ID!
$name: String!
$description: String!
$experienceLevel: ID!
$jobFamily: ID!
$location: ID!
$hiringManagers: [NewClientUserInput!]
$recruiters: [NewClientUserInput!]
$followerEmails: [String!]
$jobRequisitionJobId: String
) {
createRoleFromArchetype(
input: {
archetypeId: $archetypeId
groupId: $groupId
name: $name
description: $description
experienceLevel: $experienceLevel
jobFamily: $jobFamily
location: $location
hiringManagers: $hiringManagers
recruiters: $recruiters
followerEmails: $followerEmails
jobRequisitionJobId: $jobRequisitionJobId
}
) {
role {
id
name
description
state
group {
id
name
}
jobFamily {
id
name
}
experienceLevels {
id
label
}
locations {
id
label
}
roleHiringManagerMemberships {
id
userId
}
}
errors {
path
message
}
}
}
Variables
{
"archetypeId": "110",
"groupId": "1387",
"name": "Demo: Backend Engineer",
"description": "Backend Engineer",
"experienceLevel": "3298",
"jobFamily": "2",
"location": "1929",
"hiringManagers": [
{ "email": "jane.doe@example.com", "name": "Jane Doe" }
],
"recruiters": [
{ "email": "john.smith@example.com", "name": "John Smith" }
],
"followerEmails": ["watcher@example.com"],
"jobRequisitionJobId": "R12345678"
}
Sample Response
{
"data": {
"createRoleFromArchetype": {
"role": {
"id": "22599",
"name": "Demo: Backend Engineer",
"description": "Backend Engineer",
"state": "creating",
"group": {
"id": "1387",
"name": "Engineering"
},
"jobFamily": {
"id": "2",
"name": "Backend"
},
"experienceLevels": [
{
"id": "3298",
"label": "Junior Developer"
}
],
"locations": [
{
"id": "1929",
"label": "Main Office"
}
],
"roleHiringManagerMemberships": [
{
"id": "456",
"userId": "789"
}
]
},
"errors": null
}
}
}
Sample Response (Error – Missing Hiring Manager)
{
"data": {
"createRoleFromArchetype": {
"role": null,
"errors": [
{
"path": ["createRoleFromArchetype"],
"message": "One or more hiring managers not found"
}
]
}
}
}
Update Role Info
This mutation updates the information for an existing Role.
It allows modifying the role’s basic details (name, description, and state), reassigning its experience level, job family, and location, and updating its list of hiring managers.
If one or more provided hiring manager IDs cannot be found, the mutation will return an error.
Arguments
- id (ID, required) — The ID of the role to update.
- state (RoleStateType, optional) — The new state of the role. Allowed values: CREATING, LIVE, ARCHIVED.
- name (String, optional) — The new name of the role.
- description (String, optional) — The new description of the role.
- hiring_manager_ids ([ID], optional) — List of user IDs to assign as hiring managers. All existing hiring managers will be replaced with the provided list.
- experience_level (ID, optional) — The role’s new experience level.
- job_family (ID, optional) — The role’s new job family.
- location (ID, optional) — The role’s new location.
Returns
- role (RoleType) — The updated role if the operation succeeds.
- errors ([ErrorType]) — Errors encountered during the update, or null on success.
Sample Mutation
mutation UpdateRoleInfo($input: UpdateRoleInfoInput!) {
updateRoleInfo(input: $input) {
role {
id
name
description
state
experienceLevels {
id
resourcePath
minYearsExperience
maxYearsExperience
label
}
jobFamily {
id
name
}
locations {
id
label
resourcePath
}
roleHiringManagerMemberships {
id
userId
}
}
errors {
path
message
}
}
}
Variables
id: "297852271",
name: "Updated Role Name",
description: "Updated description for this role",
state: "LIVE",
experienceLevel: "297852272",
jobFamily: "297852273",
location: "297852274",
hiringManagerIds: ["297852275", "297852276"]
Sample Response (Success)
{
"data": {
"updateRoleInfo": {
"role": {
"id": "297852271",
"name": "Updated Role Name",
"description": "Updated description for this role",
"state": "LIVE",
"experienceLevels": [
{
"id": "297852272",
"name": "Senior",
"resourcePath": "/admin/roles/experience_levels/297852272",
"minYearsExperience": 0,
"maxYearsExperience": 2,
"label": "Junior Developer"
}
],
"jobFamily": {
"id": "297852273",
"name": "Backend"
},
"locations": [{
"id": "297852274",
"label": "San Francisco"
}],
"roleHiringManagerMemberships": [
{
"id": "123",
"userId": "297852275"
},
{
"id": "124",
"userId": "297852276"
}
]
},
"errors": null
}
}
}
Sample Response (Error – Invalid Hiring Manager)
{
"data": {
"updateRoleInfo": {
"role": null,
"errors": [
{
"path": ["hiringManagerIds"],
"message": "Hiring manager(s) with ID(s) 297852275 not found"
}
]
}
}
}
Follow Role
The followRole mutation adds or removes a user as a follower on a specific role. Followers can receive email notifications about interview events for the role, including scheduling, rescheduling, cancellation, redo requests, and result publications. Which email notifications a follower receives depends on their notification preferences.
Mutation
mutation($roleId: ID!, $followerId: ID, $follow: Boolean) {
followRole(input: {
roleId: $roleId
followerId: $followerId
follow: $follow
}) {
role {
id
}
follower {
id
}
errors {
message
}
}
}
Input Fields
| Field | Type | Required | Description |
| roleId | ID | Yes | The unique identifier of the role to follow or unfollow |
| followerId | ID | No | The unique identifier of the user who will follow the role. If omitted, defaults to the authenticated user associated with the API token |
| follow | Boolean | No | Set to true to follow the role, or false to unfollow. Defaults to true |
Response Fields
| Field | Type | Description |
| role | Role | The role that was followed or unfollowed |
| follower | User | The user who is now following or has unfollowed the role |
| errors | [Error] | null on success. Authorization failures return a top-level GraphQL permission denied error. |
Example: Follow a Role
{
"roleId": "123",
"follow": true
}
```
**Response:**
```
{
"data": {
"followRole": {
"role": {
"id": "123"
},
"follower": {
"id": "456"
},
"errors": null
}
}
}
Example: Unfollow a Role
{
"roleId": "123",
"follow": false
}
```
Response:
{
"data": {
"followRole": {
"role": {
"id": "123"
},
"follower": {
"id": "456"
},
"errors": null
}
}
}
Authorization
The follower must have access to the specified role. When followerId is omitted (defaulting to the authenticated user), this means the API token user must have role access. When followerId is provided, the specified follower's access is checked, not the API token user's. Requests where the follower lacks role access return a permission denied error.
Create or Update Vendor
This mutation creates or updates a Vendor record for an organization.
If an id is provided, the existing vendor is updated. If no id is provided, a new vendor is created under the given organization.
Access grants (scopes) can optionally be replaced at the same time.
Authorization is required: the current user must be authorized to manage vendors for the organization.
Arguments
- id (ID, optional) — The vendor to update. If not provided, a new vendor will be created.
- organization_id (ID, optional) — The organization that owns the vendor. Required when creating a new vendor.
- name (String, optional) — The vendor’s name.
- active (Boolean, optional) — Whether the vendor is active.
- partner_organization_id (ID, optional) — The partner organization linked to this vendor.
- can_invite_candidates_through_ui (Boolean, optional) — Whether the vendor can invite candidates through the UI.
- show_redacted_results_page (Boolean, optional) — Whether the vendor should see the redacted results page.
- scopes ([OrgVendorAccessGrantScopeInput], optional) — List of scopes to assign. Replaces all existing scopes when provided.
Returns
- vendor (OrgVendorType) — The created or updated vendor if successful.
- errors ([String]) — Errors encountered during creation or update. Empty array on success.
Sample Mutation
mutation UpsertOrgVendor($input: UpsertOrgVendorInput!) {
upsertOrgVendor(input: $input) {
vendor {
id
name
active
canInviteCandidatesThroughUi
showRedactedResultsPage
partnerOrganizationId
}
errors
}
}
Variables (Create)
{
"input": {
"organizationId": "793",
"name": "Averion Demo Vendor",
"active": true,
"canInviteCandidatesThroughUi": true,
"showRedactedResultsPage": false,
"scopes": [
{ "type": "ROLE", "id": "1" },
{ "type": "GROUP", "id": "1255" }
]
}
}
Variables (Update)
{
"input": {
"id": "4b0d9f7f-fa20-468b-9e72-1162ee1667cb",
"active": false,
"showRedactedResultsPage": true
}
}
Sample Response (Success)
{
"data": {
"upsertOrgVendor": {
"vendor": {
"id": "4b0d9f7f-fa20-468b-9e72-1162ee1667cb",
"name": "Averion Demo Vendor",
"active": false,
"canInviteCandidatesThroughUi": true,
"showRedactedResultsPage": true,
"partnerOrganizationId": "793"
},
"errors": []
}
}
}
Sample Response (Error – Validation)
{
"data": {
"upsertOrgVendor": {
"vendor": null,
"errors": ["Name can't be blank"]
}
}
}
Update Candidacy Status
Allows updating the candidacy status with the post-Karat outcome. By providing the updated candidacy status each time the candidate advances in your hiring process, it will ensure candidate data is reflected in the Karat portal and in reporting data.
Arguments
- id (ID, required) — the candidacy ID
-
status (CandidacyStatus, required) — the current candidacy status. Must be one of the following values:
- DECLINED_AT_KARAT_INTERVIEW
- DECLINED_AT_ONSITE_INTERVIEW
- DECLINED_AT_PHONE_SCREEN
- INVITED_TO_ONSITE_INTERVIEW
- INVITED_TO_PHONE_SCREEN
- OFFER_ACCEPTED
- OFFER_DECLINED
- OFFER_OUTSTANDING
- INACTIVE
- rejectionReasons ([String], optional) — if the candidate was rejected, optionally provide a list of reasons why. If provided, Karat may use this field to improve alignment.
- interviewNotes (String, optional) — optional onsite or other post-Karat interview notes. If provided, Karat may use this field to improve alignment.
Returns
- candidacy (Candidacy) — the updated candidacy. Only present if the update was successful.
- errors ([Error]) — a list of errors. Only present if the update failed.
Sample mutation
mutation UpdateCandidacyStatus(
$candidacyId: ID!,
$status: CandidacyStatus!,
$rejectionReasons: [String!],
$interviewNotes: String,
) {
updateCandidacyStatus(input: {
id: $candidacyId,
status: $status,
rejectionReasons: $rejectionReasons,
interviewNotes: $interviewNotes,
}) {
candidacy {
id
status
}
errors {
path
message
}
}
}
Variables
{
candidacyId: "123",
status: "DECLINED_AT_ONSITE_INTERVIEW",
rejectionReasons: ["Preferred another candidate", "Skill/qualification mismatch"],
interviewNotes: "optional onsite notes"
}
Sample response
"data": {
"updateCandidacyStatus": {
"candidacy": {
"id": "123",
"status": "DECLINED_AT_ONSITE_INTERVIEW"
},
"errors": null
}
}
Bulk Update Candidacy Status
You may update multiple candidacies with the post-Karat status. This mutation does not accept rejectionReasons or interviewNotes. The other arguments are the same as accepted by updateCandidacyStatus.
Arguments
- updates ([{ ids: [ID], status: CandidacyStatus }], required) — list of objects, each element in the list contains a status and a list of candidacy IDs to update.
Returns
- successfulUpdates (Int) — the number of candidacies that were successfully updated.
- failedUpdates ([ID]) — list of candidacy IDs that were not updated due to errors, if any.
- errors ([Error]) — list of errors that occurred, if any.
Sample mutation
mutation($updates: [CandidacyStatusWithCandidacies!]!) {
bulkUpdateCandidacyStatus(input: { updates: $updates }) {
successfulUpdates
failedUpdates
errors {
message
}
}
}
Variables
{
"updates": [
{
"ids": [
1337,
1338
],
"status": "DECLINED_AT_ONSITE_INTERVIEW"
},
{
"ids": [
1335
],
"status": "DECLINED_AT_PHONE_SCREEN"
}
]
}
Sample response
{
"data": {
"bulkUpdateCandidacyStatus": {
"successfulUpdates": 3,
"failedUpdates": [],
"errors": []
}
}
}
Archive Role
This mutation provides a streamlined way to archive a Role. It accepts only the role ID, making it ideal for workflows where archiving is a discrete action. For updating multiple role attributes at once (including state), see updateRoleInfo.
Arguments
id (ID, required) — The ID of the role to archive.
Returns
Success (Boolean) — Whether the archive operation succeeded.
Role (RoleType) — The archived role if the operation succeeds.
Errors ([ErrorType]) — Errors encountered during the update, or `null` on success.
Sample Mutation
mutation ArchiveRole($id: ID!) {
archiveRole(input: { id: $id }) {
success
role {
id
name
state
}
errors {
path
message
}
}
}
Variables
{
"id": "22599"
}
Sample Response (Success)
{
"data": {
"archiveRole": {
"success": true,
"role": {
"id": "22599",
"name": "Backend Engineer",
"state": "ARCHIVED"
},
"errors": null
}
}
}