Hiring Funnel & Time to Fill
How long hiring takes and where candidates drop out. Both numbers depend on definitions that most dashboards never state: when the clock starts, which requisitions count, and which stage entry is the one you measure.
◆ The definitions that decide the numbers— the clock, the requisition types, and the stage history.
| Decision | What goes wrong without it |
|---|---|
| The clock | Approval to offer acceptance is the common definition; posting-to-accept and approval-to-start are also in use. Any of them works. Two of them on one dashboard does not. State the definition on the report. |
| Evergreen requisitions | Evergreens stay open to build pipeline, and their finalists move to a standard requisition at offer. Time to fill belongs to the hiring requisition; evergreens are excluded from averages and measured on their own terms, pipeline size and hires produced. |
| Openings, not requisitions | One requisition can carry several openings, and parent requisitions carry children. Fill counts and fill rates count openings and hires; counting requisitions undercounts both. |
| Frozen time | Requisitions freeze and unfreeze. Decide once whether frozen days count in time to fill, and land the freeze windows so the report can honor the decision either way. |
| Stage entries repeat | Candidates skip stages, revert, and interview more than once, so stage events repeat per application. The funnel counts first entry into each stage; duration in stage runs from that first entry. Using the latest instance instead quietly changes every conversion rate. |
| Reason codes | A backfill and a new position have different urgency and different benchmarks. The requisition's reason lands in the dimension so the report can split them. |
◆ The owned model, and the SQL— stage events with first-entry logic, and the funnel with time to fill from the same fact.
fct_application_event lands one row per application stage change; dim_requisition carries type, reason, openings, and freeze windows. First entry per stage, then the funnel:
-- May cohort funnel: first entry per stage, evergreens excluded
WITH first_entry AS (
SELECT e.application_key, e.stage,
MIN(e.event_date) AS entered_on
FROM fct_application_event e
JOIN dim_requisition r ON e.requisition_key = r.requisition_key
WHERE r.requisition_type <> 'Evergreen'
AND e.cohort_month = '2026-05'
GROUP BY 1, 2
)
SELECT stage,
COUNT(DISTINCT application_key) AS candidates
FROM first_entry
GROUP BY 1
ORDER BY MIN(CASE stage WHEN 'Applied' THEN 1 WHEN 'Screen' THEN 2
WHEN 'Interview' THEN 3 WHEN 'Offer' THEN 4 WHEN 'Offer Accepted' THEN 5 END)
Sample output, May cohort:
| stage | candidates | conversion |
|---|---|---|
| Applied | 140 | |
| Screen | 42 | 30.0% |
| Interview | 15 | 35.7% |
| Offer | 4 | 26.7% |
| Offer Accepted | 3 | 75.0% |
The three accepted offers are May's three hires on the Movement report; the funnel ends where the headcount series begins. Time to fill reads the same fact: approval date from the requisition, acceptance date from the stage event, averaged with backfills and new positions split. A funnel that narrows sharply at one stage is a process finding; a long time to fill with healthy conversions is a volume finding. The two together say which problem you have. Sample values are illustrative, never client data.
- time to fill
- Requisition approval to offer acceptance, unless the report says otherwise. Say it on the report.
- evergreen requisition
- Always open, builds pipeline; finalists move to a hiring requisition at offer. Excluded from averages.
- opening
- One position to fill. A requisition can carry several.
- parent and child requisitions
- A family of linked requisitions. Roll up for volume, report on children for accuracy.
- freeze window
- The period a requisition was paused. Landed so the clock decision is enforceable.
- first entry
- The earliest event moving an application into a stage. The funnel's counting rule.
- conversion
- Candidates entering a stage over candidates entering the prior stage.
- reason code
- Backfill or new position. Different benchmarks, one split.