Bulk import
Import many candidates at once. The assistant offers two paths: a spreadsheet (CSV or Excel) where you map your columns, or a folder of CV files (PDF, Word) that Nextal parses for you.
Bulk Import is a separate page (not a dialog) reached from the Bulk Import button on the Candidates list. It walks you through a multi-step wizard. The first step asks you to choose the import type, which determines whether you also map columns or skip straight to configuring.
At a glance — the wizard steps
The wizard branches on the import type you pick at Step 1. Path A (spreadsheet) adds a mapping step in the middle; Path B (CVs) skips it because the resume parser extracts the fields. Both paths end in the same Configure + Import flow.
Step
Path A — Excel/CSV Data Import (4 steps)
Path B — CV Files Import (3 steps)
Pick import type
Two radio cards. Pick once — switching resets the upload.
1. Upload
One CSV / XLSX / XLS file. 10 MB max. Sample template available for download.
Multiple PDF / DOC / DOCX files at once. 10 MB per file. No limit on file count.
2. Map columns
One row per file header. Auto-matcher pre-fills First Name / Last Name / Email / 18 others. Required fields must be mapped before Next enables.
— skipped —
The resume parser extracts fields automatically.
3. Configure
Optional job assignment, optional tags, skip-duplicates checkbox (default ON). Data preview at the top.
Same three options. Three blue summary tiles at the top (files, candidates to create, total size) + first-10-files preview.
4. Import
One candidate is created per row, with a short pause between rows. Live progress + per-row counters.
One file is parsed at a time, with a short pause between files. Imported candidates are tagged “bulk import” as their source. Live progress + per-file counters.
The progress bar at the top of every step reflects this: it shows 4 nodes on Path A (Upload, Map Columns, Configure, Import) and 3 on Path B (Upload, Configure, Import). Completed steps are filled in; the current step is highlighted; upcoming steps are dimmed. You can click any already-completed step to jump back and adjust.
Opening the wizard
-
Go to the candidates list
Click Candidates in the main menu (URL
/candidates). -
Click Bulk Import
The button is on the left side of the toolbar (alongside Add Candidate and Export). Visible only with create-candidate permission. It navigates you to the Bulk Import page — a dedicated page, not a side panel.
Step 1 — Choose import type
The Upload step shows two clickable radio cards:
Excel/CSV Data Import
Spreadsheet path. You upload one file (CSV / XLSX / XLS), the wizard reads the columns, you map them to candidate fields, preview the parsed data, configure assignment options, then run the import. 4 steps total: Upload → Map Columns → Configure → Import.
CV Files Import
Resume-parser path. You upload many CV files at once (PDF/DOC/DOCX), each file is uploaded individually to the resume parser which creates one candidate per CV. No column mapping required. 3 steps total: Upload Files → Configure → Import.
Path A — Step 1: Upload your spreadsheet (data path)
Supported formats: CSV (.csv) and Excel (.xlsx, .xls). Excel files with macros or unusual structures are rejected for safety. For CSV files, Nextal assumes the first row is column headers and ignores empty lines.
File size limit: 10 MB. Bigger files are rejected at the drop zone with the message “File is too large. Maximum size is 10MB.”
File requirements (shown inline in the upload step):
- First row should contain column headers.
- Required columns: First Name, Last Name, Email.
- Optional columns: Phone, Address, Skills, etc.
- Empty rows will be automatically skipped.
Sample template: a downloadable starter file is available via the link “Download Sample Template” below the drop zone — it grabs /public/export/CandidateImportTemplate.csv.
What happens when you upload:
-
Drop or click
Drop a file onto the dashed zone, or click anywhere in the zone to open the file picker.
-
The file is parsed in your browser
Nextal reads the file locally — without sending it to the server yet — and extracts the column headers and the rows.
-
The wizard jumps to Step 2 (Map Columns)
You don’t click Next for this — successful parsing advances automatically.
Path A — Step 2: Map columns (data path only)
One row per file column. Each row has three pieces: the column name from your file, a dropdown of candidate fields, and a sample of the actual cell values from the first few rows so you can see whether the mapping is correct.
Auto-mapping rules
The wizard pre-fills the dropdown when it recognizes the header. The matcher is case-insensitive and language-aware:
If the header is…
It maps to…
first name, firstname, fname, prenom, prénom
First Name
last name, lastname, lname, surname, nom
Last Name
email, e-mail, mail, courriel
phone, telephone, tel, mobile, cell
Phone Number
address, adresse, location
Address
linkedin, facebook, twitter, github, website, site web
matching social field
description, bio, summary, about
Description
comment, commentaire, comentario, notes, note, remarks
Comment
salary, current salary, salaire
Current Salary
expected salary, requested salary
Requested Salary
experience, years experience, exp
Years of Experience
language, langue
Preferred Language
skills, technologies, competences, compétences
Skills/Technologies
tags
Tags
available, start date, disponible
Available Date
Headers the matcher does not recognize stay on — Do not map — (the first dropdown option). You pick the field manually.
The 21 candidate fields you can map to
Three are required (marked with * in the dropdown): First Name, Last Name, Email. If any of these isn’t mapped to a column, the Next button stays disabled with a red error banner at the top of the step listing each missing required field.
Field
Type
Required
How it’s parsed
First Name
text
yes
Trimmed, kept as string.
Last Name
text
yes
Trimmed.
yes
Lowercased + trimmed.
Phone Number
phone
no
All non-digit characters stripped. “(514) 555-1234” → “5145551234”.
Address
text
no
Trimmed string.
LinkedIn / Facebook / Twitter / GitHub / Website
text
no
Trimmed strings.
Description
text
no
Trimmed.
Comment
text
no
Trimmed. Special: after the candidate is created, the comment value is posted as a candidate comment, not a profile field.
Current Salary / Requested Salary
number
no
parseFloat(). Non-numbers become 0.
Years of Experience
number
no
parseFloat(). Non-numbers become 0.
Preferred Language
text
no
Trimmed string.
French Level (1–5) / English Level (1–5)
number
no
parseFloat(). Use 1–5 numbers.
Available Date
date
no
new Date(value). Use ISO format (YYYY-MM-DD) for safety.
Skills/Technologies (comma-separated)
array
no
Split on commas, each item trimmed, empties dropped. “react, typescript, node” → ["react","typescript","node"].
Tags (comma-separated)
array
no
Same parsing as Skills.
Validation rules in the mapping step
- Red error banner — appears if any required field is not mapped (“Required field ‘First Name’ is not mapped”) or if the same field is mapped to multiple columns (“Field ‘Email’ is mapped multiple times”). The Next button stays disabled.
- Yellow warning banner — lists unmapped columns: “N column(s) will be ignored: ColumnA, ColumnB”. This is informational, not blocking.
- Each field can only be mapped once. Once you map a column to Email, that field disappears from the other rows’ dropdowns until you free it up by mapping that column to something else or to — Do not map —.
Path A — Step 3: Configure (data path)
The Configure step has two sections:
Data preview
Shows the transformed rows (after applying your column mappings). Confirms that name/email/phone parsed cleanly. Compare the original vs the transformed values side-by-side before you commit.
Configuration options
Three options apply to every imported candidate:
- Automatically assign candidates to a job — checkbox. When ticked, an async job search appears below. Pick one job; a job application will be created for every candidate after the candidate itself is created. Defaults to off; ticking it automatically sets createJobApplications to true.
- Add tags to all imported candidates — multi-select with “create new” support. Tags you pick are prepended to whatever the row’s mapped Tags column already produces (the two lists are concatenated; no dedup).
- Skip duplicate candidates (based on email) — checkbox, ON by default. When on, duplicates (server returns 409 Conflict) are counted as successful and skipped silently. When off, duplicates are recorded as a row failure with the error “Candidate already exists”.
Path B — Step 1: Upload CV files (CV path)
Different drop zone, different accepted types:
Supported formats: PDF (.pdf), Word (.doc, .docx). Images and ODT are not accepted here — the bulk CV importer uses a stricter format set than the single Add Candidate dialog.
File size limit: 10 MB per file. You can upload many files but each one must stay under the limit. Oversized files trigger “One or more files are too large. Maximum size is 10MB per file.”
Multiple files at once: drop several or select several in the file picker — they all queue. Each newly dropped batch is appended to the existing list.
The selected files list
Below the drop zone, each queued file gets a row showing:
- An icon by type — red PDF icon for PDFs, blue Word icon for DOC/DOCX.
- The filename.
- The file size (KB/MB).
- A red × button to remove that file from the queue.
Above the list, a small total summary: “N files · X.YY MB”.
Path B — Step 2: Configure (CV path)
For CV imports, the Configure step opens with a CV Files Summary instead of a data preview:
- CV Files Selected — total count.
- Candidates to Create — same number (one candidate per CV).
- Total File Size — sum of all queued files, in MB.
Below the tiles, a scrollable preview of the first 10 files with their best-guess name extracted from the filename (separators like _ and - become spaces, first token = first name, rest = last name). If you have more than 10 files, the preview ends with ”… and N more files”.
The same three configuration options apply (Job assignment, Tags, Skip duplicates) — identical to the data path.
Final step — Import
Clicking Start Import on the Configure step takes you here. The Previous and Next buttons disappear — you can’t navigate away during the import.
What the progress shows
- Status icon + label — a spinner while running, a green check when done with no errors, a yellow triangle when done with errors.
- Progress bar — percentage of rows / files processed.
- Four counters — Total, Processed, Successful, Failed.
- Warnings count — non-blocking issues (e.g. the row imported but the comment failed to post).
The wizard imposes a 100 ms delay between rows (or files) to avoid overwhelming the API. So a 200-row import takes at least 20 seconds plus the actual creation time.
What happens per row (data path)
- The row is transformed using your column mappings.
- If you set tags on the Configure step, those are prepended to the row’s tags.
- Validation: First Name, Last Name AND Email must be present. Missing any of the three triggers “Missing required fields: firstName, lastName, or email” and the row goes to failed.
- The candidate is created (
POST createCandidate). - If a job was configured, a job application is created (
POST createJobApplication) for the new candidate. - If the mapped Comment column has a non-empty value, it’s posted as a candidate comment (
POST addCandidateComment). If the comment fails, it’s a warning, not a row failure — the candidate is still imported successfully.
What happens per file (CV path)
- The CV is uploaded to the resume parser, tagged with a “bulk import” source and the file name (accents stripped). Adding a single candidate uses the same parser, tagged as a manual import instead.
- If the parser recognizes the candidate as someone already on file, duplicate handling applies (see below).
- If new, configured tags are applied (
updateCandidateTagsById) and a job application is created if a job was selected.
Duplicate handling
On a 409 Conflict (data path) or newCandidate: false (CV path):
- Skip duplicates ON (default): the row counts as successful (the existing candidate already meets the goal) and the loop moves on silently. No error logged.
- Skip duplicates OFF: the row counts as failed with the error “Candidate already exists”. Use this when you want a strict reconciliation report of which rows were already in the system.
Downloading the error report
If at least one row failed, a Download Error Report button appears below the counters. It generates a CSV with one row per failed import and the columns: Row, Name, Email, Error, Field. The file is named import-errors-YYYY-MM-DD.csv. Fix the failed rows in Excel, save as a new CSV with only those rows, and re-run the import — the good rows you already imported will be detected as duplicates and skipped (if the toggle is on).
Retry
A Retry button lets you re-run the import with the same configuration. Useful if a transient API issue caused widespread failures.
Tips
- Pick the path that matches your source. If you have a spreadsheet of names and emails, data path. If you have a folder of PDFs, CV path. Don’t try to convert CVs to a spreadsheet — the CV parser captures dozens of fields the spreadsheet can’t.
- Test with 3-5 rows first. Trim the file, run an import, look at the resulting candidates. If the mapping was wrong, fixing 5 candidates is fast; fixing 500 is not.
- Use the Tags configuration to label the batch. Add a tag like “imported-2026-02-01” on the Configure step — afterward, the Tag filter on the candidates list lets you find this entire batch.
- Map a Comment column for sourcing context. If your spreadsheet has notes (“met at conf”, “warm intro from Marie”), map it to Comment — the value becomes a candidate comment after creation, preserving the context for whoever picks the candidate up next.
- Keep the Skip duplicates option on the first run. Default ON is safer — you don’t accidentally fail half the file because some candidates already exist.
- Big files? Expect waiting. 1000 rows = at least 100 seconds of throttle + the API time. Don’t close the tab; the import is client-side and stops if you leave the page.