Employment & HR Technology · TypeScript SDK

Keep a screening agent’s rejections inside your hiring rules

Advancing a strong candidate is low risk. Rejecting one isn’t. Wrap the outcome in the TypeScript SDK, and your rules check the bias audit, the score, and the features the model used before anyone gets a no.

Screening facts, not identity

The rule reads scores and flags. Pass the candidate ID in the input and keep names in your applicant tracking system.

FactWhat it holds
roleRole family, such as software_engineer or customer_support.
match_scoreThe model’s match score.
rubric_criteria_metHow many rubric criteria the candidate met.
candidate_nyc_basedTrue when the candidate is based in New York City.
bias_audit_currentTrue when the screening tool’s bias audit is current.
demographic_proxy_features_presentTrue when the model used features that can stand in for protected traits.

Advance, review, or stop

Three candidates from the screening simulator. Run candidate screening simulator

  1. Allowed

    When: A strong match, and the bias audit is current.

    Your code: The candidate advances, and the record keeps the facts and the rule version.

    Who picks it up: The Business Owner sees time to screen. Auditors read the record inside an engagement.

  2. Sent to a person

    When: A proposed rejection with a score close to the cutoff.

    Your code: The rejection waits for a recruiter.

    Who picks it up: The recruiter with that authority, in the VeriProof Inbox, by email, or in Slack or Microsoft Teams, with reminders and escalation.

  3. Stopped

    When: A proposed rejection for a New York City candidate when the bias audit has expired.

    Your code: The rejection is blocked and isn’t recorded.

    Who picks it up: The Governance Engineer sees the audit rule that stopped it.

TypeScript: record the outcome with a review brief

reviews is the runtime from veriproof.reviews(...). The profile names a review form set up in the portal.

TypeScript · @veriproof/sdk-core

const result = await veriproof.govern(
  'candidate.screen',
  { candidateId: candidate.id, outcome: screening.outcome },
  ({ input, invocationId }) =>
    ats.recordOutcome(input, { idempotencyKey: invocationId }),
  {
    facts: {
      role: requisition.roleFamily,
      match_score: screening.matchScore,
      rubric_criteria_met: screening.criteriaMet,
      candidate_nyc_based: candidate.city === 'New York',
      bias_audit_current: audits.isCurrent(screening.modelId),
      demographic_proxy_features_present: screening.usedProxyFeatures,
    },
    review: {
      profile: 'screening-review',
      proposedActionSummary: 'Reject this candidate for the role shown.',
    },
  },
);

if (result.status === 'review_required') {
  await reviews.save(result.review);
}

Until a result is released, ats.recordOutcome doesn’t run, so the candidate’s status stays where it was.

Why TypeScript here: Recruiting platforms and their integrations often run on Node.js, so the check fits in the service that writes to the applicant tracking system.

The audit date is a fact, too

In the simulator, the employer requires a current bias audit for screening decisions about New York City candidates. Send bias_audit_current from the system that tracks your audits, so an expired audit stops rejections the day it lapses.

When HR updates the rules

Developer fix request

If HR adds a rule on accommodation requests and your agent doesn’t send that flag, the Developer gets a fix request naming it.

Changing the rule

Moving the score cutoff for recruiter review can run in Shadow against live screening first. If a governance engineer makes the change, an administrator or compliance officer approves it before it enforces on the production agent.

See how the six role portals hand off work

Where to go next

Follow a held rejection

Open the demo portal as the recruiter who reviews it, then as the Governance Engineer who owns the audit rule.