iampro

Your AI assistant can drive iampro.io.

iampro.io speaks the Model Context Protocol. Connect your own Claude, ChatGPT or Grok and it searches live jobs, benchmarks salaries, logs applications, tailors CVs against your evidence vault and edits your public profile — on your data, under your account.

98tools, listed live below
22permission scopes
undowrite tools return an undo token — a bad edit is a correction, not a loss
rev. 20tool catalogue revision — if this number is higher than what your client showed when it connected, reconnect it

Connect

https://iampro.io/api/agent/mcp

  1. Add that URL as a custom connector in Claude, ChatGPT or any MCP-capable client.
  2. Sign in with your iampro.io account when prompted.
  3. Ask for something real: “which employers near me are hiring for my occupation?”

Every tool, read from the running server

ToolAccessWhat it does
jobs — read
market_outlookMarket outlook read
THE labour-market picture for the user's own occupation and area — use this for any question about their market, prospects, whether to stay or retrain, or where to move. Returns, from iampro's index of live postings: how many jobs exist near them vs nationwide vs across a reachable border, the weekly posting trend, jobs-per-100-seekers, the areas where their field is most CONCENTRATED (location quotient, not raw counts), median pay, the share of postings that are staffing agencies or anonymous, and — the core of it — career PIVOTS: occupations their skills transfer to, each with its real local and national market, median pay and pay delta, flagged when the transition is documented by O*NET (US Dept of Labor). Everything is scoped to their profile, worldwide. Never answer 'how is my market doing' from general knowledge — call this.
employers_for_my_occupationEmployers for my occupation read
Employers who actually post FOR THE USER'S OCCUPATION near them — including across a commutable border. Discovered from iampro's own posting index and keyed on what each posting IS (occupation), not on the employer's declared sector, so results are profession-precise for any profile (knowledge work as much as trades) in any country and language we index, with no national business registry involved. Staffing agencies and anonymous postings are excluded. Prefer this over who_is_hiring_near (raw hiring volume, any trade) whenever the question is 'who could hire ME'. Set include_pivots to also cover the occupations their skills transfer to.
recommended_jobsRecommended jobs read
The user's analyzed job matches (match_score 0-100; verdict APPLY/MAYBE/SKIP), strongest first, TOPPED UP with live not-yet-analyzed postings from the same profile-ranked feed as the site's 'For me' — this is the discovery tool for 'which job should I apply to' AND 'what's new to score', not just a replay of past scores. A slice of every page is always reserved for the live top-up, even once the analyzed history alone would fill it — an account with dozens of scored jobs still gets shown something new to score, every call. Defaults to jobs not yet applied to (and never hidden/declined). The result's `source` field says what you got: 'analyzed', 'live_profile_feed' (nothing analyzed yet), or 'analyzed_plus_live' (both, live ones last). Live items have match_score=null — use assess_job_fit for a scored verdict at no cost to the user (they're already paying for the LLM they're talking to; iampro's own credit-costing analyze_job is the exception, not the default — see its description). Passing min_score suppresses the live top-up (an unscored candidate can't honour a score floor). Each item carries the iampro job_posting_id — pass it to log_application once the user applies.
search_jobsSearch jobs read
Free keyword search over iampro's live job index — fresh, deduplicated, liveness-checked offers. Built for parameterized agent scans ('security architect near Denver, posted last 2 days'): keywords plus optional location/radius and recency window. Keyword-driven, not profile-filtered (recommended_jobs is the profile-ranked feed) — but when you omit location it centers on the user's profile location, so a bare 'jobs near me' stays local; pass an explicit location to scan elsewhere, or remote=true for remote-only. Results are compact — pass a job_posting_id to get_job_detail for the full description.
list_surfaced_opportunitiesList surfaced opportunities read
The offers/prospects currently pinned into the user's #search list — id, kind, what it points to, and the note explaining why. Check before surface_opportunity to avoid a redundant pin, or to see what's already shown.
get_job_detailGet job detail read
Full detail for one job posting (description, apply url, work mode, contract type, salary) — recommended_jobs only returns a summary. Use this before drafting a cover letter or answering questions about a specific listing.
analyze_jobAnalyze a job write
NOT the default for 'what do you think of this job' — use assess_job_fit for that, transparently, with no question asked first. Reach for THIS tool only when the user explicitly wants iampro's own canonical, credit-costing score (they say so, or ask for the scored/official version specifically) — most 'what do you think' questions should never reach this tool at all. Analyzes a specific job the user pastes (description text, optionally a URL/title/company) against their profile — iampro's core match: a fit score, strengths, and the gaps to address, computed by iampro's own model rather than by you. Costs one credit (or a free-trial analysis); re-analyzing the same job returns the cached result with no extra charge — that includes a job assess_job_fit already scored, since it wrote to the SAME record. When credits genuinely are the point (the user asked to spend one), the client's own UI already confirms — do NOT also ask in prose, that turns one question into two. For jobs already in iampro's index use recommended_jobs / get_job_detail instead — this is for an outside offer.
assess_job_fitAssess job fit read
THE DEFAULT for 'what do you think of this job' / 'does this fit me'. Call it directly and transparently the moment the user pastes a job and asks that — no permission prompt, no mention of credits, no asking which version they want first. It's free, and it's not a lesser answer: same output shape as analyze_job (score, verdict, matching/missing skills), saved to their account the same way — the only difference is who computes the judgment. The user should never have to decline a credit to get here; that question shouldn't come up at all unless THEY bring up iampro's official scored version specifically (that's analyze_job, and it's the exception, not this). Use THIS tool when you want iampro's own profile/evidence/rubric material to score against — it creates/finds the job posting and returns the candidate's real profile, active target, verified evidence, and a knowledge_index: one line per knowledge asset the account holds beyond the evidence rows included inline (more verified evidence, showcase stories, vault documents), each with the id to fetch its full content on demand (list_evidence / get_showcase_story / read_document). Scan the index before scoring; fetch only what looks relevant to THIS job — that keeps every asset reachable without shipping the whole vault on every call. Read that data, score the job yourself using the rubric, then call save_job_fit with your result and the job_posting_id this call returns; stopping after this one leaves nothing recorded and the user has to re-paste the job next time. This call re-sends the full profile/evidence context, so it's not free to repeat on a long batch — but that cost is about repeating THIS call, never a reason to skip save_job_fit afterward. Already have a verdict for this job — from earlier in this conversation, from recommended_jobs, from your own read of it? Skip straight to save_job_fit with the job description (or job_posting_id if you have one) and your result. It does not require calling this tool first. RUBRIC — mirror iampro's own model so a score means the same thing everywhere in their account: match_score 80-100 strong match (apply); 60-79 decent (worth trying); 40-59 weak (only if desperate); 0-39 don't bother. Be honest, not encouraging — flag a required skill they only have basic exposure to as a real gap, not a nuance. Weigh location/commute and remote-work claims against what they've said they'll accept ('remote' with 2+ office days/week is hybrid, not remote). Weigh work authorization: a country they're not eligible in is a blocking red flag regardless of everything else.
save_job_fitSave job fit write
Save YOUR verdict on a job's fit — the single call that makes a score real in the user's account. Does not require calling assess_job_fit first: pass job_posting_id if you already have one (from assess_job_fit, recommended_jobs, search_jobs, get_job_detail), OR pass description (+ optionally title/company/location/url) directly and this call creates/finds the posting itself, in the same request. Whichever way you got your verdict — your own read of a pasted job, a job you found earlier in this conversation, anything — this ONE call is what makes it real; nothing else does. Scoring several jobs in a row? Call this once per job, every time you reach a verdict, not just for the first one or the ones that feel worth it — an unsaved score is indistinguishable from one you never computed. Saves exactly like a paid analyze_job: same fields, shows up on the offer's card in their Search tab (sidebar, magnifying-glass icon — a filter chip there finds every offer an agent has treated), can be bookmarked, marked applied. Telling the user where to look? Describe that tab, never say 'AI Match' — that's a DIFFERENT, unrelated feature in the app (a quota-gated button that batch-scores their own local unanalyzed jobs), not this conversational path; calling it that will send them looking in the wrong place. verdict is re-derived from match_score server-side (same thresholds as iampro's own model), so an incoherent pairing — e.g. APPLY with a blocking red flag — is corrected automatically; don't worry about making them agree yourself.
market — read
apprenticeship_marketApprenticeship market read
How big is the APPRENTICESHIP market around a point, and who is posting into it. Returns how many work-study / apprenticeship contracts were advertised near the user in the last 90 days, how many distinct employers carry them, the top of that list with each one's page, and — the part that matters — the reminder that this is only the ADVERTISED half. Someone hunting an apprenticeship needs to know whether they are failing because the market is shut or because they are fishing where everyone else fishes; nobody gives them that number. Pass the user's trade keywords to scope it to their occupation, otherwise it counts every trade in the area and must be presented as such. Always follow it with find_employers_to_approach for the employers who take apprentices WITHOUT ever posting — that is where the contract usually comes from.
plan_tourPlan tour read
Build a WALKABLE canvassing tour around a place: the doors worth pushing, in walking order, ON A CLOCK. This is the tour ENGINE — find_employers_to_approach hands you employers, this orders them into a route you can actually walk today. Prefer it whenever the user asks for a tour, a route, an itinerary, or 'which agencies should I go see'. Two things it does that a map or a web search cannot. (1) It drops doors that DO NOT OPEN: private-security firms in Switzerland have an address and no counter — they recruit by form — so they are removed, with the reason recorded, not silently. (2) It respects the MIDDAY BREAK: measured on real data, 87% of Swiss and 88% of French agencies close at noon (12:00-13:30 CH, 12:00-14:00 FR), while only 3% of US ones do — so no break is applied in the US. Pass `start_at` (the user's LOCAL departure time, 'HH:MM') to get an `eta` per stop and the break inserted where it falls; a stop that would land in the dead window is PUSHED after it, never dropped, and carries `break_before` so you can tell the user why. Without start_at you get the walking order but no clock — never invent one. Pass EITHER lat+lng OR location (a city, geocoded server-side). AFTER the tour: use set_door_recon to prepare each door (pitch, who to ask for, real hours) and log_door_visit to record what actually happened — that ledger is what makes the NEXT tour better.
who_is_hiring_nearWho is hiring near me read
PREFER find_employers_to_approach for anything trade-matched — it scans the whole employer substrate by the user's trade + location (offer or not) and ranks it for them. Use who_is_hiring_near ONLY for raw hiring VOLUME regardless of trade (ranked by hiring_count), e.g. 'who's posting the most jobs near me'. Employers with a real hiring signal ('recrute N') near a point — the hidden market for a door-knocking / interim canvassing tour. Each result carries siret + name + lat/lng + distance_km + hiring_count + likely_roles + a propensity tier/reason. Use it to TRIAGE tour targets (start with high propensity, close, regularly hiring, matching the user's target). Pass exclude_sirets (from list_tours' already-canvassed stops) to propose a NEW tour that skips boxes already visited. Bounded: radius ≤ 30km, ≤ 25 results. Pass EITHER lat+lng OR location (a city, e.g. 'Houston, TX') — location is geocoded server-side, so you don't need to ask the user for coordinates.
find_employers_to_approachFind employers to approach read
Real employers to APPROACH DIRECTLY near a place — offer or NOT. The hidden market beyond posted jobs: who_is_hiring_near only sees boxes with a live signal; this scans the full establishment substrate by TRADE + geography, so it surfaces companies that would hire but haven't posted. Trade comes from `activity` (e.g. 'événementiel', 'restauration', 'logistique') OR, if you omit it, the user's ACTIVE TARGET — so you can just say 'find me places to approach'. Ranked SME-first weighted by hiring propensity; each result carries contact channels (phone/email/website), likely_roles, distance, an approachability note and a hiring_propensity reason. USE IT for orientation: pick the best-fit few, tell the user WHY each fits, draft a tailored spontaneous application per company, hand over the contact. If they asked for a TOUR or a ROUTE, go on and call save_tour in the same turn — otherwise offer it. Bounded: radius ≤ 25km, ≤ 25 results. Pass EITHER lat+lng OR location (a city, geocoded server-side). Each employer may carry a `hiring_track_record` (first_seen, days_seen, offers_now, offers_peak): how many distinct days we have observed it carrying a live offer, and when we first did. Use it to QUALIFY — many days seen means it hires regularly, a first_seen of a few days ago means it JUST opened its hiring and few people know yet. Absent means we have no record, not that the employer is bad. Never surface or reason on demographics — trade, size and place only.
salary — read
salarySalary read
Sourced salary benchmark for one role in one city. Returns p10–p90 percentile dispersion from official statistics (Eurostat/BLS/INSEE/SCB/BfS) when the benchmark covers the role, with provenance; falls back to low/median/high with is_estimate=true otherwise. One role+city per call.
profile — read
list_cv_stylesList CV styles read
The CV styles this person can use — the three built-in ones and any they have made themselves — plus EXACTLY what a custom style may contain. `active` is the style every CV of theirs currently comes out in. Read this before save_cv_style and take the allowed values from it: `font_families` lists the ONLY families the PDF engine actually embeds, and any other name would print as a different font without a word of warning; `bullet_chars` lists the bullets that have a glyph; `bounds` gives the numeric limits. Never guess these — they are properties of the renderer, not preferences. Describe a style to the user from its `summary` (typography, colours, layout in plain words); reading a raw config aloud tells them nothing about how it looks.
get_profileGet profile read
Read the user's full profile so you see what you're about to change instead of editing blind: name, title, summary, location, availability, contract type, target roles, skills, and every experience with its id, title, organization, dates and display_order. Call this before edit_experience / reorder_experiences / update_profile.
what_changed_sinceWhat changed since read
What the USER did on their own account since a moment you name — the way to catch up before you act on anything you read earlier. This connector is stateless: every call reads fresh data, but YOUR context is not refreshed, and a decision taken in the app never reaches you on its own. Measured, 2026-08-23: an assistant composed a CV and a cover letter for two jobs the user had dismissed as 'not interested' four days and two days earlier, then presented them as finds. Call this at the start of a working session, and again before a BATCH of writes — one call is cheaper than eleven wrong documents. Returns dismissed jobs (with the reason they gave), applications moved, targets changed, profile and evidence edits, documents saved. Free.
profile — write
set_online_account_visibilitySet online account visibility write
Show or hide ONE link (LinkedIn, GitHub, personal site, the iampro showcase) — on the public profile AND on the CVs and cover letters this account generates. The two always agree: a link hidden here stops being printed, it is never dropped from one surface and kept on the other. Nothing is deleted, so it can be shown again anytime. CHOOSE THE SCOPE — this is the whole point of the tool. 'everywhere' is discretion: 'I don't want this account seen at all'. 'identity' is relevance: the link is fine, it just does not belong on THIS identity. A GitHub is the strongest proof on a data CV and a liability on a building-trade one — same account, two identities, two answers. When the user objects to a link on ONE document, that is almost always scope 'identity': hiding it everywhere would also strip it from the CVs where it earns them the job. Ask which one they mean rather than guessing when it is genuinely unclear. Prefer this over update_profile: that one REPLACES the whole online_accounts list and drops by omission any account you forget to send back.
update_profileUpdate profile write
Update a small, safe subset of the user's profile on their behalf — only the field(s) passed are touched, everything else is left alone. Covers the public-profile text shown at profile.iampro.io (name, title/headline, summary, skills, online_accounts) plus search/matching metadata (location, availability, contract_type wanted, target_roles) and the CV contact phone. Use when the user mentions a change in conversation (moved city, new headline, reworded summary, a skill they now have, their LinkedIn or GitHub link) instead of telling them to go edit it in the app. For experiences, proof cards or stories use the dedicated tools.
save_cv_styleCreate or adjust a CV style write
Create or adjust one of the person's OWN CV styles, and optionally make it the one their CVs come out in. A style is the CLOTHING of a proven single-column, ATS-safe layout: type family, sizes, colours, bullet character, margins, section rules. There is deliberately no page size, no column count and no template — a CV made unreadable by its own author is still an unreadable CV. Pass `style_id` to change an existing one; pass `name` (with an optional `base` to copy) to make a new one. `config` is PARTIAL: send only what you change — the rest is inherited from the base when creating, and kept as-is when updating. ⚠️ Every value is checked server-side and REFUSED with a reason rather than quietly corrected: a colour that is not #RRGGBB, a font that is not embedded, a bullet with no glyph, a size or a margin out of bounds. Call list_cv_styles first and take the values from it. Two judgement rules worth holding: contrast is not decoration — pale text on white survives a screen and dies on a recruiter's printer; and a style the person did not ask you to change is one you leave alone.
edit_experienceEdit experience write
Edit one experience the user already has (id from get_profile). Only the field(s) you pass change; the rest is left alone. Use it to fix a name (e.g. a company/product renamed), correct dates, rewrite the description, or curate skills_used — the per-role skill list that drives the public page's 'Proven by experience' tier. TO CLEAR A FIELD, pass it as null — that is how you say 'there is no employer here'. Someone self-employed, freelancing, or on a personal project has no organization, and the CV then prints the role alone instead of 'Independent Data Engineer at Indépendant'. `title` is the one field that cannot be cleared. The response lists what was emptied under `cleared`, because a removed value does not show up in the row you get back.
add_experienceAdd experience write
Add a new experience to the profile (work, education, certification or project). At least a title is required; dates and description optional. For a CERTIFICATION, carry the proof with it: `credential_id` and `url` (the issuer's verification page), plus `start_date` for the issue date and `end_date` when it expires. Name + issuer alone is a claim; the id and the link are what a recruiter can check.
reorder_experiencesReorder experiences write
Set the display order of experiences by passing their ids (from get_profile) in the order you want them shown, first = top. Use it to lead a targeted CV with the most relevant experience. The reply returns the resulting `order` plus `applied` and `not_found` (ids that matched no experience of this user, which move nothing). `applied: false` means the order is NOT what you asked for.
delete_experienceDelete experience delete
Permanently remove an experience (and its achievements) from the profile. Irreversible — confirm with the user first. To only hide it on one target's CV, use set_experience_visibility instead.
set_document_rulesSet document rules write
Record what the user has DECIDED about their own documents, so it outlives this conversation. 'Never name my products', 'don't print my full street address', 'don't mention my birth year' — said in a chat, such a decision is lost the moment the chat ends, and the next CV breaks it. Measured, 2026-08-23: a user removed a platform list from their employer name and got it back on four freshly composed CVs. Rules are served to whoever composes a CV or a cover letter, and `banned_terms` is ENFORCED — save_cv and save_cover_letter refuse a document containing one, the way they already refuse an unbacked figure. Give the FULL list every time: this replaces what was stored (send the list without a rule to drop it, or [] to clear everything). Write the rule in the user's own words, and only when they actually asked for it — this is their instruction to the product, not your reading of their taste.
targets — read
list_targetsList targets read
The user's targets — the activable identities (capacité × canal × géo) they pursue. The active one drives search/tour/CV. By default returns only the identities the user has KEPT. Pass status='proposed' to see what the pathways engine has suggested and not yet been claimed — those are the ones to help them choose between (then adopt_target); never present a proposal as an identity they already hold. status='archived' returns what they put away.
target_gapsTarget gaps read
Is the user's evidence vault THIN for a target? Returns coverage and the missing capabilities the CV can't yet back. Use this BEFORE generating a CV for an interim/stopgap target: when `thin` is true, interview the user to surface transferable proof (volunteering, military, sports, side jobs, licenses, languages, physical/public-contact work) and write it with add_evidence — then the CV is grounded instead of hollow. Omit target_id to use the user's active target.
targets — write
adopt_targetAdopt target write
Keep one of the pathways engine's PROPOSED targets among the user's identities — 'this one is mine'. Use after list_targets(status='proposed') once the user has said which suggestions fit. Does NOT switch the working lens: adopting three of nine suggestions must not flip their search three times (activate_target is the lens verb). Idempotent and reversible.
set_experience_labelRelabel an experience for one identity's page write
Override how ONE experience reads on ONE identity — title, organization, description and/or period. An experience is a single row of truth (dates, facts), but its wording serves different readers: 'Firefighter / Watch Commander · Paris Fire Brigade' speaks to an international data recruiter, while the same row must read 'Sapeur-pompier / chef de garde · Brigade de sapeurs-pompiers de Paris' to a Geneva security recruiter. Each omitted field keeps its current value; pass an empty string to clear an override (falls back to the shared row). Clearing everything removes the override entirely. title/organization/description/period appear on BOTH surfaces this identity has — its public facet page AND its CV (get_cv reapplies them after every regeneration, so they hold without being asked twice); `bullets` is CV-only, the facet page has no per-role bullet list. Other identities and the shared base profile keep the original row either way. Find experience ids on get_profile, target ids on list_targets.
activate_targetActivate target write
Make one of the user's targets the ACTIVE lens — search, the agency tour, and generated CVs all run against it. At most one active at a time; reversible. Use the id from list_targets.
set_hub_faceSet hub face write
Choose which identity the user's MAIN public page shows (profile.iampro.io/<their-slug>). Distinct from activate_target: that one says what they SEARCH for, this one what they SHOW to whoever opens their address — switching the search lens used to repaint the shopfront. Pass on=false to hand the page back to its default (it follows the active identity). At most one at a time; reversible. Use the id from list_targets.
archive_targetArchive target write
Drop one of the user's targets from list_targets/the switcher without deleting it — status='active' is sticky (activate_target never reverts it), so a target that was ever briefly the working lens lingers forever unless explicitly archived. Reversible: activate_target or set_target on the same id brings it back. Use the id from list_targets.
set_targetSet target write
Create or refine a target (a job-search identity: capability × channel × geography). Pass label to create a new one; pass id to edit. Activation is separate (activate_target). Set `language` when this identity's CV must be written in a specific language (e.g. a border-crosser pursuing both French and German-speaking-Swiss roles from the same evidence vault) — get_cv reads it instead of auto-detecting. Set `cv_format` to 'dossier' for markets that read for depth (ESN/consulting, Swiss/French enterprise, public sector) — get_cv then writes an explicit dossier de compétences per role instead of the concise 'impact' default.
vault — read
list_evidenceList evidence read
Read the user's evidence vault (the claims backing their CVs) — verified and unverified. Use it BEFORE add_evidence to avoid asking about / duplicating something already captured, and before get_cv to know what the generated CV will actually be grounded in.
list_share_linksList shared dossiers and their opens read
The user's document share links, newest first: url, title, active/expired/revoked, view_count and last_viewed_at. Read it to answer 'has anyone looked at my dossier?' — a share opened Tuesday is a follow-up on Thursday; one never opened after two weeks means the application needs another channel. Also read it BEFORE create_share_link to reuse a still-active dossier instead of minting a duplicate.
list_referencesList references read
The user's referees — id, name, role, company, relationship, contact details, contact_visibility, and which identities each serves (from assign_to_identity). Use before assembling a dossier that needs references, or to check a referee isn't already saved before adding a possible duplicate.
get_pathwaysGet return-to-work pathways read
Read the user's return-to-employment pathways — the routes iampro projects from their evidence vault (each a direction with its own angle and CV). Use it to discuss options and pick a target to pursue. Returns null if none have been generated yet.
former_employers_hiringFormer employers hiring now read
The user's OWN past employers that are hiring RIGHT NOW — warm doors. Crosses their work history (from the active profile) with the live hiring signal. A former employer already knows the candidate → a warm reintroduction, not a cold application, and it makes an employment gap invisible. This is the top lever for a stalled search or a long career gap: reactivate people who already know them. Surface these FIRST when the user has a gap or feels stuck. Returns each former employer with its live hiring_count + likely_roles + links. No input — reads the active profile.
list_dossiersList dossiers read
The user's APPLICATION DOSSIERS — each one a named set of vault pieces assembled for a particular application, with who it is for, how many pieces it holds, and whether it has already been sent. A dossier is COMPOSED, not filed: a piece is never copied or moved into one, it APPEARS in it, so the same PSE1 certificate can sit in the firefighter dossier and the first-aid-trainer dossier at once. Removing a piece from a dossier never deletes it from the vault. A dossier that has been sent is FROZEN — the same rule as a sent CV. Duplicate it to start again from there.
open_dossierOpen dossier read
One dossier and the pieces in it, in order. Each piece carries `autres_dossiers` — how many OTHER dossiers also use it. Say that number before proposing to remove or delete anything: a piece three applications rest on is not the same object as one used nowhere else.
list_documentsList documents read
List the documents the user uploaded to their iampro vault (CVs, diplomas, certificates, attestations) — id, filename, type, title, status, and how many evidence claims each produced. Use read_document to get a document's full text.
read_documentRead document read
Read one uploaded document's faithful full-text transcription (plus metadata and the evidence extracted from it). Use it to pull an exact detail the user can't remember — a precise qualification title, a date, an issuer — straight from their own uploaded proof, instead of asking them to re-upload it. document_id from list_documents.
vault — write
organise_documentsRename, re-section and order vault pieces write
Curate how the user's vault pieces read on ONE identity's public page: rename a piece, move it to another section, and order the pieces within a section. A dossier is COMPOSED — the diploma that opens the wall when applying for fire-safety work is not the one that opens it for a first-aid trainer role. TWO LEVELS, do not confuse them. `doc_type` is what a piece IS — one of the fixed kinds, true everywhere, and it governs what may be published. `section` is the HEADING it appears under on ONE identity's page, free text, the user's own words. When they ask for a category the kinds do not have — 'SDIS 78', 'Distinctions', pieces grouped by who issued them — that is `section`, and it costs nothing: a diploma and a service record can sit under the same heading while each keeps its true kind. Never answer that such a category is impossible. Renaming and doc_type are GLOBAL; section and ORDER belong to the identity, so two identities can arrange the same pieces differently. Get ids from list_documents. Scanner filenames ('scan_20260531074439') are exactly what a recruiter should never read: rename them. The reply names what actually moved: `changed` lists the fields this call altered and is EMPTY when nothing did — the `document` block is the current state, never proof of a change.
add_evidenceAdd evidence write
Write one piece of verified-capability evidence into the user's vault on their behalf — the way a connected LLM closes the elicitation loop after interviewing the user. Added UNVERIFIED: the user confirms it on iampro before it counts. Reversible (returns an undo_token). One claim per call.
create_share_linkShare a dossier or an identity page, temporarily write
Create an expiring, revocable link that shows a HAND-PICKED selection of the user's vault documents (diplomas, service records, attestations) to one recipient — profile.iampro.io/d/<token>. This is the instrument of a targeted application: 'clean record, impeccable service' SHOWN instead of claimed. Pick the documents WITH the user (list_documents first), never share the whole vault, and mind sensitivity: detailed service evaluations or anything with personal data beyond the user's own name needs their explicit go-ahead, piece by piece. Title = what the recipient reads ('Dossier de candidature — <company>'); lang = the recipient's language; expires_days defaults to 30 (max 90, or 0 = never expires). Returns `share.url` to paste into the application email, and `share.id`. The user sees every open (view count + timestamps) — an unopened dossier is a dead application, an opened one is a follow-up. Pass `share.id` as share_link_id to log_application so list_applications can surface those opens on the right application, instead of you having to cross-reference list_share_links by hand. kind='facet' makes the SAME kind of link over an IDENTITY page: pass target_id (from list_targets). The recipient opens that identity's public page even when it is NOT published — the way to show one tailored face to one employer without putting it online, at an opaque address that reveals nothing about the account or any other identity (unlike the identity's own /{username}/{facet} address, which shares a path with every other published identity). doc_ids is then OPTIONAL: pieces you pass travel with the page (its 'Supporting documents' section lets that recipient — and only them — open the files). With expires_days=0 this becomes a DURABLE address for that identity — the one to put on a CV or reuse across every application in that trade — still revocable any time via revoke_share_link. Same revocation, same open log either way.
revoke_share_linkRevoke a shared dossier write
Kill a share link immediately (the page and every document behind it stop resolving). The record stays — what was shared, when, how often opened. Use when the process is over, the link reached the wrong hands, or the user asks. Needs the token (from list_share_links).
add_referenceAdd reference write
Add a REFEREE to the user's vault — a real person who can vouch for them, distinct from write_showcase_story (a story's who_can_verify is free text on ONE story; a referee is an entity: it gets listed, called, and attached to whichever identities it actually serves via assign_to_identity(kind='reference')). NEVER invent a name — only record a person the user actually named. `role`/`relationship` should describe the relationship AS IT WAS: a referee's authority to vouch comes from what they were to the user back then, not their current title. contact_visibility defaults to 'on_request' — ask before setting 'shared', never assume a referee's contact details may be handed out. Reversible via delete_reference.
delete_referenceDelete reference write
Remove one referee from the user's vault. Confirm with the user first and delete only on their explicit go-ahead. Soft-delete: the row is kept for audit but disappears from list_references and every identity it was attached to. reference_id from list_references.
verify_evidenceVerify evidence write
Mark evidence as confirmed by the user (verified=true) after they've told you it's accurate — or retract it (verified=false). ONLY a user-verified claim is citeable on a CV, so unverified evidence is why a CV comes out hollow; walking the user through confirming their evidence unblocks it. Never verify on your own judgement — the user must confirm the claim is true. ⚠️ CONFIRM IN ONE PASS, NOT ONE CLAIM AT A TIME. Pass `evidence_ids` with the whole batch. A document drop yields 5-10 claims; asking about each in turn is ten questions for one upload, and the person came to save time. READ THEM OUT as a numbered list, ask 'anything wrong?', and verify the rest in a single call — a 'yes except the third' is one answer, not ten. The read is the model's, not theirs: it can reassign a fact silently (a note thanking her for covering while THE PARENT'S mother was in hospital came back as her own family member, marked 'confident'), so the list must be read aloud, not summarised.
edit_evidenceEdit evidence write
Re-edit the CONTENT of one existing evidence item in the user's vault — the claim wording, claim_type, confidence, impact metric, keywords or skills — e.g. after the user corrects or sharpens a claim. Editing the content RESETS it to UNVERIFIED: the user re-confirms it on iampro before it counts again (their prior confirmation was of the old wording). Only the field(s) passed are changed. evidence_id from list_evidence. To create a new claim use add_evidence; to only confirm/retract use verify_evidence.
delete_evidenceDelete evidence delete
Remove one evidence item from the user's vault — for a claim that's simply wrong or unwanted. For DUPLICATES (the same claim saved several times — a document re-extracted more than once is a common cause) use merge_evidence instead: it keeps the union of skills/keywords and the best confidence instead of throwing a row away outright. Confirm with the user first and delete only on their explicit go-ahead; to fix wording keep the item and use edit_evidence instead. Soft-delete: the row is kept for audit but disappears from every surface (vault, CV grounding, pathways). evidence_id from list_evidence.
merge_evidenceMerge evidence write
Collapse duplicate evidence rows into ONE — the cleanup for claims that ended up saved several times (a document re-extracted more than once is a common cause: '8 years as operational firefighter' under three different ids proves nothing more than it once did, but inflates every count the vault reports, target_gaps included, and clutters CV grounding with near-duplicate bullets). Spot these by comparing claim text on list_evidence/list_documents — near-identical wording under the same or a related source is the tell. keep_id survives; every id in merge_ids is retired (kept for audit, excluded everywhere is_active matters). The survivor absorbs the UNION of skills/keywords, the HIGHEST confidence in the group, and stays verified if ANY row in the group was — nothing the user already confirmed is lost. Reversible only in the sense the retired rows are kept, not deleted; there is no single-call undo, so get user confirmation before merging rows that aren't obviously the same claim.
regenerate_pathwaysRegenerate return-to-work pathways write
Recompute the user's return-to-employment pathways from their CURRENT evidence vault + profile, then return the fresh set. Use this when get_pathways came back stale (its 'stale' flag is true, or the user just added/verified evidence or changed their profile) instead of only telling them it's out of date — this closes the loop. Costs one iampro AI generation; call it when the vault actually changed, not on every turn.
upload_documentUpload document write
Put ONE file into the user's iampro vault — the missing half of list_documents/read_document/delete_document: they can see and read the vault, this is the only way to ADD to it from the conversation. Use it the moment the user hands you a piece — a certificat de travail, a diploma scan, an attestation — instead of telling them to go upload it on iampro themselves. It runs the SAME extraction as the app's own drop zone: the file is archived, then read for verified-capability evidence in the background of this call, so the returned `evidence_created` count is final, not a promise. Content travels base64-encoded IN the call (no separate upload step) — decode the file's raw bytes yourself; do not send a URL, a path, or a text description of the file. Capped at 8 MB decoded: comfortably above any scan or DOCX a candidate actually has, and chosen so the base64 string never dwarfs the conversation it rides in. For anything larger, tell the user to drop it on iampro directly. Pass `doc_type` when you already know what the piece is — it is then AUTHORITATIVE and extraction will not override it (the same rule that protects a user's own correction on the card). Leave it out and extraction classifies the piece itself. Same for `title`: set it to skip the auto-generated one, e.g. when the user names the piece themselves. Re-dropping a file already in the vault (same bytes) is a no-op: you get back the existing document, `duplicate: true`, and nothing is re-extracted — safe to call again if you are not sure the user already sent this one. A document lands unsectioned and on no identity's public page. Use organise_documents afterward to file it under a section and an identity, and create_share_link to include it in a dossier. Reversible: delete_document removes it (the file only — any evidence it already produced stays).
create_dossierCreate dossier write
Start an application dossier. Give it the name the user will recognise in three weeks and, when known, who it is for ('SDIS 74 — Annecy', 'Klanik — Sophia Antipolis'). Pass job_posting_id when the dossier answers a specific offer. Then fill it with add_to_dossier.
add_to_dossierAdd to dossier write
Add vault pieces to a dossier — document ids from list_documents. ADDS, never moves: the pieces stay in every other dossier they are in. Adding a piece that is already there does nothing and is not an error.
remove_from_dossierRemove from dossier write
Take one piece OUT of a dossier. This does NOT delete it — the piece stays in the vault and in every other dossier. Deleting a piece for good is delete_document, a different act; never reach for it when the user asks to take something out of a dossier.
delete_documentDelete document delete
Delete one uploaded document (piece) from the user's iampro vault — use it to clear a duplicate or a wrong upload the user asked to remove. The evidence claims that document produced are KEPT (they may already be verified or cited on a CV) — only the file and its metadata go. Irreversible: the original scan is gone. Get explicit user go-ahead naming the piece before calling. document_id from list_documents.
cv — read
get_cvGet CV read
Generate (or return the cached) profile-view CV for a target, grounded in the user's VERIFIED evidence — works for any target, whether or not it came from a reviewed pathway. Check target_gaps first — if `thin` is true, interview the user and add_evidence before calling this, or the CV will be hollow. Omit target_id to use the active target. update_cv_content's edits are DURABLE — a regeneration reapplies them automatically, you never need to redo them. If this call actually regenerates (new evidence/experience changed the ledger) AND the row it replaces had prior hand-edits, the response still carries `replaced_manual_edits: true` as a heads-up that a new version superseded an edited one — informational only, the edits themselves are already carried over into this new version.
get_cv_materialGet CV material read
Get everything you (the assistant) need to COMPOSE a CV for a target YOURSELF — free, no iampro AI call: the target framing, the user's profile with visible experiences, their VERIFIED evidence (the only citeable claims), their real `contact` block (email, phone, links — so you never leave [email]/[phone]/[LinkedIn] placeholders), the honesty rules, and the exact JSON schema to hand back to save_cv. Prefer this over get_cv when the user is chatting with you: you write the CV, then save_cv stores it (renders to PDF). get_cv triggers iampro's own paid generation instead. Omit target_id for the active target.
list_cvsList cvs read
The CVs this account holds — every version, newest first, for the identity or the job you name. Until this existed, a connected assistant could WRITE a CV and never SEE the ones already there: asked to fix something across someone's CVs, it had no way to know what they contained, or even how many there were. Each line says which identity it was composed under, which job it targets, its language, whether it was actually SENT — a sent CV is a piece of that application's record and is never rewritten — and whether it still follows the profile (`follows_profile`: its entries declare their experience_ids, so employer, agency, place and dates are resolved from the profile at render time instead of being frozen copies). Read one in full with read_cv.
read_cvRead cv read
Read one stored CV in full, by id (from list_cvs). Returns what the RECRUITER would see: the facts are resolved from the profile exactly as the renderer resolves them, so what you read is what the PDF prints — not the raw stored copy. Use it before telling the user what one of their CVs says, and before fixing anything in it.
cv — write
update_cv_contentUpdate CV content write
Hand-edit a target's current CV content — the mechanism for helping the user build an optimal profile through conversation: draft/refine the professional_summary, a headline, per-experience bullets, skills emphasis, or which certifications to feature, one or several at a time. This is a direct edit, NOT a regeneration — it never overwrites the user's own prior edits with a fresh LLM pass, and it doesn't consume a version-history slot the way get_cv's regeneration does. Call get_cv first (there must be a CV to edit) and target_gaps to know what's thin. Omit target_id to use the active target.
set_experience_visibilitySet experience visibility write
Hide or show one Experience in a target's CV/profile view — e.g. hide a hospitality role from the 'Data Engineer' identity while keeping it visible on 'Restaurant Manager'. Hidden experiences are excluded from future CV generations for this target AND stripped from what's shown right now, with no regeneration needed. Fully reversible — hiding never deletes the experience or its reformulated bullets. Omit target_id to use the active target.
assign_to_identityAssign to identity write
Say which identities a piece of the vault serves — an evidence claim, a Proof Card, a document, an experience, or a referee. The user pursues several identities over ONE vault (e.g. Data engineer AND security agent), and the same diploma that opens one trade is noise for another. Use this when the user tells you a piece belongs to a particular search, or when you notice a generated CV leaning on material from the wrong life. relation='featured' foregrounds the piece for that identity; 'excluded' keeps it out. A piece with no assignment at all stays available to EVERY identity — that is the default and usually the right one, because it is what lets a transferable CV reach across trades. Featuring WEIGHTS the ranking, it does not filter: prefer it to excluding. Reserve 'excluded' for material the user actively does not want associated with that search. Fully reversible via unassign=true, and nothing is ever deleted. Omit target_ids to use the active identity. Returns the resulting STATE, not a count: `applied` is true only when every piece ended in the requested relation, `not_applied` lists the ones that did not, and each identity comes back with its excluded/featured/shared ids. Report from those fields — do not infer success from anything else.
save_cvSave CV write
Store a CV YOU composed (from get_cv_material) for a target so iampro renders it to PDF and keeps it. iampro validates it first: any quantified claim (%, ×, money) not backed by the user's verified evidence or profile is REJECTED and returned to you to fix — iampro's promise is a CV the user can defend, so never invent figures. On success the saved CV becomes the one get_cv returns. PASS job_posting_id when you composed this CV for a specific offer — that is what makes it show up on that offer's card in the app, the same way save_cover_letter already does; without it the CV exists but the user cannot find it there. When this CV is actually sent, pass the returned `id` as cv_id to log_application — that link is what lets application_funnel say which CV version actually answers better; without it the save is just storage.
showcase — read
list_showcase_storiesList showcase stories read
Read the user's showcase stories — STAR-format proof cards that can be made public on their profile.iampro.io page (a shareable link, separate from the private CV). Each item: id, title, is_public, is_highlight, experience_id, has_article (whether a long-form article_md body exists). Use it BEFORE write_showcase_story to avoid drafting a duplicate of something already captured. This is an index, not the content: call get_showcase_story to read one.
get_showcase_storyGet showcase story read
Read ONE showcase story in full — every field write_showcase_story can set, the long-form article_md body included. list_showcase_stories only returns titles and metadata, so call this BEFORE editing an existing story: write_showcase_story is a partial update, and any change that depends on the current text — fixing a URL inside the article, appending a section, translating it — is guesswork without reading it first. Rewriting a body you have not read silently discards whatever was there. Pass lang to also get one stored translation; article_langs always lists which translations exist.
showcase — write
write_showcase_storyWrite showcase story write
Create or edit (pass achievement_id) a showcase story — a.k.a. a PROOF CARD on the public profile (profile.iampro.io): this is THE tool for rewording or correcting one (its title included — e.g. a card that overstates a prototype as a production system). Find its id with list_showcase_stories. It is the mechanism for helping the user turn a conversation about a project into a public-ready STAR narrative: situation/action/result, plus optional depth (constraints, why_this_approach, challenges_faced, what_i_learned) — and, for the full recruiter-facing case study, an article_md long-form body (see that field's description; analyzing the user's actual repo before writing it produces far better articles than working from conversation alone). A new story is PUBLIC as soon as it is written (2026-08-03) — the experience carrying it already is, and a private bullet under a public entry made the user's work invisible to them. So SAY SO: tell the user the story is live at the review link, and that set_showcase_visibility(false) takes it down in one call. Never publish something you had to guess at. Protect the employer this story is about: never include unpublished financials or metrics, named clients or deals covered by an NDA, proprietary internal strategy, security details, or source code, or identifying details about named colleagues who haven't consented to being mentioned. Reframe around what the CANDIDATE did and the impact they had — describe the shape of a result (e.g. "cut latency 60%") without disclosing the specific confidential numbers or systems behind it when those would only be known internally. Reversible (returns an undo_token).
upload_story_imageUpload story image write
Store an image on the user's own bucket and return its public URL, to be passed back as write_showcase_story's image_url. Use this when you are holding the bytes — a screenshot the user just gave you, a render you produced — rather than a URL. Hot-linking someone else's host works until it doesn't; this keeps the card's visual on the user's storage. Send the raw bytes base64-encoded; JPEG, PNG, WebP and GIF are accepted and the format is detected from the content, not from what you claim it is. Keep a card hero around 200-400 KB: it is rendered a few hundred pixels tall, and the base64 payload is a third larger than the file. Only publish images the user owns or is entitled to republish — a screenshot of their own work, not a page lifted from a client's publication.
set_showcase_visibilitySet showcase visibility write
Approve (or hide) a showcase story for public display on profile.iampro.io, and/or feature it (is_highlight) at the top of the page. A story is never public until the user (or the user, via this tool, on their explicit instruction) approves it — always confirm with the user before setting is_public true.
reorder_showcase_storiesReorder showcase stories write
Set the top-to-bottom order of the proof cards on the public profile (ids from list_showcase_stories), first = top. This is the ranking the page had no way to express: a reader gets through two or three cards, so which two lead decides what the profile argues. Order for the role being targeted — the strongest checkable artifact and the most on-message engagement first, self-reported work last. Ids you omit keep their current position behind the ones you pass.
applications — read
application_funnelApplication funnel read
Where the user's applications actually die: stage-by-stage funnel (applied → answered → interview or beyond), median days to an answer, silence rate after 30 days, a by_state breakdown naming the applications that were never answered ('lapsed' — a COMPUTED state, never a stored status: do NOT re-log them as 'rejected', that would destroy the very distinction measured here and write rejections nobody ever sent into a record that doubles as proof of job search), split by channel applied through — each channel carries its own silence_rate_pct, which is what tells a dead channel apart from a weak CV — by company, and — when log_application was given cv_id/cover_letter_id — by_cv_id/by_cover_letter_id: which SAVED VERSION of a CV or letter actually answers better. This is the point of stamping those ids: 'what works' cannot be answered without knowing which artifact each application sent. Rates are null when the sample is too small to mean anything — report them as unknown, never round a 2-application streak into a percentage. Pair with market_outlook to separate a personal problem (low answer rate in a healthy market) from a market one. If the journal is thin, offer to reconstruct it from the user's mailbox: read their application confirmations and outcome emails yourself and record each with log_application, passing status_at = the date on the email.
list_applicationsList applications read
The user's job-application journal (status, follow-ups), newest first. Each line carries `needs_title: true` when it was logged from a mailbox WITHOUT a job title — pass missing_title=true to get only those. Completing them from the original thread (the employer and the date are already there) is real work: a proof-of-job-search report where half the lines carry only a company name is hard to defend. To complete one, call log_application with its `id` as application_id — NOT with company+title, which would create a second row instead of fixing the first. When a line carries share_link_id, it also carries `dossier`: view_count, last_viewed_at, active, url — the only outcome signal available with the user typing NOTHING. A dossier never opened after a week or two is worth a different channel; one opened yesterday is a follow-up due today, not next month. `cv_id`/`cover_letter_id` (from save_cv/save_cover_letter) are also on each line when recorded — see application_funnel for what they enable. Every line also carries `derived_state` (advanced | lost | lapsed | scheduled | pending) and `days_since_applied`: `status` is what the journal RECORDS, `derived_state` is what it MEANS today, `lapsed` is the answer the employer never sent, and `scheduled` means a follow-up date is still ahead — a hiring cycle that answers by season is NOT a dead application, and counting it as one inflates the silence rate. Filter with status='lapsed' or status='scheduled' to get only those. Never rewrite a lapsed line as 'rejected' — log_application refuses it, and for a reason: silence and refusal are different facts, and only one of them actually happened.
list_prospectsList prospecting pipeline read
The user's prospecting pipeline — every employer they're pursuing, with the follow-ups that are DUE surfaced first (follow_up_at ≤ today, still open). Use it to open a session ('you said you'd follow up with X today') and to see the whole outreach funnel at a glance. Advance a company with log_contact.
applications — write
log_applicationLog application write
Log (or update) a job application on the user's behalf. Upserts by job_posting_id (our index), by url, or — for off-platform applications that have neither — by company+title, so logging the same job twice doesn't duplicate. A company alone is accepted — an application that exists in the journal beats one that was never logged. But the TITLE is what makes the line usable: a job-search proof report where half the lines say only a company name is hard to defend, and the user cannot tell two applications to the same employer apart. ABAISSER LA BARRIÈRE N'EST PAS COMBLER UN VIDE SANS CHERCHER. Before logging without a title, look: the subject line, the rest of the thread, the confirmation the employer sent, the job description attached, the link in the signature. Leave the title out only after looking and finding nothing — and say so in the notes, so the gap is a known gap rather than an unexamined one. Measured 2026-08-02: 126 of one user's 211 mailbox-logged applications carry no title at all — because this very instruction said a company was enough. Reversible. When the user mentions an interview, a rejection or an offer, ALSO offer to capture what it revealed — new skills, accomplishments or lessons — as evidence (add_evidence) or a story (write_showcase_story): career memory grows from exactly these moments.
attach_sent_documentAttach sent document write
Record the CV or cover letter you ACTUALLY sent for an application — the file the employer received, whoever produced it. Call this whenever you compose the document yourself instead of using iampro's renderer, and whenever the user tells you what they sent. Why it matters: applications already carry cv_id / cover_letter_id, but those name what iampro GENERATED, not what went out. When the two diverge and nobody says so, the user's application history records documents that never reached anyone — worse than an empty history, because an empty one can be read for what it is. Pass document_id for a piece already in the vault (list_documents), or file_base64 + filename for bytes you are holding. PDF and DOCX are the useful formats here; keep it under a few MB — the base64 payload is a third larger than the file. The response says whether iampro had generated something else for this application, so you can tell the user plainly.
delete_applicationDelete application delete
Remove ONE application from the journal — the missing piece for cleaning up a wrong or duplicate entry (log_application only adds or updates). Confirm with the user and delete only on their explicit go-ahead; to record a candidacy the user pulled out of, prefer log_application with status 'withdrawn' (that keeps the funnel history). Reversible via undo_token. application_id from list_applications.
log_contactLog an employer contact write
Record or update the user's outreach state for ONE employer — the prospecting CRM that sits between 'who is hiring' and 'where my prospection stands'. Upserts by company: re-logging the same company advances its state instead of duplicating. Use it after the user reaches out ('contacted QIM Info today, follow up in a week'), and set follow_up_at so list_prospects can surface it as due. Distinct from log_application (a submitted application) and tours (door-knocking).
tours — read
list_toursList tours read
The user's saved field tours (door-knocking routes) with their per-stop canvassed state. Each tour: id, name, place, active, done, stops_count, visited_count, and stops [{siret, name, visited}]. Use it to (a) know which boxes are already canvassed (collect visited sirets → pass as exclude_sirets to who_is_hiring_near), (b) spot tours that look walked but aren't fully confirmed, and prompt the user to confirm states / mark done.
brief_tourPrepare a tour, door by door read
Prepare a tour door by door: every stop with what the user ALREADY knows about it (recon) and `gaps` — what is still unknown before knocking. This is the reconnaissance brief: read the gaps, go find those answers (the company's own site, its careers page, a phone call the user can make), and write them back with set_door_recon. `gaps` includes `pitch` — the opening lines the user will hear read aloud on the doorstep — so a stop is not ready just because you found its opening hours. `has_dossier` says whether the employer rundown exists (set_door_dossier); read the rundown itself with list_doors. Skip stops flagged `settled` — the door already answered. Omit tour_id for the active tour.
list_doorsList canvassed doors read
The user's door ledger — every establishment they have canvassed or prepared, across tours, with the field outcome, their notes, what was said on site, the recon block and the follow-up due. Read it BEFORE proposing a new tour or a spontaneous application: a door that answered 'apply on our website' needs a web application, not a second visit. Each door carries its `dossier` (the employer rundown) so you never rewrite research already done. `due_only` returns just the callbacks that have come due — the right thing to raise at the start of a day.
tours — write
save_tourSave tour write
Create or edit a field tour from a list of stops (the triaged employers). Pass stops as objects with at least siret + name + lat + lng (take them straight from who_is_hiring_near). Omit id to create; pass id to edit (the canvassed state of kept stops is preserved). The tour becomes the active one shown on the user's phone. Reversible (deletable). ⚠️ IF THE USER ASKED FOR A TOUR, BUILD IT — do not stop at a list. 'Build me a walk-in tour for Saturday' IS the confirmation; asking again how many stops they want turns a done thing into homework. Create it, then say what you created and offer to resize or drop stops — it is reversible, so the cheap mistake is the extra round-trip, not the extra tour. Ask FIRST only when intent is vague ('who's hiring near me?'): there, a list is the right answer and the tour is an offer.
set_tour_doneSet tour done write
Mark a field tour as walked (done) — or reopen it (done=false). A walked tour stops being the active one and its boxes drop out of new tours. Ask the user whether they actually completed the tour before marking it done.
set_tour_visitedSet tour visited write
Confirm the per-stop canvassed state of a tour: which boxes were actually visited and which couldn't be. Pass visited as {siret: true|false}; it is merged into the tour's ledger. Use it to help the user reconcile a tour they've walked (some stops done, some skipped) before marking it done. The reply gives the resulting ledger — `visited`, `not_visited`, and `off_tour` (sirets recorded that are not stops of this tour) — not just a tally. Read those, not the counts.
set_door_reconFill what to know before knocking write
Write what must be known BEFORE knocking at one door, field by field, each with its source. Two families. LOGISTICS of the door — entry_channel (walk_in|reception|website|email|phone|agency|none_found), access (how you physically get in), ask_for (the FUNCTION to ask for — never a private individual's name), hours, language, requirements (licence, permit, clearance — as a list), site_role (hq|operations|branch|mailbox|unknown), careers_url, contact. THE APPROACH — what the user says once the door opens, which is read AND PLAYED ALOUD on their phone on the doorstep: pitch (their opening lines, word for word, first person, in the language of the place, ~2 sentences they can actually say out loud), hook (why THIS employer, concretely — the signal that proves they did their homework), bring (what to have in hand, as a list), ask (the questions to ask, as a list). Write the pitch from the user's OWN profile and evidence (get_profile / list_targets / list_evidence) — never invent experience, and never a generic 'I am motivated'. Pass each field as {value, source, url}: source='web' when you found it on a page (give the url), 'llm' when you composed or inferred it — never dress an inference as a source. Anything the USER observed on site wins: those fields are refused and come back in `kept_observed`, and you must tell the user rather than claim you wrote them.
set_door_dossierWrite the rundown on this employer write
Write the RUNDOWN on one employer — who these people actually are, so the user walks in knowing them: what the company does in plain words, who owns it, how many sites, how they staff (in-house, agencies, seasonal), what moved recently (a new site, a contract won, a merger), and anything to be careful about. This is where a real investigation belongs; without it your research dies in the chat. `summary` is 3-4 sentences of prose. `bullets` are the citable facts — the ones the user can say out loud — each as {text, url}. `sources` are the pages you actually read, as {url, title}: a rundown with no source must not be recited in front of the person concerned, so a source without a URL is refused. Do not restate what the map already shows (headcount, activity code, typical roles) or what `salary` answers — put here what those do not have. Reason on sector, activity, size and news only; never demographics. A rundown the USER wrote is kept, and comes back as `kept_user`: tell them instead of claiming you replaced it. Then distil the single most useful line into set_door_recon's `hook` — that is the one the user says on the doorstep.
log_door_visitLog what a visit produced write
Record what a visit actually produced, in the user's own words. `outcome` is one of: cv_accepted, contact_obtained, hiring_now, web_only (they said to apply online), no_manager (nobody who decides was there), no_access (no visible way in), closed_now, not_hiring, refused (no spontaneous applications), wrong_address (nothing there). Add `met_role` — WHO answered, by function ('security officer back from a round', 'receptionist'), never a private name — and `verbatim` for what they said. The outcome drives the ledger: settled doors stop being proposed, no_manager becomes a callback (set next_at). Never infer an outcome the user did not report. When the outcome is cv_accepted or contact_obtained, follow with log_application so the visit counts as job-search proof. On a door ALREADY logged, outcome is optional: call it with just the fields to change (add the verbatim they remembered afterwards, set next_at) and the recorded outcome is kept — do NOT restate an outcome from memory to fix a note. Use clear:true when the visit did not happen or it was the wrong door: the door goes back to to-do and the outcome is dropped.
checklist — read
get_checklistGet checklist read
Read a day checklist WITH its body — including which lines the user has already ticked. Omit `id` for the active one (what their phone is showing). Read this before writing: the ticked state lives in the text, so editing what you get back is the only way to add a line without erasing their morning.
list_checklistsList checklists read
The user's checklists, most recently touched first — titles and progress (items_done / items_total), no bodies. Use it to find the id of an older plan; use get_checklist to read one.
checklist — write
save_checklistSave checklist write
Push a day plan into the user's PHONE — /app/checklist, which they tick on the pavement between two doors. The body is MARKDOWN and it is the whole document: `##` headings, `**bold**`, `[label](url)`, `---`, plain `-` bullets, and `- [ ]` for anything they must tick off. Everything else renders as plain text. ⚠️ THE TICKED STATE LIVES IN THE BODY (`- [ ]` becomes `- [x]`), so REPLACING `body` WITHOUT READING IT FIRST WIPES WHAT THEY HAVE ALREADY TICKED — possibly mid-round, on the list they are using not to forget anything. Call get_checklist, edit the text you got back, send that. Only send a fresh body when you mean to start a new day. Pass `id` to rewrite an existing one, omit it to create. A field you do not send does not move. Set `tour_id` when the plan accompanies a field tour (list_tours) — the screen then links to it; leave it out otherwise, half a real checklist (paperwork, callbacks, a weekend of preparation) has nothing to do with a walking route. LINKS: plain iampro links are fine. A SIGNED one (`?t=…`, the kind meant to be sent to a recruiter so they can open a document without an account) does not belong here — the owner is already signed in, so the token buys nothing and travels with every screenshot. The app strips it from what it displays; do not add it in the first place. Also known as: task list, to-do list, todo, day plan, action plan, reminders, things to do, agenda for the day.
cover — letter — read
get_cover_letter_materialGet cover letter material read
Get everything you (the assistant) need to COMPOSE a cover letter for one job posting YOURSELF — free, no iampro AI call: the posting (title, company, description, required/nice-to-have skills, language), the user's VERIFIED evidence sorted by relevance to THIS posting when a prior analyze_job exists, their real `contact` block, the honesty rules, the WINNING PATTERN (the same duty-bucket structure iampro's own paid generator uses), and the exact JSON schema to hand back to save_cover_letter. Same division of labour as get_cv_material / save_cv: you write the letter, save_cover_letter stores it (renders to PDF/DOCX on demand, no second LLM call). target_id is OPTIONAL here (unlike get_cv_material) — a cover letter is anchored to the JOB, an identity only narrows which experiences and skills framing to draw from; omit it to draw on the whole profile.
list_cover_lettersList cover letters read
List the user's saved cover letters — every version, newest first. Filter by job_posting_id to see the versions for one application; omit it to see everything. Use get_cover_letter to read one in full.
get_cover_letterGet cover letter read
Read one saved cover letter in full, by id (from list_cover_letters or save_cover_letter's response).
cover — letter — write
save_cover_letterSave cover letter write
Store a cover letter YOU composed (from get_cover_letter_material) for one job posting. iampro validates it first, same rule as save_cv: any quantified claim (%, ×, money) not backed by the user's verified evidence or profile is REJECTED and returned to you to fix. Versioned — a second save for the same posting adds a new version, it never destroys the first; list_cover_letters shows all of them, get_cover_letter opens one. Once saved it renders to PDF/DOCX the same way a CV does — no separate render step, and no second LLM call. When this letter is actually sent, pass the returned `id` as cover_letter_id to log_application — that link is what lets application_funnel say which letter version actually answers better; without it the save is just storage.
jobs — write
surface_opportunitySurface opportunity write
Pin a REAL find into the user's #search list — the missing half of search_jobs. search_jobs is explicitly NOT profile-filtered: it bypasses #search's own hard filters (geo radius, work permit, work mode), so you routinely find live postings the user never sees in the app. Surfacing one puts it at the TOP of #search with a 'proposed by your assistant' badge — the user sees it next time they open the app, not just in this chat. kind='job' for a posting (job_posting_id, from search_jobs/get_job_detail); kind='prospect' for a spontaneous-application opportunity with NO posting behind it — an employer worth approaching directly (from find_employers_to_approach/former_employers_hiring), which #search's list otherwise has no way to show at all. Always say WHY via `note` — the user is trusting a match they can't see the reasoning for otherwise. Idempotent: re-surfacing the same job/prospect updates the note instead of duplicating. Capped at 30 active pins — this is a curated shortlist, not a second feed; dismiss_surfaced_opportunity clears space.
save_private_postingSave private posting write
Save a job the user received PRIVATELY — a LinkedIn DM, an email from a recruiter, a message that exists nowhere else — into their own index, and pin it to the top of #search. It then behaves like any other posting: assess_job_fit scores it, get_cv_material and the cover-letter flow work on its text. It is visible to NOBODY else: excluded from the shared index, the employer map and the public pages. CHECK FIRST, AND YOU CANNOT SKIP IT. Called without confirm_new=true, this tool SEARCHES instead of writing, and returns what already exists — the same role often arrives twice (the company publishes AND an agency prospects), and a duplicate means two cards and two scores for one job. Show the candidates to the user, then call again with confirm_new=true if it is genuinely new, or surface_opportunity(kind='job') on the existing one. ⚠️ Zero results does NOT prove the posting is absent: an agency that does not name its end client ('for one of our clients') can never match on company. Say so rather than concluding it is new. `description` must be the REAL text of the offer, at least 200 characters — paste what the recruiter wrote. Without it there is nothing to judge, and a score on a title alone carries the authority of a judgement without being one. Put the agency as `company` when the end client is not named.
dismiss_surfaced_opportunityDismiss surfaced opportunity write
Unpin one item from #search — it stops appearing at the top of the list. Use when the user says they've seen it / aren't interested, or a pin has served its purpose (applied, dismissed). Reversible in spirit (surface_opportunity again re-pins it), but not literally undoable — confirm with the user before dismissing something they haven't acted on.

This page is generated from the connector's live tool registry on every request — it cannot drift from what the server actually exposes. Tool descriptions are the exact text an agent receives, including the confidentiality guardrails baked into the write tools.