Posting copy, with a language scan
Requisition, deterministic posting copy, and a rules-based exclusionary-language scan that reports rather than rewrites. A role cannot leave draft without a named human approver.
Works todayAvailable now
In build
The whole team
Nineteen specialists, each with a defined job and an honest status label.
See all nineteenA CHECK constraint refuses any stage transition where the actor is a machine and the move is adverse — not a setting, a row the database itself rejects. There is no protected-characteristic column anywhere in this product — no caste, religion, gender, age or marital status — so nothing a resume volunteers can ever become a scoring input, and the interview panel's own roundup view refuses to send a composite score even by accident.
How it works
Three steps, and every one of them keeps a machine from being the actor on an adverse decision.
Build a requisition and generate deterministic posting copy, with a rules-based scan for exclusionary language that reports what it found rather than silently rewriting your words. Every draft version and its flags are kept, and a role cannot leave draft without a named human approver.
Search returns which fields matched a role's requirements, never a ranking, and refuses outright any query that selects on a protected characteristic. A screening run stores its reasons as separate factor rows with no score column anywhere — the number shown is recomputed from them and arrives with decision: null and decided_by: "human_required" on every response.
Interview scorecards stay blind until every expected interviewer has submitted their own, keyed to the kit's own list rather than who happened to start one. The roundup view that follows shows raw per-interviewer evidence, and the route checks its own outgoing response for an average or a composite before it sends it — refusing to answer at all rather than let one through.
The boundary
Bharti never makes or executes a hiring decision. No candidate is archived, rejected or deprioritised by a background job, a threshold rule or a scoring model without an explicit, logged human confirmation at the moment of that action — and that rule is enforced twice, once in application code and once as a constraint the database itself will not let anything bypass.
Nowhere to go
The cheapest defence against a protected attribute becoming a scoring input is that the field has nowhere to be stored in the first place. There is no date_of_birth, gender, marital_status, caste, religion or photo_ref column anywhere in this module. Indian resumes routinely carry all of them, so a value that was never stored cannot become a scoring input, leak into an export, or be reverse-engineered out of an analytics breakdown. The one disability-adjacent table the law requires is deliberately unreachable from screening.
A prompt, not a verdict
A screening score exists and is shown — hiding it would be its own kind of dishonesty — but it arrives captioned as a prompt to read the application, not an outcome, and the interview panel's roundup view goes further: it runs its own payload through a guard before responding, and returns a server error instead of a composite rather than risk one reaching a decision-maker.
What it does
Each card carries its own limit, and the label is derived from the backend's capability module rather than written here.
Requisition, deterministic posting copy, and a rules-based exclusionary-language scan that reports rather than rewrites. A role cannot leave draft without a named human approver.
Works todayConfigurable stages, append-only transitions recording who moved a candidate and whether they were human, and a screening signal stored as contributing factors with no score column.
Works todaySkill, location and experience search returning which fields matched, never a ranking, and refusing any query that selects on a protected characteristic. Saved searches are refused on the same protected-characteristic terms, so a standing filter can't smuggle one through, and a real reindex worker keeps the index current on every candidate create or correction.
Works todayBlind-until-submitted is enforced in the backend, keyed on the kit's own expected interviewers so it cannot be dodged by simply not starting a scorecard.
Works todayRaw per-interviewer evidence with no average, composite or recommendation — the route checks that against its own payload before returning it. A decision requires a named decider and a written rationale.
Works todayDrafting is gated on an existing human roundup rejection, uses de-identified scorecard themes, and is approved by a named person before a send route — reachable only from an approved message, only once — can fire. Email sends for real today, since ZeptoMail is already configured on this deployment; WhatsApp needs META_ACCESS_TOKEN and META_PHONE_NUMBER_ID.
Needs a credentialAn expiring, token-authenticated portal showing candidates the same stage staff see. Withdrawal propagates across every application and revokes every live link.
Works todayHolds, candidate self-booking and a visible accommodation request at the booking step all work locally. Real availability needs a connected Google or Microsoft calendar.
Needs a credentialOffer drafting anchored to the role's own pay band — never prior salary, for which there is no column — with an in-order approval chain. Dispatch for signature needs an e-signature provider.
Needs a credentialLive stage counts, days-in-stage and conversion, plus immutable daily snapshots and real per-board source performance — both computed against this workspace's own data with no external dependency. A snapshot taken twice in one day hands back the same row rather than overwriting it, so a later dispute has one permanent answer.
Works todayA policy record and accommodation tracking, structurally unreachable from screening. An adjustment cannot be marked unfulfillable without a written reason, and nothing declines it automatically.
Works todayFlagging runs automatically when a requisition closes, on documented job-relevant signals only — never a private note — and every rediscovery match is kept for audit. Re-engaging always asks for fresh, per-role consent rather than assuming a standing one. Email sends today through this deployment's already-configured ZeptoMail; WhatsApp needs META_ACCESS_TOKEN and META_PHONE_NUMBER_ID.
Needs a credentialParses contact details, skills, employment history and education, and defines no protected characteristic at all. Bharti receives only its own candidate columns, whatever the resume states.
Built, with limitsA self-hosted careers page is real: publish, and a candidate can apply, creating a genuine application attributed to the system rather than a fabricated person. Naukri, LinkedIn and Indeed have no client here — each needs a partner business relationship this deployment hasn't set up, so a failed attempt against one is recorded honestly rather than faked.
Built, with limitsTemplates, a manual send and message history run against real data, and a stage move triggers a message automatically — notifying of a decision a human already made, never making one. Email sends for real today through this deployment's already-configured ZeptoMail; WhatsApp needs META_ACCESS_TOKEN and META_PHONE_NUMBER_ID. There is no SMS gateway anywhere in this codebase — that gap is a missing integration, not a missing key.
Built, with limitsA fixed four-question flow in four hand-checked languages, gated on explicit consent before any question is asked, converts to a real candidate and application on completion — and it's testable locally today. Two real gaps: sending anything over WhatsApp needs META_ACCESS_TOKEN and META_PHONE_NUMBER_ID, and no route here is wired to Meta's actual inbound webhook yet.
Built, with limitsA result cannot be recorded before consent is on file, checked both in the route and by a database constraint underneath it, and nothing in this codebase lets a background-check result move a candidate's stage. No India BGV provider — EPFO employment history, PAN/Aadhaar-linked identity, DigiLocker education — has a client here; each needs a licensed-intermediary relationship no operator has set up yet, so nothing is faked against a guessed contract.
Built, with limitsBoth-party consent is enforced at the database level, not only checked in the route. Paste a transcript — a provider's own captions — and it drafts a per-category note today with no extra credential, using the LLM pool already configured, and accepting a draft writes only into the scorecard's comment, never a score. The real gap: turning a recording into text needs a speech-to-text provider, and none is connected.
Built, with limitsEvery claim above traces to a route, a constraint or a status entry in the code, and a test fails the build the moment the two disagree. That proves the page matches the schema — it does not prove the schema itself satisfies every jurisdiction's hiring-compliance rules, which vary by state and change over time. Before this page goes live, a qualified employment-law or HR-compliance reviewer should read the boundary claims specifically and confirm they hold up as a compliance statement, not only as an accurate description of the code. Saying that here is a trust signal, not an admission.
Before you switch
Tell us how your hiring pipeline is structured today, and we will tell you exactly what Bharti can run now — and where it insists a human stays the one deciding.

Questions
No, and not because of a setting — because the database refuses the row. bharti_stage_transitions carries a CHECK constraint that refuses the row combination actor_type='system' with to_stage_is_adverse=true. A rule can propose moving a candidate to an adverse stage all day; it cannot execute that move — the database refuses the row, not just the request, and an adverse move that does go through must name a human actor and carry a written reason. A rule may flag or suggest all it wants; only a named human, at the moment of the decision, can actually move someone to an adverse stage.
There is no date_of_birth, gender, marital_status, caste, religion or photo_ref column anywhere in this module. Indian resumes routinely carry all of them, so a value that was never stored cannot become a scoring input, leak into an export, or be reverse-engineered out of an analytics breakdown. Resume parsing withholds them even when a resume states them outright, and the screening function's own input type is a fixed allowlist that has no field for any of them to arrive in.
A number recomputed from named factor rows, each naming the exact field it read, with no stored score column behind it. Every response that carries it also carries decision: null and decided_by: "human_required" — there is no code path in this product where that score becomes a verdict by itself.
No. The roundup view returns every submitted scorecard side by side and nothing else — no average, composite or recommendation — and the route checks its own response against that rule before sending it, raising a server error rather than letting a composite reach a decision-maker even by accident.
It can draft one, but only after a human has already recorded a rejection in the roundup, using de-identified scorecard themes rather than a person's raw notes, and a named person still has to approve it before a send route can be called — approval is reachable only once, and there is still no code path from a score, a stale-application rule or an automated stage move straight to a send. Once approved, email delivers for real today; WhatsApp needs a Meta credential this deployment hasn't configured.
No. Building requisitions, running the pipeline, structured interview kits, the hiring-team roundup, the candidate portal and equal-opportunity tracking all work today with nothing configured. Real calendar availability needs a connected Google or Microsoft account, and sending an offer for signature needs an e-signature provider — both say so on their own card below.