Guida processo
Candidati pre-onboarding (HR)
Pipeline candidati prima dell'assunzione: screening, colloqui, offerta, conversione in utente del tenant.
Candidati pre-onboarding (HR)
Ultimo aggiornamento: 2026-05-25 Owner: Product Writer Audience: hr_admin, admin tenant, owner
Scopo
Spiegare come HR puo' gestire la pipeline di candidati prima dell'assunzione: sourcing, screening, colloqui, offerta, fino all'eventuale conversione in utente del tenant. I candidati sono profili dedicati, separati da auth.users (un candidato non e' ancora un membro dell'organizzazione).
Concetti chiave
- Candidato vs dipendente: il candidato e' un *prospect* — un profilo lavorativo che stai valutando. Non ha login, non vede dati del tenant, non occupa un posto utente della suite. Diventa dipendente solo quando viene convertito (status
hired+ link a un utente del tenant). - Scope HR only: tutta la sezione
Candidati pre-onboardinge' riservata aowner,admin tenant,hr_admin. Imanagernon hanno accesso (403 con reasoncandidates_require_hr_admin). - Workflow lineare:
Nuovo → Screening → Colloquio → Offerta → Assunto, con possibili usciteRifiutato,Ritirato,Archiviato. - CV dedicati: i CV dei candidati vivono in un bucket dedicato (
hr-candidate-cvs), separato da quello dei CV dipendenti (hr-cvs). Quando carichi una nuova versione, la precedente resta archiviata ma non e' piu' lacorrente. - Archive vs Elimina: due azioni distinte.
Archivee' uno stato di workflow (Archiviato, recuperabile cambiando lo status).Eliminasposta nel cestino (deleted_at, recuperabile entro 30 giorni dalla vista cestino). - Owner e tag: ogni candidato puo' avere un *owner HR* assegnato (chi lo segue) e una lista di *tag* (es. ruolo, fonte, focus) — utili per triage e filtri.
Prerequisiti
- ruolo:
owner,admin,hr_admin - migration
0077_hr_candidates.sql+0080_hr_candidates_soft_delete.sqlapplicate sul progetto Supabase del tenant - bucket
hr-candidate-cvscreato (creato dalla migration 0077)
Accesso rapido
- Pagina pipeline:
Workspace › HR › Gestione profili › 🆕 Candidati pre-onboarding - URL diretto:
/workspace/hr/candidates
Passi operativi
1. Crea un candidato
- Apri
/workspace/hr/candidates - Click sulla CTA
+ Nuovo candidatoin alto a destra - Compila il modal: solo
Nome e cognomee' obbligatorio. Email, telefono, headline, sorgente e note sono opzionali — li puoi completare durante lo screening. - Click
Crea candidato. Lo stato iniziale e'Nuovo.
2. Aggiorna il profilo del candidato
- Le modifiche vengono salvate automaticamente quando esci dal campo (blur)
3. Estrai i dati dal CV candidato
L'estrazione AI del CV e' on-demand: carichi il PDF/DOCX, poi clicchi un bottone per popolare il draft strutturato (esperienze, skill, lingue, anagrafica). E' un prerequisito per il trasferimento profilo durante la conversione (step 4).
- Dal dettaglio candidato, sezione
CV candidato, identifica la riga del CV caricato (colonnaStatomostraCaricato) - Click su
Estrai dati CV(visibile finche' lo stato non e'Dati estratti) - Il sistema scarica il file, lo parsa (incluso OCR fallback per PDF scansionati) e chiama Claude Haiku per estrarre il profilo strutturato. Lo stato passa a
Dati estratti. - Fail-soft: se il parse o la chiamata AI falliscono, il CV resta
Caricatoe l'estrazione e' best-effort — la conversione funziona comunque, ma trasferisce 0 esperienze/skill.
4. Converti il candidato in dipendente
Quando il candidato accetta l'offerta, lo converti in dipendente del tenant. La conversione e' riservata a owner, admin, hr_admin.
- Alla conferma il sistema esegue la conversione, marca il candidato come
Assuntoe mostra l'esito con il link al profilo dipendente.
Due modalita' di conversione:
- Crea nuovo utente (
new_user): crea un nuovo account auth ex-novo, ne impostaprofiles,organization_memberships(con il ruolo scelto) eorganization_user_details(anagrafica derivata dal candidato), e invia un'email di invito con link per impostare la password. Richiede un'email non gia' presente tra gli utenti. - Collega a utente esistente (
linked_existing): linka il candidato a un membro gia' presente nel tenant. Nessun nuovo account, nessuna email — solo aggiornamento profilo + trasferimento risorse.
Cosa viene trasferito (richiede CV con Dati estratti — vedi step 3):
- CV: gli oggetti storage vengono copiati dal bucket candidati (
hr-candidate-cvs) al bucket dipendenti (hr-cvs), con una nuova riga inhr_cv_files(versione 1). Il CV candidato viene marcatomigrated. - Esperienze lavorative: le esperienze dell'estrazione CV vengono inserite sul profilo del dipendente.
- Skill: il mapping verso il catalogo del tenant avviene gia' all'estrazione (entity-linking, link salvati nel draft); la conversione applica al profilo solo i link
auto(confidenza >= 0.85), i match fuzzy 0.70-0.85 restano suggerimenti non applicati (CV_ENTITY_GRAPH_01 W01).
Se l'estrazione CV non e' disponibile, la conversione procede comunque ma non trasferisce esperienze/skill (contatori a 0).
Email di invito (solo modalita' new_user): l'invio e' *fail-soft* — se Resend non e' raggiungibile la conversione resta valida; l'esito segnala l'invito non inviato e puoi inviare manualmente un reset password.
Sicurezza transazionale: gli oggetti storage vengono copiati prima delle scritture DB. Se una scrittura DB fallisce dopo la creazione dell'utente, un rollback compensante elimina l'utente auth creato e gli oggetti copiati. Ogni conversione (riuscita o annullata) lascia una riga di audit in hr_candidate_conversions.
Limiti posti: se il piano del tenant ha un limite max_users gia' saturo, la modalita' new_user fallisce con un errore esplicito (422) prima di creare l'utente.
Da quel momento il candidato resta tracciato come record storico (audit GDPR), ma il *vero* profilo professionale e' quello del dipendente.
5. Archivia un candidato
- Dal dettaglio: bottone
Archivia(o cambio status manuale aArchiviato) - Il candidato sparisce dalla vista di default; per rivederlo seleziona
Archiviatodal filtro stato. - L'archive e' uno stato di workflow — il record resta visibile cambiando il filtro. Per la rimozione recuperabile usa
Elimina(step 6).
6. Elimina nel cestino (e ripristina)
- Dal dettaglio: bottone
Elimina(ghost, accanto ad Archivia). Conferma:recuperabile entro 30 giorni. - Il candidato e' soft-deleted (
deleted_at) e sparisce da tutte le viste di default — non e' filtrabile per status. - Per recuperarlo: nella lista, spunta
Mostra eliminati (cestino, 30gg)per vedere la vista cestino; clickRipristinasulla riga del candidato. - Hard delete automatico fuori scope: i candidati nel cestino restano finche' un operatore (o un futuro cron) non li elimina definitivamente.
Filtri lista candidati
La lista /workspace/hr/candidates espone 4 filtri primari (auto-applicati su change, reset via Pulisci filtri):
- Cerca per nome, email o headline: ILIKE su
full_name,email,headline - Stato pipeline: dropdown con tutti gli status (default: esclude
Archiviato) - Owner assegnato: dropdown popolato dagli utenti tenant con permessi HR; filtra per
assigned_to_user_id - Tag: dropdown single-select derivato dall'unione delle tag dei candidati attualmente caricati; filtra per
tags && [tag](PG overlap)
Toggle separato Mostra eliminati (cestino, 30gg) per accedere alla vista soft-delete.
Notifiche email transazionali
Su cambio di status, il sistema invia automaticamente un'email al candidato (se email non null) per 4 transizioni:
Screening— "candidatura presa in carico"Colloquio— "invito a colloquio"Offerta— "offerta estesa"Rifiutato— "esito candidatura"
Gli altri status (Nuovo/Assunto/Archiviato/Ritirato) non triggera notifiche (l'invito per Assunto e' gestito separatamente dalla conversione, step 4).
Fail-soft: la chiamata Resend e' fire-and-forget; un errore SMTP non blocca la transizione di stato. Errori vengono loggati lato server.
Credenziali: usano platform_email_settings (provider=resend) se configurato sul tenant, altrimenti RESEND_API_KEY da env. Senza nessuna delle due → skip silenzioso.
Sorgenti supportate
Il campo Sorgente e' libero ma il modal di creazione propone preset comuni:
linkedin— sourcing diretto da LinkedInreferral— segnalazione internagara— candidato emerso durante una RFP / garacv-upload— upload CV diretto (es. tramite form pubblico, in roadmap)website— applicazione spontanea da sitoagency— agenzia esternaother— altro
API endpoint (per integrazioni)
Tutti gli endpoint richiedono header Authorization: Bearer <jwt> + x-horis-org-id: <org_uuid>.
| Metodo | URL | Descrizione | |--------|-----|-------------| | GET | /api/hr/candidates | lista paginata + filtri (status, search, assignedTo, tags, includeArchived, includeDeleted, onlyDeleted, page, limit). tags accetta csv. | | POST | /api/hr/candidates | crea (body JSON: fullName obbligatorio + opzionali, incluso tags/assignedToUserId) | | GET | /api/hr/candidates/{id} | dettaglio + cvFiles | | PATCH | /api/hr/candidates/{id} | update parziale (incluso tags/assignedToUserId/status — su status triggerabile + email non null → invia email transazionale) | | DELETE | /api/hr/candidates/{id} | archive (status → archived, NON soft delete — vedi /soft-delete) | | POST | /api/hr/candidates/{id}/soft-delete | sposta nel cestino (deleted_at), recuperabile entro 30 giorni | | POST | /api/hr/candidates/{id}/restore | ripristina dal cestino | | GET | /api/hr/candidates/{id}/cv-files | lista CV candidato | | POST | /api/hr/candidates/{id}/cv-files | upload multipart (file) | | POST | /api/hr/candidates/{id}/cv-files/{cvId}/extract | estrazione AI on-demand del CV → popola extraction_draft con il profilo (esperienze/skill/lingue/anagrafica). Fail-soft: 200 con warning se parse/LLM falliscono. | | GET | /api/hr/candidates/{id}/cv-files/{cvId}/signed-url | signed URL TTL 300s (max 3600s via ?ttl=) | | DELETE | /api/hr/candidates/{id}/cv-files/{cvId} | soft delete CV | | POST | /api/hr/candidates/{id}/convert-to-user | converti candidato → dipendente. Body: mode (new_user\|linked_existing), + targetEmail/existingUserId, role, managerUserId, primaryRole, startDate, transferCv/transferExperiences/transferSkills. Backward-compat: solo existingUserId → linked_existing. Errori: 409 email_collision, 422 seat_limit_exceeded, 409 already_hired |
Stati pipeline
| Status | Significato | Transizioni valide | |--------|-------------|--------------------| | new | Appena creato, non ancora screenato | qualsiasi | | screening | In valutazione iniziale | qualsiasi | | interview | In fase colloqui | qualsiasi | | offered | Offerta inviata | hired, rejected, withdrawn | | hired | Assunto (post-convert) | solo verso archived, rejected, withdrawn (no ritorno indietro) | | rejected | Scartato dopo valutazione | terminale (puoi sempre archiviare) | | withdrawn | Si e' ritirato lui | terminale | | archived | Stato workflow, NON eliminazione | riattivabile spostando lo status |
Sicurezza & GDPR
- Tabelle
hr_candidatesehr_candidate_cv_fileshanno RLS bloccata totalmente: l'accesso e' SOLO via service-role admin client server-side, con check applicativocanManageCandidates. - I CV vivono in bucket privato
hr-candidate-cvs, con signed URL temporanei (TTL default 5 minuti). - Audit fields:
created_by_user_id,uploaded_by_user_id,deleted_by_user_idsu CV files. Suhr_candidatesc'e'created_by_user_id+updated_atautotouch via trigger +deleted_at/deleted_by_user_idper soft delete. - Soft delete (sia per candidati nel cestino che per CV): il record resta per audit, lo storage object resta nel bucket fino a hard delete esplicito (futuro).
- Email transazionali: fail-soft, mai bloccanti. Gli errori SMTP non lasciano residui silenziosi (loggati lato server).
Cosa NON c'e' (ancora)
- assegnazione interviewer + scheduling colloqui
- public form per candidatura spontanea
- job postings / pipeline ATS
- background check
- trasferimento candidato cross-org
- hard delete automatico dal cestino (cron)
- email template multi-lingua e tenant-configurable
Programma di provenienza
HORIS_HR_PRE_ONBOARDING_01(2026-05-19) — versione inizialeHORIS_HR_PRE_ONBOARDING_02(2026-05-19) — full hire workflow: conversione candidato → dipendente completa (provisioning nuovo utente, trasferimento profilo/esperienze/skill/CV, invito email)HORIS_HR_CANDIDATE_CONSOLIDATION_01(2026-05-25) — consolidamento: W01 AI extractor on-demand sui CV candidati (chiude il gap profilo-non-trasferito), W02 UI tags filter + owner picker (lista + dettaglio), W03 email transazionali + soft delete con grace period 30gg
