Skip to main content
API Reference

CVReader API

Parse CVs, match candidates to jobs, and generate branded PDFs — all via REST endpoints authenticated with your ApiKey.

Base URL: https://cvreaderpro.com
Auth: Authorization: ApiKey <key>

CVReaderPro API Documentation

Automate CV analysis, matching and generation for efficient, GDPR-compliant recruitment.

V2 public API/api/v2

New integrations should use the API-key authenticated V2 resource endpoints. Legacy paths below remain available for existing clients.

Parser: POST /api/v2/parser · Jobs: /api/v2/jobs · Webhooks: /api/v2/webhooks · PDF generations: /api/v2/pdf-generations

V2 endpoint reference

Every request uses Authorization: ApiKey YOUR_API_KEY. The success contract is listed first in green; validation and authorization failures use application/problem+json.

EndpointRequestSuccess responseValidation / event
POST /api/v2/parsermultipart: file, parserType?200 resumeNon-empty supported file
POST /api/v2/parser/jobsmultipart: file, parserType?202 parser jobAsync compatibility route
POST /api/v2/resume-parsingsJSON: text, parserType?201 + LocationNon-empty text
POST /api/v2/resumesJSON: resume201 + LocationStructured resume
GET /api/v2/resumes/{resumeId}Path: resumeId200 resumeOwned resource
PUT /api/v2/resumes/{resumeId}JSON: resume200 resumeOwned resource
DELETE /api/v2/resumes/{resumeId}Path: resumeId204Owned resource
GET /api/v2/jobsQuery: page, size 1–100200 typed pageAPI-key owner
POST /api/v2/job-parsingsFile or JSON: text, title?201 + LocationContent-Type selects parser
POST /api/v2/jobsJSON: job201 + LocationRequired job fields
GET /api/v2/jobs/{jobId}Path: jobId200 jobOwned resource
PUT /api/v2/jobs/{jobId}JSON: job200 jobOwned resource
DELETE /api/v2/jobs/{jobId}Path: jobId204Owned resource
POST /api/v2/matchesJSON IDs or two files200 scoreBoth resources/files required
GET /api/v2/resumes/{resumeId}/matchesPath: resumeId200 ordered scoresOwned resume and jobs
GET /api/v2/jobs/{jobReference}/matchesPath: jobReference200 scoresOwned job reference
POST /api/v2/resumes/{resumeId}/matchesJSON: jobReference200 scoreCompatibility route
POST /api/v2/matching-jobsJSON: jobReferences202 matching queueOwned job references
POST /api/v2/resumes/{resumeId}/job-rankingsJSON: limit 1–100202 matching runresume.matched webhook
POST /api/v2/jobs/{jobId}/resume-rankingsJSON: limit 1–100202 matching runresume.matched webhook
GET /api/v2/matching-runs/{runId}Path: runId200 status/resultsOwned matching run
GET/PUT /api/v2/webhooksJSON: url, events200 typed settingsSecret shown once on creation
PUT /api/v2/webhooks/secretNo body200 one-time secretExisting webhook
PUT /api/v2/resume-settingsJSON: validated settings200 typed settingsAPI-key owner
POST /api/v2/pdf-generationsJSON: 1–10 unique resume IDs200 resultsOwned resumes
POST /api/v2/pdf-generations/asyncJSON: 1–100 unique resume IDs202 generation jobpdf.generated webhook
GET /api/v2/pdf-generations/{jobId}Path: jobId200 status/resultsOwned generation job

Quick start: parse a CV with V2

These examples upload a CV file as multipart/form-data. Replace the API key and file path before use.

javascript
const formData = new FormData();
formData.append('file', fileInput.files[0]);

const response = await fetch('https://cvreaderpro.com/api/v2/parser', {
  method: 'POST',
  headers: { Authorization: 'ApiKey YOUR_API_KEY' },
  body: formData,
});

if (!response.ok) throw new Error(await response.text());
const resume = await response.json();
python
import requests

with open('/path/to/cv.pdf', 'rb') as cv_file:
    response = requests.post(
        'https://cvreaderpro.com/api/v2/parser',
        headers={'Authorization': 'ApiKey YOUR_API_KEY'},
        files={'file': cv_file},
    )

response.raise_for_status()
resume = response.json()

SuccessCommon success envelope example

json
{
  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  "status": "COMPLETED"
}

RFC 9457 error response

json
{
  "type": "about:blank",
  "title": "Validation failed",
  "status": 400,
  "detail": "One or more fields are invalid"
}

Key Features

FeatureDescriptionDelivery Method
CV ParsingExtract structured data (skills, experience, education, etc.) from a CV.Synchronous (API Response)
CV / Job MatchingCompare CVs with your job postings to find the best candidates.API or Webhook
Resume GenerationGenerate standard or anonymized CVs using your company template.Webhook or Email

Webhooks

Register one HTTPS endpoint and subscribe it to any supported event. The endpoint uses one signing secret for every delivery.

Configure this endpoint from Settings → Webhooks. The configuration API uses the authenticated recruiter session, while event-producing API operations continue to use your API key.

Read the registration

GEThttps://cvreaderpro.com/api/webhookRecruiter session
json
{
  "webhookUrl": "https://example.com/webhooks/cvreader",
  "events": [
    "resume.parsed",
    "resume.matched",
    "pdf.generated"
  ],
  "signatureSecret": "<base64-secret>",
  "signatureHeader": "X-CVReader-Signature",
  "signatureTimestampHeader": "X-CVReader-Timestamp",
  "signatureAlgorithm": "HMAC-SHA256",
  "signaturePayload": "<timestamp>.<raw-json-body>"
}

Create or update the registration

POSThttps://cvreaderpro.com/api/webhookRecruiter session
json
{
  "webhookUrl": "https://example.com/webhooks/cvreader",
  "events": [
    "resume.parsed",
    "resume.matched",
    "pdf.generated"
  ]
}

The events array is a subscription set: include one, several, or all supported events. Sending an empty array pauses new deliveries without deleting the endpoint.

Rotate the signing secret

POSThttps://cvreaderpro.com/api/webhook/rotate-secretRecruiter session

Rotation affects newly created deliveries. Keep the previous secret available until older pending deliveries finish, because each delivery retains the secret captured when its event was created.

Configuration API errors

StatusDescription
400Invalid or unsafe webhook URL when creating or updating the registration
401Missing or invalid recruiter session
404No webhook registration exists when rotating the signing secret

Delivery headers

http
Content-Type: application/json
X-CVReader-Event: resume.parsed | resume.matched | pdf.generated
X-CVReader-Delivery-Id: <uuid>
X-CVReader-Timestamp: <unix-seconds>
X-CVReader-Signature: v1=<hmac-sha256>

Verify the signature against the exact raw request body using<timestamp>.<raw-json-body>and return any 2xx response to acknowledge delivery. Use the delivery ID for idempotency because retries can deliver the same event more than once.

javascript
import crypto from "node:crypto";

const signedPayload = `${timestamp}.${rawBody}`;
const expected = "v1=" + crypto
  .createHmac("sha256", Buffer.from(secret, "base64"))
  .update(signedPayload, "utf8")
  .digest("hex");

const received = Buffer.from(receivedSignature);
const calculated = Buffer.from(expected);
const valid = received.length === calculated.length
  && crypto.timingSafeEqual(received, calculated);

Payloads and retries

  • resume.parsed sends the asynchronous parser result as its raw JSON body.
  • resume.matched and pdf.generated use the existing delivery envelope with a public event name, delivery ID, creation time, and payload.
  • Failed deliveries retry after approximately 1 minute, 5 minutes, 30 minutes, 2 hours, and 24 hours.
  • Every retry reuses the same delivery ID, endpoint URL, signing secret, and exact raw JSON body.
  • After the final failed attempt, the delivery is retained as failed and is no longer counted as pending.

V1 — Legacy API

The V1 routes below are historical and remain available for existing integrations. New integrations should use V2.

1. CV Parsing

Extract structured data from CV files (PDF, DOCX, etc.).

POSThttps://cvreaderpro.com/api/v1/parserApiKey

Headers

http
Authorization: ApiKey YOUR_API_KEY
Accept: application/json  (default) | application/xml

Body (multipart/form-data)

  • file: CV file (PDF, DOCX, TXT, etc.)
  • parserType: "ADVANCED" (56 fields) or "PREMIUM" (96 fields, recommended)

cURL Example

bash
curl -X POST https://cvreaderpro.com/api/v1/parser \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -F "file=@/path/to/cv.pdf" \
  -F "parserType=PREMIUM"

SuccessResponse (excerpt)

json
{
  "resumeId": "c8cd44a0-d765-4d47-ba04-67903ef650e5",
  "documentType": "CV",
  "documentLanguage": "FR",
  "candidate": { "firstName": "Jean", "lastName": "Dupont" },
  "experiences": [],
  "educations": [],
  "skills": [],
  ...
}

Error responses

StatusDescription
400Empty, unreadable, or unsupported file input
401Invalid or missing ApiKey
402Insufficient parsing credits
403The API-key owner is not allowed to parse files
422The uploaded document could not be parsed as a resume

Updating and deleting a parsed CV

PUThttps://cvreaderpro.com/api/v1/parser/{resumeId}ApiKey

Updates an existing CV's data.

Path parameters

FieldTypeDescription
resumeId*UUIDID of a resume owned by the API-key user.

Request body

FieldTypeDescription
candidateobjectCandidate identity and contact fields.
profilestringProfessional summary.
experienceLevelstringOverall experience level.
experiencesarrayWork experience entries.
educationLevelstringOverall education level.
educationsarrayEducation entries.
languagesarrayLanguage name and level entries.
skillsarrayTechnical skill entries.
softSkillsstring[]Soft skills.
referencesarrayProfessional references.
hobbiesstringInterests or hobbies.
json
{
  "candidate": {
    "firstName": "Jean",
    "lastName": "Dupont",
    "jobTitle": "Senior Java Developer"
  },
  "profile": "Backend engineer specialising in Spring services.",
  "experienceLevel": "Senior",
  "experiences": [],
  "educations": [],
  "languages": [],
  "skills": [],
  "softSkills": ["Communication"],
  "references": [],
  "hobbies": "Cycling"
}

Returns 200 OK with the submitted update payload.

StatusDescription
400Invalid update payload
401Invalid or missing ApiKey
403Resume does not belong to the API-key owner
404Resume not found

Delete a parsed resume

DELETEhttps://cvreaderpro.com/api/v1/parser/{resumeId}ApiKey

Deletes a parsed CV.

FieldTypeDescription
resumeId*UUIDID of the owned resume to delete.

Returns 204 No Content after successful deletion.

StatusDescription
401Invalid or missing ApiKey
404Resume not found or not owned by the API-key user

Async Parser

Submit a CV for asynchronous processing. Results are sent to your webhook.

POSThttps://cvreaderpro.com/api/v1/parser/asyncApiKey

PrerequisiteConfigure one webhook endpoint subscribed to resume.parsed in Settings → Webhooks.

Example

bash
curl -X POST https://cvreaderpro.com/api/v1/parser/async \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -F "file=@cv.pdf" \
  -F "parserType=PREMIUM"

SuccessImmediate response:

json
{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08" }

A successful request returns 202 Accepted. Processing continues in the background and the result is delivered to the subscribed webhook.

StatusDescription
400Empty file or no webhook subscribed to resume.parsed
401Invalid or missing ApiKey
402Insufficient parsing credits
403The API-key owner is not allowed to parse files

Webhook (client implementation)

Your endpoint must receive a POST request with the result. Verify the HMAC-SHA256 signature with your secret.

http
POST /your-configured-webhook
X-CVReader-Event: resume.parsed
X-CVReader-Delivery-Id: <uuid>
X-CVReader-Timestamp: <unix-seconds>
X-CVReader-Signature: v1=<hmac-sha256>

Body: { "id": "...", "status": "SUCCESS", "resumeData": { ... } }

2. Jobs API

Provide an API that returns your job listings in JobItem format.

GEThttps://cvreaderpro.com/api/jobsPublic

(implemented by client)

Must return an array of objects:

json
[
  {
    "title": "Senior Software Engineer",
    "reference": "JOB-2023-456",
    "description": "...",
    "company": "TechCorp",
    "country": "FR",
    "city": "Paris"
  }
]

Register your endpoint URL in CVReaderPro (Settings → API Integrations).

POST

Parse job from file

POSThttps://cvreaderpro.com/api/job/parse/api-keyApiKey

Uploads a job description file (PDF, DOCX, etc.) and returns a structured Job object. The file is parsed server-side and the result is persisted and linked to the authenticated user.

Headers

http
Authorization: ApiKey YOUR_API_KEY
Content-Type: multipart/form-data

Request body multipart/form-data

FieldTypeDescription
file*FileThe job description file to parse (PDF, DOCX, TXT…)

Request example

curl -X POST "https://cvreaderpro.com/api/job/parse/api-key" \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -F "file=@/path/to/job_description.pdf"

SuccessResponse 200 OK

FieldTypeDescription
id*UUIDUnique job identifier
titlestringParsed job title
descriptionstringFull job description text
profilestringRequired candidate profile
experiencestringRequired experience level
educationstringRequired education level
skillsstring (JSON)JSON-encoded array of required skills
companystringCompany name (if detected)
countrystringCountry code (e.g. "FR")
citystringCity name (if detected)
json
{
  "id": "d4c7a1e2-0f3b-4c8d-9e1f-000000000001",
  "title": "Senior Software Engineer",
  "description": "We are looking for...",
  "profile": "Experienced developer with leadership skills",
  "experience": "5+ years",
  "education": "Bachelor's degree",
  "skills": "[\"Java\",\"Spring Boot\",\"Docker\"]",
  "company": "TechCorp",
  "country": "FR",
  "city": "Paris"
}

Error responses

StatusDescription
401Invalid or missing ApiKey
400No file provided or unsupported file format
500Failed to parse job file
POST

Parse job from text

POSThttps://cvreaderpro.com/api/job/parse/text/api-keyApiKey

Parses a raw job description string and returns a structured DTO without creating a file upload. The body can be a plain JSON string, a quoted string, or a JSON object with a description, job_description, or text field — the API extracts the value automatically.

Headers

http
Authorization: ApiKey YOUR_API_KEY
Content-Type: application/json

Request body application/json

Send the job description as a JSON string, a raw string, or an object with a recognized field:

json
// Option A — plain JSON string
"We are looking for a Senior React Developer with 5+ years..."

// Option B — object with recognized field
{ "description": "We are looking for a Senior React Developer..." }

Request example

curl -X POST "https://cvreaderpro.com/api/job/parse/text/api-key" \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '"We are looking for a Senior React Developer with 5+ years of experience..."'

SuccessResponse 200 OK

FieldTypeDescription
jobTitlestringParsed job title
jobDescriptionstringFull job description
profileRecherchestringRequired candidate profile
experienceRequiredstringRequired experience level
educationLevelstringRequired education level
skillsstring[]Array of required skills
json
{
  "jobTitle": "Senior React Developer",
  "jobDescription": "We are looking for a Senior React Developer...",
  "profileRecherche": "Experienced frontend engineer",
  "experienceRequired": "5+ years",
  "educationLevel": "Bachelor's degree",
  "skills": ["React", "TypeScript", "Node.js", "GraphQL"]
}

Error responses

StatusDescription
400Job description is required (empty or missing body)
401Invalid or missing ApiKey
500Failed to parse job description text
POST

Create a job

POSThttps://cvreaderpro.com/api/jobs/api-keyApiKey

Creates a new job record linked to the API key owner. The payload shape is the same as the authenticated frontend endpoint. Use this to push jobs from your own ATS or data pipeline.

Request body application/json

FieldTypeDescription
title*stringJob title
referencestringInternal job reference code (e.g. "JOB-2026-001")
descriptionstringFull job description text
profilestringRequired candidate profile narrative
experiencestringRequired experience level (e.g. "5+ years")
educationstringRequired education level (e.g. "Bachelor's degree")
skillsstring (JSON)JSON-encoded array of required skills
companystringCompany name
countrystringCountry code (e.g. "FR")
citystringCity name
statusstring"ACTIVE" | "CLOSED" | "DRAFT"

Request example

curl -X POST "https://cvreaderpro.com/api/jobs/api-key" \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Senior Java Developer",
    "reference": "JOB-2026-001",
    "description": "We are looking for a Senior Java Developer...",
    "profile": "Experienced backend engineer",
    "experience": "5+ years",
    "education": "Bachelor'"'"'s degree",
    "skills": "[\"Java\",\"Spring Boot\",\"Docker\"]",
    "company": "TechCorp",
    "country": "FR",
    "city": "Paris",
    "status": "ACTIVE"
  }'

SuccessResponse 200 OK

Returns the created Job object (same shape as the parse file response).

json
{
  "id": "d4c7a1e2-0f3b-4c8d-9e1f-000000000001",
  "title": "Senior Java Developer",
  "reference": "JOB-2026-001",
  "status": "ACTIVE",
  "company": "TechCorp",
  "country": "FR",
  "city": "Paris"
}

Error responses

StatusDescription
400Invalid or missing required fields
401Invalid or missing ApiKey
GET

List my jobs

GEThttps://cvreaderpro.com/api/jobs/api-key/myApiKey

Returns a paginated list of jobs belonging to the API key owner. Supports the same filtering options as the authenticated frontend list endpoint.

Query parameters

FieldTypeDescription
pageintegerZero-based page index (default: 0)
sizeintegerItems per page (default: 10)
statusstringFilter by job status: "ACTIVE" | "CLOSED" | "DRAFT"
sourcestringFilter by source (e.g. "api_key", "manual")
referencestringFilter by reference code (partial match)
titlestringFilter by title (partial match)
postedFromISO date-timeFilter jobs posted on or after this date
postedToISO date-timeFilter jobs posted on or before this date

Request example

# All active jobs, page 0, 10 per page
curl "https://cvreaderpro.com/api/jobs/api-key/my?page=0&size=10&status=ACTIVE" \
  -H "Authorization: ApiKey YOUR_API_KEY"

SuccessResponse 200 OK

Paginated response — standard Spring Page structure:

json
{
  "content": [
    { "id": "d4c7a1e2-...", "title": "Senior Java Developer", "status": "ACTIVE", ... }
  ],
  "totalElements": 42,
  "totalPages": 5,
  "number": 0,
  "size": 10
}

Error responses

StatusDescription
401Invalid or missing ApiKey
GET

Get one job

GEThttps://cvreaderpro.com/api/jobs/api-key/my/{jobId}ApiKey

Retrieves a single job by ID. Ownership is enforced — only jobs belonging to the authenticated API key owner are returned.

Path parameters

FieldTypeDescription
jobId*UUIDUUID of the job to retrieve.

Request example

JOB_ID="d4c7a1e2-0f3b-4c8d-9e1f-000000000001"
curl "https://cvreaderpro.com/api/jobs/api-key/my/${JOB_ID}" \
  -H "Authorization: ApiKey YOUR_API_KEY"

SuccessResponse 200 OK

Returns the full Job object.

Error responses

StatusDescription
401Invalid or missing ApiKey
403Job does not belong to the API key owner
404Job not found
PUT

Update a job

PUThttps://cvreaderpro.com/api/jobs/api-key/my/{jobId}ApiKey

Replaces an existing job's data. Send the complete job representation, including every required field and every optional value you want to preserve. The payload shape is the same as POST /api/jobs/api-key. Ownership is enforced.

Path parameters

FieldTypeDescription
jobId*UUIDUUID of the job to update.

Request body application/json

FieldTypeDescription
title*stringJob title
referencestringInternal job reference code (e.g. "JOB-2026-001")
descriptionstringFull job description text
profilestringRequired candidate profile narrative
experiencestringRequired experience level (e.g. "5+ years")
educationstringRequired education level (e.g. "Bachelor's degree")
skillsstring (JSON)JSON-encoded array of required skills
companystringCompany name
countrystringCountry code (e.g. "FR")
citystringCity name
statusstring"ACTIVE" | "CLOSED" | "DRAFT"

Request example

JOB_ID="d4c7a1e2-0f3b-4c8d-9e1f-000000000001"
curl -X PUT "https://cvreaderpro.com/api/jobs/api-key/my/${JOB_ID}" \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Lead Java Developer",
    "status": "ACTIVE",
    "city": "Lyon"
  }'

SuccessResponse 200 OK

Returns the updated Job object.

Error responses

StatusDescription
400Invalid payload
401Invalid or missing ApiKey
403Job does not belong to the API key owner
404Job not found

3. Scoring API (Matching)

Get scores for a CV against jobs

GEThttps://cvreaderpro.com/api/scores/jobs-for-resume/{resumeId}ApiKey

Returns a list of scores for a given CV.

FieldTypeDescription
resumeId*UUIDAn owned resume/candidate ID.
json
[
  {
    "jobReference": "JOB-2026-001",
    "candidateId": "e918ab2f-d2f8-4890-b2d6-b049a76207a9",
    "distance": 0.1121,
    "totalScore": 88.79,
    "profileScore": 91.5,
    "experienceScore": 85.0,
    "educationScore": 90.0,
    "languageScore": 82.0
  }
]

Returns [] when the owned resume has no stored job scores. Score fields are numeric matching components; higher values indicate a stronger match, while distance is the underlying vector distance.

StatusDescription
401Invalid or missing ApiKey
404Resume not found or does not belong to the API key owner

Get CVs matching a job

GEThttps://cvreaderpro.com/api/scores/resumes-for-job/{jobReference}ApiKey
FieldTypeDescription
jobReference*stringReference of an owned job.

Returns the same score objects shown above, or [] when the API key owner has no stored resume scores for the reference.

StatusDescription
401Invalid or missing ApiKey

Direct match (job vs CV)

POSThttps://cvreaderpro.com/api/scores/match-job-resume/{resumeId}ApiKey

JSON body with job details.

FieldTypeDescription
resumeId*UUIDAn owned resume/candidate ID.

Request body

FieldTypeDescription
jobTitle*stringJob title used for occupation and title matching.
jobDescription*stringFull job text used for semantic matching.
candidateProfilestringOptional target profile context.
requiredExperiencestringOptional experience requirement.
requiredEducationLevelstringOptional education requirement.
skillsstring[]Optional required skills.
json
{
  "jobTitle": "Senior Java Developer",
  "jobDescription": "Build and maintain Spring services...",
  "candidateProfile": "Backend engineer",
  "requiredExperience": "5 years",
  "requiredEducationLevel": "Bachelor",
  "skills": ["Java", "Spring", "SQL"]
}

SuccessResponse 200 OK

json
{ "score": 88.79 }
StatusDescription
400Missing job title or job description, or unusable matching input
401Invalid or missing ApiKey
404Resume not found or does not belong to the API key owner

Async queue

POSThttps://cvreaderpro.com/api/jobs/queueApiKey

Submit a list of job references for processing. Results sent via webhook.

Request body

FieldTypeDescription
jobReferences*string[]One or more job references owned by the API-key user.
json
["offre-123", "offre-456"]

SuccessResponse 200 OK

json
{
  "message": "Resume matching queue created successfully",
  "queue_id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  "job_count": 2,
  "job_references": ["offre-123", "offre-456"]
}
StatusDescription
400No job references provided, or one or more references are missing or not owned
401Invalid or missing ApiKey

4. Resume Generation (standard or anonymized)

Generate CVs in PDF format with optional anonymization.

Authentication required

All endpoints below require an Authorization: ApiKey <YOUR_API_KEY> header. Missing or invalid keys return 401 Invalid Api Key.

Base path/api/resume-settings
PUT

Update settings + regenerate canvas

PUThttps://cvreaderpro.com/api/resume-settings/api-key/update-with-canvasApiKey

Replaces the authenticated user's resume settings and immediately regenerates canvas preview images. Returns the updated settings payload including refreshed canvas pages — ideal for updating the preview UI instantly after a settings change.

Request body application/json

FieldTypeDescription
layoutstringTemplate layout name (e.g. "modern", "classic")
fontStylestringFont family (e.g. "Calibri", "Arial")
fontSizenumberBase font size in pt (e.g. 11)
lineHeightnumberLine height multiplier (e.g. 1.35)
marginTopnumberTop page margin in mm
marginBottomnumberBottom page margin in mm
marginXnumberLeft/right page margin in mm
sectionSpacingnumberVertical spacing between sections in pt
agencyNamestringBranding: recruiter agency name (optional)
agencyWebsitestringBranding: recruiter agency URL (optional)

Request example

curl -X PUT "https://cvreaderpro.com/api/resume-settings/api-key/update-with-canvas" \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "layout": "modern",
    "fontStyle": "Calibri",
    "fontSize": 11,
    "lineHeight": 1.35,
    "marginTop": 20,
    "marginBottom": 18,
    "marginX": 18,
    "sectionSpacing": 10,
    "agencyName": "Acme Recruiting",
    "agencyWebsite": "https://acme.example"
  }'

SuccessResponse 200 OK

Returns a ResumeSettingsResponseDto JSON object:

FieldTypeDescription
id*UUIDUnique settings record ID
layout*stringActive layout name
settings*objectFull settings object (font, margins, spacing, branding)
privacy*objectPrivacy/anonymization configuration
canvas_page_count*numberNumber of regenerated canvas preview pages
canvas_pages*arrayArray of { index: number, url: string } — public preview image URLs per page

Response example

json
{
  "id": "a3f4e2b1-0c91-4d2e-bc57-7f3a6e9d0012",
  "layout": "modern",
  "settings": {
    "fontStyle": "Calibri",
    "fontSize": 11,
    "lineHeight": 1.35,
    "marginTop": 20,
    "marginBottom": 18,
    "marginX": 18,
    "sectionSpacing": 10,
    "agencyName": "Acme Recruiting",
    "agencyWebsite": "https://acme.example"
  },
  "privacy": {
    "hidePhoto": false,
    "hideEmail": false,
    "hidePhone": false,
    "hideName": false
  },
  "canvas_page_count": 2,
  "canvas_pages": [
    { "index": 0, "url": "<returned-canvas-url>" },
    { "index": 1, "url": "<returned-canvas-url>" }
  ]
}

Error responses

StatusDescription
400Invalid settings payload or unsupported settings value
401Invalid or missing ApiKey
POST

Instant PDF generation (1–10)

POSThttps://cvreaderpro.com/api/resume-settings/v1/generate-pdfApiKey

Synchronously generates up to 10 PDFs in a single blocking request. All PDFs are ready when the response arrives — download URLs are included directly in the body. For larger batches (1–500) use the async webhook endpoint below.

Limit: Maximum 10 resume IDs per request. For larger batches configure a webhook and use POST /api/webhook/generate-pdfs.

Request body application/json

FieldTypeDescription
resumeIds*UUID[]List of 1–10 resume UUIDs to generate PDFs for synchronously.

Request example

curl -X POST "https://cvreaderpro.com/api/resume-settings/v1/generate-pdf" \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '["9f6ad665-25ce-4107-8ec5-b2a2fba572cc", "a1b2c3d4-5678-90ab-cdef-000000000001"]'

SuccessResponse 200 OK

FieldTypeDescription
jobId*UUIDInternal queue ID for this batch.
status*string"COMPLETED" | "PARTIAL" | "FAILED"
progress*stringPercentage of successfully processed PDFs (e.g. "100%").
results*arrayPer-resume result objects — each contains resumeId, downloadUrl, generatedAt, or an error field.
json
{
  "jobId": "b72e9a01-3f4c-4e12-9a1d-000000000001",
  "status": "COMPLETED",
  "progress": "100%",
  "results": [
    {
      "resumeId": "9f6ad665-25ce-4107-8ec5-b2a2fba572cc",
      "downloadUrl": "<returned-download-url>",
      "generatedAt": "2026-05-14T10:00:00Z"
    }
  ]
}

Error responses

StatusDescription
400No resume IDs provided, or more than 10 IDs in a single request
401Invalid or missing ApiKey
403One or more resume IDs do not belong to the current user
404One or more resume IDs do not exist
429Webhook delivery queue full, or another PDF request is already queued
POST

Bulk async PDF generation (1–500)

POSThttps://cvreaderpro.com/api/webhook/generate-pdfsApiKey

Enqueues a large PDF batch for asynchronous processing. Returns immediately with a 202 Accepted response containing a job ID and status URL. Results are pushed to your configured webhook URL once processing completes. Requires a webhook subscribed to pdf.generated.

Prerequisite: Configure a webhook URL subscribed to pdf.generated via POST /api/webhook. Without this, the endpoint returns 400.

Request body application/json

FieldTypeDescription
resumeIds*UUID[]List of 1–500 resume UUIDs. All must belong to the authenticated user.

Request example

curl -X POST "https://cvreaderpro.com/api/webhook/generate-pdfs" \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '["<uuid-1>", "<uuid-2>", ..., "<uuid-50>"]'

SuccessResponse 202 Accepted

FieldTypeDescription
jobId*UUIDUnique identifier for the async generation job. Use with the status endpoint.
status*string"QUEUED" — processing has started asynchronously.
statusUrl*stringPolling URL: GET /api/webhook/generate-pdfs/{jobId}
message*stringHuman-readable confirmation.
webhookSecurity*objectHMAC-SHA256 signing metadata: algorithm, signatureHeader, timestampHeader, signedPayload format.
json
{
  "jobId": "c8a12f44-0000-0000-0000-000000000002",
  "status": "QUEUED",
  "statusUrl": "/api/webhook/generate-pdfs/c8a12f44-0000-0000-0000-000000000002",
  "message": "Processing started. Results will be sent to your webhook URL.",
  "webhookSecurity": {
    "algorithm": "HMAC-SHA256",
    "signatureHeader": "X-CVReader-Signature",
    "timestampHeader": "X-CVReader-Timestamp",
    "signedPayload": "<timestamp>.<raw-json-body>"
  }
}

Error responses

StatusDescription
400Resume ID list is empty, below minimum (1), above maximum (500), or webhook is not subscribed to pdf.generated
401Invalid or missing ApiKey
403One or more resume IDs do not belong to the current user
429Too many pending webhook deliveries
GET

PDF generation job status

GEThttps://cvreaderpro.com/api/webhook/generate-pdfs/{jobId}ApiKey

Polls the status of an asynchronous PDF generation job. Returns per-resume processing results and webhook delivery attempts. Poll periodically until status is "COMPLETED".

Path parameters

FieldTypeDescription
jobId*UUIDThe job ID returned by POST /api/webhook/generate-pdfs.

Request example

JOB_ID="c8a12f44-0000-0000-0000-000000000002"
curl "https://cvreaderpro.com/api/webhook/generate-pdfs/${JOB_ID}" \
  -H "Authorization: ApiKey YOUR_API_KEY"

SuccessResponse 200 OK

FieldTypeDescription
jobId*UUIDJob identifier.
status*string"QUEUED" | "PROCESSING" | "COMPLETED"
total*numberTotal number of resume items in the batch.
processed*numberNumber of items already processed.
results*arrayPer-resume: resumeId, processed (bool), downloadUrl, generatedAt.
deliveries*arrayWebhook delivery attempts with status, attemptCount, responseStatusCode, errorMessage, and retry timing.
json
{
  "jobId": "c8a12f44-0000-0000-0000-000000000002",
  "status": "PROCESSING",
  "total": 25,
  "processed": 12,
  "results": [
    { "resumeId": "9f6ad665-...", "processed": true,  "downloadUrl": "<returned-download-url>", "generatedAt": "2026-05-14T10:02:00Z" },
    { "resumeId": "a1b2c3d4-...", "processed": false, "downloadUrl": null, "generatedAt": null }
  ],
  "deliveries": [
    {
      "deliveryId": "d1e2f3a4-...",
      "type": "PDF_GENERATED",
      "delivered": false,
      "attemptCount": 1,
      "lastAttempt": "2026-05-14T10:01:00Z",
      "nextAttempt": "2026-05-14T10:06:00Z",
      "responseStatusCode": 500,
      "responseBody": "Internal Server Error",
      "errorMessage": "Connection refused"
    }
  ]
}

Error responses

StatusDescription
401Invalid or missing ApiKey
403This job does not belong to the current user
404PDF generation job not found

Use Cases

1. Automate candidate pre-screening

Parse CVs → get resumeIds → send to /api/jobs/queue → receive scores via webhook.

2. Fair recruitment with anonymized CVs

Configure anonymization in settings, then generate CVs without personal data.

Error Handling

CodeCauseSolution
400Malformed requestCheck the request body format.
401Invalid API keyCheck the Authorization header.
404Resource not foundCheck resumeId/jobId.
429Too many requestsReduce frequency.

Best Practices

  • Test in the sandbox environment before production.
  • Systematically anonymize CVs for sensitive roles.
  • Monitor your webhooks/emails for async results.
  • Archive original CVs securely (GDPR compliant).

Need help? Contact us