Documentazione API CVReaderPro
Automatizzi l'analisi, il matching e la generazione dei CV per un reclutamento efficiente e conforme al GDPR.
/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 |
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"
}Funzionalità principali
| Funzionalità | Descrizione | Modalità di consegna |
|---|---|---|
| Parsing CV | Estragga dati strutturati (competenze, esperienza, formazione, ecc.) da un CV. | Sincrono (risposta API) |
| Matching CV / Offerta | Confronti i CV con le Sue offerte di lavoro per trovare i migliori candidati. | API o Webhook |
| Generazione curriculum | Generi CV standard o anonimizzati utilizzando il modello della Sua azienda. | Webhook o E-mail |
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
{{baseapiurl}}/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
{{baseapiurl}}/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
{{baseapiurl}}/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.
1. Parsing CV
Estragga dati strutturati da file CV (PDF, DOCX, ecc.).
{{baseapiurl}}/api/v1/parserApiKeyIntestazioni
Authorization: ApiKey YOUR_API_KEY
Accept: application/json (default) | application/xmlCorpo (multipart/form-data)
file: file CV (PDF, DOCX, TXT, ecc.)parserType: "ADVANCED" (56 campi) o "PREMIUM" (96 campi, consigliato)
Esempio cURL
curl -X POST {{baseapiurl}}/api/v1/parser \
-H "Authorization: ApiKey YOUR_API_KEY" \
-F "file=@/path/to/cv.pdf" \
-F "parserType=PREMIUM"SuccessRisposta (estratto)
{
"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 |
Aggiornamento ed eliminazione di un CV analizzato
{{baseapiurl}}/api/v1/parser/{resumeId}ApiKeyAggiorna i dati di un CV esistente.
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
{{baseapiurl}}/api/v1/parser/{resumeId}ApiKeyElimina un CV analizzato.
| 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 |
Parser asincrono
Invii un CV per l'elaborazione asincrona. I risultati vengono inviati al Suo webhook.
{{baseapiurl}}/api/v1/parser/asyncApiKeyPrerequisitoConfiguri un endpoint webhook iscritto a resume.parsed in Impostazioni → Webhook.
Esempio
curl -X POST {{baseapiurl}}/api/v1/parser/async \
-H "Authorization: ApiKey YOUR_API_KEY" \
-F "file=@cv.pdf" \
-F "parserType=PREMIUM"SuccessRisposta immediata:
{ "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 (implementazione client)
Il Suo endpoint deve ricevere una richiesta POST con il risultato. Verifichi la firma HMAC-SHA256 con la Sua chiave segreta.
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. API Offerte
Fornisca un'API che restituisca le Sue offerte di lavoro in formato JobItem.
{{baseapiurl}}/api/jobsPublic(implementato dal client)
Deve restituire un array di oggetti:
[
{
"title": "Senior Software Engineer",
"reference": "JOB-2023-456",
"description": "...",
"company": "TechCorp",
"country": "FR",
"city": "Paris"
}
]Registri l'URL del Suo endpoint in CVReaderPro (Impostazioni → Integrazioni API).
Parse job from file
{{baseapiurl}}/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 "{{baseapiurl}}/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
{{baseapiurl}}/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 "{{baseapiurl}}/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
{{baseapiurl}}/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 "{{baseapiurl}}/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
{{baseapiurl}}/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 "{{baseapiurl}}/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
{{baseapiurl}}/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 "{{baseapiurl}}/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
{{baseapiurl}}/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 "{{baseapiurl}}/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. API Scoring (Matching)
Ottieni i punteggi di un CV rispetto alle offerte
{{baseapiurl}}/api/scores/jobs-for-resume/{resumeId}ApiKeyRestituisce un elenco di punteggi per un dato 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 |
Ottieni i CV che corrispondono a un'offerta
{{baseapiurl}}/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 |
Corrispondenza diretta (offerta vs CV)
{{baseapiurl}}/api/scores/match-job-resume/{resumeId}ApiKeyCorpo JSON con i dettagli dell'offerta.
| 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 |
Coda asincrona
{{baseapiurl}}/api/jobs/queueApiKeyInvii un elenco di riferimenti di offerte per l'elaborazione. I risultati vengono inviati tramite 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. Generazione curriculum (standard o anonimizzato)
Generi CV in formato PDF con anonimizzazione opzionale.
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
{{baseapiurl}}/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 "{{baseapiurl}}/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": "https://cdn.cvreader.fr/canvas/page_0.png" },
{ "index": 1, "url": "https://cdn.cvreader.fr/canvas/page_1.png" }
]
}Error responses
| Status | Description |
|---|---|
| 400 | Invalid settings payload or unsupported settings value |
| 401 | Invalid or missing ApiKey |
Instant PDF generation (1–10)
{{baseapiurl}}/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 "{{baseapiurl}}/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": "https://cdn.cvreader.fr/pdfs/resume_9f6ad665.pdf",
"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)
{{baseapiurl}}/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 "{{baseapiurl}}/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
{{baseapiurl}}/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 "{{baseapiurl}}/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": "https://cdn.cvreader.fr/pdfs/...", "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 |
Casi d'uso
1. Automatizzi la pre-selezione dei candidati
Analizzi i CV → ottenga i resumeId → li invii a /api/jobs/queue → riceva i punteggi tramite webhook.
2. Reclutamento equo con CV anonimizzati
Configuri l'anonimizzazione nelle impostazioni, poi generi CV senza dati personali.
Gestione degli errori
| Codice | Causa | Soluzione |
|---|---|---|
| 400 | Richiesta malformata | Controlli il formato del corpo della richiesta. |
| 401 | Chiave API non valida | Controlli l'intestazione Authorization. |
| 404 | Risorsa non trovata | Controlli resumeId/jobId. |
| 429 | Troppe richieste | Riduca la frequenza. |
Buone pratiche
- Esegua test nell'ambiente sandbox prima della produzione.
- Anonimizzi sistematicamente i CV per i ruoli sensibili.
- Monitori i Suoi webhook/e-mail per i risultati asincroni.
- Archivi i CV originali in modo sicuro (conforme al GDPR).
Ha bisogno di aiuto? Contattaci