HubSpot Setup for Staffing Agencies: A Practical Guide

TL;DR: The decision that determines whether a HubSpot setup survives is what a Contact record represents. Use one Contact object with a Contact Type dropdown, not HubSpot custom objects. Build separate perm and contract pipelines, add a Company Type property so employer records stop polluting your BD reporting, and accept that HubSpot cannot hard-delete on a schedule, so GDPR retention needs an API job sitting behind the workflow. Past roughly 20 consultants, HubSpot is your CRM and Bullhorn is your ATS.

This guide covers the contact architecture decision, pipeline design for perm and contract, GDPR retention workflows, Company object configuration, and where HubSpot reaches its ceiling. It's written for agencies that are either starting a HubSpot setup for the first time or unpicking one that has already gone wrong.

Why do most HubSpot setups fail staffing agencies?

Because HubSpot assumes a Contact belongs to one Company and travels a single pipeline, and staffing data does not fit that shape. One person can be a placed candidate at one company, a candidate for a role at another, and a hiring manager at a third. HubSpot will let you build on the wrong assumption without ever flagging it, and the bill arrives six months later in the form of misfiring sequences and forecasts finance cannot use.

HubSpot's default object model assumes a Contact belongs to one Company, moves through a single pipeline, and sits somewhere in a B2B marketing-to-sales journey. A staffing agency's data doesn't fit that shape. A single person in your database might be a placed candidate at one company, a potential hire for a role at another, and a hiring manager at a third. Someone who joined as a candidate four years ago might now be a client contact. The object model doesn't accommodate this natively, and the platform won't tell you that - it will just let you add the record and move on.

The failure mode I see most often is this: an agency starts building without deciding what a Contact is. Consultants add candidates and client contacts into the same CRM without any distinguishing property. Sequences get built for BD outreach. Marketing emails go out for candidate nurture. Six months in, those sequences are firing CV tips at heads of HR, and re-engagement campaigns aimed at candidates are landing in the inboxes of people who engaged with you as clients. Your email report shows a 40% open rate, but the list it's drawing from includes candidate contacts opted in under a legitimate interest basis and client contacts opted in under explicit consent - two different consent bases, one report, meaningless data.

The pipeline problem is just as bad. When placement deals and BD deals live in the same pipeline, a forecast report adds a £15,000 placement fee to a £2,000 retainer and calls it revenue. Finance can't use that number. The consultants stop trusting the pipeline because it doesn't reflect their actual work. The CRM starts getting used as a contact book rather than an operational tool.

This is a data architecture problem that happens to live in HubSpot. The tool will let you build whatever you want. That's precisely why the architecture decision has to come first.

The rest of this post covers how to make that decision, how to build the Contact object correctly, how to structure perm and contract pipelines, what a defensible GDPR retention workflow looks like in practice, and where HubSpot hands off to Bullhorn.

How should you structure contacts for a staffing agency in HubSpot?

Use a single Contact object with a custom Contact Type dropdown (Candidate, Client Contact, Both) rather than HubSpot custom objects. Custom objects cannot be enrolled in sequences, cannot receive marketing emails, and are excluded from most contact-based reports. For any agency up to roughly 50 staff, a tidier schema is not worth losing that functionality.

There are two patterns for handling the candidate-versus-client-contact problem in HubSpot. Choosing the wrong one early costs weeks of rework.

The two patterns, and why one almost always wins

Pattern 1 uses a single Contact object with a custom 'Contact Type' property - a controlled dropdown with values like Candidate, Client Contact, and Both. All contacts live in the standard HubSpot Contact object. Segmentation, sequences, workflows, and list enrolment all work as expected. Candidate-specific properties sit alongside client-specific properties on the same record, hidden from view where irrelevant using conditional property logic.

Pattern 2 uses HubSpot's custom objects feature to house candidate records separately from standard Contact records. On paper this looks cleaner - candidates have their own object with their own properties and pipelines. In practice it breaks most of what makes HubSpot worth using.

Custom objects in HubSpot cannot be enrolled in sequences. They cannot receive marketing emails through standard email tools. They are excluded from most out-of-the-box contact-based reports. List segmentation based on custom object properties requires workarounds that add maintenance overhead and fail silently when the association logic breaks. The custom report builder can pull some cross-object data, but you lose the native list and workflow behaviour that the Contact object gives you for free.

My recommendation is Pattern 1 for any agency up to roughly 50 staff. The theoretical data cleanliness of custom objects is not worth the functional cost. You end up with a cleaner schema and a CRM you can't actually use for outreach, nurture, or candidate engagement.

Do not add Contact Type as a free-text field. Within three months you will have "candidate", "Candidate", "cand", and "recruitment candidate" as values, and your active list segmentation breaks. Build it as a dropdown from the start and enforce it through a required field on forms and manual record creation.

The properties you need on the Contact record

Beyond Contact Type, build the following properties on the Contact object before anyone starts adding records:

  • Availability Date - date picker. The single most important candidate-specific property. If this isn't on the record and being updated, your candidate pipeline reports are useless.

  • Discipline or Sector - dropdown, mapped to the verticals your agency actually works in. Keep the list short. If you have 40 options, consultants stop filling it in.

  • Candidate Status - dropdown with values: Sourcing / Active / Screened / Submitted / Placed / On Hold / Do Not Contact. This drives your active list logic and your GDPR retention workflows.

  • Last Consent Confirmed - date picker. Updated by workflow every time a candidate responds to a consent confirmation or re-consent email. Essential for the GDPR retention workflow below.

  • GDPR Status - dropdown: Consent Given / Legitimate Interest / Pending Review / Flagged for Deletion.

  • Current Placement - single-line text or, preferably, driven by an association to the active placement deal. If you're using the manual version, build a workflow that clears this field when a deal associated with the contact moves to Fell Through or a terminal lost stage.

Repurposing Lifecycle Stage for the staffing model

HubSpot's default Lifecycle Stage property runs: Subscriber / Lead / MQL / SQL / Opportunity / Customer / Evangelist. That maps to a B2B marketing motion, not a recruiting funnel. If you leave it as-is, your candidate records will sit at Lead or MQL forever, and your lifecycle reports tell you nothing useful about candidate pipeline health.

For candidate contacts, repurpose Lifecycle Stage to reflect sourcing stage. A workable set of values for most mid-market agencies: Identified / Screened / In Process / Placed / Lapsed. For client contacts, the default Lifecycle Stage values work reasonably well - Lead through to Customer maps onto a BD funnel without much distortion.

The practical implication: you will have one Lifecycle Stage property serving two different journeys depending on Contact Type. Handle this by making Lifecycle Stage a conditional property on your record views - show the candidate-relevant values on candidate records and the BD-relevant values on client contact records. It is the same underlying property, but the context changes based on Contact Type, and consultants see the right options for the record they're working on.

What pipeline stages should a staffing agency use in HubSpot?

Build separate perm and contract pipelines, never one shared pipeline. Perm runs Requisition Open, Longlist, Submitted, Interview Scheduled, Offer Made, Placed, and Fell Through as a named stage in its own right. Contract shares those and adds Timesheets Active and Redeployment Due, the second of which is the stage most agencies never build.

Separate perm and contract pipelines before you build anything else. Do not attempt to serve both models from a single pipeline. The deal lifecycle is different, the value calculation is different, and the stages that matter for forecast accuracy are different. One pipeline trying to handle both ends up cluttered, consultants stop using it, and the data you need to run the business stops being captured.

Perm pipeline stages

A workable set of perm pipeline stages for a UK mid-market agency:

  • Requisition Open

  • Longlist

  • Submitted

  • Interview Scheduled

  • Offer Made

  • Placed (Closed Won)

  • Fell Through (Closed Lost - distinct stage)

Fell Through deserves a note. Most CRMs collapse all closed-lost outcomes into a single stage. For a staffing agency, a deal that falls through post-offer is meaningfully different from one that loses at the submitted stage - the drop-off rate after offer is a useful indicator of client quality, offer-to-acceptance ratio, and whether your consultants are managing expectations correctly through the process. Keep it as a named stage and report on it separately.

Contract and temp pipeline stages

Contract and temp pipelines share most of the base stages with perm - Requisition Open through to Placed - but need two additional stages:

  • Timesheets Active - confirms the contractor has started and is generating margin. Moving a deal here should trigger a workflow notification to the account manager to check in with the client at the two-week mark.

  • Redeployment Due - set to trigger automatically when the contract end date is within 30 days. This is the stage most agencies fail to build. Without it, a placed contractor rolls off an assignment and drops off the radar until they ring to say they're free - at which point your competitor has already spoken to them.

Mapping deal value correctly

Deal value needs to be set at creation, not left blank until the placement is confirmed. Blank deal values mean your pipeline weighted forecast reports are worthless from the start.

For perm: use placement fee as a percentage of estimated first-year salary. UK mid-market recruitment typically runs at 15-20%. Build a calculated field or set this manually at deal creation based on the role salary. If you don't know the salary yet, use the band midpoint.

For contract and temp: use estimated margin over contract duration. If a contractor earns £400 per day and your margin is £50 per day on a 60-day contract, the deal value is £3,000. This needs to be entered at deal creation - either manually or via a calculated field that multiplies daily margin by estimated contract duration in days.

Association labels: why they matter and how to set them up

A candidate associated with three companies in HubSpot has no useful context unless you know the nature of each association. Did they work there? Interview there? Are they a hiring manager there? Without association labels, you can't tell.

HubSpot allows custom association labels on Contact-to-Company and Contact-to-Deal relationships. Go to Settings > Objects > Contacts > Relationships and configure labels including: Placed At, Submitted To, and Hiring Manager For. For deals, add: Candidate On Deal and Client Contact On Deal.

This is worth the twenty minutes it takes to configure. When a consultant opens a candidate record and sees three company associations, the label tells them immediately what the relationship is. Without it, they either waste time investigating or make assumptions that produce bad outreach.

Company objects: separating client accounts from employer records

A Company record in HubSpot can simultaneously represent a client you invoice, an employer where a candidate is currently placed, and a target for BD outreach. Without a Company Type property, these three use cases get conflated. Your BD pipeline report starts pulling in employer records that have never been clients. Your account revenue figures include placements at companies that aren't clients - they're just employers. The data becomes untrustworthy quickly.

The Company Type property

Add a Company Type dropdown to the Company object with values: Client / Target Account / Employer (Non-Client) / Both Client and Employer. This single property lets you filter your BD dashboard to Client and Target Account records only, and your candidate placement data to Employer records. It also lets you build workflows that behave differently based on company relationship - for example, triggering an account review task when a Client company has had no deal activity in 90 days, without that workflow firing on Employer (Non-Client) records.

Additional Company-level properties worth building at this stage:

  • Account Manager - HubSpot user property, used for ownership reporting and task routing

  • Current Active Placements - number field, updated via workflow when placement deals move to Timesheets Active or equivalent

  • YTD Revenue - calculated from closed won deal values associated with the company, filtered to the current calendar year

  • Contract Terms - single-line text or a link to a file stored in HubSpot's document library

Disabling domain-based deduplication for candidate contacts

HubSpot's automatic company association assigns contacts to companies based on email domain. For a large employer - an NHS trust, a major bank, a logistics firm - this means every candidate and every hiring manager with the same corporate email domain gets associated with the same Company record. Your account-level data for that company starts mixing placed candidate records with client contact records. Your client contact lists include people who are in your database as candidates, not as relationship targets.

The fix: disable automatic company association for any Contact where Contact Type equals Candidate. Do this via a workflow that triggers on Contact creation, checks Contact Type, and strips the auto-generated company association if the type is Candidate. Flag the record for manual association. It adds a step for consultants, but it's the only way to keep Company-level data meaningful. An account manager looking at a client record should see their contacts and revenue data - not a list of 40 candidates who happen to share the same email domain.

How long can you keep candidate data in HubSpot under UK GDPR?

Two years from last meaningful contact is the standard defensible position for speculative candidates. Anyone in an active placement or live pipeline can reasonably be held longer, provided the basis is documented and tied to the ongoing relationship. HubSpot cannot enforce any of it on its own: workflows flag records, but there is no native delete-after-X-days action, so actual removal needs a monthly manual review or a scheduled API job.

This isn't legal advice. What follows is the practical mechanics of implementing a defensible retention policy in HubSpot. UK recruitment agencies are regulated by the ICO, and the specific guidance around candidate data treats it as a higher-risk category than standard B2B contact data. The REC publishes guidance on this. Read it, apply your own legal judgement, and use HubSpot to enforce whatever policy you land on.

Building the retention workflow

The standard defensible position for speculative candidates is a 2-year retention period from last meaningful contact. For candidates with an active legitimate interest basis - someone in an active placement or current pipeline - the period can reasonably be longer, but it needs to be documented and tied to the ongoing relationship.

The properties you need, beyond those listed under contact architecture above:

  • Last Consent Confirmed - date picker, updated every time a candidate actively confirms their data is correct and they're happy to remain on your database

  • GDPR Status - dropdown: Consent Given / Legitimate Interest / Pending Review / Flagged for Deletion

  • Data Retention Expiry - calculated date field, set to 24 months from Last Consent Confirmed

The active list logic for the retention workflow: Contact Type includes Candidate, AND Last Consent Confirmed is more than 730 days ago, AND Candidate Status is not Active or Placed. Records meeting all three criteria enrol into a suppression workflow.

That workflow sends a re-consent email. Use a plain-text email here, not an HTML marketing email. Plain text has better deliverability for transactional-style communications, and the consent basis for this email is different from your marketing sends - mixing them creates a consent audit problem. The email should be short: who you are, what data you hold, a single link or reply to confirm they're happy for you to keep their record. If no response within 30 days, a workflow action sets GDPR Status to Flagged for Deletion.

The hard-delete problem and how to handle it

HubSpot does not natively delete records on a schedule. The workflow above sets a property flag. It does not delete anything. This is a hard limitation of the platform - there is no native "delete record after X days" action in HubSpot workflows.

You have two options from the point where GDPR Status is set to Flagged for Deletion:

  1. Manual monthly review. Pull an active list of records where GDPR Status = Flagged for Deletion, export it, review it, bulk delete in HubSpot. This works at low volumes but becomes a monthly maintenance burden and relies on someone actually doing it.

  2. Automated deletion via API. Build a script or an n8n workflow that runs on a schedule - weekly works well - queries HubSpot's Contacts API for records where GDPR Status = Flagged for Deletion, and calls the delete endpoint for each one. This is the approach I'd recommend for any agency with more than a few hundred candidate records entering the flagged state each month. It requires building and maintaining the automation, but it removes the manual step entirely and creates a reliable audit trail of what was deleted and when.

Whichever approach you use, log the deletions. If the ICO ever asks you to demonstrate your retention policy is being enforced, "we set a flag in HubSpot" is not sufficient on its own - you need to show records are actually being removed.

Do you need Bullhorn as well as HubSpot?

Past roughly 20 consultants, usually yes. HubSpot handles CRM, BD pipeline, marketing automation and revenue reporting well. It does not do CV parsing, job board posting, timesheet management, compliance document storage, right-to-work verification or contractor payroll, and stretching it to cover those adds complexity without adding capability.

HubSpot handles CRM, BD pipeline, marketing automation, and revenue reporting well. It does not do CV parsing, job board posting, timesheet management, compliance document storage, right-to-work verification, or contractor payroll. These are ATS and back-office functions, and HubSpot was never designed to handle them. Trying to stretch HubSpot into an ATS adds complexity without capability - you end up with a weaker version of what a dedicated system does out of the box.

What Bullhorn handles that HubSpot doesn't

The typical pattern for agencies beyond about 20 consultants is Bullhorn as the ATS and system of record for candidate operational data, with HubSpot as the CRM for client relationship management and BD pipeline. Data flows in both directions: candidate stage updates and placement confirmations from Bullhorn push to HubSpot Contact and Deal records, and new client contacts or companies created through HubSpot deals flow into Bullhorn as client corporation records.

This separation works well when the integration is built correctly. It falls apart when the integration is built on a flat connector.

Why simple connectors break on this integration

Zapier and Make handle flat data reliably - a name, an email, a status field. Bullhorn's data structure is not flat. A Bullhorn placement record references a candidate ID, a job order ID, a client corporation ID, and a series of custom fields. It uses nested arrays for submissions and placements. Passing that structure through a standard webhook into HubSpot without a transformation layer produces incomplete records, duplicate contacts, or silent failures where the sync stops writing data without throwing an error you'd notice.

The additional failure mode: no error handling when a Bullhorn record is missing a required association. If a placement exists in Bullhorn without a confirmed client corporation record - which happens when consultants skip steps in the workflow - a Zapier-style connector will either error out or create an orphaned deal in HubSpot with no associated company. At low volumes this is annoying. At moderate volumes - a few hundred placement records a month - it becomes a daily maintenance problem.

The middleware pattern that actually works

The approach that holds up past a few hundred records a month is a middleware layer - n8n or a custom API integration - that handles transformation, maps Bullhorn's nested arrays to the correct HubSpot properties, and has retry logic for failed syncs. When a sync fails because a Bullhorn record is missing a required field, the middleware logs the failure and retries rather than silently dropping the record.

Worth flagging: Bullhorn's API rate-limits at 60 requests per minute on the standard tier. If your sync is processing a backlog - after an outage, or on initial data migration - you need to build throttling into the middleware. A connector that doesn't respect the rate limit will hit the ceiling, start dropping requests, and leave you with a partial sync that's hard to reconcile.

This approach adds build time and ongoing maintenance cost compared to a Zapier connector, but it's the only integration pattern I've seen hold up under real operational load.

The three dashboards a staffing agency actually needs in HubSpot

There is a version of this section that lists fifteen reports and calls them essential. That version is not useful. Below are the three dashboards that reflect how a staffing agency actually runs its operation day-to-day, with the specific report types needed for each.

BD pipeline dashboard

This dashboard covers active business development. Build it with:

  • A pipeline snapshot report showing deals by stage, with columns for associated company, contact name, deal owner, discipline, and deal value

  • A custom report (via the custom report builder - requires Sales Hub Pro or above) showing average time in stage per deal owner, benchmarked against a threshold you set. Anything sitting in Submitted for more than 10 business days without activity is worth surfacing. This is the report that tells you where deals are stalling and which consultants have pipeline that's going cold.

This is the dashboard that needs a weekly review cadence. Not monthly - weekly. BD pipeline health degrades fast when it isn't reviewed regularly, and the time-in-stage report is only useful if someone acts on what it shows.

Placement activity dashboard

This is the revenue dashboard. Finance will use it, so it needs to be accurate from day one.

  • A deal-based custom report filtered to Closed Won, with columns for deal owner, associated contact, discipline, and deal value. This gives you placements closed this month and YTD, broken down by consultant and by sector.

  • Separate views for perm and contract pipelines - do not blend them in the same report. Perm shows placement fee revenue. Contract shows margin. Combining them produces a number that nobody can act on.

Map fee or margin value at deal level during pipeline setup, as described under pipeline setup above. If the deal value field isn't being populated consistently, this report will underreport revenue and consultants will stop trusting it.

Candidate pipeline dashboard

This dashboard covers candidate availability and engagement. Build it with a contact-based custom report showing:

  • Active candidates by Candidate Status - gives you a snapshot of where your candidate pipeline is at

  • Availability dates coming up in the next 30 days, using a date range filter on the Availability Date property

  • Last activity date per candidate record, to surface contacts that are going cold

This dashboard is only as useful as the data going into it. If consultants are not updating Availability Date and Candidate Status as part of their daily workflow, the report shows nothing meaningful. That is a process problem, not a reporting problem - and it's worth establishing the expectation before go-live rather than after, when consultants have already formed habits around the old system.

For report types: use pipeline snapshot reports for deal stage views, and the custom report builder for cross-object analysis and time-in-stage calculations. The custom report builder requires Sales Hub Pro or above - worth confirming your HubSpot tier before you start designing reports that depend on it.

The setup described throughout this post - contact architecture, pipeline design, GDPR workflows, Company configuration, and integration planning - is the same starting point I work through in an audit before any build begins. If you're about to start a HubSpot setup for your agency, or you're midway through one that isn't holding together, the audit is the right first step. You can find details on the HubSpot audit page.

Jack Roberts
Written by
Jack Roberts

Jack builds automation for UK recruitment agencies - Bullhorn, JobScience, Vincere, HubSpot and Monday.com. Seven years in-house in recruitment marketing before going full-time on the technical side, so the builds start from how a desk actually runs, not what the tool demo shows.

See where your team's time is going.

It starts with a short audit of your stack. I'll show you where consultant and back-office hours are leaking, and what it would take to get them back.

Systems That Scale.

© 2026 Stack Logic. All rights reserved.
Here's our privacy policy.

See where your team's time is going.

It starts with a short audit of your stack. I'll show you where consultant and back-office hours are leaking, and what it would take to get them back.

Systems That Scale.

© 2026 Stack Logic. All rights reserved.
Here's our privacy policy.