_private/qwestly-hire-docs/external-jobs-search-api.md
Table of Contents
External Jobs Search API
This document is for integrators (e.g. contractors or partner services) that need server-to-server access to Qwestly Hire job data. It describes authentication, endpoints, query parameters, and response shapes for the Job Search API.
Overview
- Purpose: Search and retrieve job postings stored in Qwestly Hire.
- Protocol: HTTPS only. GET requests only.
- Format: JSON request/response bodies (UTF-8).
- Intended use: Backend services calling the Hire app origin you were given (not end-user browsers). Keep API keys on the server.
Base URL
All paths below are relative to the Hire application origin (for example https://hire.example.com).
{ORIGIN}/api/external/search/jobs
Replace {ORIGIN} with the environment-specific base URL provided by Qwestly (production, staging, etc.).
Authentication
Every request must include a valid Hire integration API key (HIRE_APP_API_KEY on the Hire deployment). This is the same secret used for other /api/external/* routes (e.g. company search and persist). There is no separate job-search key.
You can send the key in any one of these ways:
| Method | Header | Value |
|---|---|---|
| Bearer | Authorization |
Bearer <your-api-key> |
| ApiKey | Authorization |
ApiKey <your-api-key> |
| API key header | X-API-Key |
<your-api-key> |
Unauthorized requests receive HTTP 401 with a null JSON body.
Security:
- Do not commit keys to source control, embed them in client apps, or log them.
- Request a key rotation from Qwestly if a key may be exposed.
Endpoints
1. List and search jobs
GET /api/external/search/jobs
Returns a paginated list of jobs. Supports filtering and optional full-text search.
Query parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
page |
No | 1 |
Page number (integer ≥ 1). |
limit |
No | 50 |
Page size (integer ≥ 1, maximum 200). |
status |
No | active |
Job status. Typical values: draft, active, inactive, filled, expired. |
company_id |
No | — | Filter by company. Must be a MongoDB ObjectId string (24 hex characters). |
job_family |
No | — | Filter by job family, e.g. engineering, design, product, other. |
location_type |
No | — | Filter by work arrangement: remote, hybrid, in-person. |
search |
No | — | Full-text search string. When present, results are sorted by text relevance score, then updated_at, then _id. |
Full-text search (search): The index covers fields such as title, description, company name, and structured requirement fields (e.g. skills and experience under must_have / nice_to_have). Use concise search terms as you would in a job board search box.
Success response — 200 OK
{
"success": true,
"data": [ /* array of job objects — see [Job object](#job-object) */ ],
"pagination": {
"totalCount": 0,
"totalPages": 0,
"currentPage": 1,
"limit": 50
}
}
Error responses
| Status | When |
|---|---|
400 |
Invalid page, limit, or company_id format. Body: { "error": "<message>" }. |
401 |
Missing or invalid API key. Body: null. |
500 |
Server error. Body: { "error": "<message>", "details": "<message>" }. |
Example (curl)
curl -sS \
-H "Authorization: Bearer YOUR_HIRE_APP_API_KEY" \
"https://{ORIGIN}/api/external/search/jobs?page=1&limit=20&status=active&search=typescript"
2. Get one job by ID
GET /api/external/search/jobs/{jobId}
Returns a single job when you already know its identifier.
Path parameters
| Parameter | Description |
|---|---|
jobId |
MongoDB ObjectId of the job (24 hex characters). |
Success response — 200 OK
{
"success": true,
"data": { /* single job object — see [Job object](#job-object) */ }
}
Error responses
| Status | When |
|---|---|
400 |
jobId is not a valid ObjectId. Body: { "error": "Invalid jobId format" }. |
401 |
Missing or invalid API key. Body: null. |
404 |
No job with that ID. Body: { "error": "Job not found" }. |
500 |
Server error. Body: { "error": "<message>", "details": "<message>" }. |
Example (curl)
curl -sS \
-H "Authorization: Bearer YOUR_HIRE_APP_API_KEY" \
"https://{ORIGIN}/api/external/search/jobs/507f1f77bcf86cd799439011"
Job object
Each job in data is a JSON object. Identifiers are returned as strings where applicable. Dates are ISO 8601 strings when present.
| Field | Type | Notes |
|---|---|---|
_id |
string | Job document id. |
source_id |
string | null | Internal source reference. |
status |
string | e.g. active, draft, … |
title |
string | Job title. |
location |
string[] | Location strings (may be empty array). |
location_type |
string | null | remote, hybrid, in-person. |
employment_type |
string | null | e.g. full-time, contract, … |
company_name |
string | null | |
company_id |
string | null | |
org_unit_name |
string | null | |
org_unit_id |
string | null | |
parent_org_id |
string | null | |
exploratory_job_id |
string | null | |
job_family |
string | null | e.g. engineering, design, … |
is_epd |
boolean | null | |
has_equity |
boolean | null | |
equity_details |
string | null | |
benefits |
string | null | |
compensation |
object | null | May include salary ranges and currency. |
team |
string | null | |
description |
string | null | Full description. |
responsibilities |
string[] | |
must_have |
object | null | Requirements structure (skills, experience, etc.). |
nice_to_have |
object | null | Preferred requirements. |
metadata |
object | null | Arbitrary extra fields. |
hm_additional_info |
string | null | Hiring manager notes. |
llm_role_summary |
string | null | Internal summary field when present. |
is_publicly_accessible |
boolean | Whether the job may be shown without an account. |
created_at |
string | null | ISO 8601. |
updated_at |
string | null | ISO 8601. |
Field availability depends on what was stored for each job; many optional fields may be null.
Operational notes
- Migration: If you previously used
JOB_SEARCH_API_KEY, configureHIRE_APP_API_KEYto the same value (or rotate to one shared integration secret) and send that key on all/api/external/search/jobsrequests. RemoveJOB_SEARCH_API_KEYfrom Hire environment variables. - Pagination: Use
totalCount,totalPages,currentPage, andlimitto implement paging in your integration. - Sorting: Without
search, jobs are ordered byupdated_atdescending, then_idascending. Withsearch, text relevance is applied first. - Support: For base URL, credentials, or access issues, contact your Qwestly project contact.
Changelog
| Date | Change |
|---|---|
| 2025-03-26 | Initial contractor documentation for /api/external/search/jobs. |
| 2026-03-26 | Authentication unified on HIRE_APP_API_KEY (same as other /api/external/* routes). JOB_SEARCH_API_KEY removed. |