Founding waitlist, 10 agenciesTake a place

The Pulse Recruit MCP server

Connect any assistant that speaks the Model Context Protocol and ask your own book questions in plain language. 51 tools, as the recruiter who connected, under the agency's rules. Nothing here sends.

What it is and is not

Four verbs, and the last one is the point.

Read

Your people, companies, roles and campaigns. What is due today and what replied. Roles advertised by companies you track.

Write

Notes and tasks, marked as written by an assistant. Add people to a campaign. Record what a reply meant. Propose facts you can cite.

Spend

Searching your book, drafting a message, refreshing a record, sourcing outside the book. Each one asks first past a threshold you set.

Never

Send a message, an invitation or an email. Create a client. Add a person it only read about. Overwrite a fact somebody entered.

Connecting

An admin turns it on for the agency in Settings, AI assistants. Then each person either signs Claude in, with no token at all, or mints a personal token there for a client set up by hand. A token is shown once.

Claude on the web, desktop or phone: Settings, Connectors, Add custom connector, then Connect and Allow

https://www.pulserecruit.ai/api/mcp

Claude Code, signing in: run this, then /mcp to sign in

claude mcp add --transport http pulse-recruit https://www.pulserecruit.ai/api/mcp

Claude Code, with a personal token

claude mcp add --transport http pulse-recruit https://www.pulserecruit.ai/api/mcp --header "Authorization: Bearer pmcp_..."

Claude Desktop on macOS or Linux, in its configuration file

{
  "mcpServers": {
    "pulse-recruit": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://www.pulserecruit.ai/api/mcp",
        "--header",
        "Authorization: Bearer pmcp_..."
      ]
    }
  }
}

Claude Desktop on Windows, where npx must go through cmd

{
  "mcpServers": {
    "pulse-recruit": {
      "command": "cmd",
      "args": [
        "/c",
        "npx",
        "-y",
        "mcp-remote",
        "https://www.pulserecruit.ai/api/mcp",
        "--header",
        "Authorization: Bearer pmcp_..."
      ]
    }
  }
}

Anything else

POST https://www.pulserecruit.ai/api/mcp
Authorization: Bearer pmcp_...
Content-Type: application/json
Accept: application/json, text/event-stream

{"jsonrpc":"2.0","id":1,"method":"tools/list"}

Streamable HTTP, stateless: every request carries the bearer and no session id. The token is pmcp_ and 32 random bytes, expires on the date the settings screen shows, and can be ended there at any time, with effect on the next call. Your records go to the company behind the assistant when you use it, and the agency is responsible for that; personal plans on some assistants can train on what you send, which the picker says beside each entry.

The tools

Every one, generated from the server's own registry. 51 live.

Finding people

search_the_book

Spend. Costs money, so it reserves first and asks past your threshold.

Who the agency already knows for a role: ranked people from the ATS and from the recruiters' LinkedIn networks, each with their reasons and which pool they came from. The one answer nobody else can give. Name a job_id, or a posting_id for a role a company is advertising. The consent filter applies. Costs at most the title-widening call; a repeat on the same title is free.

job_id?idA role the agency is working.
posting_id?idOr a role a company is advertising, from roles_matching_my_desk.
limit?integerDefault 25.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-09.

who_do_we_know

Read. Touches nothing.

Colleagues already connected to people at a company, with the relationship and who holds it. The consent filter applies: a person whose consent has lapsed is not returned. Give a company_id, an outbound_company_id, or a company_name.

company_id?idA company on the ATS.
outbound_company_id?idA prospect.
company_name?stringOr just the name, if you have no id.

Since 2026-09.

find_in_market

Spend. Costs money, so it reserves first and asks past your threshold.

People NOT in the book, from the sourcing vendor, read and scored against the role, staged for a recruiter to accept in Pulse. Nobody becomes a person on the book until a recruiter accepts them. Always over the confirmation threshold: say the cost and get a yes first. Run search_the_book before this.

job_ididThe role to source for. Its title, location and description are the brief.
wanted?integerHow many people to read in detail. Default 10.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-09.

Looking things up

get_person

Read. Touches nothing.

One person: who they are now, their history on the agency's roles and their interview scorecards, their consent basis, what has happened with them recently, their upcoming interviews, and any records that may be the same person (merging happens in the product). Name exactly one of person_id (the ATS) or outbound_person_id (the recruiter's network). Ids come from search, search_the_book or who_do_we_know.

person_id?idA person on the ATS: placed, screened or applied.
outbound_person_id?idA person in the recruiter's LinkedIn network.

Since 2026-09.

get_company

Read. Touches nothing.

One company: sector, size, whether it is tracked, its open roles with the agency, and who the agency already knows there. Name exactly one of company_id or outbound_company_id.

company_id?idA company on the ATS: a client or a buyer.
outbound_company_id?idA prospect the agency sells to, not yet a client.

Since 2026-09.

get_job

Read. Touches nothing.

One role: the client, how long it has been open, its stages with how many people are in each, the titles the book is searched under for it, and its upcoming interviews (booking one happens in the product).

job_ididA role the agency is working.

Since 2026-09.

The day's work

whats_due_today

Read. Touches nothing.

The actions due now across the agency's campaigns, oldest first, grouped by where the work happens (LinkedIn, email, phone). Each is a person, the campaign, and the step. Also the recruiter's own tasks due by the end of today, as a separate list. Nothing here sends: the recruiter does each one in Pulse, and Pulse records it.

limit?integerDefault 30.

Since 2026-09.

whats_waiting_on_me

Read. Touches nothing.

People who replied and have not been answered, newest first: the thread, who they are, and the last thing they said. The most valuable list in the product.

limit?integerDefault 30.

Since 2026-09.

The desk

roles_matching_my_desk

Read. Touches nothing.

Advertised roles at tracked companies that match what the agency places: the function, the level and the place the desk has set. Newest first. Refuses until the desk's niche is set in Settings, because without it every posting would match.

limit?integerDefault 30.
days?integerPosted within this many days. Default 14.

Since 2026-09.

company_signals

Read. Touches nothing.

What a tracked company has done lately: funding, hires, departures, postings, news. Refuses for a company that is not tracked, because nothing is polled for one.

company_ididA tracked company on the ATS.
days?integerDefault 30.

Since 2026-09.

Campaigns

campaign_performance

Read. Touches nothing.

How the campaigns are doing over a window, counted in people: contacted (any send in the window), accepted (a connection request accepted), replied (a reply, or a reply label a recruiter set), interested, meetings booked, roles opened from replies and placements on them, each later stage inside the one before. An accepted invitation is not a reply. Per live campaign, with its people by status now, and the workspace's total.

days?integerThe window. Default 30.
campaign_id?idOne campaign, or every live one when omitted.

Since 2026-09.

draft_message

Spend. Costs money, so it reserves first and asks past your threshold.

A drafted message for a named person, in the org's style, from the facts Pulse holds and nothing invented. A draft, never a send: it lands where the recruiter reads it before anything goes. Give an enrolment_id for a person in a campaign, or a person id with a job_id for anybody else.

enrolment_id?idA person in a campaign, from whats_due_today. The step's template and the campaign's context are used.
outbound_person_id?idOr a person in the network with no campaign. Pair with job_id.
person_id?idOr a person on the ATS with no campaign. Pair with job_id.
job_id?idThe role the message is about, when there is no enrolment.
style?stringA style key from the org's list. Default: the step's, or 'direct'.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-09.

enrol

Write. Changes a record, as you.

Add people to a campaign. Every exclusion rule, the do-not-contact list and the off-limits register apply unchanged, and the answer says who was added, who was already in, and who was skipped and why. Nothing is sent: the first action appears in whats_due_today for a person to do. Refused for a draft or archived campaign.

campaign_ididAn active campaign, from campaign_performance.
outbound_person_ids?arrayPeople in the network.
person_ids?arrayOr people on the ATS.
off_limits_reason?stringOnly for ONE person skipped as off limits (at a client, or on the agency's register): why approaching them is allowed. Ask the recruiter; never invent it. It is kept.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

set_reply_intent

Write. Changes a record, as you.

Record what a reply meant, as the recruiter's reading of it. Interested, not now (with a date), a referral, not interested, an objection, or an auto reply. It changes what happens next in the campaign, so quote what they said in why.

enrolment_ididThe enrolment whose reply this reads, from whats_waiting_on_me or whats_due_today.
intentinterested | not_now | referral | not_interested | objection | auto_reply
why?stringOne line quoting what they said. A label with no basis cannot be argued with.
come_back?stringFor not_now: when to try again.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

preview_market_candidate

Write. Changes a record, as you.

Start marketing a candidate, free: the net starts from the candidate (companies like their employers when asked), and once judging starts the run adds the companies with jobs open now for their titles, the Dream 100 companies and market builds that hire for them, and a hiring search; minus every company the candidate worked for, applied to or is in process with, recruitment agencies, the do not contact list, and companies whose every open role is outside their area. Returns the counts, what was left out and why, and the projected cost of judging and drafting in credits. Nothing is judged and nothing is spent; market_candidate does that with this run_id and projected_credits.

person_ididA candidate who is marketable, from get_person.
lookalikes?booleanAlso companies like the candidate's previous employers, from PredictLeads, free. Default false.
off_limits_reason?stringRequired when the candidate's current employer is a client: why marketing its employee is allowed.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

market_candidate

Spend. Costs money, so it reserves first and asks past your threshold.

Run the market for a candidate inside a budget (the projected credits, reserved once): every company is judged against the candidate's characteristics from its own postings and signals, the proposal (fits, maybes with a live role, close matches) is ticked on the shortlist, up to two people a shortlisted company are found by the workspace's seniority ladder (those with a verified address and a clear greeting approved by themselves), and an email, a LinkedIn message and a connection note are drafted for each approved person. It stops at review: no message is ever sent until a person approves it on the run page. Always asks first with the projected cost.

run_ididThe run preview_market_candidate built.
projected_creditsintegerThe projection preview_market_candidate returned, in credits. The ceiling the person agrees to.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-09.

save_mpc_campaign

Write. Changes a record, as you.

Save a run as a campaign in one transaction: a list of the approved people at the shortlisted companies and the campaign marketing the candidate with the Market a candidate sequence. Saving again adds anybody approved since. The campaign opens in draft, and each message sends only once a person has approved it. Always asks first.

run_ididA run market_candidate judged.
namestringThe campaign's name, which is also the list's. Unique in the workspace.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

open_role_from_reply

Write. Changes a record, as you.

Turn an interested reply on a campaign that markets a candidate into a role: a job at the replier's company, created as a prospect when the company is not on the book, with the marketed candidate on its first stage and the reply linked to it. Needs the candidate's OK to be named to that company, which is recorded. Admins only, as every role is. Always asks first. Nothing is sent.

enrolment_ididThe enrolment whose reply was marked interested, from whats_waiting_on_me.
candidate_agreedbooleanTrue only once the candidate has agreed to be named to this company. Recorded on the enrolment with the date and who said so.
title?stringThe role's title. Default: the candidate's own title.
company_name?stringOnly when the replier has no company on record: the company the role is at. Created as a prospect when it is not on the book.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

Writing back

add_note

Write. Changes a record, as you.

Add a note about a person or a role. It appears in the activity stream marked as written by an external assistant, beside the notes people wrote. Name at least one of person_id, outbound_person_id or job_id.

bodystringThe note, in plain words.
person_id?idAbout a person on the ATS.
outbound_person_id?idAbout a person in the network.
job_id?idAbout a role. A note about a person on a role names both.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

create_task

Write. Changes a record, as you.

Create a task for the recruiter: a reminder or to-do of their own. It lands in their task list marked as created by Claude, private to them unless it is about a person or company. Nothing is assigned to anybody else and nothing is sent.

titlestringWhat has to be done, as a person would write it.
detail?string
due?stringWhen, if the person named a time. Leave out for no date.
priority?low | normal | high | urgent
person_id?idThe person it is about, on the ATS.
company_id?idThe company it is about.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

Tags

list_tags

Read. Touches nothing.

The agency's candidate tags, such as Python, Hot or Met at event: each tag's name, colour and how many people carry it, most used first.

Since 2026-10.

set_person_tags

Write. Changes a record, as you.

Add or remove tags on one person on the ATS, by name. A new name becomes a tag, as it does in the product. Up to 10 names a call. Tagging many people at once stays in the product.

person_ididA person on the ATS, from search or get_person.
add?arrayTag names to add. A name that does not exist yet is created.
remove?arrayTag names to take off.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-10.

Enrichment

enrich_person

Spend. Costs money, so it reserves first and asks past your threshold.

Refresh what Pulse holds about a person from the vendor path the sourcing panel uses. For a person in the network that is their LinkedIn profile, read once a day at most, through the agency's own connected account. Refused when their consent has lapsed.

outbound_person_id?idA person in the network. Their LinkedIn profile is read and kept.
person_id?idA person on the ATS. Not refreshable yet; see the answer.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-09.

enrich_company

Spend. Costs money, so it reserves first and asks past your threshold.

Read a prospect's LinkedIn company page and keep a dated observation of its headcount beside the record, leaving the record itself as it was. One observation per company per day.

outbound_company_ididA prospect. Its LinkedIn page is read and a dated observation kept beside it.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-09.

propose_facts

Write. Changes a record, as you.

Fill empty fields on a person Pulse already holds, from evidence you can cite. A field that already has a value is left alone whoever wrote it, a fact with no evidence is skipped, and an email or a phone is never accepted here. The answer says what was filled and what was skipped and why.

person_id?idA person on the ATS.
outbound_person_id?idA person in the network.
factsobjectField to value. ATS: title, company_name, location, linkedin_url. Network: title, headline, company_name, linkedin_url.
evidenceobjectField to the URL or quote it came from. A fact with no evidence is skipped.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

Dream 100

promote_to_dream_100

Write. Changes a record, as you.

Start tracking a company: its signals are polled weekly from now on and appear in company_signals. Refused by the database past the plan's tracking allowance. Use set_tracking_tier for daily.

company_ididA company on the ATS. A prospect is promoted by adding it as a company first, in Pulse.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

set_tracking_tier

Write. Changes a record, as you.

Move a tracked company between weekly and daily polling. Daily is a small allowance, refused by the database when it is full. A company not yet tracked is tracked at the tier you name.

company_ididA tracked company.
tierdaily | weekly
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

dream_100_status

Read. Touches nothing.

What is tracked, at which tier, what fired in the last week, and how much of the tracking allowance is used.

Since 2026-09.

Markets

save_icp

Write. Changes a record, as you.

Store a market definition once the person has confirmed it: the ICP paragraph with its exclusions, a headcount band, places, the seed accounts the client likes, and eight to twelve slices with real industry picklist values and thematic keywords. The first call refuses with the whole definition in the confirmation; the retry with the id and identical content writes. Give id to update, from_id to start from an existing market. Refuses while the desk niche is unset.

id?idAbsent to create, present to update.
from_id?idCopy an existing market as the starting draft; anything not given is taken from it.
name?stringThe client or the market. Unique in the workspace.
icp?stringOne paragraph: who qualifies and who explicitly does not.
exclusions?arrayDisqualifiers, locked.
headcount?object
places?array
seeds?arrayFive to fifteen domains the client already likes.
slices?arrayEight to twelve sub-segments, the SOP's step 1.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

list_icps

Read. Touches nothing.

Every market definition the workspace has saved: name, paragraph, exclusions, seeds, slices, who confirmed it and when, and the builds made from it. Pick one by name for the next build rather than redrafting.

include_retired?booleanAlso list markets that were retired. Default false.

Since 2026-09.

delete_icp

Write. Changes a record, as you.

Retire a saved market definition. Its builds and the companies on the book are untouched. Refused while a build on it is in progress.

icp_ididThe market to retire. Its builds and the companies they wrote stay.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

build_tam

Spend. Costs money, so it reserves first and asks past your threshold.

Build a Dream 100 from a saved definition: every slice pulled from every configured source, deduplicated by domain, relationship accounts set aside, each company judged FIT, MAYBE or NOT against the ICP and the desk, and the best hundred kept. Always asks first with the projected cost, which is a ceiling. Runs unattended for minutes to an hour; ask tam_status for progress. The full market behind the hundred, usually five to twenty five thousand companies, is a service the RecruiterGTM team builds by hand and is asked for in the product, not here.

icp_ididA saved, confirmed market definition, from list_icps.
market?stringA name for the Dream 100, so it can be found later. Defaults to the definition's name.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-09.

tam_status

Read. Touches nothing.

Where a build is: the stage, rows in and out, spend so far against the projection, which sources it used and which were missing, what it was built from, and how many companies wait on a person's verdict.

run_id?idA build, from build_tam. Absent: the latest builds in the workspace.

Since 2026-09.

tam_summary

Read. Touches nothing.

How big a market is and how it segments by slice, sector, size and place; the overlap between sources; twenty FIT companies sampled at random with their reasons, for the SOP's spot check; the companies waiting for a person's verdict, the first 300 by name with the total; and the relationship accounts set aside. Counted over every company in the build, however large.

run_ididA finished build.

Since 2026-09.

settle_tam_verdict

Write. Changes a record, as you.

Record a person's decision on a company the model could not decide. The verdict becomes fit or not, stamped as the recruiter's, and no later model verdict overwrites it.

company_ididThe company on the book, from tam_summary's needs_a_verdict.
verdictfit | notThe person's call.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

add_build_to_dream_100

Write. Changes a record, as you.

Put companies from a finished build onto the Dream 100, the set Pulse watches for signals, and take others off in the same press. The list holds a hundred: the database refuses the whole call, writing nothing, when what is coming in does not fit what is going out, and says how many more to release. A company already watched is counted, not re-added. A company with no domain cannot be watched at all and is reported. Only a qualified company joins: one still for review joins once settled as a fit, and anything else is reported as not qualified.

run_ididA finished build, from build_tam.
company_idsarrayCompanies from this build to start watching, from tam_summary's fit_sample or the build's page.
replacing?arrayCompanies on the book to stop watching, to make room. From dream_100 or the build's page. They keep their record and their signals; they stop being polled.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

Lead lists

list_lead_lists

Read. Touches nothing.

Every lead list in the workspace: its source (hand-picked, people who match, buyers at companies), the rule in prose, how many people are on it, when it was refreshed, and which campaigns point at it. Also what a new rule may use: the finished market builds, the signal kinds and job functions that exist, and the approved buyer titles.

Since 2026-09.

preview_lead_list

Read. Touches nothing.

How many people a rule would put on a list, with a sample of forty and the skips, without saving anything. Run this, show the person the count and a few names, then save_lead_list once they agree. Thirty a minute.

sourcepeople | buyerspeople: connections filtered by the ideal client and what has happened. buyers: the deciders at a set of companies.
touched?never | quiet | anynever: no message yet. quiet: nothing for quiet_days. Default any.
quiet_days?integer
has_email?booleanOnly people with a deliverable email.
kind?contact | candidate | anyDefault contact: people who could buy.
score_min?integerpeople: ideal client score at least this.
title_any?arraypeople: title contains any of these.
title_none?arraypeople: title contains none of these.
company_status?arraypeople: the employer's status on the book.
run_id?idpeople: employer fits this finished market build.
dream_100?booleanpeople: employer is on the Dream 100.
companies?objectbuyers: which companies.
who?objectbuyers: who at them. buyers means the approved ideal client titles.

Since 2026-09.

save_lead_list

Write. Changes a record, as you.

Create a lead list from a rule, or change the rule of one, and refresh it. Pulse keeps it current: every campaign pointed at it follows on the fifteen-minute sweep. The first call refuses with the rule in prose; the retry with the confirmation id and identical arguments writes. Free.

list_id?idAbsent to create, present to change the rule of an existing rule list.
namestringUnique in the workspace.
sourcepeople | buyerspeople: connections filtered by the ideal client and what has happened. buyers: the deciders at a set of companies.
touched?never | quiet | anynever: no message yet. quiet: nothing for quiet_days. Default any.
quiet_days?integer
has_email?booleanOnly people with a deliverable email.
kind?contact | candidate | anyDefault contact: people who could buy.
score_min?integerpeople: ideal client score at least this.
title_any?arraypeople: title contains any of these.
title_none?arraypeople: title contains none of these.
company_status?arraypeople: the employer's status on the book.
run_id?idpeople: employer fits this finished market build.
dream_100?booleanpeople: employer is on the Dream 100.
companies?objectbuyers: which companies.
who?objectbuyers: who at them. buyers means the approved ideal client titles.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

Other

list_tasks

Read. Touches nothing.

The recruiter's open tasks, their own reminders and to-dos, soonest first: title, due time, priority, repeat, list, and the person or company it is about. Not campaign actions; those are whats_due_today.

which?due_soon | mine | everyonedue_soon: your tasks overdue, today or tomorrow (default). mine: all your open tasks. everyone: every open task you can see.
limit?integerDefault 30.

Since 2026-09.

complete_task

Write. Changes a record, as you.

Mark one of the recruiter's tasks done, as them. A repeating task makes its next occurrence. Nothing is sent and nothing else changes.

task_ididA task from list_tasks.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-09.

list_pools

Read. Touches nothing.

Every candidate pool in the workspace: the agency's own people kept for future roles, built from stacked filters (CV words, stage reached, client, silver medallists, consent and so on) with people filed from roles or added by hand. Each with its filters in prose, how many people are in it and when it was refreshed. get_person names a person's pools.

Since 2026-09.

list_artifacts

Read. Touches nothing.

Research briefs, candidate profiles and submission packs, newest first, each at its latest version: the kind, title, version, whether it is frozen (shared or sent, so it no longer changes), and for a profile whether a newer CV is on file than the one it was built from (stale). get_artifact reads one.

kind?research_brief | candidate_profile | submission_packOnly this kind. Leave out for all three.
person_id?idOnly a candidate's profiles.
job_id?idOnly a role's packs.
limit?integerAt most this many, newest first. 20 when left out.

Since 2026-10.

get_artifact

Read. Touches nothing.

One version of a research brief, candidate profile or submission pack: its body as Pulse keeps it and the sources it rests on, each with its type (cv_file, profile_field, answer or search), label, the line it quotes and any link.

document_ididAn id from list_artifacts. Any version, not only the latest.

Since 2026-10.

get_pack_answers

Read. Touches nothing.

How the client answered on each candidate in a pack: per recipient the pack was sent to, interested or not for us, any note they left, when, and whether somebody has acted on it in Pulse. Candidates nobody has answered on are listed as unanswered.

pack_ididThe pack's id: the one create_pack returned, or a shortlist that became a pack.

Since 2026-10.

save_research_brief

Write. Changes a record, as you.

Keep research you did as a research brief in the agency's library, version 1, frozen, with the sources it rests on. Refused without at least one source. It can then be shared from Pulse like any brief.

questionstringThe question the research answers, as the person asked it.
answerstringYour answer, in plain words. Kept exactly; nothing in it may go beyond what the sources say.
sourcesarrayEvery source the answer rests on: a page with its link, or a named document. At least one.
title?stringA title for the brief. The question when left out.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-10.

build_candidate_profile

Spend. Costs money, so it reserves first and asks past your threshold.

Ask Pulse to build, or rebuild, a candidate's profile from their CV in the agency's house format. Pulse's own builder reads the CV with the person's name and contact details removed, and keeps only lines it can quote from the CV; the current employer is described, not named. About 2 credits for a text CV, more for a scan. With no CV it is built from the record and costs nothing. Returns the profile's id for get_artifact.

person_ididThe candidate. Built from the CV marked as theirs, in the agency's house format.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-10.

create_pack

Write. Changes a record, as you.

Start a submission pack for a role from candidates on it, in the order given. Returns the pack's id, who still needs a profile (build_pack_profiles) or a pitch (draft_pack_pitches), and anything that would stop the pack going out, per candidate (off limits, no lawful basis to share). Nothing is frozen or sent: a person finishes and sends it in Pulse.

job_ididThe role the pack is for.
person_idsarrayCandidates on that role, in the order the client should read them.
request_id?stringAn idempotency key. A retry with the same key returns the original result.

Since 2026-10.

build_pack_profiles

Spend. Costs money, so it reserves first and asks past your threshold.

Build a profile for each named candidate on a pack who has none yet, through Pulse's own builder, one at a time. About 2 credits each for a text CV. Reports honestly: what was built, what it cost, and who failed and why.

pack_idid
person_idsarrayThe candidates on the pack to build a profile for: those create_pack listed without one.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-10.

draft_pack_pitches

Spend. Costs money, so it reserves first and asks past your threshold.

Draft the pitch, why the agency puts them forward, for each named candidate on a pack. Pulse drafts it from the candidate's profile and the role's brief, and keeps it only if every line it quotes is in the profile; it is saved on the pack for a person to edit. About 1 credit each. Replaces a pitch already there.

pack_idid
person_idsarrayThe candidates on the pack to draft a pitch for. Each needs a profile first.
confirmation_id?idThe id from an earlier over_threshold refusal, with the same arguments.

Since 2026-10.

Confirmation and refusals

A spend over your threshold does not run. It refuses with an id, and the retry has to carry it.

A refusal, as a tool result

{
  "ok": false,
  "refused": "over_threshold",
  "message": "About 8.24 USD. Confirm to go ahead.",
  "confirmation_id": "9b1c...",
  "projected_usd": 8.24
}

The id is minted by the server after refusing, bound to a digest of the exact arguments, single use, ten minutes, and bound to your connection. Where the assistant supports elicitation the server asks the person in-call and proceeds on a click; where it does not, the assistant relays the cost and calls again with confirmation_id and the same arguments. Every success carries ok: true. Write tools accept request_id: a retry with the same key returns the original result and acts once.

over_thresholdThe projected cost is over your threshold. The answer carries confirmation_id and projected_usd. Show the person the cost; call again with the id and identical arguments once they have said yes.
over_ceilingThis connection has spent its monthly ceiling. Reading tools keep working. The answer carries ceiling_left_usd.
rate_limitedMore than sixty calls in a minute. The answer carries retry_after_s.
confirmation_mismatchThe confirmation was for different arguments, another tool, or another connection. Ask again.
confirmation_usedThe confirmation was already used. A retry after success finds it consumed, which is what makes a retry safe.
confirmation_expiredTen minutes passed. Ask again.
refused_by_ruleA rule inside the tool refused: no such person, consent lapsed, a cap reached, a fact with no evidence. The message says which.

The guards, and the log

Three limits per connection, all set on the settings screen, and one log the admin reads.

  • A monthly ceiling in dollars, default 25. Above it, spending tools refuse and reading tools keep working.
  • A confirmation threshold in dollars, default 1. A single call projected above it asks first.
  • Sixty calls a minute per connection, counting calls still in flight.
  • A vendor tool's declared cost counts against the same ceiling and threshold, and is logged as paid by the agency.
  • Every call, including refusals, is logged with who asked, which tool, what it cost, and which record it was about. The record is a reference, never a name, so erasing a person nulls it. The log keeps 180 days. Admins see every connection in the agency.

Your own vendors

An admin can put a vendor the agency already pays for beside these tools. Each tool on it is declared by hand, only reads, and returns one of the four records below or a refusal.

  • A vendor tool is named for its vendor, like clay_find_company, and is listed only to connections in the agency that declared it, never on this page.
  • It calls the vendor with GET or POST on the agency's own key. The key goes into a header on the server and is in no tool result, log row or error, and an answer that carries it back is refused.
  • The address is the admin's https base URL and a path over the declared arguments, and no argument is called url. The host is looked up at call time and refused if it resolves to a private, loopback or link-local address. Redirects are not followed, and an answer stops at 1 MB or 30 seconds.
  • The answer is mapped onto one record. Fields the map does not name are dropped, and an answer that does not fit is refused_by_rule with nothing returned.
  • Nothing a vendor returns is saved. To keep a fact about somebody already on the book, the assistant calls propose_facts.

person

A person. Needs name.
namestringTheir full name.
title?stringTheir current job title.
headline?stringThe line under their name on a profile.
company_name?stringWhere they work now.
location?stringWhere they are based.
linkedin_url?stringTheir LinkedIn profile address.

company

A company. Needs name.
namestringThe company's name.
domain?stringIts website's domain, like acme.com.
industry?stringWhat it does.
sector?stringThe sector it sells into.
headcount?numberHow many people work there, as a number.
headcount_band?stringA size band, like 51-200.
location?stringWhere it is headquartered.
open_roles?numberHow many roles it is advertising, as a number.

job

A job. Needs title.
titlestringThe role's title.
company?stringWho is hiring.
location?stringWhere the role is.
work_pattern?stringRemote, hybrid or on site.
salary_min?numberThe bottom of the range, as a number.
salary_max?numberThe top of the range, as a number.
salary_currency?stringThe currency of the range, like GBP.
opened_at?stringWhen it was posted.
closes_at?stringWhen applications close.

observation

An observation. Needs at least one of headcount, industry, location, linkedin_slug.
headcount?numberHow many people work there today, as a number.
industry?stringWhat it does, as seen today.
location?stringWhere it is, as seen today.
linkedin_slug?stringThe company's LinkedIn page name.

Changelog

Names are permanent. Arguments and results only gain fields. A retired tool keeps its name for ninety days and says what replaced it.

2026-10list_tags, set_person_tags, list_artifacts, get_artifact, get_pack_answers, save_research_brief, build_candidate_profile, create_pack, build_pack_profiles, draft_pack_pitches
2026-09search_the_book, who_do_we_know, find_in_market, get_person, get_company, get_job, search, whats_due_today, whats_waiting_on_me, roles_matching_my_desk, company_signals, campaign_performance, draft_message, enrol, set_reply_intent, preview_market_candidate, market_candidate, save_mpc_campaign, open_role_from_reply, add_note, create_task, list_tasks, complete_task, enrich_person, enrich_company, propose_facts, promote_to_dream_100, set_tracking_tier, dream_100_status, save_icp, list_icps, delete_icp, build_tam, tam_status, tam_summary, add_build_to_dream_100, settle_tam_verdict, list_lead_lists, preview_lead_list, save_lead_list, list_pools