# Offer calibration desk for recruiting and finance teams

> See every open offer next to its internal pay band and a live market estimate, then log the decision and update the band in one place.

- Workflow type: app
- Services: Ashby, Job Salary Data, Slack Bot
- Categories: HR & People, Finance
- Published: 2026-08-14

## What it does

- Lists every open offer with the candidate, the role, and the proposed pay, side by side with the range saved for that job and a current market estimate for the same title and location.
- Flags each offer twice, once against your internal band and once against the market, so you can catch offers that look fine internally but sit below what the market actually pays.
- Filters down to only the offers that fall outside a band, so a calibration meeting covers just the rows that need a decision.
- Lets you record the outcome on the candidate's file, refresh an internal range that has gone stale behind a confirmation step, and post the agreed summary to your compensation review channel.

## What you'll need

- An Ashby account with permission to read offers and jobs, add candidate notes, and edit job pay ranges
- A Job Salary Data subscription for market pay estimates (the free tier works, with a small monthly lookup allowance)
- A Slack workspace with a channel your team uses for compensation reviews

## Prompt

Build me an offer calibration desk that our recruiting lead and our finance partner work out of together. Ashby's own reporting cannot join offer data against outside market pay data, so the whole point of this app is to put our open offers, our saved internal pay bands, and a live external market estimate on one screen, and then let us act on what we see without leaving it.

The main view is a single table of every open offer, loaded with Ashby List Offers. Each row shows the candidate, the job, and the proposed compensation on the offer. Next to that, show the internal compensation range saved on that job, loaded with Ashby List Jobs and matched to the offer by job id. Next to that again, show a live market estimate for that job's title and location from Job Salary Data Get Job Salary Estimate. If an offer record only carries a candidate id rather than a name, resolve the details with Ashby Get Candidate Info so the table is readable at a glance.

Every row carries two independent verdicts, not one blended score: whether the proposed compensation sits below, inside, or above the internal band, and separately whether it sits below, inside, or above the market range. Show them as two clearly labeled indicators side by side. The case we care most about is an offer that is comfortably inside our internal band but below market, because that is the signal our band has gone stale, and a single combined score would hide it. Add a filter that narrows the table to only out of band offers, meaning anything outside either the internal band or the market range.

Cache market estimates per job title and location rather than per offer, and never refetch on every page load. Several open offers on the same role should share one cached estimate. Store the time each estimate was fetched, show a last refreshed timestamp on every row that uses it, and give the user a way to refresh a single estimate on demand so nobody calibrates against a stale number. This caching is load bearing rather than a nicety: the market data is a metered external API where the entry plan allows only about 50 lookups a month, so an uncached table would burn the quota on a single page load. Each lookup returns up to three publisher estimates, so keep the low, median, and high figures along with the confidence value the API reports, and surface confidence in the row so a thin sample is visible rather than treated as fact.

From any row I can log the calibration decision as a note on the candidate in Ashby using Create Candidate Note. The note should capture the decision we reached, both verdicts, the three numbers we compared (proposed, internal band, market range), the market estimate's refresh date, and who made the call, so the reasoning is legible later to someone who was not in the room.

From the same row I can update the job's compensation range with Ashby Update Job Compensation when we agree the internal band has gone stale. This edits a live band that other people rely on, so put a confirmation step in front of it: show the current range next to the proposed new range, state which job it affects and how many open offers reference that job, and require an explicit confirm before the write. Never write the range as a side effect of any other action.

Finally, I can post the approved summary into our compensation review channel with Slack Bot Send a Message. The summary should read as a decision record: role, candidate, proposed pay, how it compared to the internal band and to market, and what we agreed. Make the destination channel configurable rather than hardcoded.

Persist the calibration decisions the team logs so reopening the app shows what has already been decided and by whom, and so a row that was reviewed last week is visibly settled instead of resurfacing as new. This is a shared desk rather than a per person view: the recruiting lead and the finance partner should see the same offers, the same flags, and the same decision history.

Do not rebuild the reporting Ashby already ships out of the box, such as time to fill, time in stage, source quality, or offer acceptance dashboards. This app earns its place through the external join and the write back, so keep it focused on that.

## How to customize

- Change how far outside a band an offer has to sit before it gets flagged
- Choose which Slack channel approved calibration summaries are posted to
- Set how long a market estimate stays fresh before the app suggests refreshing it

## FAQ

### Does this change anything in Ashby on its own?

No. Nothing runs on a schedule and nothing writes back automatically. You open the app, review the rows, and every write is a deliberate action you take. Editing a job's pay range is a live change, so it always sits behind a confirmation screen showing the current range next to the proposed one.

### Why not just use Ashby's built-in reporting?

Ashby reports well on data that lives inside Ashby, including offer acceptance, time to fill, and time in stage. What it cannot do is pull outside market pay data in and compare your offers against it. That join is the entire reason this app exists, so it deliberately does not recreate the dashboards Ashby already ships.

### How current is the market pay estimate?

Each estimate is looked up once per job title and location and then saved, so opening the app does not trigger a fresh lookup every time. Every row shows when its estimate was last refreshed, and you can refresh any single estimate on demand when a number looks out of date.

### Can our recruiting lead and finance partner both work in it at the same time?

Yes. It is one shared desk rather than a personal view. Both people see the same offers, the same flags, and the same history of calibration decisions that have already been logged, so nobody re-litigates a call that was already made.

### What if we have not filled in pay ranges on our jobs in Ashby?

The market comparison still works, and the internal comparison shows the range as missing rather than guessing at one. That is usually a useful signal in itself, and you can set the range from the same row once you agree on it.

Use this prompt in General Input: https://www.generalinput.com/prompts/offer-calibration-desk-for-recruiting-and-finance-teams