ATS API Documentation | JuggleHire Recruitment API
HomeAPI Documentation

JuggleHire API

A REST API for reading and moving your jobs and candidates. It was built so an AI agent — Claude, ChatGPT, a script of your own — can work your pipeline with a token that only carries the permissions you grant it.

Quickstart

Create a token at Organization → API, then call the API with it. This returns the jobs the token's owner can manage:

curl https://app.jugglehire.com/api/v1/jobs \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"

Every response is JSON. Lists are paginated:

{
  "data": [
    { "id": 1711, "title": "Senior Software Engineer", "status": "open", ... }
  ],
  "links": { "first": "...", "last": "...", "prev": null, "next": "..." },
  "meta": { "current_page": 1, "last_page": 2, "per_page": 15, "total": 26 }
}

Authentication

The API uses bearer tokens. Create one in the app at Organization → API, give it a name you will recognise later, and tick only the scopes that integration needs. The token is shown once, at creation — copy it then, because it cannot be retrieved afterwards.

Authorization: Bearer YOUR_TOKEN
Accept: application/json

Tokens expire six months after they are created, and you can revoke one at any time from the same page. Team owners and admins see every token created inside their team, along with who created it and when it was last used — so an unused or forgotten token is easy to spot and kill.

A request with a missing, revoked, or expired token returns 401:

{ "error": { "code": "unauthenticated", "message": "Unauthenticated.", "details": null } }

Scopes

A token carries only the scopes you tick. Calling an endpoint without its scope returns 403 insufficient_scope — the request never reaches your data. Start read-only and add write scopes when you actually need them.

ScopeGrants
jobs:readList and read jobs, stages, and pipeline
jobs:writeCreate and update jobs
candidates:readSearch and read applicants, notes, and ratings
candidates:writeMove stages, change status, add notes and ratings
messages:sendSend emails and messages to candidates
scheduling:writeSchedule and cancel interviews

messages:send and scheduling:write can be granted today but have no endpoints yet — messaging and interview scheduling are the next additions to this API.

Conventions

  • Base URL is https://app.jugglehire.com. Every path below is relative to it.
  • A single object comes back under data; lists come back under data with links and meta. Page through with ?page=2; pages hold 15 records.
  • A token can only reach what its owner can reach in the app: every job on teams they own or administer, and on other teams only the jobs they own or are assigned to. Anything outside that returns 404 rather than 403, so the API never confirms that a job you cannot see exists.
  • Every write is recorded in your activity log with the token that made it.

Errors

Every error uses the same envelope:

{
  "error": {
    "code": "validation_failed",
    "message": "The title field is required.",
    "details": { "title": ["The title field is required."] }
  }
}

details carries field-level messages on a validation failure and is null otherwise.

StatusCodeMeans
401unauthenticatedMissing, revoked, or expired token.
403insufficient_scopeThe token lacks the scope this endpoint needs. details.required_scope names it.
403forbiddenValid scope, but the account is not allowed to do this — a plan limit, for example.
404not_foundNo such record, or one the token cannot reach.
422validation_failedThe body did not validate. See details.
429rate_limitedToo many requests. Back off and retry.
500server_errorSomething broke on our side. The message is deliberately generic.

Rate limits

60 requests per minute, counted per user rather than per token — two tokens belonging to the same person share one budget. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining; a throttled one adds Retry-After in seconds.

{ "error": { "code": "rate_limited", "message": "Too Many Attempts.", "details": null } }

Idempotency

Any write accepts an optional Idempotency-Key header. Send the same key twice and the second call returns the first call's stored response instead of acting again — useful when a network error leaves you unsure whether a job was created.

curl -X POST https://app.jugglehire.com/api/v1/jobs \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Idempotency-Key: 8f14e45f-ea6a-4c3b-9f2d-1b7c0a5e6d90" \
  -H "Content-Type: application/json" \
  -d '{"title": "Backend Engineer"}'
  • A replayed response carries Idempotent-Replay: true.
  • Keys are scoped to your team and remembered for 24 hours.
  • Only successful responses are stored, so a call that failed can be retried safely with the same key. Use a fresh UUID per distinct action.

Identity

GET/api/v1/meany valid token

Who the token belongs to, and what it can do. The cheapest way to check a token is alive.

{
  "data": {
    "id": 1,
    "name": "Zakir Hossen",
    "email": "you@example.com",
    "team": { "id": 1, "name": "Lomeyo LLC" },
    "scopes": ["jobs:read", "candidates:read"]
  }
}

Jobs

GET/api/v1/jobsrequires jobs:read

Jobs the token can manage, newest first. Query parameters: status (open, draft, or closed — defaults to open) and search, which matches the title.

curl "https://app.jugglehire.com/api/v1/jobs?status=open&search=engineer" \
  -H "Authorization: Bearer YOUR_TOKEN"
GET/api/v1/jobs/{job}requires jobs:read

One job in full.

GET/api/v1/jobs/{job}/stagesrequires jobs:read

The job's pipeline stages. You need these ids to move a candidate.

{ "data": [ { "id": 12, "name": "Applied" }, { "id": 13, "name": "Interview" } ] }
POST/api/v1/jobsrequires jobs:write

Creates a job on the token owner's current team and returns it with 201. Body: title (required, max 255) and description (optional HTML). Everything else takes a sensible default — onsite, full-time, open, with a deadline 30 days out — which you can change afterwards in the app or with a PATCH. Reaching your plan's job limit returns 403.

curl -X POST https://app.jugglehire.com/api/v1/jobs \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Backend Engineer", "description": "<p>Build our API.</p>"}'
PATCH/api/v1/jobs/{job}requires jobs:write

Updates title, description, or status. Send only the fields you are changing.

curl -X PATCH https://app.jugglehire.com/api/v1/jobs/1711 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "closed"}'

Candidates

GET/api/v1/jobs/{job}/candidatesrequires candidates:read

Applicants on a job, paginated. Query parameters: search (name or email) and stage, which takes a stage id from the stages endpoint.

{
  "data": [
    {
      "id": 5821,
      "name": "Amina Rahman",
      "email": "amina@example.com",
      "applied": "2026-08-01",
      "stage": "Interview",
      "status": "Shortlisted"
    }
  ],
  "meta": { "current_page": 1, "total": 34 }
}
GET/api/v1/jobs/{job}/candidates/{candidate}requires candidates:read

One candidate, adding resume_url and notes_count. Empty fields are omitted rather than returned as null.

POST/api/v1/jobs/{job}/candidates/{candidate}/stagerequires candidates:write

Moves the candidate to a stage on that job. Body: stage, an integer id from the stages endpoint. A stage belonging to a different job returns 422; a candidate already in that stage is left alone.

curl -X POST https://app.jugglehire.com/api/v1/jobs/1711/candidates/5821/stage \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"stage": 13}'
POST/api/v1/jobs/{job}/candidates/{candidate}/notesrequires candidates:write

Adds a note, returned with 201. Body: content, up to 5,000 characters.

POST/api/v1/jobs/{job}/candidates/{candidate}/ratingrequires candidates:write

Sets the rating. Body: rating, one of good_fit, maybe, or not_a_fit. Anything else returns 422.

curl -X POST https://app.jugglehire.com/api/v1/jobs/1711/candidates/5821/rating \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"rating": "good_fit"}'

Something missing?

Messaging and interview scheduling are next. If you need an endpoint that is not here yet, email hello@jugglehire.com and tell us what you are building — it genuinely shapes what we add.

Create your API token