Audience: HireQ platform operators (super admins)
Portal: after you sign in you land on /dashboard
Last updated: 2026-10-01 (beta)
Online: www.hireq.ca/guides/admin.html · Version française
This guide describes the admin workspace as built in the unified portal (portal/). Server setup and go-live steps are in ops/beta-go-live-checklist.md and env.md. SMTP, logos, and the Emails editor: email-setup.md.
Beta notice. The platform runs with Stripe in test mode (
PAYMENTS_MODE=sandbox). Services need test-mode Stripe Price IDs, and users pay with test cards. Do not enter live Price IDs during the beta. Live cutover later: ops/stripe-live-cutover.md.
Related role guides: Quick Guide · Candidate guide · Recruiter guide
Deeper schema design: validation-user-guide.md
How schema, taxonomy, skills, and matching scores fit together: §3
1. How you get in
1.1 Creating an admin account
Admins cannot register on /register, and there is no admin env variable. Someone with shell access to the API server runs:
cd /opt/hireq/backend
.venv/bin/python scripts/create_admin_user.py --email you@hireq.ca
The script prompts for the password twice. The password must be at least 10 characters, not only numbers, and not the email address. Running it again for an existing admin resets that admin's password (and reactivates the account). It refuses to turn an existing candidate/recruiter email into an admin.
1.2 Signing in
- Sign in on the same login page as everyone else (
/). You are routed to/dashboard. - If 2FA is on, enter the Authenticator code when asked.
- Deep links:
/dashboard?section=matching,validation-schema,taxonomy,emails,audit,skills,account, and so on. - The admin UI is English only.
- If the session is invalid, you are sent back to
/.
1.3 Account security (password and 2FA)
Sidebar → Account security.
- Change password — current password + new password (same rules as above).
- Authenticator app (2FA) — Set up 2FA → add the key to your authenticator app → enter the 6-digit code → Turn on 2FA. To turn it off you need your password and a current code.
2FA is optional in the software, but beta policy is that every admin turns it on. Admins can see all candidate data, recordings, and payments.
1.4 Lost 2FA device (support procedure)
There are no self-service recovery codes yet. For any user (admin, recruiter, or candidate) who lost their authenticator:
- Verify identity out of band (known email + a second channel you trust).
- An operator with database access clears 2FA for that user: set
totp_enabled = falseandtotp_secret = NULLon theusersrow. - Ask the user to sign in with their password and set up 2FA again.
- Note the reset in your support log.
2. What you operate
HireQ is not a “flip a feature flag” admin. You operate the rulebooks and catalogs that candidates and recruiters live inside:
| You configure | They experience |
|---|---|
| Job taxonomy versions | Recruiter canonical title picker and family baselines |
| Candidate / job validation schemas | Profile assistant gaps, job quality checklist, publish readiness |
| Matching settings | When rematch runs, who is notified, whether opportunities auto-present |
| Skills dictionary (+ optional question banks) | Skill pickers, skill validation questions, matching skill IDs |
| Lookups | Dropdown lists (job type, work model, fluency, …) |
| Paid services + Stripe Price IDs | Recruiter Billing and candidate Resume Services |
| User and job status | Who can log in; which jobs are public |
AI never auto-hires or auto-rejects. Your Matching toggles can notify and present opportunities; candidates still consent. Recruiters still move pipeline status by hand.
3. Validation Schema, Job Taxonomy, skills, and matching scores
These admin surfaces look separate in the sidebar, but they form one pipeline. Candidates and recruiters never edit them; they live inside the rules and occupation data you activate.
3.1 Validation Schema (the rulebooks)
There are two live schemas (each versioned; only the active version applies):
| Schema | What it governs |
|---|---|
| Candidate | How profile data becomes claims; profile completion checklist; Profile assistant gap prompts; readiness weights |
| Job | How posting fields become job claims; Job quality checklist; publish and recommendation readiness thresholds |
A schema is not a list of occupations or skill names. It is structural (“skills must exist,” “ask about {keyword},” “publish needs readiness ≥ X”). The same candidate schema works for any skill the candidate adds.
Default candidate settings (bundled / typical active): readiness weights self-declared 0.15 / manual 0.35 / AI 0.5; completeness_divisor 11; validated_score_threshold 0.7; low_ai_gap_threshold 0.5. Completion checks include full name, location, phone, summary, target roles, work preferences, work history, skills, languages, education, certifications (several allow “none” via assistant decline).
Default job settings: self-declared weight 1.0; publish_readiness_threshold 0.6; recommendation_readiness_threshold 0.6; min_required_skills 1; required_locales ["en"]. Completion: English title & description, ≥1 skill, location (city or location), employment type, work model.
If no DB version is active, the API falls back to bundled JSON under backend/config/.
3.2 Job Taxonomy (the occupation tree)
Taxonomy is content: categories → families → canonical job titles → family skills.
Import needs two files (CSV UTF-8 or .xlsx):
Skills / competencies file columns:
Category ID, Category, Job Family ID, Job Family, Skill ID, Skill, Skill Type, Importance, Expected Proficiency, Validation Method, Interview Weight, Question Required
Canonical titles file columns:
Category ID, Category, Job Family ID, Job Family, Job ID, Job Title
Notes that matter in production:
- Taxonomy Skill ID is an external id on the taxonomy row. Matching keys off the platform
Skill.idcreated/upserted by skill name (case-insensitive). Duplicate dictionary keywords still break matching. - Import requires a version label. Same file checksum returns the existing version (may activate it).
- Activate makes that version the only active one (
effective_atset).
When a recruiter Builds family baseline, family skills become proposed JobRequirement rows:
| Requirement field | From taxonomy |
|---|---|
required |
Importance casefold equals core |
weight |
2.0 if Interview Weight is High, else 1.0 |
target_proficiency |
5 (fixed default). Expected Proficiency is stored in rubric metadata — it is not the numeric target used in fit math today |
hard_gate |
Always false from taxonomy load (only a later edit can set a hard gate) |
confirmation_status |
proposed until the recruiter Confirms |
Without taxonomy coverage for a role, recruiters can still draft a job with freeform posting skills, but the evidence-matching path needs a canonical title + confirmed requirements.
3.3 Skills catalog vs candidate skills vs taxonomy skills
| Source | Where it lives | Role |
|---|---|---|
| Skills dictionary (Admin → Skills) | Flat keyword list + optional question bank | Shared platform skill IDs for pickers, claims, taxonomy name upsert |
| Candidate skills | Profile / resume / Profile assistant → skill claims (score + 1–10 level) | What the person can do, with evidence |
| Taxonomy family skills | Active taxonomy version | Default requirements for titles in that family |
Candidate skills are not limited to the taxonomy. Free-text skills can create new catalog entries. Skill validation (candidate UI) generates questions if the bank is empty (see §6).
3.4 How matching scores are produced (code behavior)
Evidence matching (assess_match) compares confirmed job requirements for the job’s current specification version against the candidate’s skill claims (approved skill equivalencies can map related skills).
Admin activates taxonomy + candidate/job schemas + Matching settings
↓
Candidate adds skills → claims → Profile assistant / skill validation strengthens scores & levels
↓
Recruiter picks canonical title → Build family baseline → Confirm requirements → publish
↓
Job is matchable only if: published + assessment_ready + ≥1 confirmed requirement
↓
Per requirement: fit = claim_score × min(1, level / target_proficiency)
↓
fit_score = weighted average of fit
evidence_confidence = weighted average of evidence strength
suitability = fit_score × (0.5 + 0.5 × evidence_confidence)
↓
Recommendation label (hardcoded thresholds) + Matching settings for “strong” notify/auto-present
Per-requirement status:
supportedif fit ≥ 0.7 and evidence ≥ 0.5missing_evidenceif evidence < 0.4 (or no claim)- else
partial
Hard gates: if a requirement has hard_gate and fit < 0.5, the candidate is ineligible and recommendation is needs_review.
Recommendation string (not the Matching settings thresholds):
| Result | When |
|---|---|
needs_review |
Any hard-gate failure, or not recommended and not insufficient |
recommended |
suitability ≥ 0.7 and evidence_confidence ≥ 0.5 |
insufficient_evidence |
evidence_confidence < 0.4 |
Strong match (Matching settings — used for notify / auto-present):
- eligibility is not
ineligible, and - suitability ≥
suitability_threshold(default 0.7), and - evidence_confidence ≥
evidence_threshold(default 0.5)
Matchable job requires all of: status == published, assessment_ready == true, and ≥1 confirmed requirement. assessment_ready becomes true when specification issues are cleared after confirms.
Publish (recruiter) is a second gate on top of specification: job schema completion must pass and job readiness ≥ publish_readiness_threshold (default 0.6).
Empty match reports usually mean missing taxonomy confirms, unpublished jobs, thin candidate skill evidence, or (on large datasets) the rematch candidate cap — not only “wrong thresholds.”
3.5 What you usually change vs leave alone
| Goal | Touch |
|---|---|
| New occupations / better family baselines | Job Taxonomy (import / activate) |
| Stricter profile or publish checklists, assistant prompts, readiness weights | Validation Schema |
| When to notify or present opportunities | Matching |
| Cleaner skill identity for matching | Skills dictionary (align names with taxonomy) |
| Profile assistant / skill validation for “any” skill | Usually nothing — already generic once skills exist as claims |
4. Sidebar map
Stats (candidates, recruiters, jobs, applications, scheduled vs performed interviews) stay visible while you switch sections.
| Section | You use it to |
|---|---|
| Overview | Jump cards into many other sections (does not deep-link every item; Validation and Audit are still in the sidebar) |
| Analytics | 30-day trends; CSV exports (candidates, recruiters, jobs) |
| Skills | Dictionary keywords + optional validation question bank |
| Lookups | Standardized lists |
| Services | Paid catalog and Stripe mapping |
| Candidates | Find people; deactivate / reactivate |
| Recruiters | Same for hiring accounts |
| Applications | Inspect an application and its interviews |
| Jobs | Moderate posting status (draft / published / closed) |
| Validation | Claim review queue, overrides, LLM smoke test, interview review |
| Validation Schema | Versioned candidate and job rulebooks |
| Job Taxonomy | Import / activate occupation taxonomy |
| Matching | Automation policy and strong-match thresholds |
| Emails | Default copy, merge fields, and the HireQ logo for every outbound email |
| Audit log | Recent sensitive actions |
| Account security | Your admin password and 2FA (§1.3) |
5. Suggested go-live order (empty environment)
Do this before you ask a customer to UAT matching.
- Lookups — load categories recruiters and candidates need (job type, work model, pay type, province, language fluency, education level, benefits, …).
- Skills — seed the dictionary (taxonomy import also upserts skill names). Avoid duplicate keywords.
- Job Taxonomy — download CSV templates, fill both files, Validate, Import (label required; activate-on-import defaults on), confirm an active version.
- Validation Schema — activate a candidate schema and a job schema (import known-good JSON if needed).
- Matching — review thresholds. Product defaults turn automation, notify, and auto-present ON. For conservative UAT, turn auto-present off until comfortable.
- Emails — confirm SMTP and reset URLs (email-setup.md), upload the platform logo, preview Account setup / Password reset, then skim hiring templates recruiters will inherit.
- Services — create Candidate Search (service id must match
RECRUITER_CANDIDATE_SEARCH_SERVICE_ID, default 2) and any monthly recruiter plan with a Stripe Price ID. During the beta, use Price IDs from Stripe test mode. - Validation → LLM status / test — confirm the model endpoint answers before live candidate chat, and check the reported AI caps.
- Account security — turn on 2FA for every admin.
- Smoke: recruiter picks canonical title → baseline → confirm → publish; candidate completes Profile assistant (+ skill validation); Matching report is non-empty. Register a spare inbox and confirm the password-setup email arrives. Buy Candidate Search with test card
4242 4242 4242 4242.
Export/import scripts for skills, lookups, and services also exist under backend/scripts/ for environment copies.
6. Lookups and skills
Lookups are (category, code, item) rows. Category+code must be unique. After Add, the form keeps the category so you can enter many codes quickly. Filter by category when the list is long. Unknown categories that already exist in data are listed as orphans so you can clean them.
Skills is a flat keyword list used by candidate pickers, recruiter job skills, taxonomy name upsert, and matching. Add carefully — duplicates confuse matching (§3.3).
Validation questions (per skill):
- Max 10 active questions (
skill.question_bank_fullif exceeded). - If the bank is empty, skill validation generates toward a target bank of 5; a session uses up to 5 questions.
- Prompt minimum length 12 characters. Deactivate / reactivate in the UI; no delete control.
7. Job Taxonomy
Operational steps (concepts in §3.2):
- Download the skills and jobs CSV templates (columns and examples are on screen).
- Validate uploads without activating.
- Import with a required label; optionally activate on import (default checked in UI).
- Open a version to see counts (categories, families, titles, skills).
- Activate the version recruiters should use.
Recruiters pick canonical titles from the active version. Family baseline in Hiring tools depends on this. Legacy free-text titles can be backfilled with backend/scripts/backfill_legacy_job_titles.py if operations needs it.
8. Validation Schema
There are two schemas: candidate and job. Each is versioned. Activating a version makes it the live rulebook; old versions remain for history.
Editor modes: Structured and JSON. Structured tabs cover:
- Settings (weights, thresholds; job schema also has publish/recommendation thresholds, min skills, locales)
- Categories
- Materializers
- Completion requirements
- Gap prompts
- Portal groups
Actions: Validate → Save new version → Save & activate → Export active; history Load / Activate; JSON Apply / Import as version (import alone does not activate).
Candidates never see this screen; they see Profile assistant. Recruiters see Job quality gaps driven by the job schema.
Make small, reviewed changes. A broken completion rule blocks publishing or looks like a candidate “cannot finish” their profile.
9. Validation (human review)
Queue lists candidates with validation activity.
Open a candidate to see claims, scores, statuses, and a timeline of decisions. You can:
- Patch score/status
- Override with a reason (audited write path). For skill claims, the override also sets the stored 1–10 proficiency used in matching
- Attach evidence notes
- Open interview review when assessments exist
Trust labels (for example “Strong evidence”) are derived from readiness, not a separate product.
LLM status and Test connection tell you whether the validation model is reachable. Use this when Profile assistant hangs or returns errors.
LLM status also shows the spend caps: max_output_tokens, daily_call_limit (platform-wide), and daily_call_limit_per_user. When a cap is reached, AI calls stop until midnight UTC and the product falls back to simpler, non-AI behaviour (for example, built-in profile questions instead of AI follow-ups). If users report "generic" assistant replies late in the day, check the API logs for LLM platform daily cap reached and ask ops to raise LLM_DAILY_CALL_LIMIT if the budget allows.
Interview review and recordings
Open a candidate (Validation queue or Candidates) to reach the Interviews list. Each session shows the recording player (audio/video sessions) and every question and answer. Admins can review any session.
- Recordings live in a private bucket and play through short-lived links.
- They are deleted automatically 180 days after the assessment ends by the retention job (
hireq-recording-retention.timer). Each deletion writes arecording.purgedaudit event. Transcripts and scores stay. - If a candidate asks for an earlier deletion, or a legal hold requires keeping one longer, escalate to ops — there is no per-recording delete button.
10. Matching
This section is policy on top of scores (when to rematch, notify, or auto-present). Score math lives in §3.4. Recommendation labels use hardcoded cutoffs; these settings define strong match for automation.
| Setting | Default | Effect |
|---|---|---|
| Enable automated rematch | on | Score again when candidate evidence or a matchable published job changes (debounced) |
| Notify on strong matches | on | Inbox (and email if enabled) to candidate and the job’s recruiters |
| Auto-present opportunities | on | Create an opportunity for strong matches. Candidate must still consent. Turn off for conservative UAT |
| Suitability threshold | 0.7 | 0–1 bar for “strong” |
| Evidence threshold | 0.5 | 0–1 bar for enough evidence |
| Debounce seconds | 30 | 0–600 wait before a rematch run |
| Max candidates per job run | 500 | Cap (1–10000). Rematch walks candidates by id up to this cap |
Recruiters can still click Present opportunity on a match when auto-present is off.
Matching only considers matchable jobs (§3.4). If reports are empty, check taxonomy, confirms, publish, assessment_ready, and candidate validation progress before changing thresholds.
11. Emails
Emails (/dashboard?section=emails) is the system default for every outbound HireQ message. Recruiters can override hiring types for their company; they cannot change auth, billing, sender address, or the HireQ logo.
Full SMTP + editor walkthrough, catalog, and merge fields: email-setup.md.
On this screen you:
- Upload the platform logo (image, max 5 MB) used in email headers
- Edit subject, heading, body, and button label per message type
- Insert allowed merge-field chips (unknown
{{tokens}}are rejected on save) - Preview email with sample data, Save template, or Reset to default (built-in seed)
Notify on strong matches (Admin → Matching) still gates match emails. Turning notify off does not disable account-setup or interview calendar (ICS) mail.
12. Paid services
Create rows with:
- Audience —
candidateorrecruiter - Billing —
one_timeormonthly - Name, description, price, currency (default CAD)
- For recruiter + monthly: Stripe Price ID is required (Product ID optional)
Candidate monthly services are rejected by the API/UI. Recruiter Candidate Search must exist as a service whose id matches backend RECRUITER_CANDIDATE_SEARCH_SERVICE_ID (default 2) or recruiters cannot open Candidates.
Beta (test mode): create the matching Products/Prices in the Stripe Dashboard with Test mode switched on and paste those price_… IDs. Test-mode Price IDs do not work after live cutover; you will replace them as part of ops/stripe-live-cutover.md.
Deleting a service does not automatically refund Stripe customers.
13. Moderation and analytics
Candidates / Recruiters — search by email/name (company for recruiters). Deactivate blocks login; Reactivate restores. Deleted/inactive users cannot authenticate.
Jobs — search; set status draft, published, or closed. View counts help you see demand. Closing a job is a human moderation action; it is not an AI action. Admin status changes do not replace recruiter specification/quality gates for a healthy matching path.
Applications — search and filter by status; Inspect for interviews and candidate identity. You are not moving pipeline here (recruiters do).
Analytics — 30-day cards for signups, jobs posted, applications, revenue, job views. Export candidates, recruiters, or jobs as CSV.
14. Audit log
Read-only list of recent events: actor, role, action, resource, time. Typical actions include claim override, presenting a match, consent, application status, offers, a user turning 2FA off (auth.totp_disable), and automatic recording deletion (recording.purged).
14a. Terms, Privacy, and policy versions
- Registration requires accepting the current Terms and Privacy Policy. HireQ stores the accepted versions, time, and IP.
- Current versions:
2026-10-01(PRIVACY_POLICY_VERSION,TERMS_VERSION). The published pages are drafts pending legal review. - When ops bumps a version (after editing
website/privacy.html/terms.html), every signed-in user sees a blocking We updated our Terms and Privacy Policy dialog and must accept before continuing. Bump only for material changes, and update the pages and env together. - Users who refuse can ask support to delete their account (candidates can do it themselves in Settings → Privacy).
The screen does not yet expose filters or the JSON payload. If you need a specific action, engineering can query audit_events or call GET /admin/audit?action=….
15. What you should not expect from this workspace
- Editing a candidate’s profile as if you were them
- Recruiter-style pipeline moves
- French admin copy
- A status page or support-ticket product
- Starting Stripe refunds from the UI
- Live-joining a candidate’s AI video room
- Resetting another user's 2FA from the UI (use the §1.4 procedure)
- Creating admins from the UI (use
create_admin_user.py) - Deleting a single recording on demand (retention job only)
- Live Stripe payments during the beta
- Changing the SMTP From address in the Emails editor (that is
EMAIL_FROMin env) - A visual HTML drag-and-drop email builder
16. If something fails
| Symptom | What to try |
|---|---|
| Validation Schema missing in the menu | You are not on the unified portal /dashboard; this app is portal/ only |
| Recruiter cannot publish | Job schema completion + readiness ≥ threshold and taxonomy title + confirmed specification (§3.4) |
| Empty matching | Matchable jobs (published + assessment_ready + confirms), candidate skill claims, automation settings, rematch cap |
| Matching notifies too aggressively | Auto-present and notify default on — turn off or raise thresholds for UAT |
| LLM test fails | Model URL/key in backend env; Modal/Ollama runbook if that is the provider |
| Monthly service save blocked | Enter Stripe Price ID; audience must be recruiter |
| User cannot log in | Check deactivated flag; password setup email; 2FA code if enabled; "Rate limit exceeded" clears after a minute |
| User lost their authenticator | §1.4 |
| Forgot the admin password | Use Forgot password?, or ops reruns create_admin_user.py --email … |
| Password rejected | At least 10 characters, not only numbers, not the email |
| Users stuck on the policy dialog | They must tick the box and click Accept and continue; check PRIVACY_POLICY_VERSION / TERMS_VERSION match the published pages |
| AI replies generic / "Heuristic mode" | Model down (Test connection) or daily AI cap reached (LLM status, API logs) |
| Recording missing on a session | Text session, A/V failed, recording older than 180 days, or interview storage not configured |
| Recruiter checkout declined | Beta is test mode — they must use 4242 4242 4242 4242; check the service has a test-mode Price ID |
| No setup / reset email | email-setup.md SMTP + reset URLs; user should try Forgot password and check spam |
| Emails save rejected | Remove merge tokens that are not in that type’s chip list |
| Logos missing in real inboxes | Set PUBLIC_API_BASE_URL to the public API; Preview inlines logos and can look fine locally |
| Skill validation questions won’t save | Bank full at 10 active; deactivate unused prompts |
| Taxonomy import rejects files | Both files required; columns exact; Skill IDs / Job IDs unique; category/family tuples must align |