Last updated 2026-09-19
Recruiting
Turn it on at /admin/modules β Recruiting. Off by default.
Who sees what
Money is on a need-to-know basis. Budgets on a hiring request are visible to the person who raised it and to the approvers (HR, Finance above the threshold, executives). Offer amounts live only on the offer page, which opens for HR, Finance-flag holders and executive approvers β not the hiring manager or interviewers; timeline notes say "offer sent", never the figure. Placement fees and agreed compensation are on the engagement and Finance pages only. Salary bands on a search are never published on the careers page or in the job feed. The referral bonus amount is shown to employees on purpose β it is the incentive.
- HR-flag holders and admins β everything: every hiring request, every search, every resume, settings, retention. HR has full control.
- Hiring team on a search (hiring manager + interviewers named on it) β that search's candidates only: the board, notes, scorecards. Never the pool, never other searches.
- Any manager β their own hiring requests.
- Finance-flag holders β approve budgets above the threshold and offers above the band; no candidate content.
1. Ask for a hire β /hiring/requests
A manager raises a request: backfill (pick who's being replaced) or net-new, title, department, headcount, location, target start, budget band, reason (growth / project / attrition) and a justification. It then walks the approval chain set at /hr/recruiting/settings β HR first, then Finance when the budget is above the threshold, optionally an executive approver. Each step is emailed; a step that doesn't apply is skipped without a click. Rejecting needs a reason the requester reads. The requester can withdraw until a search is opened.
Approved β HR clicks Open the search. The search is pre-filled from the request and the requester joins its hiring team as hiring manager.
2. Set up the search β /hr/recruiting/searches
Before sourcing, fix the kickoff (structured hiring):
- Pipeline stages β one per line, in order (default: applied β screen β interview β final β offer β hired). Rejected / withdrawn are implicit.
- Scorecard attributes β 5β10 things every interviewer rates on the same 1β4 scale. Add
| mustfor must-haves. - Interview plan β after saving, a grid appears: tick which stage covers which attribute and type the questions for that stage. This is the interview kit interviewers get by email.
Scorecards and plans save as templates per department so the next search starts from the last one. Add interviewers under Hiring team.
3. Work the board β /hr/recruiting/searches/<id>/board
One column per stage; cards show recruiter rating (β ), scorecards filed (π), notes (π¬) and days in stage (orange past 14). Move with the dropdown on the card. Hide names anonymises the board for screening.
On a candidate's application page:
- Move / rate β HR, the recruiter or the hiring manager move candidates; the recruiter rates 1β5.
- Scorecard β one per interviewer per stage: a score per attribute in that stage's plan, a recommendation (strong yes / yes / no / strong no) and the evidence. Your card is hidden from other interviewers until they file their own for that stage; HR sees all. Filing again for the same stage replaces yours.
- Interviews β pick time, length, kind and interviewers; everyone gets an email with the kit and a calendar (.ics) link. Tick email the candidate to send them a confirmation with their own calendar file.
- Reject / withdraw β with a reason; optionally send the candidate a polite no (never to do-not-contact). HR can reopen.
Add someone to a search from the board (dedupes on email against the pool) or from the pool with Add to search.
4. The candidate pool β /hr/recruiting/candidates
Every person who ever applied or was sourced, once. Search covers names, resume text, notes and tags; filter by country, work authorisation, source, consent, tags, and open to part-time / contract. A candidate record shows all their applications, resumes (versioned), notes, consent, and who referred them.
- Resumes β upload PDF/DOC/DOCX/TXT; text is extracted for search.
- Merge a duplicate into a record: applications, resumes, tags and blank fields move over; the merged row redirects here.
- Consent β applied (default), legacy (imported), third party (may be presented to search customers), withdrawn (also sets do-not-contact).
- Import β
/hr/recruiting/candidates/importtakes the softinway.com "Submit your resume" export (Gravity Forms CSV) or any CSV withemail,first_name,last_name. Dry run first. Re-running is safe: known people (by form Entry Id, then email) aren't duplicated. Website applications land on a parked Website β \<department> search per department. Resume files are linked by URL; runscripts/recruiting_fetch_resumes.pyon the server to pull them in and index them.
5. Offer and hire
Draft an offer from the application (HR): base, currency, bonus, start, expiry, other terms, contingencies. It is approved before it can be sent β HR always; Finance too when the base is above the search's salary band; and, by default, an executive approver (the is_exec_approver flag, or an admin) on every offer β no hire is offered without that sign-off. The executive step is a tenant setting (/hr/recruiting/settings). Send renders the letter and delivers it through OtiumWork's e-signature; the candidate signs (or declines) on the public link and the offer updates itself. Withdraw at any point before acceptance.
Hire (on an accepted offer) creates the employee record β name, work email, department, manager β files the signed letter as a confidential offer letter in their HR profile, records the start date as a key date, applies the onboarding checklist you pick, and marks the search filled once headcount is met. Tick email the Otium invite now or invite later from /admin/employees. The candidate stays in the pool, linked to the employee.
4b. Talking to candidates β email from Otium
Open an application β β Email candidate: pick a template (screen invite, still interested?, not moving forward, interview follow-up, offer heads-up β edit or add your own under Templates), the merge fields ({first_name}, {title}, {company}, {recruiter_name}, {status_url} β¦) fill in when you send, and the email lands on the application timeline and the candidate's Emails panel. On a board, β Email stage sends one template to everyone in a column (people marked do-not-contact are skipped and listed). Sends go from the company's mail account; replies come back to the recruiter's inbox.
Follow-ups: on either form choose if no reply, follow up in N days with ; the daily job (08:00) sends it unless someone pressed They replied on the application or it closed in the meantime. The daily job also sends interview reminders the day before β to the interviewers (with the kit link) and to the candidate.
4c. What the candidate sees β their status page
Every confirmation email carries a private link (/careers/me/β¦) where the candidate sees each application's status in plain words (Received, Under review, Interviewing, Offer stageβ¦), upcoming interviews, and can withdraw, update phone / location / LinkedIn / consent / open-to, and upload a newer resume (which is re-scanned for software and experience). HR sees the same link on the candidate page. A withdrawal emails the recruiter and lands on the timeline.
5a. Offer letter templates
Templates β Offer letter templates: one per country, entity or department, with merge fields ({brand}, {first_name}, {title}, {currency}, {base_comp}, {bonus_clause}, {start_date}, {other_terms}, {contingencies}, {expires_at}, {sender_name}). The offer form picks the one whose department matches, or the built-in letter. The letter is what the candidate e-signs.
5b. Talent network β people we already know
The pool is also the company's network: AxSTREAM users, AxSTREAM EDU students, people we placed at clients and people we hired ourselves. Four tags drive it β axstream-user, axstream-edu, placed, hired β plus each candidate's current employer / title, university / class of and LinkedIn URL. Placements and hires write a Career row and the tag automatically (a placement that falls off gets its end date); HR adds other moves by hand on the candidate page. The Recruiting home shows the four counts; the pool has quick filters for them plus employer and university search, and full-text search covers employer and university too.
Load lists in bulk with scripts/recruiting_import_people.py --company <id> --file people.csv --tag axstream-edu (any CSV with name/email plus optional company, title, university, graduation year, LinkedIn, city, country) β matched by email, never duplicated. Loose resume files (a folder of PDFs/DOCX) go in with scripts/recruiting_import_files.py --company <id> --dir <folder>: the text is extracted, the AI reads off who it is, and the person is created or matched by email with the file attached. scripts/recruiting_backfill_contacts.py --company <id> later reads every resume the pool holds and fills in missing email, LinkedIn, employer, university (and, with --trust-ai-names, names that came off a file name).
Software and experience as variables: every resume is scanned on arrival for a dictionary of engineering software (STAR-CCM+, ANSYS Fluent/CFX/Mechanical, AxSTREAM, NX, Abaqus, OpenFOAM, MATLAB, Pythonβ¦ β SOFTWARE_TOOLS in the code) and for the stated years of experience; both land on the candidate (Software, Experience) and are filters in the pool (Software dropdown, Min years). HR can correct them on the edit form. scripts/recruiting_backfill_regex.py --company <id> re-scans the whole pool (and fills email / phone / LinkedIn / university / class of from the text, no AI needed).
Keeping everything: Settings β Data retention β untick Anonymise inactive candidates automatically and nothing is ever purged (the candidate's own erasure link still works).
5c. Referrals, hand-off, reports, duplicates, weekly digest
- Refer someone (sidebar, every employee): name, email, LinkedIn, optional resume and role β a candidate with source referral and an application; the recruiter is told. Settings β Referral bonus sets the amount; when a referred person is hired the bonus shows on Finance β Search fees β Referral bonuses until Finance marks it paid.
- Hire hand-off: hiring opens an IT ticket (laptop, accounts β when the IT module is on), emails the hiring manager with the start date, checklist and ticket, and adds a Probation ends key date 90 days out.
- Reports (Recruiting home β Reports): funnel for the period, time to fill, time to hire, offer acceptance, source of hire, why we said no, applications per month, recruiter load, and a per-search table (CSV export).
- Duplicates (Candidate pool β Duplicates): same LinkedIn or phone (safe), same name (suggestion, stronger with the same university or email domain); one click merges into the older record.
- Weekly job (
/api/cron/recruiting-weekly, Mondays): every active search client's contacts get a digest of their engagements and each submitted candidate's state; if Settings β Your own careers page to mirror is set, the postings there are synced into open searches (removed postings go on hold).
5d. Interview scheduling, job boards, guarantee credits
- Check availability on the interview form reads each ticked interviewer's Outlook calendar (anyone who has connected Outlook in Otium) and lists the free slots inside everyone's local working hours (09:00β17:00, across time zones); click a slot to fill the time. People who never connected are named so you can ask them.
- Job boards:
/careers/<slug>/jobs.xmlis an Indeed-format feed of the posted searches (give the URL to Indeed / LinkedIn / Google for Jobs), and every public job page carries schema.org JobPosting markup so Google indexes it. - Guarantee credits: when a placement falls off inside the guarantee, an unpaid fee invoice is voided; a paid one becomes a credit held against the client's next placement and is deducted automatically on that invoice (Finance β Search fees shows open / applied credits).
- The client agreement now carries a data-protection clause (GDPR / UK GDPR / CCPA) and a 12-month non-solicitation of our staff.
- Talent network page (
/hr/recruiting/talent): each relationship group with where people are now, their software, live applications, last contact, and Move again to add them to an open search.
6. Retention and erasure β /hr/recruiting/retention
Candidates with no open application, no live offer and no employee record are anonymised in place after the retention window (default 24 months from last activity; per-country overrides in settings, e.g. United Kingdom | 12 | reconsent). Name, contact, resumes, notes and scorecards are removed; the application history stays for statistics. A nightly job does this; the page previews who's due and lets you run it now.
Every email to a candidate carries a remove my details link. A request lands in the erasure queue, stops all contact immediately, and HR completes it with one click.
Self-identification (optional, per tenant): a voluntary form on applications, stored apart from the application, reported in aggregate only at /hr/recruiting/self-id-report (groups under 5 suppressed).
7. Settings β /hr/recruiting/settings (admin)
Approval chain and Finance threshold Β· external-search fee defaults (model, %, guarantee days, representation lock β pre-fill for client engagements, overridable per client and per engagement) Β· retention policy Β· self-ID toggle Β· careers-page intro.
8. Careers page and applications β /careers/<your-slug>
Every search with Post on the careers page ticked (and status open) appears on the public page, branded with your logo and colour, with the job description rendered from markdown. The URL slug and the intro text are in settings. Applicants fill in name, contact, work authorisation, resume (required), an optional message, and two checkboxes: may your customers consider me too (β consent third party) and open to part-time or contract. If self-identification is on for the tenant and the search, the voluntary form appears; answers are stored apart and never shown on the application. A rate limit keeps bots out.
On submit: the person is matched to the pool by email (a re-application versions the resume and never duplicates the person), the application lands in the first stage, the resume text is indexed, the applicant gets a confirmation carrying the remove-my-details link, and the search's recruiter gets a heads-up.
Or keep your own website's form as the front door: Settings β Website intake gives you an endpoint and a token; the site's form (Gravity Forms Webhooks feed, or any HTTP POST) sends each submission and it lands in the pool exactly as a careers-page application would β matched by entry id then email, filed on the βWebsite β β search (or a posted search when the payload names its slug), resume fetched from the site and indexed, both consent boxes recorded. Nothing changes for applicants; the CSV export is no longer needed. The form's Desired position is matched by title to a live search, so keep the live searches in step with the website: scripts/recruiting_sync_careers.py --company <id> reads the careers page and creates/updates one open search per posted title (all locations on one search); run it whenever a posting is added or removed (--close-missing puts searches that are no longer posted on hold).
9. Re-consent for the imported pool
People imported from the website export predate the checkboxes (consent legacy). Filter the pool by consent = legacy (optionally a country) and click Ask for consent (200): each person is emailed once with a link offering yes β consider me for customer roles, keep me on file for your roles only, or remove my details; the part-time/contract box rides along. Their answer updates the record itself; a remove lands in the erasure queue.
10. AI screening (optional)
Needs the workspace's AI configured (Settings β AI billing). Nothing runs automatically; every call is a click, so cost stays in your hands, and the reasoning is stored where you can read it.
- Extract from resume (candidate page) β a structured profile: current title, years, tools (AxSTREAM, CFX, Fluent, STAR-CCM+ β¦), skills, education, languages.
- AI screen (application page) β each scorecard attribute scored 1β4 from evidence in the resume (unknown = 1, and it says so), a match score 0β100 weighted to must-haves, a three-line note, and flags for a human to check. Lands in the timeline as an AI screen entry; the score shows on the board card and Sort by match orders the columns by it.
- AI screen on the board β up to 25 unscored candidates in the chosen stage per click.
It sorts; it never rejects. Scores are a starting order for a human read, not a decision.
11. Search clients β /hr/recruiting/clients
Running a search for another company. Vocabulary follows the staffing world: a client places a job order (an engagement), you make submissions, an accepted submission is a placement, the placement carries the fee.
- Client β name, optional link to the CRM client, contacts (principal / hiring manager / viewer), and fee defaults that override the workspace's (blank = workspace default: 10% contingency, 90-day guarantee, 365-day representation lock).
- Agreement β one click sends a short search-services agreement to the principal for e-signature; the fee, guarantee and lock in the letter are the client's effective values. Until it's signed the portal shows candidates anonymised and hires can't be confirmed.
- Engagement β a search run for the client: new or an existing unassigned one; fee model contingency (full fee at placement), retained (kickoff / shortlist / placement milestones invoiced from the engagement page) or per shortlist (flat fee when the shortlist is delivered); currency, guarantee, lock, exclusivity. Pre-filled client β workspace.
- Submit β from a candidate's application page on a client search. Needs consent third party; refused while another client holds a live representation lock on that person; sets this client's lock. The client's contacts are emailed. Withdraw from client releases the lock.
- Placement β HR records it (candidate, start, agreed comp) or the client confirms it in the portal. Fee = comp Γ % (or the flat / milestone amount); an ordinary invoice is raised through Finance at net 30; the application goes to hired and the engagement to placed.
- Fell off β inside the guarantee, the engagement reopens as guarantee and Finance shows credit / replacement due; the candidate returns to the offer stage.
Search fees (/finance/recruiting, Finance flag): every placement, fee, invoice status and guarantee window in one table.
Fee tiers
Two workspace defaults (Settings β Fees): AxSTREAM customers 10%, everyone else 25% of first-year base. Tick AxSTREAM customer on the client to get the customer tier; a fee typed on the client or the engagement still overrides either. The tier shows on the client page and flows into the agreement and every engagement created from it.
12. The client portal β /search
Magic-link sign-in only (a contact asks for a link, or you send one from the client page; single use, 7 days; the login page never reveals whether an email is known). Contacts see their engagements, the submissions with profile summary and screen note, and β once the agreement is signed β names and resumes. Principals and hiring managers leave feedback (interested / interview / pass, rating, comment β each one emails the recruiter) and confirm a hire with a start date and compensation, which records the placement and raises the fee invoice. Viewers read only.
Not yet
Job-board syndication, background checks, assessments, candidate self-service login.
See something wrong or outdated in this article? Report it β