Which hiring decisions were made in Slack and never recorded in Lever
A recruiting coordinator named Priya has two windows open on a Tuesday afternoon. On the left, Lever, filtered to the Senior Backend Engineer pipeline, twenty-three candidates across five stages. On the right, Slack, the #hiring-backend channel, where the hiring manager posted forty minutes ago: "yeah agreed, let's not move forward with Daniel, the system design answer worried me more than the code."
Daniel is still sitting in Onsite in Lever. His last feedback form was submitted six days ago. Nobody has archived him, nobody has sent the rejection, and the hiring manager believes the decision is made because he said it out loud to three people who all reacted with a thumbs up.
Priya is not looking for Daniel specifically. She found him by accident. What she actually needs, before the Thursday pipeline review with the VP of Engineering, is the full list: every candidate whose fate was settled in a channel and never written into the ATS. She has twenty-three candidates in this role, four other open roles, and roughly two thousand Slack messages across the hiring channels in the last month. There is no report that produces this list, because producing it requires reading.
What Lever shows on its own
Lever is a good ATS and it is precise about the things it is designed to hold. Every candidate has a stage, a stage-change timestamp, an owner, and a history. Interview feedback forms carry ratings and written notes tied to a specific interviewer and interview. Archive reasons are structured, so when someone is rejected properly you get a clean category. The reporting surface will happily tell you time in stage, pipeline conversion between stages, offer acceptance rate, and how long each interviewer takes to submit feedback.
Lever will also tell you, without any special configuration, that Daniel has been in Onsite for six days with no stage change. That is a real signal and Priya can filter on it.
Where it stops is the reason. Lever knows the state and the duration. It does not know whether six days of silence means the loop is still scheduling a final interview, the hiring manager is on holiday, the team is waiting on a competing offer to resolve, or the decision was already made and nobody clicked the button. Those four situations produce identical rows. A stalled-candidate report will surface all four together and hand you back a list you still have to investigate one candidate at a time.
Lever's integration with Slack is genuinely useful and worth turning on: it posts feedback reminders and can push notifications into channels. But that flow runs outward, from Lever into Slack. It does not read the conversation back and reconcile it against candidate state.
What Slack shows on its own
Slack has the decision. It is sitting in #hiring-backend in plain language, with the reasoning attached, which is often better reasoning than anything that made it into the feedback form. Slack search is capable: you can scope by channel, by person, by date range, by thread. Search from:@marcus daniel and you will find the message.
Where it stops is that you have to already know to search for Daniel. Slack has no concept of a candidate. "Daniel" in that message is a first name typed by a person, not an entity with an ID. There is no way to ask Slack for a list of people whose names appear in a hiring channel in a rejecting sentence, because Slack does not know which words are names, which names are candidates, and which sentences are rejections. It stores messages, threads, reactions and timestamps. That is what it is for.
There is a second problem, subtler than the first. Decisions in Slack are rarely phrased as decisions. Nobody writes "REJECT: Daniel Okonkwo." They write "let's not move forward," or "I'd pass," or "I think we've seen enough here," or a thumbs-down emoji under someone else's message. A keyword search built on the word "reject" will find almost none of the real rejections.
Why the gap is structural, not a missing feature
The two halves of the evidence are different kinds of object.
| Lever | Slack | |
|---|---|---|
| Unit | candidate record | message |
| Identity | stable candidate ID | a first name someone typed |
| The decision | a state change, if someone made it | a sentence, always |
| The reasoning | a form field, sometimes | the message, usually |
| Queryable by | stage, date, owner, rating | keyword, channel, author, date |
The record half is structured and complete about state. The conversation half is unstructured and complete about intent. The question Priya is asking sits precisely across the seam: it is about candidates whose intent and state disagree.
This is worth being fair about, because the reflex is to blame the tools. Neither vendor is failing at its job. Lever cannot ship this report because Lever cannot see the channel, and even a deep Slack integration would be importing text it has no reason to parse as decisions. Slack cannot ship it because Slack has no candidate list to reconcile against and no view into stage state. The seam is where the answer lives, and the seam belongs to neither product.
It is also why this is not an automation problem. A trigger-based tool fires when something happens and moves a record forward: a candidate is archived, so post a message; a form is submitted, so notify the panel. Priya's question is about the thing that did not happen. There is no event to hang a trigger on. The absence of a stage change is not a trigger, and forty minutes of channel discussion is not a record moving.
Nor is it a BI problem in the usual sense. A dashboard built on the Lever data warehouse can show you stalled candidates beautifully. It cannot show you why, because half the evidence never enters the warehouse. Modelled sources hold what was recorded. The sentence the hiring manager wrote was never a row.
What the join actually requires
Two things, and only one of them is mechanical.
The mechanical part is the key. There is no candidate ID in Slack, so the join runs on the human name, matched against the candidate list Lever already holds, scoped to the channels and the time window that matter. First names alone are ambiguous, so the match has to be scoped: candidates active in this role, in this channel, in this period. If two Daniels are live in the same pipeline, the match is genuinely uncertain and should be reported as uncertain rather than guessed.
The part that cannot be matched is the judgement. Someone has to read "the system design answer worried me more than the code" and decide whether that is a rejection, a reservation to raise at the panel, or the hiring manager thinking out loud. That distinction is not in a keyword, a field, or a reaction emoji. It is in the language, and it needs reading. This is the reason the question survives every attempt to automate it: the last step is comprehension, not lookup.
Answering it with Skopx
Skopx connects to Lever and Slack alongside nearly 1,000 other tools, and reads across them in one pass. Priya opens chat and types something close to what she would say to a colleague:
Look at #hiring-backend and #hiring-general for the last 30 days. For every candidate still in an active stage in Lever, find any message that sounds like a decision was made about them, advance or reject, and tell me which ones have no matching stage change in Lever. Show me the message and who wrote it.
The answer comes back as a list, and every row cites the Slack message it came from with a link, plus the Lever stage and the date it last changed. Priya can click through and check the reading herself, which matters, because she is going to be asked about it on Thursday.
When she finds herself asking that same question every week, she can describe the console she wants and get one, which is what Internal Apps does: a sentence in chat becomes a live view she keeps. That console shows candidates in active stages with no Lever movement in the last five days, each one next to the most recent channel message that mentions them, ranked by how decision-shaped the message reads. Each row carries a button: archive in Lever with the reason and the message quoted, which asks for confirmation before it does anything. The console reads and it acts on a click. It stores nothing of its own.
Honest limits
This does not catch decisions made in a DM, on a call, or in a channel Skopx has not been given access to. It reads what it can see.
It does not create records, hold a queue, run on a schedule, or send alerts when something appears. The console is a view you open, not a watcher. The only write is a button someone presses, with a confirmation step, and the person pressing it is the one accountable for the decision.
The reading is a judgement and it will sometimes be wrong in both directions. It will flag a hiring manager's hesitation as a decision, and it will miss a rejection phrased so obliquely that a human skimming the channel would miss it too. That is why every row shows the message rather than a verdict. The tool narrows two thousand messages to eleven candidates worth looking at. Priya still decides.
What it removes is the accident. Priya found Daniel because she happened to scroll past the message. Everyone else in that pipeline is still waiting on an email nobody knows they owe them.
Skopx Team
The Skopx engineering and product team