# Connect Jobsite to Claude

The Jobsite connector lets Claude, or any assistant that speaks the Model Context Protocol (MCP), work from a company's live Jobsite data as the approving manager: what needs them today, a job's status and money, vendor bills, the dispatch board, workers and their training, and compliance answers Jobsite computes itself. It always acts as you, behind your own permissions, in one company, with every change logged.

## Free, no sign-in
Server URL `https://jobsitecerts.com/api/mcp/public`. No account, no key. Reads no company's records. Requests are limited to 60 a minute from one network address.

| Tool | What it does |
| --- | --- |
| **Search the Jobsite product guide** (`lookup_jobsite_docs`) | Look up how Jobsite (this product) works — features, UI navigation, terminology, account / billing, worker-side experience, what counts as a 'group' vs 'crew', etc. Use this for any 'how do I…' / 'where do I find…' / 'what does X mean in Jobsite' question. Returns matching sections from the curated product guide. NOT for OSHA / regulatory questions (use search_law_corpus) and NOT for a company's own records (its workers, certificates, jobs). If a returned section is flagged todo:true, the topic is in scope but not yet documented — say so rather than inventing an answer. |
| **Search Jobsite articles** (`search_articles`) | Search Jobsite's published marketing content — blog articles, head-to-head COMPETITOR COMPARISON pages (e.g. 'Jobsite vs Procore', 'Jobsite vs SiteDocs', 'Jobsite vs SafetyCulture'), and pillar guides. Use this whenever the user asks how Jobsite compares to a competitor ('how are you better than Procore', 'Jobsite vs SafetyCulture', 'why pick you over X'), or asks for a published article on a construction-safety topic. Returns matching articles with a relevant excerpt and a link to the full page. This is DISTINCT from lookup_jobsite_docs (product how-to) and search_law_corpus (OSHA regulatory text). If nothing matches, say you don't have an article on it rather than inventing a comparison. |
| **Search the regulation corpus** (`search_law_corpus`) | Search the VERBATIM authority corpus behind Jobsite's training: 29 CFR regulation text (per-section, with citations), the CA/WA/OR state rules for heat illness and wildfire smoke, plus OSHA interpretations and NIOSH/CDC/EPA technical guidance the training cites. Use this when the user asks what an authority ACTUALLY SAYS — exact rule language, a specific citation ('what does 1926.501 require'), trigger heights, employer duties — and quote the returned text with its citation heading and jurisdiction. EVERY match carries an `authority` field: T1 is binding law, T2 is an agency interpretation and T3 is technical guidance. READ IT BEFORE YOU CHARACTERISE THE MATCH — quote a T2/T3 match as what that agency recommends or says, NEVER as what a regulation requires, and never call it a rule. It returns the source text itself, not a paraphrase of it — prefer it whenever the user wants what the rule says. The corpus spans the topics Jobsite trains on plus the authorities they cite, NOT all of 29 CFR: an empty result means 'not in the corpus', never 'no such rule exists'. |
| **Search regulatory news** (`search_regulatory_news`) | Search recent regulatory / safety news from the Federal Register (filtered to OSHA) and the OSHA news RSS feed. Use for questions about recent rule changes, enforcement memos, or news that might affect a training course's content (e.g. 'any silica updates this year', 'recent fall protection rulemaking'). Returns titles, agencies, dates, URLs, and short summaries — pure pass-through, nothing is persisted. Prefer specific multi-word queries built around an OSHA topic or standard ('respirable silica §1926.1153', 'lockout tagout enforcement', 'fall protection 1926.501'). NOT for tenant-data questions; NOT for general OSHA reference (use search_law_corpus for established standards). |
| **OSHA recordability check** (`osha_recordability_check`) | Runs Jobsite's deterministic OSHA 29 CFR 1904 decision tree on the facts of an injury or illness: returns the proposed 300-log classification (or the questions still needed), the rationale with verbatim 1904 quotes, and any report-to-OSHA deadline (8 or 24 hours). Map the story to the answers; leave out anything not known — unknowns come back as questions, never guesses. Reads and writes no records. |
| **Get a toolbox talk** (`get_toolbox_talk`) | A ready-to-deliver construction toolbox talk from Jobsite's library of nearly 300 talks, in English or Spanish: title, talking points, a discussion question, duration, and the OSHA standards it is built on. Find one by topic or hazard in plain words ("trenching", "heat", "ladder setup") or by standard number ("1926.501"), or fetch one by talk_number. Returns the best match plus up to five other matching titles. |
| **Training a trade needs, from Jobsite's inventory** (`training_required_for`) | What training Jobsite's requirements inventory assigns a construction trade in a US state, optionally for roles the worker also holds (supervisor, competent person, forklift operator, scaffold erector and others). Every obligation comes from Jobsite's inventory, never from general knowledge, and carries its citation and the regulation text quoted verbatim: quote it as given. Each row's kind matters and must be kept: a mandate is an unconditional legal training duty; a condition applies only when the condition it names exists on the job; a policy is training Jobsite assigns without a legal mandate there, with its basis stated. A not-found answer means "not in Jobsite's requirements inventory", never "no training required". The inventory holds federal OSHA rows plus California, Washington, Oregon and Nevada rows. search_law_corpus returns the full text of any cited rule. |
| **Check a worker's shared certificate** (`verify_certificate`) | Check a certificate, or a whole certificate wallet, that a worker shared from Jobsite as a link (https://jobsitecerts.com/w/…). Returns the holder's name, each credential with its issuer, issue and expiry dates, current status (current, expiring soon, expired or revoked), and its basis: earned in Jobsite training, an outside credential the employer recorded, or an employer record Jobsite has not verified. Only works with a link the worker made; it never looks a person up by name, and an expired or revoked link returns not found. |
| **Start a sample Jobsite workspace** (`start_jobsite_workspace`) | Create a new Jobsite workspace that holds only sample data (a sample crew, jobs and safety records; nothing real) for the person you are helping, so they can try Jobsite without setting anything up. Returns a one-time claim link to give them: opening it lets them create their login (or sign in) and own the workspace. The link expires in 7 days, and an unclaimed workspace is then deleted. Exploring the sample data is free; adding real workers needs a Jobsite plan. Takes no personal details. Limited to 3 workspaces a day per network address. Optional: company_name, focus (safety or home services) and the company's trade. |

## Connecting your company
Needs a Jobsite manager account with permission to manage jobs. In Claude: Settings > Connectors > Jobsite > Connect (or add a custom connector with `https://jobsitecerts.com/api/mcp`). Review the consent card, which names the company and the site you return to, and Approve. The two ticks (draft things for you to finish; allow changes) are off until you tick them. Clients that do not support MCP sign-in can use an API key from Integrations > API & agent access as a bearer token.

## What it never does
It cannot pay, schedule or export a payment, run or release a pay run, or mark anything paid; it cannot send anything to a customer, vendor or general contractor; it cannot delete workers, change anyone's role or override a certification. There is no free-form query tool.

## Tools
Generated from the registry the server uses.

### Answers (read-only)

| Tool | What it does |
| --- | --- |
| **What needs my attention** (`get_attention_brief`) | Everything waiting on the user, ranked by urgency then due date, each with a link: approvals, RFIs and submittals in their court, change orders, pay apps, punch, schedule slips, safety observations, inspection corrective actions, cert lapses, overdue training, documents to sign, coverage gaps, insurance renewal, vendor bills awaiting their approval, unread messages, and today's dispatch exceptions. Optionally one job; workforce, insurance, message and dispatch items stay company-wide and are marked so. Each item's id is the suggestion it rolls up into and next_action is its next step (url, next_tool when a read-only tool shows it, needs_person when it is the user's decision); for "what should I do now" across everything, get_suggestions returns the ranked suggestions themselves. |
| **What to do now** (`get_suggestions`) | The ranked suggestions Jobsite's Home shows the user for this moment — the same list, ids and order — but complete, for answering "what should I do now". Each has a stable id (the same moment gives the same id), kind, title, line, reason (why now), urgency, due date and freshness; url opens it in Jobsite; next_tool, when present, is the read-only tool and arguments that show the same thing; needs_person is true when the step is the user's own decision (approve, send, pay, sign) — prepare, but leave that to them. The ranking weighs the user's role, the company's kind of work, the time of day and the company calendar (month end, payroll, billing cutoff, the OSHA 300A season). Optionally one job; company-wide suggestions are then marked company_wide. |
| **List workers** (`list_workers`) | List workers at the user's company. Supports filtering by status, trade, group, or a name search. Returns id, full_name, trade, status, badge_type, email, phone. Use when the user asks about people on the team. |
| **Compliance stats** (`get_compliance_stats`) | Get company-wide compliance counts: expired, expiring soon (configurable window), total certifications, active workers. Use for overview questions like 'how are we doing' or 'how many expired certs do I have'. |
| **List expiring certifications** (`list_expiring_certs`) | List worker certifications whose expiration falls within a window (default 30 days). Sorted soonest first. Returns worker_name, trade, topic, track, expires_at, days_remaining. Use when the user asks who's about to expire. |
| **Worker detail** (`get_worker_detail`) | Get full detail for one worker by id or exact full name match: personal info, all certifications with status/expires_at, recent training attempts. Use when the user asks about a specific worker by name. |
| **Recent toolbox talks** (`list_recent_toolbox_talks`) | List recent toolbox talk sessions at this company. Returns title, topic, held_at, attendance_count. Use for questions about recent safety meetings. |
| **List certification tracks** (`list_cert_tracks`) | List available certification tracks (training programs) and their topics. Use when the user asks what trainings/certs exist or which one someone should take. |
| **List groups** (`list_groups`) | List worker groups at the user's company. Returns id, name, is_system. Use to resolve a group name to its id before calling a write tool. |
| **Search the training source library** (`search_training_source`) | Search Jobsite's curated OSHA / regulatory training research (the proprietary 'training_source' knowledge base of ~353 module entries — confirmed-fact notes plus slide narration scripts). Use this for safety, regulatory, or training questions: PPE requirements, exposure limits / PELs, fall-protection thresholds, training renewal cadences, inspection frequencies, etc. Returns up to N matching modules with s_num, topic, track, module_title, and a relevant text snippet. Prefer multi-word queries (e.g. 'respirable silica PEL', 'fall protection 6 feet roofing'). Cite the matched module(s) by topic + module_title in your reply so the user can dig deeper. Do NOT use this tool for non-safety questions (HR, scheduling, code, creative writing). |
| **Summarize a thread** (`summarize_thread`) | Summarize a conversation thread on an object (feed post, incident, JHA, or approval request). Returns a 1–3 paragraph plain-language summary with people involved, decisions, and open questions. Reads from object_thread_messages and is cached per (thread_id, last_message_id, target_lang) so repeated asks cost zero. Use when the user asks 'what's going on with the incident at Acme Tower?' or 'summarize the thread on the daily check at Job 3' or 'catch me up on RFI-12'. The thread_id is the UUID from object_threads; if you don't know it, look up the source object first (e.g. an incident_id) and tell the user you need the thread reference. Output is read-only; nothing is written to the thread itself. |
| **List jobs** (`list_jobs`) | List the company's jobs (projects). Returns id, name, status. Use to answer questions about jobs and — importantly — to resolve a job_id before calling route_to_surface for a job-specific target (draft_document, create_rfi, punch_list, view_job). Defaults to active + on-hold jobs. |
| **List RFIs** (`list_rfis`) | List RFIs, optionally for one job (resolve job_id via list_jobs). Defaults to open RFIs (open + pending_gc_review). Returns id, job_id, number, subject, status, due_date. Use for 'how many RFIs are open', 'what RFIs are outstanding on <job>'. |
| **List punch items** (`list_punch`) | List punch-list items, optionally for one job. Defaults to open items (open + assigned + completion_claimed). Returns id, job_id, number, title, severity, status, due_date. Use for 'how much punch is open', 'what's left on the punch list for <job>'. |
| **List submittals** (`list_submittals`) | List submittals, optionally for one job. Defaults to the open ones (draft, pending architect, pending GC review, revise & resubmit); approved / rejected / closed are not open. Returns id, job_id, number, title, status, due_date. |
| **Job status** (`get_job_status`) | Health snapshot for ONE job: schedule task count, average % complete, overdue task count, longest slip in days, plus open RFI and open punch counts. Resolve job_id via list_jobs first. Use for 'how's North Tower doing', 'is the hospital job on track'. |
| **Financial summary** (`get_financial_summary`) | Financial snapshot: count of change orders pending approval and pay apps awaiting action, plus accounts payable (open vendor bills owed, past due, retainage held). Optionally scope to one job (job_id) to also get its budget rollup (budget vs committed vs actual vs variance). Use for 'any pending change orders', 'what's my budget variance on <job>', 'what pay apps need action'. |
| **List inspections** (`list_inspections`) | List inspection responses (site inspections, permits, JHAs), optionally by job, status, or outcome. Returns recent responses with template, status, outcome. Use for 'recent inspections', 'any failed inspections', 'inspections on <job>'. |
| **List safety observations** (`list_safety_observations`) | List safety observations / near-misses / hazards reported from the field, optionally by status. Returns kind, category, severity, status. Use for 'recent safety observations', 'open hazards', 'near misses this month'. (For overall BBS rates, note these are leading-indicator observations.) |
| **Resource status** (`get_resource_status`) | Check equipment/resource checkout status from the Equipment register (vehicles, tools, PPE, other). Optionally filter by a name query (also matches identifier/serial and category). Returns each resource's status (available, checked_out, maintenance, lost, retired), who has it checked out and at which worksite when it's out, and its home worksite. Use for 'who has the generator', 'is truck 12 available', 'what equipment is checked out right now'. |
| **Check who is qualified for a task** (`check_qualification`) | Can these workers (or a crew) do a task on a date, per Jobsite's training records? The task is a training track, an equipment asset (its required training), or a designation (a topic + role, e.g. forklift operator). Returns qualified yes/no per person, what is missing, expired, revoked or lapsing before the date, and why each requirement applies (the rule, the asset, or a prerequisite). |
| **Jobsite Safety Index** (`get_safety_index`) | The company's Jobsite Safety Index (or one job's / worksite's): the score, whether it is provisional, the operational and outcome sub-scores, the factors that would raise it most (with the rate a factor needs for full credit), and — company-wide — the experience mod (EMR) and days to the policy renewal. |
| **Accounts payable overview** (`ap_payables_overview`) | What the company owes vendors: bills waiting on the acting user's approval, approved bills aged by days past due, the cash needed week by week, retainage held, bills blocked from payment by holds (insurance, W-9, lien waivers) and pay runs in progress. Optionally one job. Read-only: payments happen in Jobsite. |
| **Vendor bill details** (`ap_get_bill`) | One vendor bill: vendor, invoice number and dates, totals and retainage, each line's job, cost code and purchase-order match, the approval chain (who has decided, who is next), payment holds, possible duplicates, and the pay run it is in. |
| **Service dispatch board** (`service_dispatch_board`) | The home-services dispatch board for a day (or up to 7 days): each technician's visits with arrival windows, visits with no lead technician, calls not yet scheduled, and the exceptions flagged — unassigned, late (window passed, nobody on site), held website bookings, no-access visits. Times are ISO instants; `timezone` is the company's. |
| **Service call details** (`service_get_call`) | One home-services call: customer, location, status and priority, every visit with its technicians, the call's estimates (each option's total) and invoices (total and balance), with a link to the call in Jobsite. |
| **Service receivables** (`service_receivables_overview`) | Money and follow-ups owed on home-services work: unpaid customer invoices (most overdue first, with totals), estimates still in draft or waiting on the customer, and maintenance-agreement visits due soon or overdue. |
| **Search drawings** (`search_drawings`) | Find drawing sheets by topic, sheet number or the words printed on them — "paving plan", "curb", "fire line", "paving details", "CP100", "heavy duty concrete". Searches the current sheets' numbers, titles and scope, their keynotes, legends, general notes, detail titles and labels — and, on civil and architectural sets, station labels ("12+50", "STA 12+50"), structure ids ("MH-3", "CB 12", "inlet A-4"), room tags ("room 104", "apparatus bay") and spec sections a sheet cites ("03 30 00"). Returns each sheet with the sheet's own matching text (quote it as given; never restate a number or material) and a link that opens the sheet zoomed to the match. Optionally one job. For what is between two grid lines use drawing_area_lookup. |
| **Drawings between grid lines** (`drawing_area_lookup`) | What the drawings show between two grid lines (a band: "between G and H") or inside a bay ("between G and H and 3 and 4"), on every current sheet that carries those lines: the keynotes printed there with their full text, the detail and section markers (where the detail is drawn), and labels — grouped by discipline (A, S, C …), each with the legend materials its text names and a zoomed link. Says which sheets lack one of the lines. Read-only. |
| **Every source at one spot** (`drawing_location_sources`) | Everything that speaks to one spot on a job, from every source at once: for a grid bay or band ("between G and H and 3 and 4"), a detail ("detail 5 on S401"), a station or station range on a civil set ("STA 12+00 to 13+00" — placed on every plan and profile by their printed stations), a structure ("MH-3", "CB 12" — its plan label, profile and structure-table row) or a room ("room 104", "the apparatus bay"), what each current sheet prints there (architectural, structural, civil: keynotes, notes, detail titles, labels), the detail each marker in the bay points at, and every RFI (with its answer — the newest word), submittal (with the latest disposition, and its read shop drawing's own text page by page: "SUB-012's shop drawing (page 3)", the submittal's number as Jobsite prints it) and observation pinned there. Each source's own words are quoted with a link to the spot, and `disagreements` names both sources and both texts where dimensions, thicknesses, member sizes, rebar, strengths or materials differ ("S401 says 8\" CMU; A102 says 12\" CMU"). Never judge which is right. Each disagreement carries an `rfi_draft_link`: offer it in words — the RFI composer pre-filled with both quotes and pinned at the spot, for the person to review and file in Jobsite. Never file that RFI yourself (never create_rfi). Read-only. |
| **Search the spec book** (`search_specs`) | Search a job's specifications (the uploaded spec book, read section by section) for what they require — "curing", "slump", "vapor retarder", "concrete cover", "joint spacing" — or list one section ("03 30 00", a division "03"). Returns each matching paragraph with its section and paragraph number as printed ("03 30 00 §3.7.B"), its article heading, the paragraph's own words (`quote`: say them as given, never restate a number or material) and its page, with a link that opens the spec book there. Optionally one job. Read-only. |
| **Brief me on a trade's work on a job** (`scope_brief`) | Everything one trade needs on one job, at once — "I'm on Sanibel doing concrete", "estoy con el colado de la losa": the current sheets for that work with their strongest notes (quoted), the details those sheets point to, the spec sections and paragraphs in scope, the open RFIs (with the newest answer) and pending or returned submittals for that work, and where an architectural and a structural sheet (or an RFI answer and a sheet) disagree. The scope comes from `scope`, else the person's own `words`, else the company's trade; when none settles it, the result asks which work. Returns `summary` (at most three sentences, in the person's language) plus each source with its link. Read-only. |
| **List hazard suggestions** (`list_hazard_suggestions`) | Hazards Jobsite read in a job's own documents (drawing sheets, schedule tasks, daily logs, RFIs and their answers, submittals, walkthroughs, emails filed to the job and their attachments, job files — text, PDF, Word, photos — and the job's details) and is waiting for a person to accept or dismiss. Each has the hazard category, the source's own words (quote them as given), a link to the source, how it was found, and the required designations accepting it adds. Only hazards the worksite doesn't carry yet. Optionally one job; with a job it also reports which of the job's files couldn't be fully read for hazards (file_reads), so no suggestions is not the same as all clear. Read-only. |
| **List what Jobsite knows about the person** (`list_memory`) | Every memory entry the person may see — their own and the company's shared ones — with its kind, scope, source (said, read from an upload, noticed, onboarding), whether they confirmed it and when it was learned; plus what they said no to. Read-only. |
| **List what's left** (`list_commitments`) | "What's left?" — the person's open commitments: each promise, who owes the next step (the person or Jobsite), its state, when Jobsite checks back, and when it was made. include_done adds the closed ones. Read-only. |
| **Where the move stands** (`get_move`) | The company's move plan from their old system (Procore first) and this person's follow-through on it: "N of M moved · next: <step>"; every row — what it was in the old system, where it lands in Jobsite and how it comes over (the page, or "not in Jobsite yet"), the old system's export steps, its state on the plan and its tracked commitment; whether the move has gone quiet and when the next reminder email goes out; what they kept up front; which of the three interview questions (likes, frustrations, wishes) are still open. Read-only. |
| **Google listing: connection status** (`gbp_get_status`) | Whether the business's Google Business Profile is connected to Jobsite, whether it needs reconnecting, its Google listings (a business with several branches can have several, each serving the whole company or one division — each has a listing_id the other gbp_ tools take as `listing`), whether hours sync is on, and whether changes to Google are currently live. Use before any other gbp_ tool when you don't know the listing is connected. |
| **Google listing: hours (Jobsite's and the listing's)** (`gbp_get_hours`) | One listing's business hours as Jobsite holds them (weekly + special days — the company's hours, or the listing's own when a branch has different ones) and the hours change waiting to go to that Google listing: waiting for a person's approval, held because Google shows something else, or going automatically under hours sync. In Ask it also reads what the listing shows on Google right now and says whether they match. Its push_id and hours_sha (and observed_sha for a held change) are what gbp_push_hours needs. |
| **Google listing: updates and photos (ready, sent, failed)** (`gbp_list_publications`) | For one listing: the updates ready to post (approved listing_update drafts from Marketing — each posts to one listing), the job photos cleared for it, and what Jobsite already sent there (posted, failed). In Ask each ready update carries the fingerprint gbp_post_update needs and each photo the sha256 gbp_add_photo needs. |
| **List changes made through this connection** (`list_recent_writes`) | List the changes this connection has made, newest first: each change's id, what it did, when, and whether it can still be undone with undo_write. |
| **Read the website draft** (`website_get_site`) | Read the company's public website DRAFT: its pages, sections and blocks (with ids), staged services and specials, the look, whether it is published, and the exact edit grammar (block kinds + props, op kinds + arguments). Returns base_revision_id and document_sha — pass base_revision_id to website_apply_ops, and document_sha with the revision id to website_publish (website_apply_ops needs the base_revision_id this returns). Acts as the API key's bound user. |
| **List change orders** (`list_change_orders`) | List change orders (id, number, title, status, job_id, contract_id, is_sample, submitted_at, created_at). Read-only — the same company-scoped projection GET /api/v1/change_orders returns. Filters apply server-side, so a filtered page is complete; page with offset. is_sample marks sandbox demo rows. |
| **List pay apps** (`list_pay_apps`) | List pay applications (id, number, status, job_id, contract_id, is_sample, submitted_at, created_at). Read-only — the same company-scoped projection GET /api/v1/pay_apps returns. Filters apply server-side, so a filtered page is complete; page with offset. is_sample marks sandbox demo rows. |
| **List daily logs** (`list_daily_logs`) | List daily logs (id, job_id, log_date, posted_at, is_sample, created_at). The log's narrative is not returned. Daily logs have no status, so there is no status filter. Read-only — the same company-scoped projection GET /api/v1/daily_logs returns. Filters apply server-side, so a filtered page is complete; page with offset. is_sample marks sandbox demo rows. |
| **List certifications** (`list_certifications`) | List the company's workers' certifications (id, worker_id, track_id, track_name, status, earned_at, expires_at, last_renewed_at, revoked_at, is_sample). status is the EFFECTIVE status: a revoked certification reads "revoked". Use expiring_within_days for upcoming renewals (e.g. 30), soonest first; resolve worker_id with list_workers. Read-only — the same company-scoped projection GET /api/v1/certifications returns. Filters apply server-side, so a filtered page is complete; page with offset. is_sample marks sandbox demo rows. |
| **List incidents** (`list_incidents`) | List incidents — register-level facts only (id, job_id, worksite_id, severity, category, status, occurred_at, reported_at, osha_recordable, osha_classification, closed_at, is_sample, created_at). The narrative, witnesses, photos, location, the injured worker and every OSHA privacy-case detail are deliberately not returned. Read-only — the same company-scoped projection GET /api/v1/incidents returns. Filters apply server-side, so a filtered page is complete; page with offset. is_sample marks sandbox demo rows. |
| **List bills** (`list_bills`) | List vendor bills, retainage releases and vendor credit memos (id, invoice_number, kind, status, external_company_id, vendor_name, job_id, invoice_date, due_date, total_usd, retainage_usd, paid_on, is_sample, created_at, approved_at, net_due_usd), newest first. A credit memo appears with kind='credit' and a POSITIVE total_usd (its size); it is never payable, so due_within_days leaves it out and its net_due_usd is 0. total_usd is the invoice as billed; net_due_usd is what is still owed: total less retainage held and less vendor credits applied. Use due_within_days for what is due to be paid soon (overdue included) and vendor_id for one vendor's bills. The scanned document, the AI extraction, notes and the hold note are deliberately not returned. Read-only — the same company-scoped projection GET /api/v1/bills returns. Filters apply server-side, so a filtered page is complete; page with offset. is_sample marks sandbox demo rows. |

### Drafts and changes (only with the consent ticks)

| Tool | What it does |
| --- | --- |
| **Add workers to a group** (`add_workers_to_group`) | Add one or more workers to an existing group. Safe, reversible. Use for requests like 'add Jane and John to the Electricians group'. Resolve worker names and group name to IDs via list_workers / list_groups first. |
| **Remove workers from a group** (`remove_workers_from_group`) | Remove one or more workers from a group. Safe, reversible. Use for 'take Jane off Electricians' style requests. |
| **Create a group** (`create_group`) | Create a worker group at the user's company. Use for 'add a new group called Foremen' or as a follow-up when the user wants to organize workers. Reversible via undo while the group is still empty. |
| **Create a post category** (`create_post_category`) | Add a new job-feed post category for the company. The slug is auto-derived from the label. Use for 'add a new category called Equipment Down' or similar. Reversible via undo (archives the row). |
| **Rename a post category** (`rename_post_category`) | Rename an existing post category by id or by exact current label. The slug is preserved (categories keep their old hashtag even after rename). Optionally update the emoji. Reversible via undo (restores prior label/emoji). |
| **Archive a post category** (`archive_post_category`) | Archive (hide) a post category so it stops appearing on the composer. Existing posts that were tagged with it keep the tag. Reversible via undo. |
| **Create a worksite** (`create_worksite`) | Create a worksite (jobsite location) at the user's company. Every worksite is created together with its primary Job of the same name (the same thing the Worksites page's + New Worksite does), and both ids are returned. Use for 'add a new worksite called Main St Tower'. If the user is describing a JOB/PROJECT to track (RFIs, schedule, punch, financials), prefer create_job — it creates the same worksite + Job pair and also saves a description. Needs the jobs.create permission. Reversible via undo until the worksite or its Job is used (workers assigned, logs, photos, files, …) — undo removes both. |
| **Create a job** (`create_job`) | Create a job (construction project) at the user's company. Each job mints its own worksite + the job record in one transactional step (the same thing the Jobs > New form does in new-address mode). Use when the user says 'create a job called North Tower', 'add the hospital project', 'set up 3 new jobs: …', etc. To create several at once, call this tool once per job (the user asking for all of them is explicit consent — no need to confirm each). Optional address + description. Needs the jobs.create permission. Reversible via undo until the job or its worksite is used (workers assigned, logs, photos, files, …) — undo removes both the job and its worksite. After creating, the returned worksite_id can be passed to set_worksite_hazards / set_worksite_jurisdiction to tag the new job. |
| **Promote a service call to a Job** (`service_promote_call_to_job`) | Turn a service call whose scope became a project (a repair that turned into a remodel) into a Job — the same as Promote to Job on the call's page. Jobsite creates a new Job with its own jobsite at the call's address, links the call's customer to the Job (the customer then sees the Job in their customer portal), and files the call under the Job, so its later visit photos appear in the Job's gallery. Nothing financial is copied: no budget, contract or cost lines; an accepted estimate is noted as text in the Job's scope notes and stays on the call. The Job name defaults to "Customer — call summary". Needs the service.write and jobs.create permissions. Refused for a call already booked against an existing Job, a website booking still on hold, or a canceled call. Promoting a call that is already a Job returns that same Job and changes nothing. Undo removes the new Job, its jobsite and the customer link and puts the call back, only while nothing else has used the Job (photos, other calls, logs, invoices, …) and the call still points at it. |
| **Create a resource** (`create_resource`) | Add a resource (vehicle, tool, PPE, or other) to the company inventory. Safe, reversible (undo deletes it). Use when the user says things like 'add a new F-150 to my fleet' or 'create a new drill called Hilti TE-70'. Ask for the type if it's ambiguous. |
| **Run a compliance audit** (`run_compliance_audit`) | Run an end-to-end compliance audit for the user's company and return a summary of findings — counts by severity (critical/high/medium/low) plus total workers affected and the audit_run_id. Engine v1 checks: expired required certs, missing required certs, certs expiring within 14d (HIGH) or 30d (MEDIUM), workers assigned to a worksite who haven't signed its active JHA, and toolbox talks overdue more than 7 days past their scheduled date. The full findings list is persisted at /dashboard/audit?run=<id>. Use for questions like 'how are we doing on compliance?', 'are we ready for an inspection?', 'run an audit', or 'check our posture'. The audit itself is read-only against tenant data (no edits); it logs the run to audit_log + writes one compliance_audit_runs row. |
| **Send a toolbox talk to workers** (`send_toolbox_talk`) | Send a toolbox talk to a target audience. Materializes a talk_assignments row workers see in their phone feed; does NOT mint magic-link emails/SMS (the dashboard /toolbox-talks page is the magic-link surface). Use when the admin says 'send the X talk to the Y crew', 'kick off Tuesday's talk to all clocked-in workers', etc. Target kinds: 'company' (every active worker), 'worksite' (every worker assigned to that worksite/Job), 'worker' (one specific worker). Requires a topic_id — call list_recent_toolbox_talks or ask the admin to pick from the seeded library if you don't have one. Undo: delete the assignment row by id. Permission gate: owner/manager/super_manager. |
| **Assign a worker to a job** (`assign_worker_to_worksite`) | Assign one worker to a worksite/Job. Composes the existing dashboard pattern: resolves the worksite's primary Job, then upserts a job_workers row. Idempotent — re-assigning a currently-active assignment is a no-op. Use when the admin says 'put Maria on the Tempe project', 'assign Carlos to the North Tower site', etc. To assign in bulk, call once per worker (small loops are fine — there's no per-call rate limit). For the procore Phase-1-and-up data model, today's worksite_id transparently resolves to the primary Job; post-Phase-1 the tool will accept job_id directly. Undo: call remove_worker_from_worksite (not exposed in v1 — the click-walk path is the dashboard). Permission gate: workforce.workers.assign. |
| **Post a notice to workers** (`create_notice`) | Post a notice (announcement) to one of: 'all' (every active worker), 'group' (one training rule's members), 'worksite' (one worksite's assigned workers), 'worker' (one worker). Use when the admin says 'send a notice to the night-shift crew about Friday close-down', 'post a company-wide announcement', etc. Photos are not supported via Ask in v1 — the campaign narrates 'open /dashboard/notices to add evidence photos'. Severity is one of info / warning / urgent. require_ack adds a 'tap to acknowledge' checkbox to the worker-side notice. Permission gate: 'all' target requires notifications.companywide.send; group/worksite/worker target requires notifications.group.send. |
| **Assign training** (`assign_training_track`) | Assign a training/refresher track to a set of workers. Writes a renewal_assignments row per worker — the cert engine then routes those workers through the track's refresher modules on their phone the next time they log in. Skips workers who already have an open assignment for the same track (idempotent dedupe). Use when the admin says 'assign the OSHA 10 refresher to Carlos and Antonio', 'put the bilingual crew on the Hot Work refresher', etc. The campaign Scene 2 narrates this tool. To get track_id call list_cert_tracks first. Cap is 100 workers per call. Undo: undo_write closes the open assignments this call created (prerequisites included); completed ones are never touched. Permission gate: training.assign (managers, foremen and leads by default; a company can revoke it per role). |
| **Change an incident's status** (`update_incident_status`) | Update the status of an incident (open / investigating / closed). 'resolved' is accepted as an alias for 'closed' (the user-visible terminal state). When the new status is 'closed', stamps closed_at + closed_by + optional resolution_notes. Use when the admin says 'flip Carlos's incident to investigating', 'close out the slip-trip from yesterday', etc. The campaign Scene 3 narrates this tool. Permission gate: owner/manager/super_manager. Undo: revert to the previous status via another call. |
| **Pause a worker's assignments** (`pause_worker_assignments`) | Composite write: pauses a worker without losing cert state. Clears the worker's training-rule memberships (training_rule_matches) + stamps unassigned_at on every currently-active job_workers row. **Leaves worker_certifications intact** — cert renewals continue on cadence; this is a temporary archive, not a termination. Use when the admin says 'pause Carlos for two weeks', 'put Maria on leave', etc. The campaign Scene 3 narrates this tool. Permission gate: workforce.workers.assign (managers, foremen and leads by default; a company can revoke it per role). Undo: undo_write puts back exactly the memberships it cleared and reopens exactly the job assignments it paused (campaign Scene 3 narrates 'his certs stay valid; his slot frees up'). |
| **Set a worksite's hazards** (`set_worksite_hazards`) | Replace-not-merge the worksite's hazard register: hazards not in the given set are removed, new ones are added (recorded as set by hand), and the worksite's default required designations follow — seeded for the set's hazards, retired for removed hazards no remaining hazard maps. To add ONE hazard Jobsite suggested from the job's documents, use accept_hazard_suggestion instead. Valid categories: fall, struck_by, caught_in, electrical, slip_trip, chemical, heat, silica, confined_space, excavation, crane_lift, hot_work, noise, respiratory, other. Pass an empty array to clear all hazards. Use when the admin says 'add heat to the Tempe Marketplace hazard register', 'set the high-rise hazards to fall + heat + electrical', etc. The campaign Scene 4 narrates this tool. Permission gate: jobs.create. Undo: re-call with the previous hazard set. |
| **Set a worksite's jurisdiction** (`set_worksite_jurisdiction`) | Update a worksite's jurisdiction. Triggers a compliance-cache refresh server-side — jurisdiction-driven required tracks recompute for every worker assigned to the site. Accepts either a `jurisdiction_id` UUID OR a 2-letter `state_code` (e.g. 'CA'). Provide one of them, not both. Use when the admin says 'we're growing into Nevada — change the Phoenix site to NV', 'set the Tempe site's jurisdiction to Arizona', etc. The campaign Scene 4 narrates this tool (optional jurisdiction-expansion CTA). Permission gate: worksites.write. Undo: re-call with the previous jurisdiction. |
| **Transfer a worker to another job** (`transfer_worker`) | Composite write: moves a worker from one worksite's primary Job to another's. Pauses every active job_workers row for the worker (stamps unassigned_at) + upserts a fresh row on the new worksite's primary Job + optionally updates the worker's crew membership. Worker certifications and group memberships preserved — transfer is a crew/site move, not a re-onboarding. Use when the admin says 'transfer Maria to the Tempe job', 'move Roberto from Phoenix to the new bid', etc. The campaign Scene 6 narrates this tool. Permission gate: workforce.workers.assign (plus workforce.workers.manage when a crew is named). Undo: clears the new assignment and re-clears unassigned_at on the previously-active rows. |
| **Make a worker the job's foreman** (`promote_to_foreman`) | Composite write on the worksite's primary Job: if the Job has NO lead foreman, names the worker lead foreman (`jobs.foreman_worker_id`); if it already has one, the lead is never replaced — the worker is granted Daily Log access alongside them instead (they must already be assigned to the Job, else `not_assigned`). Also adds the worker to the company's foreman role group for their trade (if one exists), which fans out supervisor-track required-courses. If the worker can ALREADY file that Job's Daily Log (an existing grant, or a crew lead) nothing is granted: `access: 'already_can_file'` with `why` ('granted' / 'crew_lead') — tell the admin they already could. Access given (named lead or grant) closes the worker's open crew-lead request as approved. Refuses a closed Job (`job_closed`) and an inactive worker (`worker_inactive`). Audited. Use when the admin says 'promote Maria to foreman on the new job', 'make Roberto the foreman of the Tempe crew', 'let Ana file the daily log on Coyote Wash', etc. — and tell them which happened (named lead vs. access alongside the existing lead vs. already could file). The campaign Scene 6 narrates this tool. Permission gate: worksites.write. Undo (only when `undoable`): ends the grant this call created, or clears the lead stamp this call set, and removes the role-group membership this call added — never access the worker had before. |
| **Create an audit share link** (`create_audit_share_link`) | Create a read-only public link to a compliance audit run that the user (or anyone they send the URL to) can open in any browser without logging in. Use when the user asks to 'share an audit with a customer / inspector / prequal portal', 'send the audit to Avetta', 'make a public link for the compliance report', etc. The link is permission-gated server-side (training.view) and the manager can revoke it any time from /dashboard/audit. Default expiry is 30 days; the tool also accepts 7, 90, 365 (one year), or null (no expiry). Returns the share_id + the absolute public_url to copy/send (and a public_url_path fallback). Always confirm the audit_run_id with the user — typically the most recent run from get_compliance_stats or run_compliance_audit. The undo for this tool is revoking the link. |
| **Save a drafted document** (`save_drafted_document`) | Persist a narrative document you drafted WITH the user in this chat (owner update letter, scope clarification, lessons learned, project closeout report, weekly owner digest). Call this only after the user approves the draft. It creates a real document in the job's Documents (with PDF / share / sign), stamped as AI-drafted. Pass the final approved markdown as content_md. Resolve job_id via list_jobs first. Returns a link to open it. (For non-narrative kinds — OSHA forms, change-order narrative, sub scope of work, safety plan, schedule recovery plan — use route_to_surface(draft_document) instead.) |
| **Accept a hazard suggestion** (`accept_hazard_suggestion`) | Accept one suggested hazard: adds that hazard to its worksite (recorded as coming from the quoted evidence) and the worksite's default required designations for it; every other hazard and requirement is left as it is. Use only when the person says to accept it. Permission gate: jobs.create. Undo: undo_write removes the hazard again (and the requirements only it added) and puts the suggestion back to pending. |
| **Dismiss a hazard suggestion** (`dismiss_hazard_suggestion`) | Dismiss one suggested hazard: the same source will not suggest it again (another source still may). Nothing on the worksite changes. Use when the person says the suggestion is wrong or not needed. Permission gate: jobs.create. Undo: undo_write puts the suggestion back to pending. |
| **Remember something about the person** (`remember`) | Save one thing the person told you about themselves, their company or how they work, so every Jobsite surface knows it next time: a fact ("we're a 12-person drywall sub"), a preference ("Money first on Mondays"), a habit, or a goal ("off Procore by January"). In their own words, one entry per call. scope company saves it for everyone in the company (needs the “Edit company settings” permission). The person sees every entry on What Jobsite knows about you and can edit or delete it. Never save something they said no to. If it refuses with memory_paused the person turned off remembering: nothing was saved, so tell them so and never promise to remember it. Undo: undo_write forgets it. |
| **Forget a memory entry** (`forget`) | Delete one memory entry the person no longer wants Jobsite to keep (their own, or a company entry with the “Edit company settings” permission). It disappears from every surface. Or, only when they ask to forget ALL of it ("forget everything about me"), pass everything:true: every personal entry goes at once, and the habits Jobsite noticed with them; company-wide entries stay. Give memory_id OR everything, never both. Undo: undo_write puts it (or them) back within 30 days. |
| **Track a promise** (`make_commitment`) | Record a promise as a tracked commitment, in the same answer that makes it: whenever you say Jobsite will do something later ("I'll check back Friday" → owes jobsite), ask the person to do something later ("once you export the RFI log…" → owes person), or name something Jobsite doesn't do yet (state gap_noted). The person sees it under What Jobsite knows about you and in "what's left?", and a step they owe that goes quiet earns a reminder email. Undo: undo_write drops it. |
| **Update a commitment** (`update_commitment`) | Move one of the person's commitments: done when the promise is kept, dropped when it no longer matters, waiting_on_person or jobsite_on_it when the next step changes hands, gap_noted when it turns out Jobsite can't do it yet; reword it or set when to check back. A done or dropped commitment stays closed. Undo: undo_write puts back what it was while it is still open. |
| **Follow the move plan through** (`track_move_plan`) | Follow the company's move plan through for this person: every row becomes a tracked commitment — a row Jobsite takes today is a step the person owes; a row Jobsite can't take yet is filed for the Jobsite team and closes when it ships. Rows already tracked, done or skipped are left alone; tracking again picks up new rows. A move that goes quiet earns one reminder email after 3 days and one more a week later. Undo: undo_write drops the rows it added. |
| **Record an answer about the old system** (`answer_move_question`) | Save the person's answer to one of the move's three questions — what they like about their old system, what frustrates them, what they wish it did — in their own words, to what Jobsite knows about them. When the frustration is about Jobsite itself (about_jobsite), it goes to the Jobsite team as feedback instead. Undo: undo_write forgets it. |
| **Add a worker custom field** (`create_custom_field`) | Define a custom field on every worker's profile — something the company tracks that Jobsite has no column for ("Union local", "Shirt size", "Badge expires"). Custom fields are on workers only. data_type is text, number, date, boolean (yes / no), single_select or multi_select (give the choices in options). The key is made from the label unless given. A field archived earlier with the same key comes back, with its saved values. Refuses when workers already have a field with that key or label (fill it with set_worker_custom_values). Needs the custom fields permission. Undo: undo_write archives it (values are kept). |
| **Change a company notification** (`update_notification_setting`) | Change how one of Jobsite's notifications reaches the company (Company ▸ Notifications): turn its email, text (sms) or push on or off, and, for notifications with reminders (certifications expiring, insurance renewals…), the days before when they go out (lead_days, each 1–30). Only the fields given change; a channel the notification doesn't offer is refused, and a push each person turns on for themselves is never set for the company. Applies to the whole company and needs the edit-company-settings permission. Undo: undo_write puts the earlier setting back while nobody has changed it since. |
| **Set a record number code** (`set_record_number_code`) | Set the code Jobsite numbers a record type with, company-wide (Company ▸ Record numbers): purchase orders (PO-0001), RFIs (RFI-001), submittals (SUB-001), punch items (PUNCH-001) or change orders (CO #1) — e.g. their old system's "Q" for RFIs. 1–8 letters or digits starting with a letter; an empty code goes back to the default. New records get the new code; existing records keep their numbers and the count continues. Needs the edit-company-settings permission. Undo: undo_write puts the earlier code back while nobody has changed it since. |
| **Arrange my Home** (`arrange_home`) | Arrange this person's own Home (no one else's): put sections first (order — the rest keep their order after them), hide or show sections, or go back to the usual order (reset). Sections: needs_you ("Needs you" — open items), jobs_attention ("Jobs needing attention"), today_strip ("The numbers" — money, safety or service today), feed_today ("Today"), recent_activity ("Feed"). Only what the person asked for; a hidden section is offered back on Home. Undo: undo_write puts the earlier arrangement back while Home wasn't arranged again. |
| **Pin a page to my sidebar** (`pin_sidebar_item`) | Pin a page to this person's own sidebar (no one else's), or take it off with unpin. page is a sidebar or All tools page by its name ("RFIs", "Daily reports", in English or Spanish) or its path (/dashboard/rfis); a name that fits several pages comes back as candidates to ask about. The sidebar holds 12 pins. Undo: undo_write puts the earlier pins back while they haven't changed since. |
| **Load the sample data** (`load_sample_data`) | Load Jobsite's sample data into this company, as Home's Explore with sample data does: a sample crew, jobs and the records a working company has, shaped to what the company does (a service business gets service calls and a small crew), every row marked Sample. It is how a new company looks around before entering its own records; it changes nothing real and doesn't count as going live. When the sample is already there, nothing happens. Needs the person's sample-data permission. Undo: undo_write clears the sample rows, as Company ▸ Sample data ▸ Clear does. |
| **Add a punch item** (`create_punch_item`) | Add an item to a job's punch list — something to fix before closeout ("drywall ding by the unit 4 door"). The title comes from the description unless given. Assign it to one of the company's workers, or name a subcontractor (the item is then left unassigned with a link, because assigning a sub emails them and that is a person's step in Jobsite). Attach the photos the person showed you (photos: the image bytes, stored privately on the item). Needs the worksites.write permission and a plan with project management. Undo removes it, with its photos, while nobody has worked it (no status change, comment or photo of theirs). |
| **Log a safety observation** (`log_safety_observation`) | Log what someone saw on a job — a hazard, a near miss, an unsafe act, or a safe act worth recognizing — the same as Safety observations ▸ New. A hazard, near miss or unsafe act opens for the safety lead to triage; a safe act is recorded as recognized. Optionally assign a worker the follow-up. Attach the photos the person showed you (photos: the image bytes, stored privately with it). Needs the worksites.write permission. Undo removes it, with its photos, while it is untriaged and untouched. |
| **Record a first incident report** (`report_incident`) | Record the first report of a near miss or a first-aid injury — what happened, when (a time, minutes ago, or now), where, who, and the photos the person showed you — as the open incident Jobsite's report form makes, for a person to finish (witnesses, OSHA details, investigation). It never puts anything on the OSHA 300 log and sends no notice. An injury beyond first aid, a hospitalization or a death is report_serious_incident's. Pair it with osha_recordability_check. Undo removes the report, with its photos, while nobody has touched it. |
| **File a recordable or serious incident (confirmed each time)** (`report_serious_incident`) | File an incident beyond first aid — recordable, serious, or a death — as Jobsite's incident form files it. CONFIRMED EVERY CALL: the first call files nothing and states every consequence filing triggers (refresher training assigned and its due date, a training hold on the worker at serious, the OSHA 1904 recordability outcome from Jobsite's decision tree, any 8-hour / 24-hour report-to-OSHA deadline, the company's automations on a new incident) with a confirm_token. Tell the person all of it and ask; only on their yes, call again with the same arguments and confirm = that token. It never reports to OSHA, never sets the OSHA classification, and can't be undone from here. A near miss or a first-aid case is report_incident's. |
| **Draft a change order** (`draft_change_order`) | Draft a change order (a PCO by default, or a COR) on a job's owner contract — or on a subcontract by contract_id — with its reason, schedule impact in days and cost lines (description, dollars, cost code as Jobsite shows it). It stays a DRAFT: never submitted, approved or sent to the owner or GC, and no budget or contract value changes. A supplier PO's change order is drafted in Jobsite. Needs the worksites.write permission and a plan with financials. Undo voids the draft while it is still a draft with the lines written here. |
| **Update a schedule task** (`update_schedule_task`) | Update one task on a job's schedule (by task_id, or job_id + task_name): mark it started or complete, set its percent complete, or move its start and finish dates. Successor tasks are not moved — the result names any that would now overlap, to cascade in Jobsite's schedule. Needs the worksites.write permission and a plan with project management. Undo puts the earlier values back while nobody has changed the task since. |
| **Draft a submittal** (`draft_submittal`) | Draft a submittal on a job — a spec section and item ("08 71 00", "Door hardware") with an optional description, manufacturer, model number, due date and the responder company (the architect / engineer, from the directory). It stays a DRAFT and nothing is sent: a person attaches the product data and submits it in Jobsite, and only submitting sends it to the responder. The number is assigned by Jobsite. Needs the worksites.write permission and a plan with project management. Undo removes the draft while nobody has touched it (no edit, revision, file, comment or approval step). |
| **Add a time entry** (`log_time_entry`) | Add one shift to a worker's timesheet — the same as Timesheets ▸ Add manual entry ("Ben worked 7 to 3:30 on Elm St Monday"). Give the worker (list_workers), the job or worksite, the date (default today, company calendar), the start as 24-hour "HH:MM" company time, and either the end "HH:MM" or hours; an end earlier than the start is an overnight shift. entry_type is work (default), pto, sick or holiday (no place needed). Times are read in the company's time zone. A shift ending in the future, or in a week that is already approved or exported, is refused. Needs the timesheets.edit permission and a plan with time tracking. Undo removes the entry while its week is still open. |
| **Start an inspection** (`start_inspection`) | Start an inspection from one of the company's published templates ("scaffold pre-use on Elm St for Ben") — the same as Inspections ▸ Run. Give the template by name or id, the job (or worksite), the worker who will fill it, and the equipment asset when it is for one. It is created assigned with NO answers: the worker answers on their phone (it appears under their assigned inspections; no message is sent), and the assistant never fills one in. The result links the inspection in Jobsite. Needs the worksites.write permission. Undo cancels it while nobody has answered a question. |
| **Draft a service invoice** (`service_draft_invoice`) | Create a DRAFT customer invoice on a service call. By default an accepted estimate's lines come in; give lines (description, quantity, unit_price_usd) to replace them, with tax_rate_percent, due_on and notes. It is never sent: no pay link, no email, nothing to the customer — a person reviews and sends it from Jobsite. A deposit the customer already paid is applied to it. If a deposit payment is still in progress on the call it refuses (creating an invoice would cancel it); draft the invoice in Jobsite once it settles. One open invoice per call. Needs the service.invoice permission. Undo voids the draft while it is still a draft at the total left here. |
| **Add manpower to a daily log** (`add_daily_log_manpower`) | Key one manpower row on a job's daily log — who was on site and for how long ("six from the framing crew, 8 hours"). Name the crew, subcontractor or customer by its id (target_kind + target_id), or use other for general labor. The day is the company's calendar day (default today; never a future day); the log is started if the day has none. Once any manpower is hand-keyed the day's manpower reads from those rows instead of the time clock. The log stays unsigned (this never signs it); a signed day is refused. Needs the worksites.write permission and a plan with project management. Undo removes the row while the log is still unsigned. |
| **Set a daily log's weather** (`set_daily_log_weather`) | Set the weather a person observed on a job's daily log — text ("Clear, light wind") and low / high °F. It REPLACES the day's weather reading (what you leave out is left empty) and marks it as a person's reading, so the auto-fill won't overwrite it. The day is the company's calendar day (default today; never a future day). The log stays unsigned (this never signs it); a signed day is refused. Needs the worksites.write permission and a plan with project management. Undo puts the earlier reading back only while the day still holds exactly what this set. |
| **Draft a marketing post from a finished job** (`marketing_draft_post`) | Write a DRAFT marketing post about one finished service call — by default a Google listing update — with Jobsite's marketing writer and checks (no price promise, no claim the company's records don't back, no street address, no customer name). It is never published or posted: a person reviews and approves it in Marketing; an approved Google listing update can then be posted with gbp_post_update in Ask. What was done, the problem and the town default to the call's own record (its final visit's notes, its summary, the city of its address); pass them only to use the person's own words. |
| **Draft a vendor bill** (`ap_draft_bill`) | Create a DRAFT vendor bill from an invoice you read: vendor, invoice number, dates, lines and total, optionally the document itself (base64 pages, up to 10). Jobsite matches the vendor, checks for duplicates, suggests cost coding and returns the draft's link with anything that would block it. It never submits, approves or pays — a person finishes it in Jobsite. |
| **Approve or reject a bill (your step)** (`ap_decide_approval`) | Record the user's own decision on ONE vendor bill's approval step that is waiting on them — approve, or reject with a reason. The result echoes the vendor, amount and invoice number. A decision is final. It never schedules or makes a payment. |
| **Create a service call** (`service_create_call`) | Create a home-services call for an existing customer and location, or a new customer / address. Internal only — nothing is sent to the customer. Undo cancels it. |
| **Schedule a service visit (draft)** (`service_schedule_visit`) | Add a visit to a service call with an arrival window. The visit is a DRAFT: the customer is not notified until a person publishes the day on the dispatch board. Undo cancels the visit. |
| **Assign technicians to a visit** (`service_assign_technicians`) | Set a visit's crew: one lead technician and optional helpers (replaces the current crew). Undo puts the previous crew back unless someone changed the visit since. |
| **Draft a service estimate** (`service_draft_estimate`) | Create a DRAFT estimate on a service call: title, scope, tax rate, optional good/better/best options and priced lines. It is never sent — a person reviews and sends it from Jobsite. |
| **Draft a daily log narrative** (`draft_daily_log`) | Draft a job's daily-log narrative for a day from Jobsite's own records (weather, manpower, deliveries, crew posts, safety activity) plus what you observed in the field. It lands in the UNSIGNED log for the foreman or a manager to review and sign. Undo restores the previous narrative. |
| **Add workers (proposal or attested add)** (`propose_workers`) | Add workers to the roster. When the company has recorded its standing roster attestation in Jobsite, they are added now under it; otherwise this returns a proposal link where a person confirms the employer statement and adds them. Never returns sign-in PINs. |
| **Undo a change made through this connection** (`undo_write`) | Reverse one change made through this connection, by the id its tool result returned (or one listed by list_recent_writes). Runs the same reversal as the Undo button in Jobsite, as the connection's team member: it is refused when the record has been used since (for example a worker who has clocked in, or a talk workers have signed). Undoing a change that was already undone does nothing. |
| **Create a worker** (`create_worker`) | Add a worker to the company roster. Runs through the same permission gate (workforce.workers.manage), validation, seat limits, and PIN generation as the web app, acting as the API key's bound user. Custom fields must already be defined for the company. Pass dry_run:true to preview without saving. |
| **Set a worker's custom field values** (`set_worker_custom_values`) | Set or clear custom-field values on an existing worker. Each value is validated by the same validator the web app uses; unknown or invalid fields are rejected. Acts as the API key's bound user behind workforce.workers.manage. Pass dry_run:true to preview. |
| **Create an RFI** (`create_rfi`) | File an RFI on a job. Runs through the same gate (worksites.write), tenant checks, validation, and audit as the web app, acting as the API key's bound user and attributing the RFI to them. Pass dry_run:true to preview. |
| **Edit the website draft** (`website_apply_ops`) | Edit the company's website DRAFT with typed operations (never HTML), applied atomically as one new draft revision. The operations are the grammar website_get_site returns (page.create, block.add, block.update, site.set_theme, service.upsert, special.upsert, …); localized text is {en, es}. base_revision_id must be the head you read, else the edit is refused as stale. Acts as the API key's bound user behind the website edit permission. Cannot publish: a publish op is refused. Pass dry_run:true to get the diff and the publish check without saving. Never claim a license, insurance or certification in text — unsupported claims block publishing. |
| **Publish the website** (`website_publish`) | Publish a draft revision of the company's website so it goes LIVE on the public internet, replacing the current live version. Needs the separate publish:website scope and a bound user who holds the website publish permission. Runs the same publish check as the web app (blocking problems refuse) and publishes only if the draft still matches expected_document_sha from website_get_site — so you publish exactly what you reviewed. An agent cannot accept the website terms of use: if they are not yet accepted, a person publishes once from the Website page. |

Every change tool accepts `dry_run: true` to preview without saving, and an `idempotency_key` so a retried call is never applied twice. `undo_write` reverses a change.

## Access and safety
- One company per connection; nothing an assistant sends can reach another company's records.
- Never more than the approving manager can see; re-checked on every request.
- OAuth 2.1 with PKCE; access credentials expire after one hour and refresh credentials rotate on every use.
- Each connection can make up to 120 requests a minute.
- Revoke any time under Integrations > API & agent access.

Human page: https://jobsitecerts.com/mcp. Developer overview: https://jobsitecerts.com/developers.md. Support: support@jobsitecerts.com
