The Harly REST API is available at:
The OpenAPI document is available from a running installation at
/api/v1/openapi.json. Use it as the source of truth for exact schemas and
route parameters.
The repository also includes a generated snapshot at /openapi.json. With the
Harly dev server running on port 3000, refresh it after changing an API
contract with:
Authenticate
Create a key from Settings → Developers → API keys, copy the raw value
once, and send it as a bearer token:
Keys are scoped. Give an integration only the resources it needs. Rotate or
revoke keys when an owner or vendor changes.
Response envelopes
Success
Error
Common error codes: validation_error, not_found, forbidden,
conflict, rate_limited.
List endpoints use cursor-based pagination:
- Do not assume a stable sort order or page number.
meta.nextCursor is absent when you have reached the last page.
limit defaults to 25 and caps at 100.
Idempotency
POST requests that support it accept an Idempotency-Key header. Use a
unique value per logical operation — for example a UUID or a stable key
derived from the operation’s inputs:
A 409 may mean a real concurrency conflict or an idempotency collision.
Inspect the error envelope before retrying a mutation. Do not retry a 409
blindly — the operation may have already succeeded.
Rate limits
The API applies per-key rate limits. When a response returns 429, back off
using the Retry-After header value before retrying. Do not hammer the API
with immediate retries.
HTTP status codes
Main resources
Jobs, candidates, applications, interviews, offers, scorecards, tasks,
pool entries, activity events, webhooks, and API keys are available through
the versioned API.
Use webhooks to react to changes instead of polling
every resource. See API resource reference for
the full scope and operation inventory.
Try it
The OpenAPI document at /api/v1/openapi.json is importable into Insomnia,
Postman, or any OpenAPI-compatible tool:
See the API integration tutorial for an
end-to-end walkthrough with webhook verification.
Public jobs and applications API
The public API is the same API used by Harly’s embedded job widget. It is
CORS-enabled and intentionally exposes only published, open jobs and the
application intake flow. It never exposes candidate or application records
after submission.
Every request must identify the workspace in one of these ways:
- Add
?workspace=<workspace-slug> to the URL. This is zero-configuration and
is suitable when the public workspace slug is already known.
- Send a publishable key (
pk_) in X-API-Key, Authorization: Bearer, or
?pk=. The key is bound to one workspace and is useful for browser embeds,
analytics, and revocation.
The public routes do not infer a workspace automatically, even on a
single-workspace installation. Never send a secret (sk_) key to a browser.
1. List open jobs
With a publishable key:
The list endpoint supports the optional department, location,
workplaceType, and q query filters. It returns a standard { data }
success envelope containing the workspace branding and serialized public jobs.
Use the returned job’s slug as the public job identifier. A custom frontend
should use the slug rather than the internal job id.
2. Fetch a job and its application configuration
The response includes applicationConfig. Its questions array is the source
of truth for custom questions:
The question’s id is the key used in questionAnswers. Supported question
types are text, textarea, url, and select; select questions also
return an options array. The same response describes standard fields under
applicationConfig.sections, including resume, links, education, experience,
and cover letter visibility (required, optional, or disabled).
3. Submit the application
The endpoint is:
It returns 201 with this success envelope:
The API validates required standard fields and every custom answer, so the
frontend should render from applicationConfig instead of duplicating the
form configuration. If CAPTCHA is configured for the workspace, the
application request must include a valid token in captchaToken (the legacy
turnstileToken name is also accepted).
The public application endpoint is rate-limited to protect the intake flow.
Clients must handle 429 and retry after the Retry-After response header.
The request also accepts the _hp honeypot field; real forms should leave it
empty.
Uploads
For a resume or profile image, request a presigned upload URL first, upload the
file directly to that URL, and send the returned key and file metadata in the
application payload:
Both endpoints accept { filename, contentType, contentLength } and are scoped
to the same workspace or publishable key. The resume fields are
resumeKey, resumeFileName, resumeFileType, and resumeFileSize; the image
field is photoUrl.
For a browser integration, the publishable-key variant is usually preferable:
Never expose a secret (sk_) key in frontend code.
Public API errors
Public routes use the same error envelope as the authenticated API:
Common statuses are 400 for a missing or malformed workspace selector, 403
for a failed CAPTCHA or missing publishable-key scope, 404 for a job that is
not public, 422 for invalid application fields, and 429 for rate limiting.