Partner API

Atolla Partner API

A secure REST API for 3rd-party systems (ATS, sourcing tools, job boards) to push candidates and jobs into Atolla programmatically. Authenticate with an issued API key, call the endpoints below, and your data flows straight into the platform.

Base URL
https://api.atolla.ca/partner/v1
Local development
http://localhost:8087/partner/v1

Overview

The Partner API is a small, focused surface. Every request is authenticated with an API key, scoped to exactly what that key is allowed to do, rate-limited, and audited. Two ingest endpoints are available today:

Create candidates

Import a candidate profile (with skills, experience, education and an optional résumé) into your searchable talent pool.

Create jobs

Post a job under a specific employer. It appears in that employer's dashboard and counts against their plan.

Each key is granted a set of scopes when it is created. A key without thejob:create scope cannot call the jobs endpoint, and vice-versa.

Getting a key

Keys are issued by an Atolla administrator from the admin console. When you request a key, specify what you need — the admin configures it and hands you the secret:

  • Scopescandidate:create and/or job:create.
  • Employer — for job:create, every job the key creates is posted under this one employer.
  • Candidate modeprofile_only imports a searchable profile (no login account).
  • Expiry, rate limit, allowed IPs — optional guards on the key.
The full secret (atolla_pk_…) is shown only once, at creation. Store it in your secret manager immediately — Atolla keeps only a hash and can never show it again. If it's lost, ask for the key to be revoked and a new one issued.

Authentication

Send your key as a Bearer token on every request (or via the X-Api-Key header):

Authorization header
Authorization: Bearer atolla_pk_YOUR_SECRET_HERE

Quick check that your key works — call GET /me:

curl
curl https://api.atolla.ca/partner/v1/me \
  -H "Authorization: Bearer atolla_pk_YOUR_SECRET_HERE"

Missing, malformed, expired or revoked keys all return a single generic401 Unauthorized — we never reveal which.

Conventions

Content type

Send JSON bodies as application/json. The candidate endpoint also accepts multipart/form-data when you attach a résumé file.

Idempotency

Add an Idempotency-Key header (any unique string) to a POST. A retry with the same key returns the original result instead of creating a duplicate.

Rate limits

Each key has a per-minute limit (default 60). Exceeding it returns429 with a Retry-After header.

Errors

Errors use application/problem+json (RFC 7807): a status, a title, and a human-readable detail.

Example error (400)
{
  "type": "https://atolla/problems/400",
  "title": "Bad Request",
  "status": 400,
  "detail": "invalid input: invalid experience_level"
}

GET /me

GET/partner/v1/mescope: any

Returns the calling key's capabilities. Use it to verify a credential.

Response 200
{
  "name": "Acme ATS integration",
  "scopes": ["candidate:create", "job:create"],
  "candidate_mode": "profile_only",
  "employer_org_id": "1f5856d6-…",
  "rate_limit_per_min": 60,
  "expires_at": null
}

Create a candidate

POST/partner/v1/candidatesscope: candidate:create

Imports a candidate into the talent pool bound to your key. In profile_only mode (the default) it creates a searchable profile with no login account, and — once created — it is immediately findable in employer search.

You must provide at least one of full_name,email or title. Keys set to account_invite mode return 501 until that mode is enabled for your deployment.
Have only a résumé and no structured fields? Use POST /candidates/from-resume instead — Atolla parses the file with the configured résumé parser and creates the candidate for you in one call.
Body fields
FieldTypeRequiredDescription
full_namestringone ofCandidate's full name.
emailstringone ofContact email. Stored on the profile.
titlestringone ofProfessional title / role. Falls back to headline, then a default.
headlinestringoptionalShort professional headline.
summarystringoptionalProfile summary / about.
phonestringoptionalContact phone.
locationstringoptionalCity / region text.
resume_urlstringoptionalURL to an already-hosted résumé (alternative to uploading a file).
skillsarrayoptionalSkills — each item is a string, or an object { name, isCustom }.
experiencearrayoptionalWork history — see experience object below.
educationarrayoptionalEducation — see education object below.
certificationsarrayoptionalCertifications — see certification object below.
experience[]
FieldTypeRequired
companystringoptional
titlestringoptional
locationstringoptional
startDatestringoptional
endDatestringoptional
descriptionstringoptional
isCurrentbooleanoptional
  • companyEmployer name.
  • titleRole title.
  • locationWhere the role was based.
  • startDateStart date (e.g. 2020-01).
  • endDateEnd date; omit / empty when current.
  • descriptionFree-text description.
  • isCurrentTrue if this is the current role.
education[]
FieldTypeRequired
institutionstringoptional
degreestringoptional
fieldstringoptional
startDatestringoptional
endDatestringoptional
  • institutionSchool / university.
  • degreeDegree earned.
  • fieldField of study.
  • startDateStart date.
  • endDateEnd date.
certifications[]
FieldTypeRequired
namestringoptional
issuerstringoptional
issueDatestringoptional
expiryDatestringoptional
credentialUrlstringoptional
  • nameCertification name.
  • issuerIssuing body.
  • issueDateIssue date.
  • expiryDateExpiry date, if any.
  • credentialUrlLink to verify the credential.

Example — JSON

curl
curl -X POST https://api.atolla.ca/partner/v1/candidates \
  -H "Authorization: Bearer atolla_pk_YOUR_SECRET_HERE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cand-2024-0001" \
  -d '{
    "full_name": "Asha Rao",
    "email": "asha.rao@example.com",
    "title": "Senior Data Engineer",
    "headline": "Data platforms at scale",
    "summary": "10 years building data pipelines.",
    "location": "Hyderabad",
    "skills": ["Python", "Spark", { "name": "Airflow", "isCustom": true }],
    "experience": [
      { "company": "BigData Inc", "title": "Data Engineer", "startDate": "2018-01", "isCurrent": true }
    ],
    "education": [
      { "institution": "IIT", "degree": "B.Tech", "field": "Computer Science" }
    ]
  }'

Example — with a résumé file (multipart)

Send the structured data as a payload form field (JSON string) and attach the file as resume. Files are validated (PDF / DOC / DOCX, ≤ 10 MB) and stored securely.

curl
curl -X POST https://api.atolla.ca/partner/v1/candidates \
  -H "Authorization: Bearer atolla_pk_YOUR_SECRET_HERE" \
  -F 'payload={"full_name":"Ravi K","title":"Backend Engineer","skills":["Go"]}' \
  -F "resume=@/path/to/ravi_cv.pdf;type=application/pdf"
Response 201
{
  "candidate_id": "0e1cf1c2-…",
  "profile_id": "cc82070a-…",
  "is_active": true,
  "resume_url": "/…/resumes/…/cv.pdf"
}

Create a candidate from a résumé

POST/partner/v1/candidates/from-resumescope: candidate:create

Upload a résumé file and nothing else. Atolla runs it through the configured résumé parser, extracts the fields (name, title, contact, skills, experience, education), creates the candidate from them, and returns the created candidate alongside what was extracted. No JSON body is needed — the résumé is the whole request.

On a partial parse the candidate is still created from whatever was found, and the response carries parse_status: "incomplete" with a reason. If the parser fails or can't read the file, nothing is created and the error explains why.
Request
FieldTypeRequiredDescription
resumefilerequiredThe résumé — multipart form field 'resume' (PDF / DOC / DOCX, ≤ 10 MB). Alternatively send the file as the raw request body with its Content-Type.

Example

curl
curl -X POST https://api.atolla.ca/partner/v1/candidates/from-resume \
  -H "Authorization: Bearer atolla_pk_YOUR_SECRET_HERE" \
  -H "Idempotency-Key: cand-resume-0001" \
  -F "resume=@/path/to/candidate_cv.pdf;type=application/pdf"
Response 201
{
  "candidate_id": "0e1cf1c2-…",
  "profile_id": "16f96617-…",
  "is_active": true,
  "resume_url": "/…/resumes/…/candidate_cv.pdf",
  "parse_status": "completed",
  "parse_reason": "",
  "extracted": {
    "full_name": "Ravi Kumar",
    "title": "Senior Backend Engineer",
    "email": "ravi.kumar@example.com",
    "phone": "+91…",
    "location": "Bangalore, IND",
    "skills": 7,
    "experience": 2,
    "education": 1
  }
}
Failure (422 — parser couldn't read it)
{
  "type": "https://api.atolla.io/problems/422",
  "title": "Parse Failed",
  "status": 422,
  "detail": "the parser could not read this résumé"
}

Create a job

POST/partner/v1/jobsscope: job:create

Creates a job under the single employer your key is bound to. Only title anddescription are required; everything else is optional. The job is created as a draft and counts against the employer's plan — if their live-job limit is reached you get a403.

Body fields
FieldTypeRequiredDescription
titlestringrequiredJob title.
descriptionstringrequiredFull job description (free text / HTML-free).
locationstringoptionalDisplay location text.
location_typeenumoptionalremote · hybrid · onsite
experience_levelenumoptionalinternship · entry · associate · mid_senior · director · executive
employment_typeenumoptionalfull_time · part_time · contract · temporary · volunteer · internship · other
comp_min / comp_maxnumberoptionalCompensation range.
comp_currencystringoptionalISO currency, e.g. INR, USD.
salary_periodenumoptionalannual · monthly · hourly
regionenumoptionalus · ca · in · sg (defaults to the employer's region).
work_modeenumoptionalonsite · hybrid · remote · remote_first · field
company_typeenumoptionalstartup · smb · enterprise · agency · non_profit · government
education_minenumoptionalnone · high_school · associate · bachelor · master · phd
industrystringoptionalPrimary industry name.
role_categorystringoptionalFree-text role category.
hiring_urgencyenumoptionalasap · 30_days · 60_days · flexible
skillsarrayoptionalJob skills — each { skill_name, required } (see below).
industry_idsstring[]optionalUp to 3 industry taxonomy ids.
job_function_idsstring[]optionalUp to 3 job-function taxonomy ids.
country_code / state_code / city / postal_codestringoptionalStructured location (country_code is ISO-2).
work_auth_requiredstringoptionalWork-authorization note.
duration_min_months / duration_max_monthsnumberoptionalFor contracts / internships.
stipend_min / stipend_max / stipend_currency / stipend_periodmixedoptionalInternship / stipend details.
skills[]
FieldTypeRequired
skill_namestringrequired
skill_idstringoptional
requiredbooleanoptional
  • skill_nameSkill name (e.g. "Go").
  • skill_idAtolla skill id, if known.
  • requiredTrue = required skill, false = preferred.

Example

curl
curl -X POST https://api.atolla.ca/partner/v1/jobs \
  -H "Authorization: Bearer atolla_pk_YOUR_SECRET_HERE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: job-2024-0001" \
  -d '{
    "title": "Senior Go Engineer",
    "description": "Build backend services in Go. Postgres, gRPC.",
    "location": "Bangalore",
    "location_type": "hybrid",
    "employment_type": "full_time",
    "experience_level": "mid_senior",
    "comp_min": 2500000,
    "comp_max": 4000000,
    "comp_currency": "INR",
    "skills": [
      { "skill_name": "Go", "required": true },
      { "skill_name": "PostgreSQL", "required": true }
    ]
  }'
Response 201
{
  "job": { "id": "1c022c5c-…", "status": "draft", "title": "Senior Go Engineer", … },
  "version": { "version": 1, "posted_by_role": "employer", … }
}

Error reference

CodeHTTPWhen
invalid_key / expired / revoked401The key is missing, malformed, past its expiry, or revoked.
ip_blocked401The request came from an IP not on the key's allow-list.
scope_denied403The key lacks the scope for this endpoint.
job_limit_reached403The bound employer's plan live-job limit is reached.
validation400A field failed validation (bad enum value, missing required field, etc.).
mode_unavailable501The key's candidate mode (account_invite) is not enabled.
rate_limited429Per-key rate limit exceeded — retry after the window.
Need a key or a higher rate limit? Contact your Atolla account manager — keys are issued from the Atolla admin console with the exact scopes you need.