CVReaderPro API Documentation
Automate CV analysis, matching and generation for efficient, GDPR-compliant recruitment.
/api/v2New 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.
| Endpoint | Request | Success response | Validation / event |
|---|---|---|---|
| POST /api/v2/parser | multipart: file, parserType? | 200 resume | Non-empty supported file |
| POST /api/v2/parser/jobs | multipart: file, parserType? | 202 parser job | Async compatibility route |
| POST /api/v2/resume-parsings | JSON: text, parserType? | 201 + Location | Non-empty text |
| POST /api/v2/resumes | JSON: resume | 201 + Location | Structured resume |
| GET /api/v2/resumes/{resumeId} | Path: resumeId | 200 resume | Owned resource |
| PUT /api/v2/resumes/{resumeId} | JSON: resume | 200 resume | Owned resource |
| DELETE /api/v2/resumes/{resumeId} | Path: resumeId | 204 | Owned resource |
| GET /api/v2/jobs | Query: page, size 1–100 | 200 typed page | API-key owner |
| POST /api/v2/job-parsings | File or JSON: text, title? | 201 + Location | Content-Type selects parser |
| POST /api/v2/jobs | JSON: job | 201 + Location | Required job fields |
| GET /api/v2/jobs/{jobId} | Path: jobId | 200 job | Owned resource |
| PUT /api/v2/jobs/{jobId} | JSON: job | 200 job | Owned resource |
| DELETE /api/v2/jobs/{jobId} | Path: jobId | 204 | Owned resource |
| POST /api/v2/matches | JSON IDs or two files | 200 score | Both resources/files required |
| GET /api/v2/resumes/{resumeId}/matches | Path: resumeId | 200 ordered scores | Owned resume and jobs |
| GET /api/v2/jobs/{jobReference}/matches | Path: jobReference | 200 scores | Owned job reference |
| POST /api/v2/resumes/{resumeId}/matches | JSON: jobReference | 200 score | Compatibility route |
| POST /api/v2/matching-jobs | JSON: jobReferences | 202 matching queue | Owned job references |
| POST /api/v2/resumes/{resumeId}/job-rankings | JSON: limit 1–100 | 202 matching run | resume.matched webhook |
| POST /api/v2/jobs/{jobId}/resume-rankings | JSON: limit 1–100 | 202 matching run | resume.matched webhook |
| GET /api/v2/matching-runs/{runId} | Path: runId | 200 status/results | Owned matching run |
| GET/PUT /api/v2/webhooks | JSON: url, events | 200 typed settings | Secret shown once on creation |
| PUT /api/v2/webhooks/secret | No body | 200 one-time secret | Existing webhook |
| PUT /api/v2/resume-settings | JSON: validated settings | 200 typed settings | API-key owner |
| POST /api/v2/pdf-generations | JSON: 1–10 unique resume IDs | 200 results | Owned resumes |
| POST /api/v2/pdf-generations/async | JSON: 1–100 unique resume IDs | 202 generation job | pdf.generated webhook |
| GET /api/v2/pdf-generations/{jobId} | Path: jobId | 200 status/results | Owned 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.
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();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
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"status": "COMPLETED"
}RFC 9457 error response
{
"type": "about:blank",
"title": "Validation failed",
"status": 400,
"detail": "One or more fields are invalid"
}Key Features
| Feature | Description | Delivery Method |
|---|---|---|
| CV Parsing | Extract structured data (skills, experience, education, etc.) from a CV. | Synchronous (API Response) |
| CV / Job Matching | Compare CVs with your job postings to find the best candidates. | API or Webhook |
| Resume Generation | Generate 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
https://cvreaderpro.com/api/webhookRecruiter session{
"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
https://cvreaderpro.com/api/webhookRecruiter session{
"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
https://cvreaderpro.com/api/webhook/rotate-secretRecruiter sessionRotation 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
| Status | Description |
|---|---|
| 400 | Invalid or unsafe webhook URL when creating or updating the registration |
| 401 | Missing or invalid recruiter session |
| 404 | No webhook registration exists when rotating the signing secret |
Delivery headers
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.
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.parsedsends the asynchronous parser result as its raw JSON body.resume.matchedandpdf.generateduse 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.).
https://cvreaderpro.com/api/v1/parserApiKeyHeaders
Authorization: ApiKey YOUR_API_KEY
Accept: application/json (default) | application/xmlBody (multipart/form-data)
file: CV file (PDF, DOCX, TXT, etc.)parserType: "ADVANCED" (56 fields) or "PREMIUM" (96 fields, recommended)
cURL Example
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)
{
"resumeId": "c8cd44a0-d765-4d47-ba04-67903ef650e5",
"documentType": "CV",
"documentLanguage": "FR",
"candidate": { "firstName": "Jean", "lastName": "Dupont" },
"experiences": [],
"educations": [],
"skills": [],
...
}Error responses
| Status | Description |
|---|---|
| 400 | Empty, unreadable, or unsupported file input |
| 401 | Invalid or missing ApiKey |
| 402 | Insufficient parsing credits |
| 403 | The API-key owner is not allowed to parse files |
| 422 | The uploaded document could not be parsed as a resume |
Updating and deleting a parsed CV
https://cvreaderpro.com/api/v1/parser/{resumeId}ApiKeyUpdates an existing CV's data.
Path parameters
| Field | Type | Description |
|---|---|---|
| resumeId* | UUID | ID of a resume owned by the API-key user. |
Request body
| Field | Type | Description |
|---|---|---|
| candidate | object | Candidate identity and contact fields. |
| profile | string | Professional summary. |
| experienceLevel | string | Overall experience level. |
| experiences | array | Work experience entries. |
| educationLevel | string | Overall education level. |
| educations | array | Education entries. |
| languages | array | Language name and level entries. |
| skills | array | Technical skill entries. |
| softSkills | string[] | Soft skills. |
| references | array | Professional references. |
| hobbies | string | Interests or hobbies. |
{
"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.
| Status | Description |
|---|---|
| 400 | Invalid update payload |
| 401 | Invalid or missing ApiKey |
| 403 | Resume does not belong to the API-key owner |
| 404 | Resume not found |
Delete a parsed resume
https://cvreaderpro.com/api/v1/parser/{resumeId}ApiKeyDeletes a parsed CV.
| Field | Type | Description |
|---|---|---|
| resumeId* | UUID | ID of the owned resume to delete. |
Returns 204 No Content after successful deletion.
| Status | Description |
|---|---|
| 401 | Invalid or missing ApiKey |
| 404 | Resume not found or not owned by the API-key user |
Async Parser
Submit a CV for asynchronous processing. Results are sent to your webhook.
https://cvreaderpro.com/api/v1/parser/asyncApiKeyPrerequisiteConfigure one webhook endpoint subscribed to resume.parsed in Settings → Webhooks.
Example
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:
{ "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.
| Status | Description |
|---|---|
| 400 | Empty file or no webhook subscribed to resume.parsed |
| 401 | Invalid or missing ApiKey |
| 402 | Insufficient parsing credits |
| 403 | The 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.
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.
https://cvreaderpro.com/api/jobsPublic(implemented by client)
Must return an array of objects:
[
{
"title": "Senior Software Engineer",
"reference": "JOB-2023-456",
"description": "...",
"company": "TechCorp",
"country": "FR",
"city": "Paris"
}
]Register your endpoint URL in CVReaderPro (Settings → API Integrations).
Parse job from file
https://cvreaderpro.com/api/job/parse/api-keyApiKeyUploads 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
Authorization: ApiKey YOUR_API_KEY
Content-Type: multipart/form-dataRequest body multipart/form-data
| Field | Type | Description |
|---|---|---|
| file* | File | The 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
| Field | Type | Description |
|---|---|---|
| id* | UUID | Unique job identifier |
| title | string | Parsed job title |
| description | string | Full job description text |
| profile | string | Required candidate profile |
| experience | string | Required experience level |
| education | string | Required education level |
| skills | string (JSON) | JSON-encoded array of required skills |
| company | string | Company name (if detected) |
| country | string | Country code (e.g. "FR") |
| city | string | City name (if detected) |
{
"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
| Status | Description |
|---|---|
| 401 | Invalid or missing ApiKey |
| 400 | No file provided or unsupported file format |
| 500 | Failed to parse job file |
Parse job from text
https://cvreaderpro.com/api/job/parse/text/api-keyApiKeyParses 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
Authorization: ApiKey YOUR_API_KEY
Content-Type: application/jsonRequest body application/json
Send the job description as a JSON string, a raw string, or an object with a recognized field:
// 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
| Field | Type | Description |
|---|---|---|
| jobTitle | string | Parsed job title |
| jobDescription | string | Full job description |
| profileRecherche | string | Required candidate profile |
| experienceRequired | string | Required experience level |
| educationLevel | string | Required education level |
| skills | string[] | Array of required skills |
{
"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
| Status | Description |
|---|---|
| 400 | Job description is required (empty or missing body) |
| 401 | Invalid or missing ApiKey |
| 500 | Failed to parse job description text |
Create a job
https://cvreaderpro.com/api/jobs/api-keyApiKeyCreates 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
| Field | Type | Description |
|---|---|---|
| title* | string | Job title |
| reference | string | Internal job reference code (e.g. "JOB-2026-001") |
| description | string | Full job description text |
| profile | string | Required candidate profile narrative |
| experience | string | Required experience level (e.g. "5+ years") |
| education | string | Required education level (e.g. "Bachelor's degree") |
| skills | string (JSON) | JSON-encoded array of required skills |
| company | string | Company name |
| country | string | Country code (e.g. "FR") |
| city | string | City name |
| status | string | "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).
{
"id": "d4c7a1e2-0f3b-4c8d-9e1f-000000000001",
"title": "Senior Java Developer",
"reference": "JOB-2026-001",
"status": "ACTIVE",
"company": "TechCorp",
"country": "FR",
"city": "Paris"
}Error responses
| Status | Description |
|---|---|
| 400 | Invalid or missing required fields |
| 401 | Invalid or missing ApiKey |
List my jobs
https://cvreaderpro.com/api/jobs/api-key/myApiKeyReturns a paginated list of jobs belonging to the API key owner. Supports the same filtering options as the authenticated frontend list endpoint.
Query parameters
| Field | Type | Description |
|---|---|---|
| page | integer | Zero-based page index (default: 0) |
| size | integer | Items per page (default: 10) |
| status | string | Filter by job status: "ACTIVE" | "CLOSED" | "DRAFT" |
| source | string | Filter by source (e.g. "api_key", "manual") |
| reference | string | Filter by reference code (partial match) |
| title | string | Filter by title (partial match) |
| postedFrom | ISO date-time | Filter jobs posted on or after this date |
| postedTo | ISO date-time | Filter 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:
{
"content": [
{ "id": "d4c7a1e2-...", "title": "Senior Java Developer", "status": "ACTIVE", ... }
],
"totalElements": 42,
"totalPages": 5,
"number": 0,
"size": 10
}Error responses
| Status | Description |
|---|---|
| 401 | Invalid or missing ApiKey |
Get one job
https://cvreaderpro.com/api/jobs/api-key/my/{jobId}ApiKeyRetrieves a single job by ID. Ownership is enforced — only jobs belonging to the authenticated API key owner are returned.
Path parameters
| Field | Type | Description |
|---|---|---|
| jobId* | UUID | UUID 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
| Status | Description |
|---|---|
| 401 | Invalid or missing ApiKey |
| 403 | Job does not belong to the API key owner |
| 404 | Job not found |
Update a job
https://cvreaderpro.com/api/jobs/api-key/my/{jobId}ApiKeyReplaces 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
| Field | Type | Description |
|---|---|---|
| jobId* | UUID | UUID of the job to update. |
Request body application/json
| Field | Type | Description |
|---|---|---|
| title* | string | Job title |
| reference | string | Internal job reference code (e.g. "JOB-2026-001") |
| description | string | Full job description text |
| profile | string | Required candidate profile narrative |
| experience | string | Required experience level (e.g. "5+ years") |
| education | string | Required education level (e.g. "Bachelor's degree") |
| skills | string (JSON) | JSON-encoded array of required skills |
| company | string | Company name |
| country | string | Country code (e.g. "FR") |
| city | string | City name |
| status | string | "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
| Status | Description |
|---|---|
| 400 | Invalid payload |
| 401 | Invalid or missing ApiKey |
| 403 | Job does not belong to the API key owner |
| 404 | Job not found |
3. Scoring API (Matching)
Get scores for a CV against jobs
https://cvreaderpro.com/api/scores/jobs-for-resume/{resumeId}ApiKeyReturns a list of scores for a given CV.
| Field | Type | Description |
|---|---|---|
| resumeId* | UUID | An owned resume/candidate ID. |
[
{
"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.
| Status | Description |
|---|---|
| 401 | Invalid or missing ApiKey |
| 404 | Resume not found or does not belong to the API key owner |
Get CVs matching a job
https://cvreaderpro.com/api/scores/resumes-for-job/{jobReference}ApiKey| Field | Type | Description |
|---|---|---|
| jobReference* | string | Reference 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.
| Status | Description |
|---|---|
| 401 | Invalid or missing ApiKey |
Direct match (job vs CV)
https://cvreaderpro.com/api/scores/match-job-resume/{resumeId}ApiKeyJSON body with job details.
| Field | Type | Description |
|---|---|---|
| resumeId* | UUID | An owned resume/candidate ID. |
Request body
| Field | Type | Description |
|---|---|---|
| jobTitle* | string | Job title used for occupation and title matching. |
| jobDescription* | string | Full job text used for semantic matching. |
| candidateProfile | string | Optional target profile context. |
| requiredExperience | string | Optional experience requirement. |
| requiredEducationLevel | string | Optional education requirement. |
| skills | string[] | Optional required skills. |
{
"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
{ "score": 88.79 }| Status | Description |
|---|---|
| 400 | Missing job title or job description, or unusable matching input |
| 401 | Invalid or missing ApiKey |
| 404 | Resume not found or does not belong to the API key owner |
Async queue
https://cvreaderpro.com/api/jobs/queueApiKeySubmit a list of job references for processing. Results sent via webhook.
Request body
| Field | Type | Description |
|---|---|---|
| jobReferences* | string[] | One or more job references owned by the API-key user. |
["offre-123", "offre-456"]SuccessResponse 200 OK
{
"message": "Resume matching queue created successfully",
"queue_id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"job_count": 2,
"job_references": ["offre-123", "offre-456"]
}| Status | Description |
|---|---|
| 400 | No job references provided, or one or more references are missing or not owned |
| 401 | Invalid 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.
/api/resume-settingsUpdate settings + regenerate canvas
https://cvreaderpro.com/api/resume-settings/api-key/update-with-canvasApiKeyReplaces 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
| Field | Type | Description |
|---|---|---|
| layout | string | Template layout name (e.g. "modern", "classic") |
| fontStyle | string | Font family (e.g. "Calibri", "Arial") |
| fontSize | number | Base font size in pt (e.g. 11) |
| lineHeight | number | Line height multiplier (e.g. 1.35) |
| marginTop | number | Top page margin in mm |
| marginBottom | number | Bottom page margin in mm |
| marginX | number | Left/right page margin in mm |
| sectionSpacing | number | Vertical spacing between sections in pt |
| agencyName | string | Branding: recruiter agency name (optional) |
| agencyWebsite | string | Branding: 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:
| Field | Type | Description |
|---|---|---|
| id* | UUID | Unique settings record ID |
| layout* | string | Active layout name |
| settings* | object | Full settings object (font, margins, spacing, branding) |
| privacy* | object | Privacy/anonymization configuration |
| canvas_page_count* | number | Number of regenerated canvas preview pages |
| canvas_pages* | array | Array of { index: number, url: string } — public preview image URLs per page |
Response example
{
"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
| Status | Description |
|---|---|
| 400 | Invalid settings payload or unsupported settings value |
| 401 | Invalid or missing ApiKey |
Instant PDF generation (1–10)
https://cvreaderpro.com/api/resume-settings/v1/generate-pdfApiKeySynchronously 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.
10 resume IDs per request. For larger batches configure a webhook and use POST /api/webhook/generate-pdfs.Request body application/json
| Field | Type | Description |
|---|---|---|
| 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
| Field | Type | Description |
|---|---|---|
| jobId* | UUID | Internal queue ID for this batch. |
| status* | string | "COMPLETED" | "PARTIAL" | "FAILED" |
| progress* | string | Percentage of successfully processed PDFs (e.g. "100%"). |
| results* | array | Per-resume result objects — each contains resumeId, downloadUrl, generatedAt, or an error field. |
{
"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
| Status | Description |
|---|---|
| 400 | No resume IDs provided, or more than 10 IDs in a single request |
| 401 | Invalid or missing ApiKey |
| 403 | One or more resume IDs do not belong to the current user |
| 404 | One or more resume IDs do not exist |
| 429 | Webhook delivery queue full, or another PDF request is already queued |
Bulk async PDF generation (1–500)
https://cvreaderpro.com/api/webhook/generate-pdfsApiKeyEnqueues 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.
pdf.generated via POST /api/webhook. Without this, the endpoint returns 400.Request body application/json
| Field | Type | Description |
|---|---|---|
| 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
| Field | Type | Description |
|---|---|---|
| jobId* | UUID | Unique identifier for the async generation job. Use with the status endpoint. |
| status* | string | "QUEUED" — processing has started asynchronously. |
| statusUrl* | string | Polling URL: GET /api/webhook/generate-pdfs/{jobId} |
| message* | string | Human-readable confirmation. |
| webhookSecurity* | object | HMAC-SHA256 signing metadata: algorithm, signatureHeader, timestampHeader, signedPayload format. |
{
"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
| Status | Description |
|---|---|
| 400 | Resume ID list is empty, below minimum (1), above maximum (500), or webhook is not subscribed to pdf.generated |
| 401 | Invalid or missing ApiKey |
| 403 | One or more resume IDs do not belong to the current user |
| 429 | Too many pending webhook deliveries |
PDF generation job status
https://cvreaderpro.com/api/webhook/generate-pdfs/{jobId}ApiKeyPolls 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
| Field | Type | Description |
|---|---|---|
| jobId* | UUID | The 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
| Field | Type | Description |
|---|---|---|
| jobId* | UUID | Job identifier. |
| status* | string | "QUEUED" | "PROCESSING" | "COMPLETED" |
| total* | number | Total number of resume items in the batch. |
| processed* | number | Number of items already processed. |
| results* | array | Per-resume: resumeId, processed (bool), downloadUrl, generatedAt. |
| deliveries* | array | Webhook delivery attempts with status, attemptCount, responseStatusCode, errorMessage, and retry timing. |
{
"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
| Status | Description |
|---|---|
| 401 | Invalid or missing ApiKey |
| 403 | This job does not belong to the current user |
| 404 | PDF 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
| Code | Cause | Solution |
|---|---|---|
| 400 | Malformed request | Check the request body format. |
| 401 | Invalid API key | Check the Authorization header. |
| 404 | Resource not found | Check resumeId/jobId. |
| 429 | Too many requests | Reduce 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