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.
https://api.atolla.ca/partner/v1http://localhost:8087/partner/v1Overview
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:
Import a candidate profile (with skills, experience, education and an optional résumé) into your searchable talent pool.
Post a job under a specific employer. It appears in that employer's dashboard and counts against their plan.
job: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:
- Scopes —
candidate:createand/orjob:create. - Employer — for
job:create, every job the key creates is posted under this one employer. - Candidate mode —
profile_onlyimports a searchable profile (no login account). - Expiry, rate limit, allowed IPs — optional guards on the key.
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: Bearer atolla_pk_YOUR_SECRET_HEREQuick check that your key works — call GET /me:
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
Send JSON bodies as application/json. The candidate endpoint also accepts multipart/form-data when you attach a résumé file.
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.
Each key has a per-minute limit (default 60). Exceeding it returns429 with a Retry-After header.
Errors use application/problem+json (RFC 7807): a status, a title, and a human-readable detail.
{
"type": "https://atolla/problems/400",
"title": "Bad Request",
"status": 400,
"detail": "invalid input: invalid experience_level"
}GET /me
/partner/v1/mescope: anyReturns the calling key's capabilities. Use it to verify a credential.
{
"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
/partner/v1/candidatesscope: candidate:createImports 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.
full_name,email or title. Keys set to account_invite mode return 501 until that mode is enabled for your deployment.| Field | Type | Required | Description |
|---|---|---|---|
| full_name | string | one of | Candidate's full name. |
| string | one of | Contact email. Stored on the profile. | |
| title | string | one of | Professional title / role. Falls back to headline, then a default. |
| headline | string | optional | Short professional headline. |
| summary | string | optional | Profile summary / about. |
| phone | string | optional | Contact phone. |
| location | string | optional | City / region text. |
| resume_url | string | optional | URL to an already-hosted résumé (alternative to uploading a file). |
| skills | array | optional | Skills — each item is a string, or an object { name, isCustom }. |
| experience | array | optional | Work history — see experience object below. |
| education | array | optional | Education — see education object below. |
| certifications | array | optional | Certifications — see certification object below. |
| Field | Type | Required |
|---|---|---|
| company | string | optional |
| title | string | optional |
| location | string | optional |
| startDate | string | optional |
| endDate | string | optional |
| description | string | optional |
| isCurrent | boolean | optional |
- company — Employer name.
- title — Role title.
- location — Where the role was based.
- startDate — Start date (e.g. 2020-01).
- endDate — End date; omit / empty when current.
- description — Free-text description.
- isCurrent — True if this is the current role.
| Field | Type | Required |
|---|---|---|
| institution | string | optional |
| degree | string | optional |
| field | string | optional |
| startDate | string | optional |
| endDate | string | optional |
- institution — School / university.
- degree — Degree earned.
- field — Field of study.
- startDate — Start date.
- endDate — End date.
| Field | Type | Required |
|---|---|---|
| name | string | optional |
| issuer | string | optional |
| issueDate | string | optional |
| expiryDate | string | optional |
| credentialUrl | string | optional |
- name — Certification name.
- issuer — Issuing body.
- issueDate — Issue date.
- expiryDate — Expiry date, if any.
- credentialUrl — Link to verify the credential.
Example — JSON
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 -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"{
"candidate_id": "0e1cf1c2-…",
"profile_id": "cc82070a-…",
"is_active": true,
"resume_url": "/…/resumes/…/cv.pdf"
}Create a candidate from a résumé
/partner/v1/candidates/from-resumescope: candidate:createUpload 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.
parse_status: "incomplete" with a reason. If the parser fails or can't read the file, nothing is created and the error explains why.| Field | Type | Required | Description |
|---|---|---|---|
| resume | file | required | The 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 -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"{
"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
}
}{
"type": "https://api.atolla.io/problems/422",
"title": "Parse Failed",
"status": 422,
"detail": "the parser could not read this résumé"
}Create a job
/partner/v1/jobsscope: job:createCreates 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.
| Field | Type | Required | Description |
|---|---|---|---|
| title | string | required | Job title. |
| description | string | required | Full job description (free text / HTML-free). |
| location | string | optional | Display location text. |
| location_type | enum | optional | remote · hybrid · onsite |
| experience_level | enum | optional | internship · entry · associate · mid_senior · director · executive |
| employment_type | enum | optional | full_time · part_time · contract · temporary · volunteer · internship · other |
| comp_min / comp_max | number | optional | Compensation range. |
| comp_currency | string | optional | ISO currency, e.g. INR, USD. |
| salary_period | enum | optional | annual · monthly · hourly |
| region | enum | optional | us · ca · in · sg (defaults to the employer's region). |
| work_mode | enum | optional | onsite · hybrid · remote · remote_first · field |
| company_type | enum | optional | startup · smb · enterprise · agency · non_profit · government |
| education_min | enum | optional | none · high_school · associate · bachelor · master · phd |
| industry | string | optional | Primary industry name. |
| role_category | string | optional | Free-text role category. |
| hiring_urgency | enum | optional | asap · 30_days · 60_days · flexible |
| skills | array | optional | Job skills — each { skill_name, required } (see below). |
| industry_ids | string[] | optional | Up to 3 industry taxonomy ids. |
| job_function_ids | string[] | optional | Up to 3 job-function taxonomy ids. |
| country_code / state_code / city / postal_code | string | optional | Structured location (country_code is ISO-2). |
| work_auth_required | string | optional | Work-authorization note. |
| duration_min_months / duration_max_months | number | optional | For contracts / internships. |
| stipend_min / stipend_max / stipend_currency / stipend_period | mixed | optional | Internship / stipend details. |
| Field | Type | Required |
|---|---|---|
| skill_name | string | required |
| skill_id | string | optional |
| required | boolean | optional |
- skill_name — Skill name (e.g. "Go").
- skill_id — Atolla skill id, if known.
- required — True = required skill, false = preferred.
Example
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 }
]
}'{
"job": { "id": "1c022c5c-…", "status": "draft", "title": "Senior Go Engineer", … },
"version": { "version": 1, "posted_by_role": "employer", … }
}Error reference
| Code | HTTP | When |
|---|---|---|
| invalid_key / expired / revoked | 401 | The key is missing, malformed, past its expiry, or revoked. |
| ip_blocked | 401 | The request came from an IP not on the key's allow-list. |
| scope_denied | 403 | The key lacks the scope for this endpoint. |
| job_limit_reached | 403 | The bound employer's plan live-job limit is reached. |
| validation | 400 | A field failed validation (bad enum value, missing required field, etc.). |
| mode_unavailable | 501 | The key's candidate mode (account_invite) is not enabled. |
| rate_limited | 429 | Per-key rate limit exceeded — retry after the window. |