Release notes
The Recruitment Agency Operating System — Streamlined operations, better submissions.
When a new tenant is set up, the global search capability is switched off unless explicitly enabled. This gives administrators deliberate control over which tenants have access to the feature rather than having it active automatically.
A copy-paste leftover caused the bulk CV upload endpoint to be registered twice in the API, producing a duplicate entry in the API's auto-generated documentation and potentially confusing any tools or integrations that consume it. The redundant registration has been removed; the endpoint itself is unchanged and continues to work as before.
When EzRecruit classifies a candidate's role or company into an industry, that result is now stored in a shared database cache rather than discarded on each restart or across parallel servers. This reduces repeated AI calls for the same lookups, lowering cost and speeding up CV processing at scale.
The full test suite (219 tests) was previously never executed automatically, meaning code issues could reach a release undetected. Every build now runs all tests and will block a release if any fail.
Both the paste-text and file-upload paths for job description parsing were being rejected before any processing occurred, meaning no JD submitted through the normal UI was ever parsed. The routing layer now correctly handles both input types and directs each to the appropriate downstream endpoint.
Streamed responses containing em-dashes, accented letters, or emoji were being corrupted before reaching the user and being stored in chat history. The encoding mismatch has been corrected, so all characters now display and save correctly.
When ranking a batch of candidates, up to 4 out of 15 could come back with no score, strengths, or gaps shown — silently discarded with no error. The root cause was the AI model occasionally shortening a candidate's identifier, causing the score lookup to miss. Candidates are now labelled with simple positional references during ranking so the model never needs to reproduce a long identifier, and any remaining mismatches are logged as warnings rather than dropped.
The override permission used to bypass a blocked automated review has been aligned across two additional repositories, matching the safeguard already standard elsewhere in the organisation.
An automated dependency update introduced a version of a machine-learning library that is incompatible with the Python version this service runs on, causing every UAT build to fail for roughly 24 hours and preventing any changes from reaching the UAT environment. The dependency has been pinned back to the last known-good version, restoring the pipeline. Upgrading to a newer version will be done deliberately as a separate piece of work.
Eight service credentials, including database, search, and AI API keys, have been moved out of build artefacts into runtime-injected secret storage, extending a protection standard already applied elsewhere in the platform. Four additional credentials were moved to properly secured storage in the deployment pipeline, verified to match previous values before going live.
In live UAT traffic, the AI model occasionally returned a candidate identifier with extra text run into it, which the ranking logic did not recognise, causing that candidate to be left unscored. The fix detects this specific malformed pattern and recovers the correct identifier. Previously the failure was silent; it only came to light because earlier logging improvements made unranked candidates visible.
The Global Search screen was designed to show step-by-step progress as a search runs, but the backend was returning a single blocked response only after all work was complete, causing the server to reject the streaming request entirely with an error. The endpoint now streams results progressively to the frontend as they arrive, matching the intended user experience.
A baseline of expected search results has been generated from 58 real recorded conversations, enabling the team to run future configuration changes against it and detect any drift in candidate results. This unblocks a pending decision about a caching feature that could not be evaluated without a reference baseline.
The image-handling framework used across the application has been updated to the latest patched version, addressing a publicly disclosed vulnerability. Separately, service credentials embedded in container images have been moved to runtime-only injection, and a previously incomplete credential rotation has now been completed.
Recruiters can now page through all matching candidates beyond the first 20 results, with a clear count of total matches shown. If a network drop interrupts an in-progress search, a Reconnect button lets the session resume from where it left off rather than losing the results entirely. Error messages throughout the search experience have been rewritten in plain language instead of displaying raw technical codes.
Users navigating to the received-goals settings page when they had no goals assigned were triggering a recurring server-side crash that had been occurring every few minutes since June. The page now loads correctly in all cases.
Five database tables used to track AI call attribution and per-tenant AI balance and quota were missing from the standard tenant setup process, meaning UAT had none of them and production was missing the balance tables entirely. Calls continued to work but attribution rows and balance lookups were silently failing on every call. These tables are now part of the standard tenant provisioning flow, closing the gap between environments and ensuring cost and usage data is captured correctly going forward.
The application has been prepared to send emails via a second provider at a significantly lower cost (approximately $0.10 per 1,000 emails versus a $35 flat monthly fee). The existing email provider remains active; no sending behaviour changes until the switch is explicitly enabled.
After every deployment, users who had the application open in a tab would receive an error on any action — submitting a form, saving a record — until they manually refreshed the page. This was caused by a security key being regenerated on every build, making the newly deployed server unable to recognise requests from pages loaded before the deployment. The key is now fixed and stable across all future builds, so this class of disruption will not recur.
One tenant was missing four database tables required for AI balance and quota tracking, and all tenants were missing a user identifier column on AI usage records. Both gaps are now corrected in production. The changes are safe to apply repeatedly and have no effect on tenants whose schemas were already up to date.
Previously, switching email infrastructure required moving all notification types at once. A new configuration option now allows individual notification types — welcome emails, OTP codes, interview invites, and so on — to be moved to the new provider independently, while everything else continues on the current provider. This enables a controlled, low-risk rollout with the ability to pause or reverse at any step.
The automated pull-request review tool can now detect when a finding it raises was also raised in an earlier review of the same change. Repeated findings are labelled as such and counted separately, making it easier for reviewers to see which problems have persisted across revisions rather than treating every round as a fresh list.
A July code change accidentally removed the database session that these scheduled jobs depend on, causing every single run to fail silently — 219 consecutive failures across all production tenants, with zero post-interview messages delivered in two weeks. The jobs now open a proper transaction per interview, load all required data in one query, and skip a single failing record instead of halting the entire tenant's queue. A data clean-up also marks the backlog of past interviews that can no longer be reminded as resolved, so they stop appearing as outstanding work.
Candidates applying for jobs through the public portal were receiving a one-time password email intended for admin users, complete with admin portal branding and a greeting placeholder that was never filled in. The verification email for external candidates now uses its own dedicated template with the correct audience and content.
Goal creation and edit notification emails were silently broken when the email provider requires an explicit subject line. This was caught during pre-launch testing of the new email provider and fixed before it could affect live traffic.
A recent deployment shipped without the expected credentials baked into the image, causing all resume uploads to fail for approximately 30 minutes. The service now falls back gracefully to the server's attached identity when explicit credentials are absent, so a missing build secret no longer takes the upload feature offline.
Every deployment of the admin portal was generating a new random encryption key, which invalidated any in-flight server actions from the previous version. The key is now stable across deploys, preventing unexpected failures for users active at the moment of a release.
Three in ten candidate self-bookings were failing silently due to a race condition when generating offer IDs, and every attempt to decline an interview returned an authentication error. Both are now fixed. Additionally, raw database error messages that were previously shown verbatim to candidates on booking failure have been replaced with plain, readable text. The 'Not Interested' and 'Request other times' buttons are now also visible even when no slots remain.
Candidates were receiving a WhatsApp confirmation for bookings that subsequently failed, and a failed message send could roll back an otherwise successful booking. The confirmation now only fires once the booking is fully committed. Separately, the interview time shown in the message was displaying 5.5 hours off due to a timezone handling error; it now shows the correct local time.
The platform can now send notifications through either the existing email provider or a new alternative, controlled by configuration with no code change required. If the new provider fails on a given send, the system automatically retries through the original provider and raises an alert, so no notification is silently lost. Three additional notification types have also been prepared for use with the new provider when needed.
All database-backed search routes were returning errors on UAT because the database connection was being made without the encryption the server requires. Encrypted connections are now enforced, restoring search functionality. Internal database calls within the search pipeline were also aligned with the service's asynchronous design to ensure consistent, predictable behaviour.
Re-running a rollback to the currently deployed image previously failed partway through, causing the forced redeploy step to be skipped. The process now recognises that the image tag is already correct and proceeds to trigger the redeploy as intended.
Two crashes in the reporting endpoints, a conversion ratio correct only by accident, a permanent guard against the startup crash recurring, three fields the interface expected but never received, and code linting re-enabled after having been switched off.
Ten of twenty-three client databases were stranded up to 140 versions behind. Reactivating any one of them would have run the entire backlog against live traffic.
A limit in how cloud services are listed meant that four of the twenty-three services — including the main production backend — were invisible to the rollback process. Had a rollback been triggered during an incident, it would have failed silently at exactly the wrong moment. All services are now found correctly regardless of how many exist.
The full internal API reference, covering 582 routes with parameters and data schemas, is no longer served on production. Development and staging environments retain access for internal teams, tightening the boundary between public and internal surfaces.
The web framework used by the AI routing service was updated to address a known session-handling security advisory. A YAML parsing vulnerability in the admin portal's linting configuration was also resolved. Both updates eliminate publicly disclosed security risks.
Database passwords and third-party API keys used in build pipelines have been moved to encrypted secret storage, matching the protection standard used elsewhere in the platform. Automated deployment to the UAT environment is also restored.
AI calls served from cache were being billed correctly but logged as if no caching occurred, making it impossible to measure whether caching was working or saving money. Both paths that write billing records now capture the actual cache token counts, so reports and future decisions about caching strategy will reflect reality.
JD parsing was locked to one setting at ₹0.66 per call with no way to tune it, plus a quality guardrail added on ranking.
Users with an expired session saw a raw system code instead of the login page; admin portal tenant links rendered as broken URLs; and Goal Settings demanded create-level permission on a read-only screen.
A candidate's technology field grew each time they were tagged to a requirement, with no cap, eventually overflowing and crashing evaluation submit.
Each silently lost or double-counted revenue rather than producing an error, which is why none had ever been reported. Also fixed: US-region résumé file paths were being corrupted.
Automation had produced eleven unreviewed automatic production releases in a single day. An explicit approval is now required, and direct pushes to production are blocked — everything must pass through UAT first.
Previously a failure left no trace whatsoever and retried silently forever. The single biggest visibility gain of the period — and what surfaced the two items below.
Monthly budget per client with per-person and per-feature allowances, a Super Admin panel to set them live without a release, a client-facing usage dashboard, and every AI call attributed to the employee who triggered it.
Custom tracker and full redesign — the largest single product change of the period.
Seven filters were shown in the interface but ignored by the system. Those now work, along with column sorting and Recruiter Name in both the grid and the Excel export.
Resolved, and the underlying database update process re-enabled after having been switched off since early August. Three client workspaces that could not receive any further updates were unblocked at the same time.
A duplicate component name crashed the application at startup, so every release was automatically rolling back and no new code could reach production.
A database query was reading a field it never requested. Masked until now by the startup crash above.
Editing older requirements crashed outright, and long skill lists were rejected on save.
Any client without location data saw an error rather than an empty list, affecting nine separate modules.
Two production tables were missing entirely, JD parsing and search-query generation were reported as a single cost line hiding each one's real cost, and queries were hardened against schema drift.
Eighteen internal section labels — "Fintech", for example — were being extracted as real company names on every CV.
Users hitting an AI Search limit saw nothing because the message went to a component that no longer existed; Global Search was hidden from the menu while still in build; and a health check was reporting a false alarm on a healthy service.
Credentials across six services moved into masked storage. Previously every collaborator could read them.
Deep health checks, encrypted-connection verification, pinned dependencies and two production alarms. The service previously had none.
Signing in now evicts the previous device, so one account cannot be shared across people simultaneously.
A supervisor can now have several active collaborators attached at once, selected together rather than one at a time.
Batch upload and parsing of résumés, rather than one file at a time.
Filtering, sorting and paging now happen on the server, so the roster stays fast as record counts grow.
A single malformed Word document could permanently hang the conversion service, taking down CV parsing for everyone on that instance. Now isolated per document, with the monitoring alarm repaired and extended.
Image zoom, a logo link back to the dashboard, a status picker, validation feedback on tags, and current-designation autocomplete.
The analytics module and its ROI enhancement, including the menu and permission wiring to reach it.
The menu entry, permissions and proxy layer for Global Search. The feature remains in build and is not yet live for clients.
Path traversal, credentials being written to logs, an injection route into the parsing function, and personal data appearing in log output — all fixed together.
Supporting-document input handling, designation autocomplete with exact matches ranked first, and module URL segregation behind a proxy layer.
Nothing matches those filters.
Release notes
Bridging talent, building futures — verified student data, honest match scores.
Candidates can now complete registration fees through a real payment checkout flow. The integration is signature-verified and webhook-confirmed, meaning activation is only granted after the payment provider confirms receipt — not just on a successful screen. The previous stub behaviour remains the fallback until the live provider is explicitly configured.
Prospective candidates can browse available opportunities on the platform without logging in. Company identities and sensitive details are masked in these public listings, and the pages are indexed by search engines to improve discoverability.
Universities can now link their local departments and courses to a common platform-wide taxonomy. This allows eligibility rules on job requirements to be defined and matched consistently across institutions, without replacing any university's own data.
Every student who had not yet had their CV analysed was incorrectly shown a score of 70/100 with a 'Good match' label. The score is now blank until a real analysis has been run, and the match-tone indicator on screen reflects the actual result.
A defect in the public showcase caused some listings to display unredacted job description text, occasionally including the real company name. The public endpoint now always shows the intended generic description, regardless of what the underlying record contains.
Enabling live payments in the production environment caused the entire API service to crash-loop because the required payment provider configuration had never been committed to the codebase. The configuration has been restored and the service starts cleanly with live payment settings active.
During a brief period of backend instability, visitors clicking into a public opportunity listing were shown a permanent-looking 'not found' page. Only a genuine missing listing now produces that message; any other error prompts the visitor to try again.
A combination of issues meant the rate limit on public opportunity pages was effectively one shared budget for all visitors simultaneously, causing legitimate users to see random access-denied errors. The platform now correctly identifies each visitor's real IP address using a trusted network header, and a separate fix ensures that header cannot be forged by a visitor to claim a fresh budget. Rate limits have also been raised to a level appropriate for normal browsing now that they apply per person rather than site-wide.
A database permissions gap meant that the final step of activating a new independent candidate — creating their account record — failed silently after payment and email verification had already been recorded. Affected candidates had paid ₹499 and completed verification but could not log in, with no automatic recovery path. The activation step now runs with the correct elevated permissions, and the issue has been confirmed resolved against a live database.
Two separate data patterns were causing internship and trainee postings to be incorrectly labelled with top-tier salary bands (e.g. "25+ LPA") on the public opportunities list. The first fix targeted postings that explicitly label their pay as a stipend; the second introduced a plausibility ceiling so that any figure implausibly large to be an annual salary (such as a bare monthly rupee amount with no unit text) is shown as "Not disclosed" rather than a misleading band. Genuine high-salary offers are unaffected.
The opportunities page previously displayed postings in newest-first order using a two-column card grid, making pay comparisons awkward. Roles are now sorted highest pay first, with stipend or undisclosed-pay postings moved to the end. The layout has been changed to a single-column list — role title, type, region, and skills on the left, salary band on the right — making it easier to scan at a glance on both desktop and mobile.
Departments and courses administration with course targeting on requirements, and seven previously hardcoded screens rewired to use it. The NIRF tab, which had displayed fabricated all-zero figures, was replaced with only data the system actually computes.
An unauthenticated remote-code-execution advisory plus seven others. The only externally disclosed vulnerability in the period — everything else here was found internally.
University-wide announcement broadcast with a composer, an internal Posting For field, an Open/Closed filter, the ability to reopen a closed requirement, and a LinkedIn profile link on the candidate header.
An invited sub-admin received no permissions at all until an administrator manually granted them. Now granted at invite, with a backfill for everyone already stuck.
Saving showed no error but changed nothing. The company name also fell back to a hardcoded demo value.
It was quietly falling back to a crude check whenever the AI response was formatted unexpectedly, telling students to add a phone number that was already on their résumé. The dashboard widget was separately showing placeholder values.
Universities can manage the student roster for a drive directly, closing a bug where students could not reliably register or apply. Drives now carry a required-skills list, so match scores reflect what the drive actually asks for.
Photo upload, additional documents, academic semester records, a proof-of-work editor, and a genuine multi-member family details list replacing a single-entry form.
An action on the candidates page was updating the wrong candidate and crashing the Advance step. Résumé preview, broken by an earlier change, was restored.
Sample requirements flashing on page load indistinguishable from real client data, document verification gating, request resolution, rate limiting, requirement validation, PDF export, image uploads and the notification bell.
Invitation emails rate-limited, and institution separation on resend confirmed — an invite can only ever go out under the correct institution.
Saving a company silently downgraded managed institutions and wiped their creation date. Separately, open browser tabs failed every action after a release until reloaded, with nothing telling the user why.
A critical finding closed: candidates could skip stages or reach Hired with no signed offer. Also fixed — adding a company never created its admin account or sent the invite, and publishing a requirement with skills attached failed outright.
AI-generated interview preparation questions, an AI résumé analyzer with a rule-based fallback, semantic match scoring using cached embeddings, and a super-admin AI usage and billing view. Built on an independent AI client with per-call usage logging from day one.
Native interview scheduling with meeting links, a candidate-facing view of scheduled interviews, and in-browser self-introduction video recording.
Fake university statistics, a fabricated platform-health panel, invented impact and compliance report tabs and a placeholder NIRF export were all removed rather than left to mislead. Alongside this: mobile usability repairs across the student, university and admin portals, and a full visual sweep.
Thirty-five findings closed, including several high severity: the Career Passport document vault never returned a working link, university invitations never sent the promised email, offer state was not backed by real closures data, and the Permission Console silently did nothing. Also fixed dashboards freezing after fifteen idle minutes.
A dedicated push to stop the product displaying fabricated identity, pipeline and profile content. Each persona portal was connected to the real backend, and independent-candidate self-registration was added.
Data erasure on request, retention policies, an age gate, and audited release of candidate contact details — a company now has to be granted access rather than simply seeing everyone's details.
Row-level isolation on student data so one institution's records cannot be reached from another, plus a partitioned audit trail recording who accessed what.
Invitations, quotas, consent capture and administrator CSV import; a rules engine for job eligibility; candidate matching and scoring; and a background worker handling mail and notifications reliably rather than inline.
Every file download is now authenticated and authorised, unsigned file links are rejected outright, uploads are validated and rate-limited, and the application refuses to start on a missing or default signing secret. Role guards added for each portal — student, university, company and admin.
A proper identity and roles model replacing a single role field: scope-based permissions enforced endpoint by endpoint across every portal, signed tokens with published verification keys, and rotating refresh tokens that detect and shut down token reuse.
A shared design-token system across the four portals, a component gallery, and a rebuilt marketing landing page.
Secret scanning, code ownership rules, cloud identity federation that removes static access keys entirely, separated database roles, database updates moved out of application startup, and least-privilege deployment permissions. This is the groundwork that makes everything after it safe to change.
Nothing matches those filters.
Release notes
Join. Refer. Earn. — turn your network into rewards.
Each deployment was generating a new encryption key, which invalidated any in-flight server-side actions from the previous version. A stable key is now supplied consistently across all deployments, so users no longer experience failures caused by this rotation.
The first live run delivered 512 emails but left no record in monitoring. Delivery was never at risk — every run is recorded in the database — but a nightly job whose successes are invisible cannot be alarmed on.
A nightly digest to candidates and referrers, sending at 20:00 IST, with no backfill flood on first enable.
Reports for super-admins covering clients, candidates and referrers, tenant-scoped reports for enterprise admins and recruiters, and every number on the page clickable through to the underlying detail.
Nothing matches those filters.