ooligo
n8n-flow

Hiring funnel alerts with Ashby, PostgreSQL and n8n

Difficulty
intermediate
For
recruiting-leader · talent-acquisition · recruiting-ops
Recruiting & TA

Stack

Use this workflow when recruiting operations needs to know which role or stage merits a closer look. The download includes an Ashby collector, a PostgreSQL schema and an inactive n8n workflow. Its synthetic example produces four aggregate alerts. You can inspect the evidence behind each one before connecting any accounts.

Run the example

Extract the download and run python3 -B check_fixture.py with Python 3.10 or later. Then print the snapshot that feeds both detectors:

python3 -B ashby_adapter.py --config config.example.json --fixture fixtures/ashby-pages.json --as-of 2026-11-02

The date is the exclusive UTC cutoff. The daily window covers November 1 and the weekly window starts October 26. The fixture contains two pages of applications for the Analyst role and two history pages for one application. It also includes a watched role with no applications and a recently added role. The adapter reads ten synthetic response pages and carries all three roles into the snapshot.

Expected alertEvidence in the fixture
Stage conversion dropOne of four completed Screen departures advanced to Onsite. The 25% rate falls below the configured 75% reference mean minus two deviations of 10 percentage points.
Completed stage dwellThe median completed Screen interval was 48 hours. The alert threshold is 1.5 times the configured 24-hour limit.
Quiet roleThe watched Quiet role had zero entries or exits in seven days. The newer role had fewer than seven monitored days and produced no alert.
Hire trendOne application reached the configured hire stage 59 days after creation, above the example 30-day threshold. The fixture’s minimum hire count is one.

fixtures/expected-alerts.json contains the expected payloads. For one alert, run node check_postgres.mjs --one-alert after the PGlite setup in _README.md. Its quiet-role scenario queues one alert on the first run and zero new alerts on the second. The SQL check runs the hire detector first, then the stage detector. It gets one new hire alert and three new stage/role alerts. Running both again creates zero additional alerts. The example’s thresholds and small sample sizes illustrate the rules. Choose reference values and minimum samples that fit your own funnel metrics.

Prepare the source and database

Copy config.example.json and replace every invented job ID, stage ID, label, monitoring date, reference value and limit. Define which successor stages count as advancement. Calculate reference mean and deviation from eligible daily rates using the same completed-departure denominator. The rule requires at least 20 reference days and a positive deviation. Refresh dates older than 35 days are rejected. Reference backfill and refresh scheduling belong to the operator.

The separate collector calls Ashby application.list for each watched job, then application.listHistory for each application. Both are paginated POST requests using candidatesRead. The collector orders histories by stage number and uses stage IDs and timestamps. A missing page, duplicate record or inconsistent sequence aborts collection. Zero activity is meaningful only after that complete collection succeeds.

Apply schema.sql with psql, set ASHBY_API_KEY for the collector and schedule the live command in _README.md before 02:00 UTC. --persist stores one complete snapshot using the configured PostgreSQL connection. The two tables are hfa.snapshots and hfa.alerts. An identical snapshot can be loaded again. A different payload for the same date requires review. Candidate names, emails and application IDs stay out of the persisted aggregates. Review role labels before sending them to external services.

Connect the two detectors and delivery

Import hiring-funnel-anomaly-n8n.json into n8n. Bind PostgreSQL, Anthropic Header Auth and Slack Header Auth credentials. Set the approved stage and leadership channel IDs in Check channel configuration. Keep the workflow inactive until the two detectors return the expected fixture rows in a disposable database. The collector uses its own Ashby credential outside n8n.

The 02:00 UTC detector evaluates conversion, completed dwell and seven-day role activity. Conversion counts completed exits with a consecutive observed successor inside the cutoff. Terminal exits without a successor and gaps in stage order are excluded. Dwell measures completed intervals, so an alert describes a stage rather than an individual currently waiting in it. The 03:00 detector independently checks elapsed time from application creation to first entry into the configured hire stage. It counts a hire only when history is complete from ordinal zero through that hire-stage entry. A missing prefix leaves the first hire date unverified and excludes the application from this statistic. Confirm the meaning of the stage in your ATS before treating the metric as time to hire. Either detector fails visibly if that day’s snapshot is missing.

Both schedules write stable alert keys to the same outbox. A delivery tick claims one pending row after detection and every 15 minutes. Claude receives aggregate metrics for a short explanation. Slack receives the figures, explanation and reference key. Check the stored delivery_state result and the row’s state. A confirmed Slack response includes ok:true, a channel and a timestamp. A known rejection such as ok:false with channel_not_found records a failed delivery. Slack says internal_error and fatal_error can follow partial success. Those errors, unknown codes, incomplete acknowledgements and send timeouts leave acceptance uncertain. Inspect the channel for the reference before deciding whether to retry. The bundle holds uncertain sends for review. See Slack’s response contract.

Verification and operating cost

Twelve offline Python checks cover pagination, invalid inputs, date boundaries, empty roles and export structure. The shipped SQL ran on PostgreSQL 18.6 through PGlite and independently on native PostgreSQL 18.4 with synthetic records. The tests exercised both schedules, exact alert payloads and replay. The PGlite checks also covered delivery leases, confirmed acknowledgements, definite rejection and uncertainty. The unmodified export imported into n8n 2.45.7. Seven CLI runs used copies with manual triggers, local test credentials and loopback model/Slack endpoints. The actual SQL, Code and HTTP nodes processed four mock explanations and four confirmed mock posts, two per channel. Repeating the stage, hire and delivery runs added zero calls or alerts. The README gives the fixture commands. Wall-clock schedules and live provider accounts still require an operator run.

Budget for the number of applications and history pages scanned per watched role, PostgreSQL storage, n8n executions and model calls on claimed alerts. In the four-alert example, four successful first attempts would request four explanations and four Slack posts. Pre-send retries can add model usage. Measure actual token counts, delivery delay and reviewer time in your environment before committing to a cost or response target.

Use a spreadsheet or your existing ATS report when nobody owns the reference values, routing or follow-up. Revisit the detector when stable stage definitions and a named reviewer are in place. The output supports investigation of aggregate recruiting operations. The graph contains no ATS mutation step.

Files in this artifact

Download all (.zip)