Skip to content

Latest commit

 

History

History
1338 lines (1083 loc) · 73.7 KB

File metadata and controls

1338 lines (1083 loc) · 73.7 KB

Karat Java Client

A reactive Java 21 client for the Karat GraphQL API. Built with Spring Boot 3 and Spring WebFlux.

Project Structure

Karat/
├── api/        # Library + runnable tool — reactive GraphQL client; produces plain jar and boot jar
├── server/     # Spring Boot WebFlux REST server with OpenAPI/Swagger UI
└── examples/   # Profile-activated demos of the api module (invitation, decisions, reference data)

Requirements

  • Java 21+
  • Gradle 9+
  • A Karat API token and subdomain

Configuration

Set two environment variables before running:

export KARAT_SUBDOMAIN=your-company   # e.g. acme → acme.karat.io
export KARAT_TOKEN=your-api-token

Or override in application.yml:

karat:
  subdomain: your-company         # used to compute https://your-company.karat.io/api/v1/graphql
  token: your-api-token
  page-size: 50                   # records per page request (default: 50)
  output-dir: ~/karat-exports     # where CSV files are written (export CLI only)
  organization: org_abc123        # default organisation (optional — see below)

Default Organisation

Set KARAT_ORGANIZATION to scope all requests to a single organisation:

export KARAT_ORGANIZATION=org_abc123

When configured:

  • All REST endpoints that accept organization or organizationId use the default automatically
  • Swagger UI hides the organisation parameter from the docs
  • CLI exports apply the default to all resource filters (roles, groups, users, job requisitions)
  • Callers can still override the default by passing the parameter explicitly

Corporate Proxy

If the Karat API must be accessed through a corporate HTTP proxy, set these environment variables:

export KARAT_PROXY_HOST=proxy.corp.com
export KARAT_PROXY_PORT=3128              # default: 8080

For authenticated proxies, add credentials:

export KARAT_PROXY_USERNAME=proxyuser
export KARAT_PROXY_PASSWORD=proxypass

Or configure in application.yml:

karat:
  proxy-host: proxy.corp.com
  proxy-port: 3128
  proxy-username: proxyuser
  proxy-password: proxypass

The proxy is automatically applied to all outbound Karat API requests when proxy-host is set.

REST Server

The server module exposes all Karat API endpoints as a RESTful HTTP API with full OpenAPI documentation.

Starting the server

Set credentials via environment variables (recommended):

export KARAT_SUBDOMAIN=your-company
export KARAT_TOKEN=your-api-token
./gradlew :server:bootRun

Or pass them as command-line arguments:

./gradlew :server:bootRun --args='--karat.subdomain=your-company --karat.token=your-api-token'

The server starts on port 8080 by default. To use a different port:

./gradlew :server:bootRun --args='--karat.subdomain=your-company --karat.token=your-api-token --server.port=9090'

The server shuts down gracefully on Ctrl+C — in-flight requests are given up to 30 seconds to complete before the process exits.

Swagger UI

Once running, open your browser to:

http://localhost:8080/swagger-ui.html

The Swagger UI documents every endpoint, all filter parameters, request/response bodies, and example payloads. The raw OpenAPI JSON is at http://localhost:8080/api-docs.

Versioning

All requests must include the version header:

Accept: application/vnd.karat.v1+json

Omitting it or using an incompatible Accept value returns 406 Not Acceptable.

CSV export

Every list endpoint also supports Accept: text/csv for bulk download. Use the same path and query parameters — just change the Accept header:

curl http://localhost:8080/candidacies?status=COMPLETED \
     -H 'Accept: text/csv' \
     -o candidacies.csv

Endpoints

Candidacies

Method Path Description
GET /candidacies List candidacies (paginated, full filters)
GET /candidacies/{id} Get a single candidacy
GET /candidacies/search Full-text search on candidacies
GET /candidacies/demos List demo candidacies
POST /candidacies/{id}/decision Record a hiring decision
PATCH /candidacies/{id}/status Update candidacy status
POST /candidacies/{id}/advance Advance candidacy to a role stage
POST /candidacies/{id}/reassign Reassign candidacy to a different role
PATCH /candidacies/{id}/job-requisition Update candidacy job requisition
PATCH /candidacies/{id}/vendor Update candidacy vendor
PATCH /candidacies/{id}/dispositions Update candidacy dispositions
POST /candidacies/{id}/follow Follow/unfollow a candidacy
PATCH /candidacies/{id}/recruiter Update candidacy recruiter
PATCH /candidacies/{id}/ats-url Update candidacy ATS URL

Interviews

Method Path Description
GET /interviews List interviews (paginated)
GET /interviews/{id} Get a single interview
GET /interviews/search Full-text search on interviews
GET /interviews/demos List demo interviews
POST /interviews/{id}/cancel Cancel an interview
POST /interviews/{id}/reschedule Reschedule an interview
POST /interviews/{id}/schedule Schedule an interview
PATCH /interviews/{id}/state Update interview state
PATCH /interviews/{id}/integrity-state Update integrity state
PATCH /interviews/{id}/cheat-detection Update cheat detection flag
GET /interviews/{interviewId}/moments List moments for an interview
POST /interviews/{interviewId}/moments Create an interview moment
PATCH /interviews/moments/{id} Update an interview moment
DELETE /interviews/moments/{id} Delete an interview moment

Roles

Method Path Description
GET /roles List roles (paginated, filter by org/state/archived)
GET /roles/{id} Get a single role
GET /roles/search Full-text search on roles
GET /roles/demos List demo roles
GET /roles/{id}/candidacies List candidacies for a role
POST /roles/{id}/follow Follow/unfollow a role
GET /roles-with-counts List roles with candidacy/interview counts
POST /roles Create a role
PATCH /roles/{id} Update a role
DELETE /roles/{id} Archive a role
POST /roles/copy Copy roles to a group
POST /roles/{id}/publish Publish a role

Users

Method Path Description
GET /users List users (paginated, full filters)
GET /users/{id} Get a single user
GET /users/search Full-text search on users
POST /users Create a new client user
PATCH /users/{id} Update a client user
GET /me Get the current authenticated user
GET /users/{userId}/notification-preferences Get user notification preferences
PATCH /users/{userId}/notification-preferences Update user notification preferences

Organizations

Method Path Description
GET /organizations List organizations (paginated)
GET /organizations/{id} Get a single organization
GET /organizations/search Full-text search on organizations
GET /organizations/{id}/groups List groups for an organization
GET /organizations/{id}/vendors List vendors for an organization
GET /organizations/{id}/roles List roles for an organization
GET /organizations/{id}/users List users for an organization
GET /organizations/{id}/job-requisitions List job requisitions for an organization

Groups

Method Path Description
GET /groups List groups (paginated)
GET /groups/{id} Get a single group
GET /groups/search Full-text search on groups
GET /groups/{id}/candidacies List candidacies for a group
GET /groups/{id}/roles List roles for a group
GET /groups/{id}/users List users scoped to a group

Job Requisitions

Method Path Description
GET /job-requisitions List job requisitions (paginated)
GET /job-requisitions/{id} Get a single job requisition
GET /job-requisitions/search Full-text search on job requisitions
GET /job-requisitions/{id}/candidacies List candidacies for a job requisition

Candidates & Assessments

Method Path Description
GET /candidates List candidates (paginated)
GET /candidates/{id} Get a single candidate
GET /candidates/search Full-text search on candidates
GET /code-challenges List code challenges (paginated)
GET /code-challenges/{id} Get a single code challenge
GET /code-challenges/search Full-text search on code challenges
PATCH /code-challenges/{id}/state Update code challenge state
GET /platform-assessments List platform assessments (paginated)
GET /platform-assessments/{id} Get a single platform assessment
GET /competencies List all competencies

Quality & Reviews

Method Path Description
GET /quality-controls List quality controls (paginated)
GET /quality-controls/{id} Get a single quality control
POST /quality-controls/{id}/approve Approve a quality control review
POST /quality-controls/assign-reviewer Assign a reviewer to an interview
PATCH /quality-controls/{id}/priority Update quality control priority
PATCH /quality-controls/{id}/review-type Update quality control review type
GET /result-reviews List result reviews (paginated)
GET /result-reviews/{id} Get a single result review
POST /result-reviews Create a result review
DELETE /result-reviews/{id} Delete a result review
PATCH /result-reviews/{id}/include Update result review include status

Invitations & Bulk Operations

Method Path Description
POST /invitations Send an interview invitation
POST /candidate-invites Batch-invite candidates
POST /accelerations Create an acceleration (redo) candidacy
POST /bulk-invites Upload CSV of candidates to invite
GET /bulk-invites/{id}/status Check bulk invite processing status
POST /bulk-invites/validate Validate a bulk invite CSV
POST /decisions Record disposition decisions (bulk)

Practical Demos

Method Path Description
GET /practical-demos/by-interview/{uuid} Get practical demo by interview UUID
GET /practical-demos/by-user/{userId} Get practical demo by user ID
POST /practical-demos Create a practical demo
DELETE /practical-demos/{id} Delete a practical demo
POST /practical-demos/{id}/start Start a practical demo session

Vendors

Method Path Description
GET /org-vendors/{id} Get a single org vendor
POST /vendors Create an organization vendor
PATCH /vendors/{id} Update an organization vendor
PUT /vendors Upsert an organization vendor

Interview Series Templates

Method Path Description
GET /interview-series-templates List interview series templates
GET /interview-series-templates/{id} Get a single interview series template
POST /interview-series-templates Create an interview series template
PATCH /interview-series-templates/{id} Update an interview series template

Quizzes & Questions

Method Path Description
GET /quizzes List quizzes (paginated)
GET /quizzes/{id} Get a single quiz
GET /quizzes/search Full-text search on quizzes
GET /quiz-topics List quiz topics (paginated)
GET /questions List questions (paginated)
GET /questions/{id} Get a single question
GET /questions/search Full-text search on questions
GET /question-pools List question pools (paginated)
GET /question-pools/{id} Get a single question pool
GET /question-pools/search Full-text search on question pools
GET /question-types List all question types

Campaigns & Archetypes

Method Path Description
GET /campaigns/{id} Get a single campaign
GET /campaign-memberships List campaign memberships (paginated)
GET /campaign-memberships/search Full-text search on campaign memberships
GET /archetypes List archetypes (paginated)
GET /archetypes/{id} Get a single archetype
GET /archetypes/search Full-text search on archetypes

Role Configuration

Method Path Description
GET /role-stages/{id} Get a single role stage
GET /role-experience-levels List role experience levels
GET /role-experience-levels/{id} Get a single role experience level
GET /role-locations List role locations
GET /role-locations/{id} Get a single role location
GET /rubrics/{id} Get a single rubric

ATS Integrations

Method Path Description
GET /ats-integrations List ATS integrations (paginated)
GET /ats-integrations/{id} Get a single ATS integration
POST /ats-integrations Create an ATS integration
PATCH /ats-integrations/{id} Update an ATS integration
DELETE /ats-integrations/{id} Delete an ATS integration
GET /ats-stage-mappings List ATS stage mappings
GET /ats-stage-mappings/{id} Get a single ATS stage mapping
POST /ats-stage-mappings Create an ATS stage mapping
PATCH /ats-stage-mappings/{id} Update an ATS stage mapping
DELETE /ats-stage-mappings/{id} Delete an ATS stage mapping
GET /ats-considerations List ATS considerations

Analytics

Method Path Description
GET /analytics/activity-metrics Aggregate interview activity counts
GET /analytics/funnel-stats Candidacy funnel statistics
GET /analytics/experience-stats Average candidate experience rating
GET /analytics/homepage-metrics Dashboard-level candidate metrics
GET /analytics/above-bar-contributors Above-bar contributor funnel stats
GET /analytics/vendor-performance Aggregated vendor performance data
GET /analytics/vendor-trends Vendor performance trends over time
GET /analytics/role-distribution Candidacy distribution across roles
GET /analytics/job-family-metrics Performance metrics by job family

Workspaces

Method Path Description
GET /workspaces List workspaces (paginated)
GET /workspaces/{id} Get a single workspace
GET /workspaces/search Full-text search on workspaces
POST /workspaces Create a workspace
DELETE /workspaces/{id} Delete a workspace
PATCH /workspaces/{id}/settings Update workspace settings

Billing

Method Path Description
GET /billables List billable items
GET /billables/{id} Get a single billable
GET /billing-entries List billing entries
GET /billing-entries/{id} Get a single billing entry
GET /billing/user-aggregate Get aggregated billing for a user

Content & History

Method Path Description
GET /history List history entries
GET /history/search Full-text search on history entries
GET /content-tags List content tags (paginated)
GET /contracts/{id} Get a single contract
GET /recordings/{id} Get a single recording
GET /performance-incidents List performance incidents
GET /performance-incidents/search Full-text search on performance incidents

Reference Data

Method Path Description
GET /scheduling-availabilities List scheduling availabilities
GET /languages List all supported languages
GET /job-families List all job families

Example requests

# List completed candidacies (first page) — response shows role > candidate hierarchy
curl http://localhost:8080/candidacies?status=COMPLETED \
     -H 'Accept: application/vnd.karat.v1+json'

# List roles — response is grouped: organization > group > roles
curl http://localhost:8080/roles \
     -H 'Accept: application/vnd.karat.v1+json'
# Example response:
# {
#   "totalCount": 1,
#   "next": null,
#   "organizations": [{
#     "id": "org_abc123",
#     "name": "Acme Corp",
#     "groups": [{
#       "id": "grp_xyz789",
#       "name": "Karat Partner Group",
#       "type": "CLIENT",
#       "active": true,
#       "archived": false,
#       "roles": [{
#         "id": "role_eng456",
#         "name": "Senior Software Engineer",
#         "state": "live",
#         "archived": false,
#         "type": "STANDARD",
#         "assessmentType": "TECHNICAL_PHONE_SCREEN",
#         "description": "Backend systems engineering role",
#         "candidatesCount": 12,
#         "createdAt": "2025-01-15T09:00:00Z",
#         "updatedAt": "2025-06-01T14:30:00Z"
#       }]
#     }]
#   }]
# }

# Fetch next page using pageToken from previous response
curl 'http://localhost:8080/candidacies?status=COMPLETED&pageToken=eyJpZCI6Ii4uLiJ9' \
     -H 'Accept: application/vnd.karat.v1+json'

# List all groups with their parent organisation
curl http://localhost:8080/groups \
     -H 'Accept: application/vnd.karat.v1+json'

# List roles for a group
curl http://localhost:8080/groups/grp_xyz789/roles \
     -H 'Accept: application/vnd.karat.v1+json'

# Send an invitation
curl -X POST http://localhost:8080/invitations \
     -H 'Accept: application/vnd.karat.v1+json' \
     -H 'Content-Type: application/vnd.karat.v1+json' \
     -d '{"name":"Alice Engineer","email":"alice@example.com","roleId":"role-eng456"}'

# Batch-invite candidates
curl -X POST http://localhost:8080/candidate-invites \
     -H 'Accept: application/vnd.karat.v1+json' \
     -H 'Content-Type: application/vnd.karat.v1+json' \
     -d '[{"name":"Alice","email":"alice@example.com","roleId":"role-eng456"},{"name":"Bob","email":"bob@example.com","roleId":"role-eng456"}]'

# Create an acceleration (redo) candidacy
curl -X POST http://localhost:8080/accelerations \
     -H 'Accept: application/vnd.karat.v1+json' \
     -H 'Content-Type: application/vnd.karat.v1+json' \
     -d '{"email":"alice@example.com","previousInterviewId":"interview-abc123"}'

# Send a bulk invite via CSV
curl -X POST http://localhost:8080/bulk-invites \
     -H 'Accept: application/vnd.karat.v1+json' \
     -H 'Content-Type: application/vnd.karat.v1+json' \
     -d '{"roleId":"role-eng456","csv":"name,email\nAlice,alice@example.com\nBob,bob@example.com"}'

# Record a hiring decision
curl -X POST http://localhost:8080/decisions \
     -H 'Accept: application/vnd.karat.v1+json' \
     -H 'Content-Type: application/vnd.karat.v1+json' \
     -d '[{"status":"OFFER_ACCEPTED","ids":["cand-abc123","cand-def456"]}]'

# Get current user
curl http://localhost:8080/me \
     -H 'Accept: application/vnd.karat.v1+json'

# Update candidacy status
curl -X PATCH http://localhost:8080/candidacies/cand-abc123/status \
     -H 'Accept: application/vnd.karat.v1+json' \
     -H 'Content-Type: application/vnd.karat.v1+json' \
     -d '{"status":"INTERVIEW_PENDING"}'

# Advance candidacy to a role stage
curl -X POST http://localhost:8080/candidacies/cand-abc123/advance \
     -H 'Accept: application/vnd.karat.v1+json' \
     -H 'Content-Type: application/vnd.karat.v1+json' \
     -d '{"roleStageId":"stage_002"}'

# Follow a candidacy
curl -X POST http://localhost:8080/candidacies/cand-abc123/follow \
     -H 'Accept: application/vnd.karat.v1+json'

# Follow a role
curl -X POST http://localhost:8080/roles/role-eng456/follow \
     -H 'Accept: application/vnd.karat.v1+json'

# Update a user
curl -X PATCH http://localhost:8080/users/usr_abc123 \
     -H 'Accept: application/vnd.karat.v1+json' \
     -H 'Content-Type: application/vnd.karat.v1+json' \
     -d '{"name":"Alice Updated","phone":"+15559876543"}'

# Check bulk invite status
curl http://localhost:8080/bulk-invites/bi-123/status \
     -H 'Accept: application/vnd.karat.v1+json'

# List candidates
curl http://localhost:8080/candidates \
     -H 'Accept: application/vnd.karat.v1+json'

# List competencies
curl http://localhost:8080/competencies \
     -H 'Accept: application/vnd.karat.v1+json'

Error responses

Errors are returned as RFC 9457 Problem Details:

Status Cause
400 Invalid request body or parameters
406 Accept header doesn't match application/vnd.karat.v1+json
502 Upstream Karat GraphQL API returned an error
500 Unexpected server error

Export CLI

The api module includes a production-ready export CLI activated by the export profile. Set credentials first (see Configuration):

export KARAT_SUBDOMAIN=your-company
export KARAT_TOKEN=your-api-token

# Export candidacies and interviews to CSV (default)
./gradlew :api:bootRun --args='--spring.profiles.active=export'

# Export all resources
./gradlew :api:bootRun --args='--spring.profiles.active=export --export=candidacies,interviews,users,organizations,groups,roles,job-requisitions'

# Export only completed candidacies from the last 3 months
./gradlew :api:bootRun --args='--spring.profiles.active=export --export=candidacies --candidacy.status=COMPLETED --candidacy.lastStatusUpdateAfter=2025-09-01'

# Export shipped interviews with integrity results
./gradlew :api:bootRun --args='--spring.profiles.active=export --export=interviews --interview.state=SHIPPED --interview.integrityState=PASS'

# Export only active interviewers
./gradlew :api:bootRun --args='--spring.profiles.active=export --export=users --user.clientUserType=INTERVIEWER_MEMBER --user.disabled=false'

# Export roles for a specific vendor
./gradlew :api:bootRun --args='--spring.profiles.active=export --export=roles --role.group=grp-abc --role.archived=false'

Running the Examples

The examples module contains ready-to-run demos. Each is activated by a Spring profile:

Profile What it does
reference-data Lists vendors, active roles, and interviewers (default)
invitation Sends single and bulk interview invitations
candidate-decision Records candidacy outcome decisions back to Karat
create-user Creates interviewer and admin users
batch-invitation Batch-invites candidates using createCandidateInvites
acceleration Creates an acceleration (redo) candidacy
bulk-invite Uploads a CSV of candidates to invite

Set credentials first (see Configuration):

export KARAT_SUBDOMAIN=your-company
export KARAT_TOKEN=your-api-token

# Default — list reference data (vendors, roles, interviewers)
./gradlew :examples:bootRun

# Send invitations
./gradlew :examples:bootRun --args='--spring.profiles.active=invitation'

# Record candidate decisions
./gradlew :examples:bootRun --args='--spring.profiles.active=candidate-decision'

# Create users
./gradlew :examples:bootRun --args='--spring.profiles.active=create-user'

Export CLI — All Options

--export=<comma-separated list> — which resources to export (default: candidacies,interviews). Valid values: candidacies, interviews, users, organizations, groups, roles, job-requisitions.

Candidacy filters:

Argument Description
--candidacy.id= Filter by candidacy ID
--candidacy.status= Filter by status (e.g. COMPLETED)
--candidacy.group= Filter by vendor/group ID
--candidacy.role= Filter by role ID
--candidacy.recruiter= Filter by recruiter ID
--candidacy.candidate= Filter by candidate ID
--candidacy.archived= true or false
--candidacy.demo= true or false
--candidacy.codeChallengeState= Filter by code challenge state
--candidacy.interviewState= Filter by interview state
--candidacy.integrityState= Filter by integrity check result (e.g. PASS)
--candidacy.platformAssessmentState= Filter by platform assessment state
--candidacy.quizState= Filter by quiz state
--candidacy.applicationId= Filter by ATS application ID
--candidacy.opportunityId= Filter by ATS opportunity ID
--candidacy.atsUrl= Filter by exact ATS URL
--candidacy.atsUrlSubstring= Filter by ATS URL substring
--candidacy.jobRequisition= Filter by job requisition ID
--candidacy.search= Free-text search
--candidacy.createdAfter= ISO date (e.g. 2025-01-01)
--candidacy.createdBefore= ISO date
--candidacy.lastStatusUpdateAfter= ISO date
--candidacy.lastStatusUpdateBefore= ISO date

User filters:

Argument Description
--user.id= Filter by user ID
--user.clientUserType= e.g. INTERVIEWER_MEMBER, ADMIN
--user.type= Filter by user type (e.g. INTERNAL)
--user.group= Filter by vendor/group ID
--user.organization= Filter by organization ID
--user.teams= Filter by team (e.g. ENGINEERING)
--user.disabled= true or false
--user.search= Free-text search

Role filters:

Argument Description
--role.id= Filter by role ID
--role.group= Filter by vendor/group ID
--role.organization= Filter by organization ID
--role.states= Filter by role state (e.g. ACTIVE)
--role.archived= true or false
--role.demo= true or false
--role.search= Free-text search

Organization filters:

Argument Description
--organization.id= Filter by organization ID
--organization.search= Free-text search

Group filters:

Argument Description
--group.id= Filter by group ID
--group.type= Filter by group type (e.g. CLIENT)
--group.organizationId= Filter by organization ID
--group.search= Free-text search

Interview filters:

Argument Description
--interview.id= Filter by interview ID
--interview.group= Filter by vendor/group ID
--interview.role= Filter by role ID
--interview.recruiter= Filter by recruiter ID
--interview.recommendation= Filter by recommendation (e.g. YES, NO)
--interview.state= Filter by interview state (e.g. SHIPPED)
--interview.integrityState= Filter by integrity state (e.g. PASS)
--interview.cheatingDetected= true or false
--interview.candidacyStatus= Filter by parent candidacy status
--interview.archived= true or false
--interview.demo= true or false
--interview.startTimeAfter= ISO date (e.g. 2025-01-01)
--interview.startTimeBefore= ISO date
--interview.originalInterviewsOnly= true to exclude redo interviews
--interview.redoInterviewsOnly= true to show only redo interviews
--interview.search= Free-text search

Job requisition filters:

Argument Description
--jobRequisition.organizationId= Filter by organization ID
--jobRequisition.search= Free-text search

Edit the example source files under examples/src/main/java/ to adjust filters, IDs, and other parameters before running.

Using the API Module as a Library

Add the api module as a dependency and inject the services you need:

// Paginated query services
@Autowired CandidacyService               candidacyService;
@Autowired InterviewService               interviewService;
@Autowired UserService                    userService;
@Autowired RoleService                    roleService;
@Autowired GroupService                   groupService;
@Autowired OrganizationService           organizationService;
@Autowired JobRequisitionService          jobRequisitionService;
@Autowired CandidateService               candidateService;
@Autowired CodeChallengeService           codeChallengeService;
@Autowired PlatformAssessmentService      platformAssessmentService;
@Autowired QualityControlService          qualityControlService;
@Autowired ResultReviewService            resultReviewService;
@Autowired InterviewSeriesTemplateService interviewSeriesTemplateService;

// Standalone mutation services
@Autowired InvitationService              invitationService;
@Autowired CandidateInvitesService        candidateInvitesService;
@Autowired CandidateDecisionService       candidateDecisionService;
@Autowired AccelerationCandidacyService   accelerationCandidacyService;
@Autowired BulkInviteService              bulkInviteService;
@Autowired PracticalDemoService           practicalDemoService;

// Singleton/list clients (no pagination)
@Autowired MeClient                       meClient;
@Autowired OrgVendorClient                orgVendorClient;
@Autowired CompetencyClient               competencyClient;
@Autowired SchedulingAvailabilityClient   schedulingAvailabilityClient;
@Autowired RoleWithCountsClient           roleWithCountsClient;

// CSV export (file writing for CLI, byte arrays for REST)
@Autowired CsvExportService               csvExportService;
@Autowired CsvBytesService                csvBytesService;

Exporting to CSV

CsvExportService writes timestamped CSV files to karat.output-dir (default: ~/karat-exports).

@Autowired CsvExportService csvExportService;

// Candidacies and interviews
var candidacies = candidacyService.listAll(CandidacyFilter.newBuilder()
        .status("COMPLETED")
        .lastStatusUpdateAfter("2025-09-01")
        .build());

Mono.zip(
        csvExportService.export(candidacies),
        csvExportService.exportInterviews(candidacies)
).block();

// Users, organizations, groups, roles, job requisitions
csvExportService.exportUsers(userService.listAll(UserFilter.newBuilder().build())).block();
csvExportService.exportOrganizations(organizationService.listAll(OrganizationFilter.newBuilder().build())).block();
csvExportService.exportGroups(groupService.listAll(GroupFilter.newBuilder().build())).block();
csvExportService.exportRoles(roleService.listAll(RoleFilter.newBuilder().build())).block();
csvExportService.exportJobRequisitions(jobRequisitionService.listAll(JobRequisitionFilter.newBuilder().build())).block();

Querying Reference Data

// List all groups (useful for discovering group names)
groupService.listAll(GroupFilter.newBuilder().build())
        .doOnNext(g -> System.out.println(g.getId() + " " + g.getName()))
        .blockLast();

// List all active roles (useful for finding roleId to pass to CreateInvitationInput)
roleService.listAll(RoleFilter.newBuilder().archived(List.of(false)).build())
        .doOnNext(r -> System.out.println(r.getId() + " " + r.getName()))
        .blockLast();

// List all interviewers
userService.listAll(UserFilter.newBuilder().clientUserType(List.of(ClientUserType.INTERVIEWER_MEMBER)).disabled(List.of(false)).build())
        .doOnNext(u -> System.out.println(u.getId() + " " + u.getEmail()))
        .blockLast();

Recording Candidate Decisions

@Autowired CandidateDecisionService decisionService;

// Single outcome
var result = decisionService.record("OFFER_ACCEPTED", List.of("cand-1", "cand-2")).block();
log.info("successful={} failed={}", result.getSuccessfulUpdates(), result.getFailedUpdates());

// Multiple outcomes in one API call
decisionService.record(List.of(
        CandidacyStatusWithCandidacies.newBuilder()
                .status(CandidacyStatus.OFFER_ACCEPTED)
                .ids(List.of("cand-3"))
                .build(),
        CandidacyStatusWithCandidacies.newBuilder()
                .status(CandidacyStatus.DECLINED_AT_ONSITE_INTERVIEW)
                .ids(List.of("cand-4", "cand-5"))
                .build()
)).block();

Creating Users

@Autowired UserService userService;

// name, email, and clientUserType are required; all other fields are optional
var request = CreateClientUserInput.newBuilder()
        .name("Alice Smith")
        .email("alice@example.com")
        .clientUserType(ClientUserType.INTERVIEWER_MEMBER)   // valid values: INTERVIEWER_MEMBER, ADMIN, TEAM_MEMBER, LIMITED_TEAM_MEMBER, EXTERNAL_MEMBER, ORGANIZATION_ADMIN
        .phone("+15551234567")           // optional
        .timeZone("America/New_York")    // optional, IANA timezone
        .build();

var result = userService.create(request).block();
log.info("Created user: id={}", result.getUser().getId());

Inviting Candidates

@Autowired InvitationService invitationService;

// name, email, and roleId are required; all other fields are optional
var request = CreateInvitationInput.newBuilder()
        .name("Alice Smith")
        .email("alice@example.com")
        .roleId("role-42")
        .phone("+15551234567")
        .atsUrl("https://ats.example.com/candidates/alice-smith")
        .resume("https://cdn.example.com/resume.pdf")
        .githubUrl("https://github.com/alice")
        .linkedinUrl("https://linkedin.com/in/alice")
        .build();

var result = invitationService.send(request).block();
log.info("Created: assessment={}", result.getAssessment().getId());

// Bulk invitations — all requests fire concurrently
Flux<CreateInvitationInput> requests = Flux.just(request1, request2, request3);
invitationService.sendAll(requests).blockLast();

Batch-Inviting Candidates

@Autowired CandidateInvitesService candidateInvitesService;

// name, email, and roleId are required per invite; all other fields are optional
var invites = List.of(
        CandidateInviteInput.newBuilder()
                .name("Alice Smith").email("alice@example.com").roleId("role-42")
                .phone("+15551234567").build(),
        CandidateInviteInput.newBuilder()
                .name("Bob Jones").email("bob@example.com").roleId("role-42")
                .githubUrl("https://github.com/bob").build()
);

var result = candidateInvitesService.send(invites).block();
log.info("Assessments: {}, Errors: {}", result.getAssessments().size(), result.getErrors().size());

Creating Acceleration Candidacies

@Autowired AccelerationCandidacyService accelerationCandidacyService;

// email and previousInterviewId are required; all other fields are optional
var request = CreateAccelerationCandidacyInput.newBuilder()
        .email("alice@example.com")
        .previousInterviewId("interview-123")
        .name("Alice Smith")
        .roleId("role-42")
        .recommendation(3)
        .build();

var result = accelerationCandidacyService.create(request).block();
log.info("Errors: {}", result.getErrors() != null ? result.getErrors().size() : 0);

Sending Bulk Invites via CSV

@Autowired BulkInviteService bulkInviteService;

// roleId and csv are required; all other fields are optional
var request = SendBulkInviteInput.newBuilder()
        .roleId("role-42")
        .csv("name,email\nAlice Smith,alice@example.com\nBob Jones,bob@example.com")
        .build();

var result = bulkInviteService.send(request).block();
log.info("Bulk invite statusId: {}", result.getBulkInviteStatusId());

// Check bulk invite status
var status = bulkInviteService.fetchStatus("bi-123").block();
log.info("Invited: {}, toInvite: {}, error: {}", status.getInvited(), status.getToInvite(), status.getError());

// Validate CSV before sending
var validation = bulkInviteService.validate("role-42", csvContent).block();
log.info("ToInvite: {}, error: {}", validation.getToInvite(), validation.getError());

Updating Users

@Autowired UserService userService;

var request = UpdateClientUserInput.newBuilder()
        .name("Alice Updated")
        .phone("+15559876543")
        .timeZone("America/Chicago")
        .build();

var result = userService.update("usr-abc123", request).block();
log.info("Updated user: id={}", result.getUser().getId());

Candidacy Lifecycle Mutations

@Autowired CandidacyService candidacyService;

// Update status
candidacyService.updateStatus("cand-1", "INTERVIEW_PENDING").block();

// Advance to a role stage
candidacyService.advance("cand-1", "stage-2").block();

// Reassign to a different role
candidacyService.reassign("cand-1", "role-new").block();

// Follow a candidacy
var result = candidacyService.follow("cand-1").block();
log.info("Following: {}", result.getFollower() != null);

// Update recruiter
candidacyService.updateRecruiter("cand-1", "user-42").block();

// Update ATS URL
candidacyService.updateAtsUrl("cand-1", "https://ats.example.com/apply/123").block();

Following Roles

@Autowired RoleService roleService;

var result = roleService.follow("role-eng456").block();
log.info("Following: {}", result.getFollower() != null);

Getting the Current User

@Autowired MeClient meClient;

var me = meClient.fetch().block();
log.info("Current user: id={}, name={}", me.getId(), me.getName());

Listing Competencies

@Autowired CompetencyClient competencyClient;

var competencies = competencyClient.fetchAll().block();
competencies.forEach(c -> log.info("{}: {}", c.getId(), c.getName()));

CSV Output

candidacies-<timestamp>.csv

One row per candidacy. Columns:

Column Description
candidacy_id Karat candidacy ID
status Candidacy status (e.g. COMPLETED)
archived Whether the candidacy is archived
last_status_update ISO 8601 timestamp of last status change
created_at ISO 8601 timestamp when the candidacy was created
ats_url Link to the candidate in your ATS
ats_candidate_id ATS candidate identifier
ats_application_id ATS application identifier
candidate_id Karat candidate ID
candidate_name Candidate full name
candidate_email Candidate email
candidate_resume_url Safe resume download URL
recruiter_id Recruiter ID
recruiter_name Recruiter name
recruiter_email Recruiter email
role_id Role ID
role_name Role name
group_id Group ID
group_name Group name
interview_count Number of interviews
interview_states Semicolon-separated interview states
interview_recommendations Semicolon-separated recommendations (e.g. YES;NO)
interview_end_times Semicolon-separated interview end timestamps
interview_overall_thoughts Semicolon-separated interviewer written feedback
interview_integrity_states Semicolon-separated integrity check results (e.g. PASS;FAIL)
interview_cheating_detected Semicolon-separated cheating-detected flags (true/false)
code_challenge_count Number of code challenges
code_challenge_states Semicolon-separated code challenge states
code_challenge_recommendations Semicolon-separated code challenge recommendations

interviews-<timestamp>.csv

One row per interview. Columns:

Column Description
interview_id Karat interview ID
candidacy_id Parent candidacy ID
candidate_name Candidate full name
candidate_email Candidate email
role_name Role name
group_name Vendor/group name
state Interview state (e.g. SHIPPED)
recommendation Interviewer recommendation (e.g. YES, NO)
is_redo Whether this was a redo interview
created_at Interview creation timestamp
end_time Interview end timestamp
shipped_time Time the result was shipped
overall_thoughts Interviewer's written feedback
integrity_state Integrity check result (e.g. PASS, FAIL)
cheating_detected Whether cheating was detected (true/false)

users-<timestamp>.csv

One row per user.

Column Description
user_id Karat user ID
name User full name
email User email
phone Phone number
timezone IANA timezone (e.g. America/New_York)
client_user_type e.g. INTERVIEWER_MEMBER, ADMIN
last_seen ISO 8601 timestamp of last login
disabled Whether the user account is disabled
groups Semicolon-separated group names

organizations-<timestamp>.csv

One row per organization.

Column Description
organization_id Karat organization ID
name Organization name
created_at ISO 8601 creation timestamp
updated_at ISO 8601 last-updated timestamp
groups Semicolon-separated group names
vendors Semicolon-separated vendor names
job_requisitions Semicolon-separated job IDs

groups-<timestamp>.csv

One row per group.

Column Description
group_id Karat group ID
name Group name
type Group type (e.g. CLIENT, INTERNAL)
active Whether the group is active
archived Whether the group is archived
organization_id Parent organisation ID
organization_name Parent organisation name

roles-<timestamp>.csv

One row per role.

Column Description
role_id Karat role ID
name Role name
state Role lifecycle state (e.g. live)
archived Whether the role is archived
type Role type (e.g. STANDARD)
assessment_type Assessment format (e.g. TECHNICAL_PHONE_SCREEN)
description Role description
candidates_count Number of candidates assessed
created_at ISO 8601 creation timestamp
updated_at ISO 8601 last-updated timestamp
organization_id Parent organisation ID
organization_name Parent organisation name
group_id Associated group ID
group_name Associated group name

job-requisitions-<timestamp>.csv

One row per job requisition.

Column Description
job_requisition_id Karat job requisition ID
job_id External job ID
created_at ISO 8601 creation timestamp
updated_at ISO 8601 last-updated timestamp
organization_id Parent organisation ID
organization_name Parent organisation name

Scripts

Prerequisites: jq must be installed:

brew install jq

Schema Introspection

scripts/introspect-comprehensive.sh reads the downloaded schema at .schema/karat-schema.json and produces a structured report of all queries, mutations, and types. No authentication required.

./scripts/introspect-comprehensive.sh                          # default schema
./scripts/introspect-comprehensive.sh path/to/schema.json      # custom schema

Output: scripts/schema-comprehensive.json — contains meta (counts and type breakdown), queries, mutations, and types sections with resolved type references.

Build & Test

./gradlew test               # run all tests
./gradlew dependencyUpdates --no-parallel  # check for available dependency updates
./gradlew :api:bootJar       # build executable fat jar → api/build/libs/api-1.0-SNAPSHOT-boot.jar

API Reference

Endpoints

All requests go to https://{subdomain}.karat.io/api/v1/graphql with an Authorization: Bearer {token} header. Pagination uses Relay-style cursors (nextCursor, hasNextPage).

Endpoint Type Service class Description
candidacies Query CandidacyService Paginated candidacies with interviews & results
users Query UserService Paginated admin/interviewer user list
organizations Query OrganizationService Paginated organization list
groups Query GroupService Paginated group list
roles Query RoleService Paginated role list
jobRequisitions Query JobRequisitionService Paginated job requisition list
bulkUpdateCandidacyStatus Mutation CandidateDecisionService Bulk-update candidacy outcome statuses
createInvitation Mutation InvitationService Create a candidacy by inviting a candidate
createCandidateInvites Mutation CandidateInvitesService Batch-invite candidates
createAccelerationCandidacy Mutation AccelerationCandidacyService Create a redo candidacy from a prior interview
sendBulkInvite Mutation BulkInviteService Upload CSV of candidates to invite
bulkInviteStatus Query BulkInviteService Check processing status of a bulk invite
bulkInviteValidate Query BulkInviteService Validate a bulk invite CSV
createClientUser Mutation UserService Create an admin or interviewer user
updateClientUser Mutation UserService Update an existing user
me Query MeClient Get the current authenticated user
orgVendor Query OrgVendorClient Get a single org vendor by ID
candidates Query CandidateService Paginated candidate list
codeChallenges Query CodeChallengeService Paginated code challenge list
platformAssessments Query PlatformAssessmentService Paginated platform assessment list
qualityControls Query QualityControlService Paginated quality control list
resultReviews Query ResultReviewService Paginated result review list
interviewSeriesTemplates Query InterviewSeriesTemplateService Paginated interview series template list
rolesWithCounts Query RoleWithCountsClient Roles with candidacy/interview counts
competencies Query CompetencyClient List all competencies
schedulingAvailabilities Query SchedulingAvailabilityClient List scheduling availabilities
interviews Query InterviewService Paginated interview list
practicalDemoByInterviewUuid Query PracticalDemoService Get practical demo by interview UUID
practicalDemoByUserId Query PracticalDemoService Get practical demo by user ID
createPracticalDemo Mutation PracticalDemoService Create a practical demo
deletePracticalDemo Mutation PracticalDemoService Delete a practical demo
startPracticalDemoSession Mutation PracticalDemoService Start a practical demo session
updateCandidacyStatus Mutation CandidacyService Update candidacy status
advanceCandidacyToRoleStage Mutation CandidacyService Advance candidacy to a role stage
reassignCandidacy Mutation CandidacyService Reassign candidacy to another role
updateCandidacyJobRequisitionId Mutation CandidacyService Update candidacy job requisition
updateCandidacyOrgVendor Mutation CandidacyService Update candidacy vendor
updateCandidacyDispositions Mutation CandidacyService Update candidacy dispositions
followCandidacy Mutation CandidacyService Follow/unfollow a candidacy
updateRecruiter Mutation CandidacyService Update candidacy recruiter
updateAtsUrl Mutation CandidacyService Update candidacy ATS URL
followRole Mutation RoleService Follow/unfollow a role
cancelInterview Mutation InterviewService Cancel an interview
rescheduleInterview Mutation InterviewService Reschedule an interview
scheduleInterview Mutation InterviewService Schedule an interview
updateInterviewState Mutation InterviewService Update interview state
updateIntegrityState Mutation InterviewService Update interview integrity state
updateCheatDetection Mutation InterviewService Update cheat detection flag
approveQualityControl Mutation QualityControlService Approve a quality control review
assignQualityControlReviewer Mutation QualityControlService Assign a reviewer
updateQualityControlPriority Mutation QualityControlService Update QC priority
updateQualityControlReviewType Mutation QualityControlService Update QC review type
createResultReview Mutation ResultReviewService Create a result review
deleteResultReview Mutation ResultReviewService Delete a result review
updateResultReviewInclude Mutation ResultReviewService Update include status
createOrgVendor Mutation OrgVendorService Create an organization vendor
updateOrgVendor Mutation OrgVendorService Update an organization vendor
upsertOrgVendor Mutation OrgVendorService Upsert an organization vendor
createRole Mutation RoleService Create a role
updateRole Mutation RoleService Update a role
archiveRole Mutation RoleService Archive a role
copyRoles Mutation RoleService Copy roles to a group
publishRole Mutation RoleService Publish a role
createInterviewSeriesTemplate Mutation InterviewSeriesTemplateService Create a template
updateInterviewSeriesTemplate Mutation InterviewSeriesTemplateService Update a template
updateCodeChallengeState Mutation CodeChallengeService Update code challenge state
interviewMoments Query InterviewMomentService List moments for an interview
createInterviewMoment Mutation InterviewMomentService Create an interview moment
updateInterviewMoment Mutation InterviewMomentService Update an interview moment
deleteInterviewMoment Mutation InterviewMomentService Delete an interview moment
quizzes Query QuizService Paginated quiz list
quizTopics Query QuizTopicClient Paginated quiz topic list
questions Query QuestionService Paginated question list
questionPools Query QuestionPoolService Paginated question pool list
questionTypes Query QuestionTypeClient List all question types
campaigns Query CampaignClient Get a single campaign
campaignMemberships Query CampaignMembershipService Paginated campaign membership list
archetypes Query ArchetypeService Paginated archetype list
roleStages Query RoleStageClient Get a single role stage
roleExperienceLevels Query RoleExperienceLevelService List role experience levels
roleLocations Query RoleLocationService List role locations
rubrics Query RubricClient Get a single rubric
atsIntegrations Query AtsIntegrationService Paginated ATS integration list
atsStageMappings Query AtsStageMappingService List ATS stage mappings
atsConsiderations Query AtsConsiderationClient List ATS considerations
createAtsIntegration Mutation AtsIntegrationService Create an ATS integration
updateAtsIntegration Mutation AtsIntegrationService Update an ATS integration
deleteAtsIntegration Mutation AtsIntegrationService Delete an ATS integration
createAtsStageMapping Mutation AtsStageMappingService Create an ATS stage mapping
updateAtsStageMapping Mutation AtsStageMappingService Update an ATS stage mapping
deleteAtsStageMapping Mutation AtsStageMappingService Delete an ATS stage mapping
activityMetrics Query AnalyticsService Aggregate interview activity counts
funnelStats Query AnalyticsService Candidacy funnel statistics
experienceStats Query AnalyticsService Average candidate experience rating
homepageMetrics Query AnalyticsService Dashboard-level candidate metrics
aboveBarContributors Query AnalyticsService Above-bar contributor funnel stats
vendorPerformance Query AnalyticsTrendsService Aggregated vendor performance data
vendorTrends Query AnalyticsTrendsService Vendor performance trends
roleDistribution Query AnalyticsTrendsService Candidacy distribution across roles
jobFamilyMetrics Query AnalyticsTrendsService Performance metrics by job family
workspaces Query WorkspaceService Paginated workspace list
createWorkspace Mutation WorkspaceService Create a workspace
deleteWorkspace Mutation WorkspaceService Delete a workspace
updateWorkspaceSettings Mutation WorkspaceService Update workspace settings
billables Query BillableService List billable items
billingEntries Query BillingEntryService List billing entries
userBilling Query UserBillingClient Get aggregated billing for a user
historyEntries Query HistoryService List history entries
contentTags Query ContentTagClient List content tags
contracts Query ContractClient Get a single contract
recordings Query RecordingClient Get a single recording
performanceIncidents Query PerformanceIncidentService List performance incidents
languages Query LanguageClient List all supported languages
jobFamilies Query JobFamilyClient List all job families
userNotificationPreferences Query UserNotificationPreferencesClient Get user notification prefs
updateUserNotificationPreferences Mutation UserNotificationPreferencesClient Update notification prefs

Filters

Filter Fields
CandidacyFilter id, status, group, role, recruiter, candidate, archived, demo, codeChallengeState, interviewState, integrityState, platformAssessmentState, quizState, applicationId, opportunityId, atsUrl, atsUrlSubstring, createdAfter, createdBefore, lastStatusUpdateAfter, lastStatusUpdateBefore, jobRequisition
InterviewFilter id, group, role, recruiter, recommendation, state, integrityState, cheatingDetected, candidacyStatus, archived, demo, startTimeAfter, startTimeBefore, originalInterviewsOnly, redoInterviewsOnly
UserFilter id, clientUserType, type, group, organization, teams, disabled
RoleFilter id, group, organization, states, archived, demo
OrganizationFilter id
GroupFilter id, type, organizationId
JobRequisitionFilter organizationId
CandidateFilter email, group, role
CodeChallengeFilter state, candidacy, group, role
PlatformAssessmentFilter state, group, role
QualityControlFilter state, priority, reviewType
ResultReviewFilter interviewId, userId
InterviewSeriesTemplateFilter name

All filters support free-text search via a separate parameter on the service and controller methods.

Karat GraphQL API

Resource Link
Official SDK examples (TypeScript/Python) https://github.com/Karat/api-sdk
API tracker overview https://apitracker.io/a/karat
Partner documentation (requires login) https://karat.slab.com/posts/karat-api-overview-6l4gprc3
API schema documentation (requires login) https://karat.slab.com/posts/karat-api-documentation-dezzam7y

Spring WebFlux / Spring for GraphQL

Resource Link
Spring WebFlux overview https://docs.spring.io/spring-framework/reference/web/webflux.html
WebClient reference https://docs.spring.io/spring-framework/reference/web/webflux-webclient.html
Spring for GraphQL client docs https://docs.spring.io/spring-graphql/reference/client.html
HttpGraphQlClient API https://docs.spring.io/spring-graphql/reference/client.html#client.requests.http
Spring for GraphQL testing https://docs.spring.io/spring-graphql/reference/testing.html
Project Reactor reference https://projectreactor.io/docs/core/release/reference/
StepVerifier testing https://projectreactor.io/docs/test/release/api/reactor/test/StepVerifier.html

Important: HttpGraphQlClient.Builder.url(String) replaces the WebClient's baseUrl entirely — it does not append a path. Always set the full GraphQL endpoint URL (including /api/v1/graphql) in karat.base-url, and do not call .url() on the builder.