dropbox icon
List icon
ForEach icon
If icon
Download icon
Script icon
Move icon
SlackIncomingWebhook icon
Schedule icon

HR resume intake and archival with Dropbox

Automate HR resume intake on Dropbox with Kestra: list incoming PDF and DOCX resumes, parse each candidate file, then archive into a dated folder with retries and Slack alerts.

Categories
Business

Automate the first mile of recruiting operations with a Dropbox resume intake pipeline. Every weekday morning this flow scans a Dropbox intake folder for new candidate resumes, downloads and parses each PDF or DOCX, then moves the processed file into a dated archive sub-folder so the intake folder always reflects work that still needs a recruiter's attention. It solves the problem of resumes piling up in a shared Dropbox folder with no record of what has been reviewed, parsed, or pushed into your applicant tracking system.

How it works

  1. A io.kestra.plugin.core.trigger.Schedule trigger fires at 07:00 Europe/Paris on weekdays (cron: 0 7 * * 1-5).
  2. list_resumes (io.kestra.plugin.dropbox.files.List) lists the intake folder with fetchType: FETCH. It returns every entry and the flow filters by extension rather than a name query, because real resumes are named Jane_Doe_CV.pdf or lebenslauf.docx and a keyword search would miss them.
  3. process_each_resume (io.kestra.plugin.core.flow.ForEach) loops over the entries with a concurrencyLimit of 2.
  4. process_file (io.kestra.plugin.core.flow.If) keeps only file entries whose extension is pdf or docx, skipping folders and other file types.
  5. download_resume (io.kestra.plugin.dropbox.files.Download) pulls the file into Kestra internal storage.
  6. parse_resume (io.kestra.plugin.scripts.python.Script) runs a python:3.13-slim container with pdfminer.six to extract candidate fields and emit candidate_file and size_bytes outputs.
  7. archive_resume (io.kestra.plugin.dropbox.files.Move) moves the file into {{ inputs.archive_folder }}/<run-date>/, where the date comes from trigger.date ?? execution.startDate so backfills file correctly.

What you get

  • A hands-off morning sweep of new resumes without manual folder triage.
  • A clean intake folder that only holds candidates still needing attention.
  • A dated archive so you can trace exactly when each resume was processed.
  • Per-task retries on every Dropbox call and a Slack alert when a run fails.

Who it's for

  • HR and talent acquisition teams collecting resumes in Dropbox.
  • Recruiting operations engineers wiring Dropbox into an ATS or data pipeline.
  • Small teams that need reliable intake automation without a custom service.

Why orchestrate this with Kestra

Dropbox can store and share files, but it cannot react to new uploads with conditional logic, retries, and parsing in one place. Kestra adds a declarative YAML pipeline where the Schedule trigger (or a file-event trigger) drives the run, every Dropbox task carries a retry block to survive rate limits, the errors block sends a Slack alert with the execution id, and concurrencyLimit stops a slow run from overlapping the next schedule. Every execution is captured with full lineage and outputs, so you can see which files were listed, parsed, and archived. This is the gap Dropbox's own sharing and notification features cannot fill.

Prerequisites

  • A Kestra instance with the Dropbox, Slack, and Python script plugins installed.
  • A Dropbox app with an access token that can list, download, and move files.
  • A Docker-compatible task runner for the Python parsing step.
  • An intake folder and a parent archive folder in Dropbox.

Secrets

  • DROPBOX_ACCESS_TOKEN: Dropbox API access token used by every Dropbox task. Grant the minimum scopes (files.metadata.read, files.content.read, files.content.write) and scope the app to the intake and archive folders.
  • SLACK_WEBHOOK: Slack incoming webhook URL used by the failure alert.

Quick start

  1. Add the DROPBOX_ACCESS_TOKEN and SLACK_WEBHOOK secrets to your namespace.
  2. Set the incoming_folder and archive_folder inputs to match your Dropbox layout.
  3. Replace the stub in parse_resume with your real parser (for example an ATS push or an LLM extraction step). Resume content is PII; handle downstream storage and access accordingly.
  4. Enable the flow and let the schedule pick up new resumes.

How to extend

  • Swap the Schedule trigger for a polling or webhook trigger to process resumes as they land.
  • Push parsed fields into an ATS, Google Sheets, or a database task after parse_resume.
  • Add a deduplication or virus-scan step before archiving.
  • Branch on candidate role or location to route resumes to different folders.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.