# AI assistants (MCP)

TruTec hosts a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server. Connect an AI assistant to it and the assistant can work with your TruTec data: look up customers, sites and jobs, read estimates and proposals, total a job's costs against its invoices, see what changed this week, find the parcels at an address, measure a site from aerial imagery, save the takeoff on a job and show its map in the chat, rate pavement condition, hand you a download link for any file the app makes, and do anything else the TruTec API can do when you ask it to: every API action has its own tool.

It works with ChatGPT, Claude (claude.ai, Claude Desktop and Claude Code), Cursor, VS Code and any other MCP client. There is nothing to install or host.

| | |
|---|---|
| Server URL | `https://trutec.ai/api/v1/mcp` |
| Transport | Streamable HTTP (stateless, JSON responses) |
| Auth | Sign in with TruTec (OAuth 2.1), or your API key in the `X-API-Key` header or `Authorization: Bearer sk_...` |
| Plan | Same as the REST API |

## Same key, same permissions

The MCP server is another way to call the REST API. Every tool runs the matching REST endpoint as your key, so:

- **Permissions carry over.** A key without **Write jobs** gives a read-only assistant: `create_job` is refused with the same error the REST API returns. A key without **Read measurements** cannot measure sites or rate pavement.
- **Usage and billing carry over.** A tool call that wraps a billed REST call is billed the same way. Reading records is never billed.
- **Workspace scope carries over.** The assistant only sees the workspace the key belongs to.

Create a dedicated key for each assistant under **Settings › API keys**, give it only the permissions it needs, and revoke it when you are done. See [Authentication](authentication.md) for key types and permissions.

## Connect

### ChatGPT

ChatGPT connects by signing in to TruTec. It works on ChatGPT on the web with developer mode turned on (Plus, Pro, Business, Enterprise and Edu; on Business, Enterprise and Edu plans a ChatGPT admin turns it on).

1. In ChatGPT on the web, turn on **Developer mode** in Settings (under **Security and login**, or **Apps › Advanced settings** on some plans).
2. Add an app: **Plugins › Add › Create MCP App** (or **Settings › Apps › Create**).
3. Name it **TruTec**, set the server URL to `https://trutec.ai/api/v1/mcp`, set Authentication to **OAuth**, accept the warning and create it.
4. Sign in to TruTec in the window that opens. A workspace owner picks **Read only** or **Read and change** and presses **Allow**.
5. In a chat, open **+ › Developer mode** and turn TruTec on, or mention TruTec in your message.

### Claude (claude.ai and Claude Desktop)

1. Open **Settings › Connectors › Add custom connector**.
2. Name it **TruTec** and paste `https://trutec.ai/api/v1/mcp` as the URL.
3. Press **Connect**, sign in to TruTec, pick **Read only** or **Read and change**, and press **Allow**.

On Team and Enterprise plans, an organization owner adds the connector first.

### What signing in does

Signing in creates an API key for the assistant in **Settings › API keys**, named after the assistant, with the access the owner picked: **Read only** (reads everything, changes nothing) or **Read and change** (also creates and updates records; never deletes). Only a workspace owner can approve, on a plan that includes the API. To disconnect, revoke that key; the assistant asks to sign in again next time.

### Claude Code

```bash
claude mcp add --transport http trutec https://trutec.ai/api/v1/mcp \
  --header "X-API-Key: sk_your_api_key_here"
```

### Cursor, VS Code and other clients

Add this to the client's MCP config file (for example `.cursor/mcp.json` or `.mcp.json`):

```json
{
  "mcpServers": {
    "trutec": {
      "type": "http",
      "url": "https://trutec.ai/api/v1/mcp",
      "headers": { "X-API-Key": "sk_your_api_key_here" }
    }
  }
}
```

VS Code's `.vscode/mcp.json` uses `"servers"` instead of `"mcpServers"`. The rest is the same.

### Claude Desktop with an API key

To use an API key you already have instead of signing in, Claude Desktop connects through the `mcp-remote` bridge (needs Node.js):

```json
{
  "mcpServers": {
    "trutec": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://trutec.ai/api/v1/mcp", "--header", "X-API-Key:${TRUTEC_API_KEY}"],
      "env": { "TRUTEC_API_KEY": "sk_your_api_key_here" }
    }
  }
}
```

The key is checked when the client connects. A missing, wrong or revoked key gets `401 Unauthorized` right away, with a `WWW-Authenticate` header that tells sign-in clients where to sign in.

## Tools

Ids link the records together: a job has `client_account_id` (its customer) and `location_id` (its site), and an estimate has `job_id`. Money is in dollars. Times are UTC, ISO 8601. Coordinates are `[latitude, longitude]`.

### Business data

| Tool | What it does | Permission |
|------|--------------|------------|
| `find_customers` | Search customers (companies) by name or your own `external_ref`. Returns id, name, parent company, QuickBooks Online customer id, addresses and billing contact | Read jobs |
| `find_sites` | Search sites (properties) by name or address. Returns id, name, address, `external_ref` and the customer id | Read jobs |
| `find_jobs` | Search jobs by name, company, address or contact. Filter by `status`, owner (`assigned_to_user_id`), `external_ref` or `updated_after` | Read jobs |
| `get_job` | One job: contact, address, status, dates, value, owner, linked customer and site, custom fields | Read jobs |
| `list_estimates` | Estimates, most recently changed first. Filter by `job_id`, `status` (`draft`, `estimate`, `won`, `lost`, `change_order`) or `updated_after`. Each row has totals (price, tax, cost, margin) and proposal state: `current_version` is the latest version sent to the customer, `awarded_at` is when it was won | Read jobs |
| `get_estimate` | One estimate: totals, every issued version with its date and price, and line items by area (quantity, unit, unit price, price, cost, margin, the price printed on the proposal). `version` returns a sent version exactly as issued | Read jobs |
| `get_job_financials` | One job's money and field time in one call: original and revised contract, actual costs by category, invoiced / paid / open totals with each invoice, crew hours and labor cost, and the crew, people and equipment on each work order | Read jobs |
| `list_job_photos` | A job's photos with URLs, caption, stage (before, during, after, general), who took it, when, location and tags. Filter by `stage` or `visibility` (`external` = customer can see it) | Read jobs |
| `list_recent_events` | What changed: jobs created or moved, estimates created, sent, revised, won or declined, change orders approved, invoices created, payments received, work scheduled or completed. Last 7 days by default; filter by `since`, `event_type` or `job_id` | Read jobs |
| `create_job` | Create a job. `name` is required. Link the customer by `client_account_id` or `company`. A job with the same name is refused with its id unless `allow_duplicate_name` is true. With `external_ref` the call updates the job that already carries it instead | **Write jobs** |

### Sales & CRM

| Tool | What it does | Permission |
|------|--------------|------------|
| `find_contacts` | Search people (property managers, owners, billing contacts) by name, email, phone, company or title | Read jobs |
| `create_contact` | Add a person, optionally linked to a job | **Write jobs** |
| `update_contact` | Change a person's details, tags or custom fields | **Write jobs** |
| `create_customer` | Add a customer company. Safe to repeat: an existing one with the same name or `external_ref` is returned | **Write jobs** |
| `update_customer` | Change a customer's name, addresses, billing contact, parent company or custom fields | **Write jobs** |
| `update_job` | Change a job's sales details and/or move it to another pipeline stage, with the same rules as the board | **Write jobs** |
| `add_note` | Write a note on a job, contact or customer, or log a call, email or meeting | **Write jobs** |
| `list_tasks` | Open follow-ups, soonest due first | Read jobs |
| `create_task` | Schedule a follow-up on a job or a customer | **Write jobs** |
| `complete_task` | Mark a follow-up done, with notes | **Write jobs** |
| `list_leads` | The lead inbox, newest first | Read jobs |
| `convert_lead` | Move a lead forward into the sales pipeline | **Write jobs** |

### Estimating & proposals

| Tool | What it does | Permission |
|------|--------------|------------|
| `search_catalog` | Find services, materials, crews, equipment and other catalog items with their prices and units | Read jobs |
| `create_estimate` | Start an estimate on a job, blank or from a template | **Write jobs** |
| `add_estimate_item` | Add a service or item to an estimate with a quantity; the estimate re-prices itself | **Write jobs** |
| `update_estimate_item` | Change a line's quantity, price or wording | **Write jobs** |
| `remove_estimate_item` | Remove a line from an estimate | **Write jobs** |
| `price_estimate` | Re-price an estimate and return its totals; optionally change its markup, tax, price list or name first | **Write jobs** |
| `issue_estimate` | Issue a version: a frozen copy of the estimate as the customer will see it | **Write jobs** |
| `set_estimate_outcome` | Mark an estimate won or lost | **Write jobs** |
| `get_proposal` | A proposal's status, sent and viewed times, signature status and links | Read jobs |
| `prepare_proposal` | Create or refresh the proposal for an estimate | **Write jobs** |
| `send_proposal` | Email the proposal to the customer, exactly like Send in the app. Confirm first | **Write jobs** |

### Operations & money

| Tool | What it does | Permission |
|------|--------------|------------|
| `get_schedule` | The crew schedule for a date range, by crew or job, or only visits with no crew (`unassigned`); `include_queue` adds sold work not yet scheduled | Read jobs |
| `schedule_visit` | Put work on the schedule for a job: dates or exact working days, crew, foreman, gate code, staging notes, equipment and how the customer is alerted | **Write jobs** |
| `reschedule_visit` | Move a scheduled visit or change its details (same fields as `schedule_visit`) | **Write jobs** |
| `assign_crew` | Put a crew or a person on a visit | **Write jobs** |
| `publish_schedule` | Publish the schedule to crews, the same one-way step as in the app | **Write jobs** |
| `list_crews` | Crews and who is on them (`include_inactive` adds archived crews) | Read jobs |
| `log_time` | Add a time entry for a person on a job | **Write jobs** |
| `list_time_entries` | Time entries by person, job, date, category or approval; `open` shows who is still on the clock | Read jobs |
| `create_change_order` | Start a change order on a job: a quick dollar amount, or (`kind` `tm` or `full`) a change-order estimate to price | **Write jobs** |
| `add_job_cost` | Record a cost (material, subcontractor, equipment, other) on a job, optionally against an estimate line | **Write jobs** |
| `create_invoice` | Create a draft invoice from line items, optionally billing an estimate. It is never sent or charged by this tool (`send_invoice` sends it) | **Write jobs** |
| `list_invoices` | Invoices with status, totals and balance; filter by job, status, payment method or `updated_after` | Read jobs |

### Files, field & workspace

| Tool | What it does | Permission |
|------|--------------|------------|
| `get_workspace` | Company name, plan, timezone, office address and which products are on | Read jobs |
| `list_users` | People in the workspace with ids, roles and titles | Read jobs |
| `list_job_files` | Documents on a job (plans, contracts, permits, signed proposals) | Read jobs |
| `upload_job_photo` | Add a photo to a job from a public image link | **Write jobs** |
| `save_measurement_to_job` | Save a finished `measure_site` result onto a job as a takeoff, so it opens in the takeoff editor and can feed an estimate | **Write measurements** |
| `get_takeoff_quantities` | Every quantity a saved takeoff provides, with units, plus quantities per service | Read measurements |
| `list_site_assessments` | Site assessments (field walkthroughs), newest first | Read jobs |
| `get_site_assessment` | One assessment with totals per service and every pin | Read jobs |
| `add_assessment_pin` | Drop an observation pin on a job at a location | **Write jobs** |

### Catalog setup

An assistant can build and maintain the catalog from a conversation or a supplier's price sheet. Catalog writes act as the key's creator and need the workspace owner or an estimating admin. See [Catalog](catalog.md).

| Tool | What it does | Permission |
|------|--------------|------------|
| `get_catalog_item` | One service, cost item or vendor in full: a service's qualifiers, types and recipe lines with their ids, a material's vendor prices | Read jobs |
| `create_catalog_item` | Add a service, a cost item (material, crew, equipment, truck, subcontractor, misc item, overhead) or a vendor | **Write jobs** |
| `update_catalog_item` | Change a service, cost item or vendor. Returns each change as before and after | **Write jobs** |
| `archive_catalog_item` | Archive a service, cost item or vendor. Estimates that use it keep it | **Write jobs** + **Delete records** |
| `set_service_recipe` | Set what one unit of a service takes: materials, crews, equipment and other costs with quantity formulas | **Write jobs** |
| `set_service_qualifiers` | Set the questions a service asks (for example *Coats: 1 coat / 2 coats*); each answer combination becomes a type that can carry its own price | **Write jobs** |
| `set_vendor_price` | Record a vendor's price for a material, or stop using a vendor's prices for it | **Write jobs** |
| `create_catalog_group` | Add a service group (division) or a group of cost items | **Write jobs** |
| `edit_catalog_group` | Rename a service group, or remove an empty one | **Write jobs** (**Delete records** to remove) |
| `list_catalog_groups` | The catalog's groups with their ids | Read jobs |
| `find_vendors` | Suppliers, rental houses and subcontractor firms by name | Read jobs |
| `approve_catalog_items` | Approve draft rows, such as rows saved by `import_catalog_rows` | **Write jobs** |
| `import_catalog_rows` | Add or update many rows at once, matched by name. Previews first; saves only when called with `dry_run: false` | **Write jobs** |

### Workspace settings

Settings writes need a key created by a workspace owner, the same as the Settings pages. See [Settings](settings.md).

| Tool | What it does | Permission |
|------|--------------|------------|
| `get_settings` | Every settings section with its current values, the fields each takes, and what can only be changed in TruTec | Read jobs |
| `update_settings` | Change one section: company profile, branding, units, estimating defaults, sales tax, proposal terms and clauses, job numbering and creation, takeoff, photo, scheduling and notification preferences | **Write jobs**, owner key |
| `edit_workspace_list` | Add, rename, remove or reorder pipeline stages, job types and pick lists such as lead sources | **Write jobs**, owner key (**Delete records** to remove a stage or job type) |

### Files and downloads

An assistant can't hand you a file, so these tools give you a link to one. `export_file` makes any file the app can download: proposal PDFs, estimate and catalog workbooks, work orders, change orders, daily reports and field logs, the crew schedule, payroll and timesheets, job costing, invoices, site assessment reports, a job's photos, takeoff maps, quantities and KMZ/GeoJSON shapes, customer, contact, site and job lists, capital plans, management reports and whole datasets. The link opens or downloads the file for 24 hours with no sign-in, and the file is made when you open it, by the same code as the app's Download button. See [File exports](exports.md) for every kind and option.

| Tool | What it does | Permission |
|------|--------------|------------|
| `export_file` | A 24-hour download link for one file: `kind` (for example `proposal`, `payroll`, `takeoff_geometry`), the record's `id` where the kind names one, `format`, and options such as `job_id`, `from` and `to`. Returns `url`, `filename` and `expires_at` | Read jobs (Read measurements for takeoffs), plus what the app requires for that export |
| `export_kinds` | Every kind with its formats, what its `id` is, its options and what it needs | Any read permission |
| `export_job_documents` | A job's work-order sheets, warranty records and warranty certificates with their ids | Read jobs |

Anyone with an export link can open the file until it expires, so share it only with the person who asked. Revoking the key stops every link it made.

### Customers: email, texts, portal & proposals

Email and texts reach a real customer: the assistant shows you the recipient and the words and sends only after you agree. See [Email & text customers](messages.md), [Customer portal](customer-portal.md) and [Proposals](proposals.md).

| Tool | What it does | Permission |
|------|--------------|------------|
| `send_customer_email` | Email a customer about a job, a contact or a company, from your connected mailbox, logged on the timeline. Unsubscribed and do-not-contact addresses are refused. Confirm first | **Write jobs** |
| `send_customer_text` | Text a customer from the workspace's texting number, logged on the contact and job. Refused for STOP, do not contact and outside 8am-9pm. Confirm first | **Write jobs** |
| `get_text_conversations` | Texting conversations, most recent first (unread, opted out), or one conversation's texts | Read jobs |
| `get_message_templates` | The workspace's email or text templates and merge fields, filled in for a customer | Read jobs |
| `get_proposal_content` | A proposal's sections: About us, scope, terms and their option rows, custom sections, pages, deposit, photos, display switches, link state | Read jobs |
| `update_proposal` | Change those sections, the title, addressee, valid-until date or wording, exactly as the proposal builder saves them. Sends nothing | **Write jobs** |
| `create_proposal_link` | The proposal's customer link, created if it has none, without emailing it (`regenerate` replaces it) | **Write jobs** |
| `set_proposal_link_access` | Turn the proposal's customer link off or on, or set the day it stops working | **Write jobs** |
| `turn_on_customer_portal` | Turn on a customer company's portal and return its link | **Write jobs** |
| `turn_off_customer_portal` | Turn the portal off (the link stops working until it's turned back on) | **Write jobs** |
| `reset_customer_portal_link` | Give the portal a new link; the old one stops working | **Write jobs** |
| `request_customer_payment` | Bill an estimate online (full, deposit, progress or itemized): the workspace's payments account emails the customer a pay link. Returns the link. Confirm first | **Write jobs** |

### Reports & job housekeeping

See [Owner reports](owner-reports.md) and [Delete, merge & follow-ups](job-cleanup-and-follow-ups.md).

| Tool | What it does | Permission |
|------|--------------|------------|
| `get_reports` | The owner reports this connection can run | Read jobs |
| `get_report` | Run one report for a date range: money owed and how late, cash forecast, KPIs, weekly sales, win / loss, top customers, bid vs actual job costs and more | Read jobs, a user who may view management reports |
| `get_labor_hours_report` | Regular, overtime and double-time hours per person, day and job | Read jobs, **Approve payroll** |
| `get_month_end_job_cost_report` | A month's revenue invoiced, direct cost by type, gross profit and GP % per job, plus job to date, to reconcile against your accounting system | Read jobs, a user who may see job costs |
| `delete_job` | Delete a job and everything on it. Cannot be undone. Confirm first | **Write jobs** + **Delete records** |
| `merge_jobs` | Merge a duplicate job into the one to keep, then delete the duplicate. Cannot be undone. Confirm first | **Write jobs** + **Delete records** |
| `get_follow_up_sequence` | Whether the automatic follow-up sequence is on, and running on a job | Read jobs |
| `start_follow_up_sequence` | Start the follow-up sequence on a job; its steps email the customer later. Confirm first | **Write jobs** |
| `stop_follow_up_sequence` | Stop a job's follow-up sequence (owners) | **Write jobs** |
### Crew operations: notices, dispatch, holds, chat & materials

Tools for what happens around the schedule: telling the customer and the crew, holding dates, schedule requests, crew chat, a won job's operations workflow and materials, and no-charge warranties. They need operations turned on for the workspace. See [Customer notices & dispatch](customer-notices-and-dispatch.md), [Holds & schedule requests](holds-and-schedule-requests.md), [Crew messages](crew-messages.md), [Job operations & materials](job-operations.md) and [Job warranties](job-warranties.md).

| Tool | What it does | Permission |
|------|--------------|------------|
| `get_customer_schedule_notice` | What "Email customer" would send for a visit, the customer emails and phones it can go to, and whether they confirmed the date | Read jobs |
| `send_schedule_notice_to_customer` | Email and/or text the customer that their work is scheduled, optionally asking them to confirm the date. Confirm first | **Write jobs** |
| `record_customer_schedule_approval` | Record that the customer approved the date by phone, email or in person | **Write jobs** |
| `get_customer_text_retry` | Check a schedule text that failed before it went out: the phone and message a retry would send | Read jobs |
| `retry_customer_schedule_text` | Send that failed schedule text again. Confirm first | **Write jobs** |
| `send_dispatch_to_crew` | Send a visit's dispatch packet to its crew (in-app alert, text and email). Confirm first | **Write jobs** |
| `send_day_dispatch_to_crews` | Send the dispatch for every visit on one day, or for some crews. Confirm first | **Write jobs** |
| `create_dispatch_packet_link` | The secure link to a visit's crew packet, to share yourself | **Write jobs** |
| `revoke_dispatch_packet_link` | Turn off a visit's packet links | **Write jobs** + **Delete records** |
| `get_schedule_holds` | "Hold a date" blocks by date range, crew or job | Read jobs |
| `create_schedule_hold` | Hold a date for a job or a new placeholder job, locking a crew's days | **Write jobs** |
| `update_schedule_hold` | Move a hold or change its crew, days, times or details | **Write jobs** |
| `convert_hold_to_visit` | Turn a hold into a real draft visit | **Write jobs** |
| `link_hold_to_job` | Tie a hold to the real job, merging its placeholder job | **Write jobs** |
| `release_schedule_hold` | Let a held date go. The job stays | **Write jobs** + **Delete records** |
| `get_schedule_requests` | Teammates' requests for a date, a move or a skipped day | Read jobs |
| `create_schedule_request` | Ask the schedulers for a date, a move or a skipped day | **Write jobs** |
| `approve_schedule_request` | Apply a request the way the scheduler would | **Write jobs** |
| `decline_schedule_request` | Turn a request down with a note | **Write jobs** |
| `send_daily_report` | Email a crew daily report to the customer or general contractor. Confirm first | **Write jobs** |
| `get_crew_messages` | A job's office and crew chat | Read jobs |
| `send_crew_message` | Post in a job's crew chat; the crew is alerted. Confirm first | **Write jobs** |
| `send_crew_broadcast` | One message to every crew with work in the next 14 days. Confirm first | **Write jobs** |
| `get_job_operations` | A won job's operational stage, materials to order and supplier confirmations due | Read jobs |
| `hand_job_to_operations` | Start a won job's operations workflow | **Write jobs** |
| `set_job_operations_stage` | Move a job to another operational stage | **Write jobs** |
| `update_material_order` | Record a material's order status, supplier, PO and quantities | **Write jobs** |
| `update_material_availability` | Record a supplier's availability answer for a material | **Write jobs** |
| `add_material_actual_cost` | Record a material's invoice or ticket amount in job costing | **Write jobs** |
| `confirm_supply_order` | Mark a supplier confirmation done | **Write jobs** |
| `undo_supply_confirmation` | Put a supplier confirmation back to open | **Write jobs** |
| `get_operations_pipeline_columns` | The Operations Pipeline's columns, including the owner's own | Read jobs |
| `move_operations_pipeline_card` | Move a visit or queued work to a column, like dragging it | **Write jobs** |
| `get_job_warranties` | No-charge warranty records on jobs, or one by id | Read jobs |
| `create_job_warranty` | Draft a warranty on a job for the customer to approve | **Write jobs** |
| `update_job_warranty` | Change a draft or sent warranty | **Write jobs** |
| `send_job_warranty_to_customer` | Email the customer the warranty's review link. Confirm first | **Write jobs** |
| `void_job_warranty` | Void a warranty | **Write jobs** |

### Every other API action

Every action in the [API reference](/developers/reference) has its own tool, so an assistant can do anything the API can do, from ChatGPT and Claude too. The tools above cover everyday work. The ones below are made from the API reference, one per action, and named for what they do:

- `get_*` reads. A tool such as `get_photos` lists records a page at a time (`limit`, `offset`), or returns one record when you give its id (`photo_id`).
- `create_*`, `add_*`, `upload_*` and `start_*` add records.
- `update_*` and `set_*` change only the fields sent. To empty a field, name it in `clear_fields`.
- `delete_*`, `remove_*`, `archive_*`, `revoke_*`, `end_*` and `deactivate_*` remove or archive something.
- Other verbs do one step: `approve_*`, `void_*`, `complete_*`, `reopen_*`, `restore_*`, `mark_*`, `move_*`, `send_*` and the like.

Each tool needs the same key permission as its API endpoint (the API reference lists it): reads need **Read jobs**, or **Read measurements** for takeoffs and imagery; changes need **Write jobs**, or **Write measurements** for takeoffs; removing also needs **Delete records**. Some settings-style actions (crews, invitations) need a key created by a workspace owner. `send_invoice`, `send_change_order`, `cancel_work_order` and `invite_user` email or alert people outside your workspace, so the assistant should confirm with you first. When the API gains an action, its tool comes with it.

#### Sales & CRM actions

| Tool | What it does |
|------|--------------|
| `get_job_types` | The workspace's job types (scopes of work). Use the value strings as job_type. |
| `get_job_stages` | List pipeline stages. The workspace's pipeline columns in board order, with what each means for reports and the questions a move into a lost / excluded stage must answer. |
| `get_job_team` | List the job team. The sales owner and teammates on the job. |
| `add_job_teammate` | Add a teammate to a job. Puts a member on the job team (they get the usual "you were assigned" email). Change the sales owner with update_job assigned_to_user_id. |
| `remove_job_teammate` | Remove a teammate from a job. Takes a teammate off the job. The sales owner cannot be removed. |
| `get_customer` | Get a customer. One customer by id, archived or not, with its salesperson, notes, custom fields and divisions. |
| `delete_customer` | Archive or delete a customer. Archives (default). hard=true deletes, refused (with dependents) while records point at it or when the workspace turned deleting off. |
| `get_customer_contacts` | List a customer's contacts. People whose primary company this is, or who hold a current role there (roles listed). |
| `get_customer_jobs` | List a customer's jobs. The customer's jobs, newest first. |
| `get_customer_sites` | List a customer's sites. Sites the customer owns (relationship customer) and sites it is linked to in another role. |
| `create_site` | Create a site. Creates and geocodes a site, or returns the matching one (by external_ref, then normalized address): created false. |
| `get_site` | Get a site. One site by id, archived or not, with coordinates, customer and custom fields. |
| `update_site` | Update a site. Changes name, external_ref or customer. The address cannot be changed. |
| `delete_site` | Archive or delete a site. Archives (default); hard=true deletes when nothing points at it. |
| `get_site_contacts` | List a site's contacts. People with a current role at the site. |
| `get_site_jobs` | List a site's jobs. Jobs at the site, newest first. |
| `get_rebid_cycles` | List re-bid services. The services the workspace re-bids and how often. |
| `get_rebids` | List re-bids. Every property x service with its last-done date (from won jobs and hand entries), when it comes due again and the re-bid status, soonest due first. |
| `get_contact` | Get a contact. One person with all phones and emails, companies and sites (with roles) and jobs. |
| `delete_contact` | Archive or delete a contact. Archives the person (default). |
| `restore_contact` | Restore a contact. Un-archives a person. |
| `add_contact_phone_or_email` | Add a phone or email. Adds a phone number or email. The primary of each kind is mirrored to the contact's phone / email. |
| `update_contact_phone_or_email` | Update a phone or email. Edit the value or label, make it primary, or mark it do-not-contact. |
| `delete_contact_phone_or_email` | Remove one of a person's phone numbers or emails. The next one of that kind becomes primary. |
| `set_contact_role` | Set a role at a company or site. Records that the person works with a company (client_account_id) or site (location_id), with roles. Sending it again replaces the roles. |
| `end_contact_role` | End or remove a role. end=true keeps it as history ("no longer works there", ended_on today or ended_on); otherwise the link is removed. |
| `get_job_contacts` | List the people on a job. Primary contact first, then by role. |
| `add_job_contact` | Add a person to a job. Links a saved person (contact_id) or creates one from name / email / phone / company. The first person on a job becomes primary and fills the job's contact fields. |
| `update_job_contact` | Change a person's role on a job. Role, make primary, or turn "additional customer" on or off. |
| `remove_job_contact` | Remove a person from a job. Unlinks the person (the contact stays). If they were primary the next person becomes primary. |
| `get_job_activity` | List job activity. The job's activity log, newest first: stage moves, notes, calls, emails, task lines, files and takeoffs. |
| `get_job_notes` | List job notes. What people wrote on the job: team notes and logged calls, emails, meetings and site visits. |
| `get_contact_notes` | List a contact's notes. The person's feed, newest first. |
| `get_customer_notes` | List a customer's notes. The company feed, newest first: company notes and its people's notes, calls, emails, meetings and texts. |
| `get_contact_history` | A contact's history. Everything that happened with the person, newest first: emails and texts, calls, notes, follow-ups, stage moves, proposals, signatures and invoices. Page with before. |
| `get_customer_history` | A customer's history. The company's merged history, as on its page in the app. |
| `get_job_task` | Get a job follow-up. One follow-up. |
| `update_job_task` | Update a job follow-up. Edit, reschedule, reassign, complete (completed true: completion_notes post to the job as a call / meeting / note, like the task window) or reopen. |
| `delete_job_task` | Delete a job follow-up. Deletes the follow-up. |
| `get_company_task` | Get a company follow-up. One company follow-up. |
| `update_company_task` | Update a company follow-up. Edit, reschedule, reassign or change the repeat of an open follow-up. |
| `create_lead` | Create a lead. |
| `get_lead` | Get a lead. One lead. |
| `mark_lead_contacted` | Mark a lead contacted. Someone reached out. A call, email or meeting is logged on the job and company feed; the lead's "Call new lead" follow-up closes. |
| `assign_lead` | Assign a lead. Hands the lead, its job and its open follow-up to a teammate. A new lead emails them the new-lead alert. |
| `dismiss_lead` | Take a lead out of the inbox as dismissed, or as spam (spam also archives the contact and company it created when nothing else uses them). Its follow-up closes. restore_lead undoes it. |
| `restore_lead` | Restore a lead. Back into the inbox. A submission the form flagged as spam on its own runs through the full intake now and comes back as a new lead (replaced_lead_id). |
| `get_custom_fields` | List custom fields. The workspace's custom field definitions, in page order. |
| `create_custom_field` | Create a custom field. Adds a field to every record of one type. A field with the same label (any case) is returned instead (created false), restored if archived. |
| `set_site_custom_fields` | Set custom fields on a site. Sets the values sent; others are left alone. |

#### Estimating actions

| Tool | What it does |
|------|--------------|
| `get_price_lists` | List price lists. Price columns (for example per county or customer tier). An estimate prices from one (price_list_id). |
| `get_estimate_templates` | List estimate templates. Saved starting points for new estimates (template_id on create_estimate). |
| `archive_estimate` | Archive an estimate. It is never deleted: restore_estimate brings it back. |
| `get_estimate_revisions` | List revisions. The edit log, newest first. undone is true when the estimator undid that edit. |
| `restore_estimate` | Restore an estimate. Brings an archived estimate back. |
| `duplicate_estimate` | Duplicate an estimate. Copies the estimate as a new draft (areas, lines, cost lines, pricing; takeoff links are not copied), on the same job or another. |
| `start_change_order_estimate` | Start a change order. Creates a change-order estimate linked by parent_estimate_id, plus its job change order. |
| `add_estimate_area` | Add an area. Adds a zone of the site. With takeoff_id, line quantities in it come from that takeoff. |
| `update_estimate_area` | Update an area. Rename, or bind or unbind a takeoff. |
| `remove_estimate_area` | Remove an area. Removes the area and its lines (kept in the revision log). |
| `get_estimate_line_item` | Get a line item. One line with its cost lines. |
| `add_estimate_cost_line` | Add a cost line. Adds a catalog cost item to one line's recipe, on this estimate only. |
| `update_estimate_cost_line` | Update a cost line. Edits the cost line on this estimate only; the catalog never changes. |
| `remove_estimate_cost_line` | Remove a cost line. Removes the cost line from this estimate. |
| `get_proposals` | List proposals. Most recently changed first; with updated_after, oldest change first. Customer copies are included and flagged. |
| `get_proposal_pdf` | Download the proposal PDF. The same document as Download PDF in TruTec. Returns the file details; the customer link is share_url from get_proposal. |
| `get_proposal_signature` | Signature status. Who accepted and when, and the latest e-signature request. |
| `apply_proposal_template` | Apply a proposal template. Applies a saved proposal template to a draft proposal. |
| `get_proposal_templates` | List proposal templates. The workspace's saved proposal templates. |
| `get_proposal_clauses` | List proposal clauses. Reusable terms clauses; is_default ones are added to every new proposal. |

#### Operations & money actions

| Tool | What it does |
|------|--------------|
| `get_work_order` | Get a work order. One visit. |
| `delete_work_order` | Delete a work order. Remove a visit and free its crew days. Visits with recorded crew time or workflow history must be cancelled instead. |
| `complete_work_order` | Complete a work order. Mark the visit complete; whole_job finishes every open visit on the job. |
| `reopen_work_order` | Reopen a work order. Put a completed visit back to scheduled (or unscheduled without a date). |
| `cancel_work_order` | Cancel a work order. Cancel the visit. A customer who was told about it gets a cancellation alert. |
| `get_schedule_publish_status` | Get publish status. What the next publish would send (draft changes in the next 30 days) and the last publication. |
| `get_schedule_notes` | List schedule notes. Notes on dates that are not tied to a job. |
| `create_schedule_note` | Create a schedule note. |
| `update_schedule_note` | Update a schedule note. |
| `delete_schedule_note` | Delete a schedule note. |
| `create_crew` | Create a crew. A crew with the same name (any case) is returned and reactivated if archived. |
| `get_crew` | Get a crew. |
| `update_crew` | Update a crew. Rename and/or replace the role-slot line-up. |
| `archive_crew` | Archive a crew. Soft archive. A crew with upcoming scheduled work is refused. |
| `add_crew_member` | Add a crew member. A TruTec login (user_id) or an employee from the labor catalog (ep_individual_id / ep_person_id). Changes nothing when they were already on the crew. |
| `update_crew_member` | Update a crew member. Set work_role (blank clears). |
| `remove_crew_member` | Remove a crew member. |
| `get_crew_people` | List people. Everyone on a crew roster, one row per person, with their crews. person_key identifies people on visits. |
| `get_time_entry` | Get a time entry. With its audit trail. |
| `update_time_entry` | Correct a time entry. Each changed field gets an audit row; approval is cleared. |
| `approve_time_entry` | Approve a time entry. Open and voided entries cannot be approved. |
| `unapprove_time_entry` | Unapprove a time entry. |
| `void_time_entry` | Void a time entry. Soft void with a required reason; the entry stops counting. |
| `restore_time_entry` | Restore a voided time entry. |
| `get_daily_logs` | List daily logs. One log per visit per work day: sign-off status and notes. |
| `save_daily_log_notes` | Add office notes to a day. Saves the day's notes, creating the log if needed. Refused once payroll approved the day. |
| `approve_daily_log` | Approve a day. Office (or payroll) sign-off of a submitted day. Refused when the workspace does not use sign-off. |
| `send_back_daily_log` | Send a day back. Returns the day to the foreman as a draft with a note. |
| `get_daily_reports` | List crew daily reports. Reports crews file from the crew app, newest first. |
| `get_change_orders` | List change orders. Change orders for accounting, most recently changed first. This list uses its own envelope (change_orders, total, limit, offset). |
| `update_change_order` | Update a change order. Draft and sent change orders only. |
| `send_change_order` | Send a change order to the customer. Emails the customer (customer_email) a link to review and sign online, and marks it sent. emailed is false when there is no customer email. |
| `approve_change_order` | Approve a change order. Moves it to the workspace's Approved stage: updates the job's contract and budget and runs any accounting export. |
| `decline_change_order` | Decline a change order. Moves it to the Declined stage. A signed change order cannot be declined. |
| `move_change_order` | Move a change order to a stage. Any key from get_change_order_stages. |
| `void_change_order` | Void a change order. A signed or approved priced change order cannot be voided. |
| `get_change_order_stages` | List change order stages. The workspace's stages in order, each with its meaning. |
| `update_job_cost` | Update a cost. Changes the fields sent. unit and incurred_on cannot change. |
| `delete_job_cost` | Delete a cost. Labor rows from time entries are rebuilt from the timesheet. |
| `review_job_cost` | Review a cost. Approve, reject or reset a cost, and mark it reconciled. A crew-app record without a verified price needs a resolution first. |
| `get_completed_sites` | List completed sites. Every site (not archived) where the workspace has completed work, with the last completion. With updated_after, sorted by updated_at ascending. |
| `get_invoice` | Get an invoice. |
| `update_invoice` | Update an invoice. Changes the fields sent; line_items replaces the list. Edits to an invoice already in QuickBooks Online are pushed there. Online and imported invoices are refused. |
| `send_invoice` | Email an invoice to the customer. Emails the customer (client_email) the invoice with a link to view it. A draft becomes sent and is created in the connected accounting system. |
| `mark_invoice_sent` | Mark an invoice sent. For invoices delivered another way; also moves a paid invoice back to unpaid. Nothing is emailed. Online invoices are refused. |
| `mark_invoice_paid` | Mark an invoice paid. Records the payment today and posts it to the connected accounting system. Nothing is emailed. Online invoices are refused. |
| `void_invoice` | Void an invoice. Voids it here and in the connected accounting system. Online invoices are refused. |
| `get_payments` | List payments. One row per paid invoice, most recent first. Partial payments are not tracked. |

#### Photo, file, field & team actions

| Tool | What it does |
|------|--------------|
| `get_photos` | List photos across jobs. Every job photo in the workspace, for keeping a copy in sync. With updated_after, sorted by updated_at oldest first; otherwise newest first. |
| `update_photo` | Update a photo. Change a photo's caption, title, stage, visibility, report inclusion, tags, map pin or camera direction. Send only what changes. |
| `delete_photo` | Delete a photo. Deletes the photo and its stored files (unless the site assessment still shows the same file) and logs it on the job. Cannot be undone. |
| `get_photo_tags` | List photo tags. The workspace's photo tags. Use their ids as tag_ids. |
| `get_files` | List files. Every file in the workspace: job files, customer and contact files, Company Library files and files TruTec made (executed proposals, dispatch packets). |
| `upload_job_file` | Save a file on a job, like an upload in the app (logged on the job, copied to connected cloud storage). Send its contents as content_text (text, CSV) or content_base64. |
| `upload_customer_file` | Save a file on a customer (company): W-9s, insurance certificates, contracts. Send its contents as content_text (text, CSV) or content_base64. |
| `update_file` | Update a file. Change the description, or for job files who can see it: audience (field shares it with crews in the field app and dispatch packets) and customer_visible (the customer portal). |
| `delete_file` | Delete a file. Deletes the file and its stored copy. A Company Library file is also unlinked from every job. Logged on the job for job files. Cannot be undone. |
| `download_file` | A short-lived link to download one file (see get_files). |
| `start_site_assessment` | Start a site assessment. Starts a job's assessment, the same as Start assessment on the job page. |
| `update_site_assessment` | Update a site assessment. Rename it or change its notes. |
| `complete_site_assessment` | Complete a site assessment. Closes the assessment so later pins stop joining it, attaches any loose pins on the job, and saves the handoff snapshot the operations team works from. |
| `reopen_site_assessment` | Reopen a site assessment. Reopens a completed assessment so new pins on the job join it again. |
| `get_assessment_services` | List assessment services. The services a pin can be recorded against (service_type_id), as set up under Site Assessment settings, with the measurement each one takes. |
| `get_assessment_pins` | List assessment pins. Pins across the workspace. Newest first; with updated_after, oldest first. |
| `update_assessment_pin` | Update an assessment pin. Move it, change what it is for, its quantity, severity, notes, name or who can see it. Send only what changes. Moving a pin also moves any shape drawn around it. |
| `delete_assessment_pin` | Delete an assessment pin. Deletes the pin and its photo rows and updates the job's draft estimate. Cannot be undone. |
| `add_assessment_pin_photo` | Attach a photo to an assessment pin from a public https:// image link (up to 15 MB). Returns the pin with all its photos. |
| `get_user` | Get a user. One team member with their role in this workspace (including a company-defined role) and their teams. |
| `invite_user` | Email someone an invitation to join the workspace as a Member (a sign-up link valid 7 days); again for the same email sends a fresh link. Owners are invited in TruTec. Needs an owner's key. |
| `get_invites` | List invitations. Invitations, newest first. |
| `revoke_invite` | Revoke an invitation. Revokes a pending invitation; its link stops working. |
| `get_roles` | List roles. The built-in roles (Owner, Member, Crew) and the roles the company defined in Settings › Team › Roles, with the permissions each company role grants and how many people hold it. |
| `get_teams` | List teams. Teams: named groups of people that jobs can be filed under (team_id on a job). |
| `get_pick_list` | The options of one pick list: lead sources, customer types, property types or contact types. |

#### Measurement & takeoff actions

| Tool | What it does |
|------|--------------|
| `get_takeoff_tile_layer` | Get a Nearmap tile layer. For drawing a takeoff on your own map over the same Nearmap imagery it was measured on: an XYZ tile URL template that needs no headers, limited to the area around the site. |
| `get_imagery_tile` | One aerial picture tile from the url_template that get_takeoff_tile_layer returns (token, z, x, y from that template). |
| `rename_takeoff` | Rename a takeoff. Renames a saved takeoff. To change its shapes, use `edit_takeoff_items`. |
| `delete_takeoff` | Delete a takeoff. Deletes the takeoff the way the editor's Delete does, logs it on the job and removes its stored plan sheets and drone photos. |
| `get_subscription_plans` | TruTec subscription plans with prices, credits and features. |
| `get_detection_categories` | What site measurement detects: each category with how it is measured (area, count or length) and how the app shows it. |
| `measure_uploaded_tiles` | Measure a site from your own aerial pictures instead of TruTec imagery: north-up Web Mercator tiles (EPSG:3857) as base64 PNG or JPEG, each with its WGS84 bounds, together covering one rectangle. |
| `get_api_status` | Whether the TruTec API is up. |

#### Webhook & bulk data actions

| Tool | What it does |
|------|--------------|
| `get_webhooks` | List webhooks. The workspace's webhook subscriptions, newest first, including deactivated ones ("active": false). The signing secret is never returned. |
| `create_webhook` | Create a webhook. Subscribes target_url to an event type. |
| `update_webhook` | Update a webhook. Change event_type, target_url, secret or active. Send only what changes; a new secret rotates it. |
| `deactivate_webhook` | Turn a webhook off: deliveries stop. The subscription and its delivery history are kept; update_webhook with active true turns it back on. |
| `get_webhook_deliveries` | List webhook deliveries. One row per delivery attempt chain, newest first: delivered (a 2xx came back), pending (another attempt is scheduled at next_attempt_at) or failed (no attempts left). |
| `redeliver_webhook` | Redeliver a delivery. Queues the same payload (same event_id) again as a new delivery and starts sending it right away. The original row is kept. |
| `replay_webhooks` | Replay a time window. Re-sends everything one subscription should have received in a window. |
| `get_bulk_datasets` | List export datasets. The datasets you can download whole or incrementally. |
| `read_bulk_dataset` | Rows of one bulk dataset (filename like "jobs.jsonl" from get_bulk_datasets), in id order. updated_after returns only rows changed since; the watermark comes back for the next read. |

### Money records

| Tool | What it does | Permission |
|------|--------------|------------|
| `mark_invoice_draft` | Put a manual invoice back to draft, as the invoice's status menu does: undoes a void, or clears a payment recorded by mistake. Nothing is emailed | **Write jobs** |
| `duplicate_site_assessment` | Copy a site assessment with all its pins and photos onto the same job, to split a walkthrough into phases (the copy is saved completed) | **Write jobs** |

Change orders and invoices can't be deleted in TruTec, only voided (`void_change_order`, `void_invoice`), and payments are recorded per invoice (`mark_invoice_paid`), not in parts.
### Warranty, inbox & notifications

Warranty packets and work notices, cloning a job, the Emails inbox ("did the customer reply?"), your notifications and mentions, and the Daily Summary. See [Warranty](warranty.md), [Inbox & notifications](inbox-and-notifications.md) and [Clone a job](jobs.md#clone-a-job).

| Tool | What it does | Permission |
|------|--------------|------------|
| `get_job_warranty` | A job's warranty packets, work notices, live coverage, templates, and the packet TruTec suggests sending now | Read jobs |
| `preview_warranty_document` | A warranty packet or work notice as the customer will see it. Nothing is saved or sent | Read jobs |
| `send_warranty_packet` | Email the customer the job's warranty packet (their certificate link) and add its items to the tracker | **Write jobs** |
| `resend_warranty_packet` | Send an existing packet: a draft for the first time, or again with changes | **Write jobs** |
| `void_warranty_packet` | Void a packet: its link stops working and its items leave the tracker | **Write jobs** |
| `send_warranty_notice` | Email the customer that a repair is being done under warranty (or at no charge, when coverage has run out) | **Write jobs** |
| `void_warranty_notice` | Void a work notice | **Write jobs** |
| `get_warranty_packets` | Warranty packets, e.g. drafts waiting to be sent; or one by id | Read jobs |
| `get_warranty_notices` | Work notices sent to customers | Read jobs |
| `get_warranty_coverage` | The warranty tracker: every covered item, soonest to end first, active, expiring or expired | Read jobs |
| `get_warranty_templates` | Warranty templates with their standard items and term rules | Read jobs |
| `clone_job` | Start a new job from an existing one (re-bid or repeat work), copying its details, takeoffs and site assessments | **Write jobs** |
| `get_customer_emails` | Email to and from customers, newest first; `needs_reply` lists replies nobody has answered | Read jobs |
| `get_email_thread` | One email and the conversation around it, with each message's text | Read jobs |
| `get_full_email` | The whole text of one email, read from the connected mailbox | Read jobs |
| `mark_email_handled` | Tick a customer email handled (or put it back in Needs reply) | **Write jobs** |
| `mark_emails_handled` | Tick many customer emails handled at once | **Write jobs** |
| `get_notifications` | Your in-app notifications (the bell) | Read jobs |
| `mark_notification_read` | Mark one notification read | **Write jobs** |
| `mark_all_notifications_read` | Mark every notification read | **Write jobs** |
| `get_mentions` | Team notes where someone tagged you | Read jobs |
| `mark_mention_read` | Mark a mention read | **Write jobs** |
| `get_daily_summary` | What the team did today, yesterday, this week or this month: leads, stage changes, emails, calls, wins, visits, follow-ups | Read jobs |

`send_warranty_packet`, `resend_warranty_packet` and `send_warranty_notice` email your customer, so the assistant should confirm with you first.
### Sales & CRM extras

Re-bid actions, reminders, job details and tags, merges, search, share and review links, and sales automation. See [Re-bids, reminders & My day](rebids-and-reminders.md), [Merge & search](merge-and-search.md), [Job details, tags & share links](job-details-and-links.md) and [Sales automation](sales-automation.md).

| Tool | What it does | Permission |
|------|--------------|------------|
| `search_workspace` | Search jobs, customers, contacts, sites, leads, proposals, invoices, notes and more at once, like the search box in TruTec | Read jobs |
| `create_rebid_jobs` | Create the re-bid job for one or many re-bids | **Write jobs** |
| `snooze_rebid` | Hide a re-bid until a date | **Write jobs** |
| `mark_rebid_not_doing` | Take a re-bid off the list with a reason | **Write jobs** |
| `reset_rebid` | Undo a snooze or "not doing" | **Write jobs** |
| `create_rebid_follow_up` | Schedule a company follow-up about a re-bid | **Write jobs** |
| `add_past_work` | Record work done at a site before it was in TruTec, so its re-bid comes due on time | **Write jobs** |
| `remove_past_work` | Delete past work that was added by hand | **Write jobs** + **Delete records** |
| `get_reminders` | Contract-renewal, maintenance check-in and other reminders | Read jobs |
| `update_reminder` | Reschedule, snooze, reassign or change a reminder | **Write jobs** |
| `complete_reminder` | Mark a reminder done, with notes | **Write jobs** |
| `dismiss_reminder` | Stop a reminder | **Write jobs** |
| `update_follow_ups` | Mark done, snooze or reassign many follow-ups and reminders at once | **Write jobs** |
| `create_customer_reminder` | "Remind me" about a customer or person, optionally repeating and emailed to you | **Write jobs** |
| `set_birthday_reminder` | A yearly call on a person's birthday | **Write jobs** |
| `get_job_details` | Job number, accounting and work order numbers, bid requirements, tags, site, division | Read jobs |
| `update_job_details` | Change those details, including renumbering one job | **Write jobs** |
| `update_job_operations` | Set a job's division and operations assignee (crew scheduling) | **Write jobs** |
| `set_job_site` | Link a job to a site, make a site from its address, or unlink it | **Write jobs** |
| `get_job_tags` | The workspace's job tags | Read jobs |
| `create_job_tag` | Add a job tag | **Write jobs** |
| `add_job_tags` | Tag a job (new tag names are created) | **Write jobs** |
| `remove_job_tag` | Take a tag off a job | **Write jobs** |
| `get_portal_messages` | A job's conversation with the customer in their portal | Read jobs |
| `reply_in_customer_portal` | Reply in that conversation; the customer sees it. Confirm first | **Write jobs** |
| `get_merge_preview` | What merging one customer, contact or site into another would do | Read jobs |
| `merge_customers` | Merge a duplicate customer into another. Confirm first | **Write jobs** + **Delete records** |
| `merge_contacts` | Merge a duplicate person into another. Confirm first | **Write jobs** + **Delete records** |
| `merge_sites` | Merge a duplicate site into another. Confirm first | **Write jobs** + **Delete records** |
| `create_review_link` | A review link for a job's customer (nothing is sent) | **Write jobs** |
| `get_reviews` | Review links and the reviews customers wrote | Read jobs |
| `get_job_share_links` | A job's photo report link and chosen-photo links | Read jobs |
| `share_job_photos` | Make or return a job's photo report link (nothing is sent) | **Write jobs** |
| `update_photo_share_link` | Add or remove an access code, or change the expiry | **Write jobs** |
| `share_selected_photos` | A link to exactly the photos you pick | **Write jobs** |
| `revoke_photo_selection_link` | Turn a chosen-photos link off | **Write jobs** |
| `share_assessment_report` | A site assessment report link, optionally only some services for a sub | **Write jobs** |
| `share_capital_plan` | Turn on a site's capital plan link | **Write jobs** |
| `stop_sharing_capital_plan` | Turn a capital plan link off | **Write jobs** |
| `rotate_capital_plan_link` | Replace a capital plan link | **Write jobs** |
| `get_pipeline_automation` | The jobs pipeline's automation settings | Read jobs |
| `update_pipeline_automation` | Change them (sequences can email customers). Confirm first | **Write jobs**, owner key |
| `get_company_automations` | Company pipeline automation rules, and what a rule can use | Read jobs |
| `create_company_automation` | Add a rule (it can email customers). Confirm first | **Write jobs**, owner key |
| `update_company_automation` | Change a rule. Confirm first | **Write jobs**, owner key |
| `enable_company_automation` | Switch a rule on. Confirm first | **Write jobs**, owner key |
| `disable_company_automation` | Switch a rule off | **Write jobs**, owner key |
| `delete_company_automation` | Delete a rule | **Write jobs** + **Delete records**, owner key |
| `get_email_campaigns` | Email campaigns with results, or one with its emails | Read jobs |
| `launch_email_campaign` | Start a draft campaign: it emails its whole audience. Confirm first | **Write jobs** |
| `pause_email_campaign` | Pause a campaign | **Write jobs** |
| `resume_email_campaign` | Resume a paused campaign; it emails again. Confirm first | **Write jobs** |
| `cancel_email_campaign` | Stop a campaign for good | **Write jobs** |
### Quote requests, agreements, production & proposal workflow

Ask vendors and subcontractors for prices and lock the winner into the bid, send subcontract agreements for e-signature, turn won estimates into crew production sheets, and run the rest of the proposal workflow. Tools that email or text someone, call the e-signature provider or sign an agreement say so and ask you to confirm. See [Quote requests](rfqs-and-subcontractors.md), [Subcontract agreements](agreements.md), [Production work orders](production-work-orders.md) and [Proposal workflow](proposal-workflow.md).

| Tool | What it does | Permission |
|------|--------------|------------|
| `get_quote_requests` | Quote requests to vendors and subcontractors: items, vendors asked, due date, status (draft, sent, answered, closed) and quotes back. rfq_id returns one with every vendor's status and quote. | Read jobs |
| `get_vendor_quote_comparison` | One quote request's quotes side by side: each item with every vendor's newest price, the cheapest, outliers (20%+ over the cheapest), missing quotes and the accepted one. | Read jobs |
| `get_job_quote_options` | Before create_quote_request: the job's estimates, the items its estimate buys (materials, equipment, trucking, subcontractor work) with quantities and the vendor ids set up to quote each, a catalog search (type, q), the vendor book and the job's requests so far. | Read jobs |
| `create_quote_request` | Draft a request for prices from vendors or subcontractors on an estimate (estimate_id), or on a job (job_id: uses its newest estimate, or makes a blank one). items: what to price; vendor_ids: who to ask. Nothing is sent: send_rfq_to_vendors emails it. | **Write jobs** |
| `send_rfq_to_vendors` | EMAILS EACH VENDOR on the request their private link to enter prices (texts it to vendors with only a cell number), from the user's connected mailbox when they have one, and marks the request sent. Confirm first. | **Write jobs** |
| `send_combined_rfqs_to_vendors` | EMAILS EACH VENDOR once, covering every listed request they are on (a section and link per request), and marks the requests sent. Confirm first. | **Write jobs** |
| `text_rfq_link_to_vendor` | TEXTS ONE VENDOR their private link to enter prices on a quote request. phone saves a cell number on the vendor first. Confirm first. | **Write jobs** |
| `add_vendor_quote` | Record prices a vendor gave by phone or email. Multi-item requests take item_prices (type, catalog_item_id, price, unit); single-item requests take price and unit. | **Write jobs** |
| `accept_vendor_quote` | Accept a quote: it becomes the vendor's saved price and is locked onto the estimate's matching lines (the estimate re-prices; another vendor's accepted price on the same item is replaced). items picks lines; without it every line the vendor priced is accepted. Nothing is emailed. | **Write jobs** |
| `use_vendor_quote_in_estimate` | Put a quoted price on the estimate's matching lines without accepting the quote (no saved vendor price; the request stays open). items picks lines; without it every priced line is used. | **Write jobs** |
| `send_quote_accepted_email` | EMAILS THE VENDOR that their quote was accepted (after accept_vendor_quote). Multi-item quotes name the item (type, catalog_item_id). Optional subject and plain-text message. Confirm first. | **Write jobs** |
| `get_agreement_templates` | The workspace's subcontract agreement templates (MSA, subcontract, rate sheet...) with whether each is ready to send and whether it asks for an insurance certificate. | Read jobs |
| `get_agreements` | Agreements sent to subcontractors: who, when sent / viewed / signed, who is next to sign (recipient or company), insurance received, fully executed. agreement_id returns one, including the company fields to fill when countersigning. | Read jobs |
| `send_agreement_for_signature` | EMAILS A SUBCONTRACTOR a private link to review and e-sign an agreement template, from the user's mailbox; the user also gets a link to sign for the company. vendor_id, or vendor_name + vendor_email to add a new subcontractor. Confirm first. | **Write jobs** |
| `text_agreement_signing_link` | TEXTS THE SUBCONTRACTOR the signing link for an agreement. phone saves a cell number on the vendor first. Confirm first. | **Write jobs** |
| `countersign_agreement` | Signs the agreement for the company, as the API key's creator, with a typed signature (a legal signature in their name). field_values fills the company's text fields by index (see get_agreements). If the subcontractor already signed, the executed copy is EMAILED TO THEM. Confirm first. | **Write jobs** |
| `delete_agreement` | Delete an agreement nobody has signed (a test send, the wrong vendor); its signing link stops working. Signed agreements stay. Needs Delete records. Confirm first. | **Write jobs** + **Delete records** |
| `get_production_work_orders` | Production sheets made from won estimates: header, status (issued, done) and, with work_order_id, every scope row (trade, production quantities, crew plan, schedule visit ids) and totals (tons, gallons, crew days, square feet). | Read jobs |
| `create_production_work_order` | Generate a production sheet from a job's estimate (newest by default): every service, one service (line_item_id) or one crew's work (crew_id). Its rows go to the crew schedule's waiting queue and the PDF to the crew packet. Nobody is notified. | **Write jobs** |
| `update_production_work_order` | Change a production sheet's header: customer and site details, gate code, summary, contacts (up to 2), flags (certified_payroll, osha_certified, badging, drone, nto), permit, subcontractor and notes. | **Write jobs** |
| `refresh_production_work_order` | Re-derive the sheet's rows from the estimate. Quantities and crew plans edited by hand are kept; new rows go to the schedule queue. | **Write jobs** |
| `issue_production_work_order` | Mark the sheet issued (stamps the issue date the first time). | **Write jobs** |
| `mark_production_work_order_done` | Mark the sheet done. | **Write jobs** |
| `delete_production_work_order` | Delete the sheet. Visits already on the crew schedule stay. Needs Delete records. Confirm first. | **Write jobs** + **Delete records** |
| `attach_production_work_order_to_dispatch` | Render the sheet as a PDF and save it as the job's crew-packet document, replacing the earlier copy. Crews see it in the field app. | **Write jobs** |
| `add_production_work_order_line` | Add a scope row: label, trade, production quantities and crew plan. | **Write jobs** |
| `update_production_work_order_line` | Change a scope row. Quantities you set are kept through refreshes; reset_quantities hands named ones back to the estimate. | **Write jobs** |
| `remove_production_work_order_line` | Remove a scope row from the sheet. Confirm first. | **Write jobs** |
| `get_proposal_approvals` | Proposals whose send is held for an owner because the sender was over their approval limit: amount, limit, who asked and when. | Read jobs |
| `approve_proposal_send` | An owner approves a held send. The teammate who asked is emailed; the proposal is not sent until they (or send_proposal) send it. Needs an owner's key. | **Write jobs**, owner key |
| `reject_proposal_send` | An owner rejects a held send, with an optional note emailed to the teammate who asked. Needs an owner's key. | **Write jobs**, owner key |
| `void_proposal` | Withdraw a proposal: the customer's link shows it is no longer available and an open e-signature request is cancelled with the provider. Sending it again later revives the link. Confirm first. | **Write jobs** |
| `delete_proposal` | Delete a proposal from its job: it is voided, its customer link stops working for good and an open e-signature request is cancelled. Needs Delete records. Confirm first. | **Write jobs** + **Delete records** |
| `resend_proposal_signature_request` | EMAILS THE SIGNER the open e-signature request for a proposal again. Confirm first. | **Write jobs** |
| `void_proposal_signature_request` | Cancel a proposal's open e-signature request with the provider and reopen the proposal for changes (it stays void until sent again). Confirm first. | **Write jobs** |
| `make_proposal_customer_copies` | Make (or refresh) a copy of the proposal for every additional customer on the job, each addressed to that customer. Copies already sent stay as sent. Nothing is emailed. | **Write jobs** |
| `apply_terms_preset_to_proposal` | Switch the proposal to a saved Terms set (preset_id from get_terms_presets, or "company-default"). Its own terms edits are dropped. | **Write jobs** |
| `apply_payment_terms_to_proposal` | Apply saved payment terms (deposit, due days and their line in the terms) from get_terms_presets, or preset_id null to clear them. | **Write jobs** |
| `add_library_content_to_proposal` | Add saved content to a draft proposal: sections, clauses, company PDF pages, and at most one Terms set and one payment terms preset (these replace the current ones). items: kind + id from get_proposal_library. | **Write jobs** |
| `get_terms_presets` | The workspace's saved Terms sets (with their wording and clauses) and payment terms presets (due days, deposit rate, terms line), for apply_terms_preset_to_proposal and apply_payment_terms_to_proposal. | Read jobs |
| `get_proposal_library` | Everything add_library_content_to_proposal can add, each with its kind and id: saved sections, clauses, company PDF pages, Terms sets and payment terms. | Read jobs |
| `get_proposal_arrangement` | A proposal's items in customer order: phase, item number, base bid or optional add-on, "customer can remove", price group and named bid option, and whether it can still change. | Read jobs |
| `update_proposal_arrangement` | Change items on a draft: phase, item_number, optional (add-on), removable (customer can untick), group_heading, alternate_name (named bid option). Items left out keep their settings; list every item to reorder. Optional or group changes re-price the estimate. | **Write jobs** |
| `get_proposal_starter_templates` | The ready-made proposal designs (commercial lot, driveway, municipal bid, site assessment report, 5-year plan...) for apply_proposal_starter_template. | Read jobs |
| `apply_proposal_starter_template` | Put a starter design on a draft proposal in the workspace's brand (pages, order, look). Company PDF pages already on it stay. | **Write jobs** |
| `set_proposal_client_logo` | Put the customer's logo on the proposal cover from a public link to the picture; TruTec downloads it and keeps its own copy. | **Write jobs** |
| `remove_proposal_client_logo` | Take the customer's logo off the proposal cover. Confirm first. | **Write jobs** |
| `set_proposal_walkthrough_video` | Show a walkthrough video on the proposal from a link to it. | **Write jobs** |
| `remove_proposal_walkthrough_video` | Take the walkthrough video off the proposal. Confirm first. | **Write jobs** |
| `set_estimate_item_wording` | Set an estimate line's wording (HTML): what_to_expect prints under the line on the proposal, internal_scope rides quote requests to subcontractors, proposal_text is the line's description. An empty string shows nothing. | **Write jobs** |
| `create_proposal_template` | Save a proposal's layout (pages, order, display choices, reusable sections and wording, design) as a named template. Never copies the customer, prices or job notes. pdf_page_ids adds company PDF pages to it. | **Write jobs** |
| `update_proposal_template` | Rename or describe a proposal template, re-save its layout from a proposal (layout_from_proposal_id), or make it the one new proposals start from (is_default). | **Write jobs** |
| `delete_proposal_template` | Remove a saved proposal template. Proposals that used it keep their layout. Needs Delete records. Confirm first. | **Write jobs** + **Delete records** |
| `create_proposal_clause` | Add a reusable clause to the library. is_default clauses go on every new proposal. Needs an owner's key. | **Write jobs**, owner key |
| `update_proposal_clause` | Change a library clause's title, wording, bold or default. Proposals that already include it keep their copy. Needs an owner's key. | **Write jobs**, owner key |
| `delete_proposal_clause` | Remove a clause from the library. Proposals that include it keep their copy. Needs an owner's key with Delete records. Confirm first. | **Write jobs** + **Delete records**, owner key |
| `create_estimate_template` | Save an estimate's areas, services and cost lines (not the job or customer) as a template new estimates can start from. | **Write jobs** |
| `update_estimate_template` | Rename or describe an estimate template, or re-save it from an estimate (refresh_from_estimate_id). Estimates already made from it don't change. | **Write jobs** |
| `duplicate_estimate_template` | Copy an estimate template ("... (copy)" unless you give a name). | **Write jobs** |
| `archive_estimate_template` | Archive an estimate template so it is no longer offered. Estimates made from it keep everything. Needs Delete records. Confirm first. | **Write jobs** + **Delete records** |
### Takeoff editing

An assistant can change a saved aerial takeoff the way your team does in the takeoff editor: "add two arrows at the north entrance", "that white line is yellow", "remove the duplicate bollard". It lists the takeoff's shapes first, then saves its edits together; the takeoff's quantities follow and linked estimates re-pull them. Stalls, painted crosswalks and hatching, and items with a typed quantity stay in the editor. See [Editing takeoff shapes](takeoff-editing.md).

| Tool | What it does | Permission |
|------|--------------|------------|
| `get_takeoff_shapes` | The shapes on a saved aerial takeoff with their ids, category or custom item, phase, type, quantity and `[lat, lng]` coordinates, plus the takeoff's `rev` and the categories, types, custom items and phases an edit may use | Read measurements |
| `edit_takeoff_items` | Add areas, counts, lines and custom item shapes; remove shapes; reclassify a count, line or custom shape; set a type, phase or area surface. Refused with the current `rev` if anyone saved the takeoff since it was read | **Write measurements** (**Delete records** to remove shapes) |
### CRM records

Change the setup records behind the CRM: company follow-ups, custom fields and photo tags. Changing, moving, archiving or restoring a custom field needs a key created by a workspace owner, as in the app. See [Custom fields](custom-fields.md#field-setup), [Photo tags](photos-and-files.md#photo-tags) and [Notes & tasks](notes-and-tasks.md#delete-a-company-follow-up).

| Tool | What it does | Permission |
|------|--------------|------------|
| `delete_company_task` | Delete a company follow-up (one on a customer, not a job) for good. Workspaces that can't delete company follow-ups in TruTec refuse it | **Write jobs** + **Delete records** |
| `update_custom_field` | Rename a custom field, make it required or optional, pick the section of the record's screens it shows in, and for job fields show it on the job card or as a board filter | **Write jobs**, owner key |
| `move_custom_field` | Move a custom field one place up or down in the order record pages show them | **Write jobs**, owner key |
| `archive_custom_field` | Archive a custom field. Values already filled in stay and show read-only | **Write jobs** + **Delete records**, owner key |
| `restore_custom_field` | Bring an archived custom field back with its values | **Write jobs**, owner key |
| `create_photo_tag` | Add a photo tag with a color and what it means | **Write jobs** |
| `update_photo_tag` | Rename a photo tag, or change its color or meaning | **Write jobs** |
| `delete_photo_tag` | Delete a photo tag. It comes off every photo; the photos stay | **Write jobs** + **Delete records** |

### Anything else in the API

| Tool | What it does | Permission |
|------|--------------|------------|
| `list_api_endpoints` | Search every documented REST endpoint by keyword or area | None |
| `get_api_endpoint` | Parameters, body fields and responses for one endpoint | None |
| `call_api` | Run any REST request (read, create, update or delete) with this connection's key | Whatever that endpoint needs |

Every API action already has a dedicated tool, so these are rarely needed; they suit scripts that want a raw REST request. The key's permissions still apply, so a read-only key can't change anything through `call_api`. They're for API-key connections only: apps that sign in with TruTec (ChatGPT, Claude) get the dedicated tools, and a *read only* sign-in lists only the read tools.

### Measurement and condition

| Tool | What it does | Permission | Billed? |
|------|--------------|------------|---------|
| `find_parcels` | Property parcels near an `address` or `lat` + `lng`, nearest first: id, address, owner, APN, acres and distance. `include_outline` adds each boundary | Read measurements | No |
| `measure_site` | Measure a site from an `address`, a `polygon`, `bounds`, named `areas`, or `parcel_ids` from `find_parcels`. Returns pavement area, stall counts, striping linear feet, crosswalks, crosshatching, stop bars, arrows and symbols (plus carports, concrete, sidewalks, curbs, wheel stops, speed bumps, bollards, parallel stalls and landscaping on request, in the same detection) | Read measurements | **Yes**, one detection per call |
| `get_measurement_result` | The summary for a `measure_site` `job_id`; waits if it is still running | Read measurements | No |
| `get_measurement_items` | Item detail for one category of a finished measurement: each stall's location, each stripe's centerline and length, each crosswalk's outline and area, and with add-ons each curb line (`curbs`), wheel stop (`wheel_stops`), speed bump, bollard or parallel stall (`site_objects`), landscape area (`landscape_areas`) or tree and landscape object (`landscape_points`). Paged | Read measurements | No |
| `list_saved_measurements` | Takeoffs saved in your workspace, optionally for one job | Read measurements | No |
| `get_saved_measurement` | Quantities of one saved takeoff, as the app shows them after edits | Read measurements | No |
| `create_takeoff` | Measure a site and save it on a job in one step: `job_id` plus the same site inputs as `measure_site`. Returns the takeoff id, its quantities, `editor_url` (opens it in TruTec) and `view_url` (a link to its map) | Read measurements and **Write measurements** | **Yes**, one detection per call |
| `get_map_image` | A saved takeoff's map as a picture, by `measurement_id` or `job_id`. `style: "annotated"` (default) draws the takeoff on the aerial with its legend; `"aerial"` is the clean imagery. Also returns `view_url` | Read measurements (and Read jobs with `job_id`) | No |
| `rate_pavement_condition` | PASER 1-10 (`scale: "paser"`) or PCI 0-100 (`scale: "pci"`) for the pavement at a `polygon`, `lat` + `lng`, `address`, or in your own photo (`image_url`). Returns the rating, label, recommended treatment and the distresses seen. Follows PASER and ASTM D6433 | Read measurements | **Yes**, one API call per successful rating |

`measure_site` waits up to about 45 seconds. A big lot can take longer: then it returns a `job_id`, and the assistant calls `get_measurement_result` to keep waiting. It never needs to measure the same site twice.

To measure by parcel, the assistant calls `find_parcels`, picks the parcels (and asks you, showing owner, address and acres, when it isn't clear which), then calls `measure_site` with their ids in `parcel_ids` and the same address. Parcels that touch are measured as one outline; parcels apart come back as one named area each. See [Measure by parcel](measure-a-site.md#measure-by-parcel).

`create_takeoff` takes every `measure_site` input, `parcel_ids` included, and saves the result on the job you name. It checks that the key can save to that job before it measures, so a refused save never uses a detection. If the site is still measuring after about 45 seconds, it returns `detection_job_id`; the assistant calls `create_takeoff` again with `job_id` and that `detection_job_id`, which waits and saves without measuring again.

### Blueprint takeoffs

Measure a paving job from its PDF plans, the way the AI plan editor does. Positions are sheet pixels (144 dpi, `[x, y]` from the top-left), or fractions of the sheet with `coordinates: "fraction"`. See [Blueprint takeoffs](blueprints.md).

| Tool | What it does | Permission | Billed? |
|------|--------------|------------|---------|
| `create_blueprint_takeoff` | Start a blueprint takeoff on a job from a PDF (`file_url` or a job `file_id`; `pages` picks the sheets). Stores each page as a sheet and reads its scale and title block | **Write measurements** | **Yes**, one takeoff credit, as in the app |
| `add_blueprint_sheets` | Add pages from another PDF to a takeoff, or retry a failed import | **Write measurements** | No |
| `get_blueprint_takeoffs` | Blueprint takeoffs, newest first, optionally for one job | Read measurements | No |
| `get_blueprint_takeoff` | One takeoff: each sheet's number, title and scale, every item with its quantity, recent runs | Read measurements | No |
| `get_blueprint_sheet` | A sheet as a picture (`annotated` draws the takeoff on it; `crop` zooms in) with its legend, scale, size and totals, plus a 24-hour `view_url` | Read measurements | No |
| `set_blueprint_scale` | Set a sheet's scale (`feet_per_inch`, `paper_inches` + `real_feet`, or two `points` + `distance_ft`) or `verify` the one read off the sheet | **Write measurements** | No |
| `detect_blueprint_scale` | Read the printed scale and scale bar off a sheet | **Write measurements** | No |
| `detect_blueprint_sheet_labels` | Read sheet numbers, titles, disciplines and legends | **Write measurements** | No |
| `run_blueprint_ai_takeoff` | Run the takeoff models on a sheet inside a work area (`region_box`): paved area (or each surface type), curb and striping lines, stalls and other counted items, crosswalks, hatching and stop bars | **Write measurements** | No |
| `detect_blueprint_curbs` | Read curb and gutter by type from a CAD sheet's curb layers and add each type as a line item | **Write measurements** | No |
| `fill_blueprint_area` | Fill the outlined area around a point onto a surface | **Write measurements** | No |
| `match_blueprint_pattern` | Outline one patch of a hatch; every region hatched the same way is added to a surface | **Write measurements** | No |
| `count_blueprint_symbols` | Box one symbol; every match is counted on a count item | **Write measurements** | No |
| `find_blueprint_text` | Find words on a sheet, each with its position | Read measurements | No |
| `get_blueprint_run` | Status and result of a sheet import, scale or title read, model run, fill, match, count or text search; waits while it runs | Read measurements | No |
| `get_blueprint_shapes` | The drawn shapes with ids, item, sheet, phase, source and geometry. Paged | Read measurements | No |
| `add_blueprint_shapes` | Draw areas, lines and counts on items, up to 500 at a time | **Write measurements** | No |
| `update_blueprint_shape` | Move a shape to another item of the same kind, reshape it or change its phase | **Write measurements** | No |
| `remove_blueprint_shapes` | Erase shapes by id, or by sheet, item and source | **Write measurements** | No |
| `create_blueprint_item` | Add a count, area, line or surface item to a takeoff | **Write measurements** | No |
| `update_blueprint_item` | Rename or recolor an item, or set a line item's width | **Write measurements** | No |

Model work runs in the background: these tools wait up to about 40 seconds and then return a `run_id` for `get_blueprint_run`. They never need to be started twice. `create_blueprint_takeoff` uses a credit every time it is called, so the assistant asks before calling it and never calls it twice for the same PDF.

### Landscaping

Landscape takeoffs end to end, aerial and plan. See [Landscaping](landscaping.md).

| Tool | What it does | Permission | Billed? |
|------|--------------|------------|---------|
| `get_landscape_classes` | The area classes (lawn, mulch beds, hedges, gravel, ponds...) and tree and object classes landscape measuring uses | Read measurements | No |
| `get_landscape_takeoffs` | Landscape takeoffs in the workspace, aerial and plan, optionally for one job | Read measurements | No |
| `get_landscape_quantities` | One takeoff's quantities. Aerial: square feet by class and tree and object counts. Plan: each plant with its quantity (from callouts, dots or a typed total), plant areas, irrigation heads, valves, pipe feet and zones, and the plant schedule | Read measurements | No |
| `get_landscape_items` | Each area, tree or plant dot, paged, with the ids the change tools take | Read measurements | No |
| `create_landscape_aerial_takeoff` | Start an aerial landscape takeoff on a job (`manual: true` for Manual Aerial) | **Write measurements** | No |
| `create_landscape_plan_takeoff` | Start a plan takeoff on a job from a PDF: `file_url` (a public link) or `file_id` (a PDF on a job). Storing a big plan set can take a few minutes: it waits up to about 45 seconds | **Write measurements** | **Yes**, one takeoff credit once the PDF is stored (free on landscape.trutec.ai) |
| `run_landscape_detection` | Measure lawns, beds, hedges, trees and more on an aerial takeoff inside an outline, and save the result on it like Run in the editor (`replace_existing` to replace instead of add). Waits up to about 45 seconds | **Write measurements** | **Yes**, a takeoff's first run uses one credit; re-runs on it are free |
| `get_landscape_run` | The result of a detection, irrigation or plan upload run by `run_id`; waits if it is still running | Read measurements | No |
| `add_landscape_items` | Add areas (`class` + `polygon`) and trees or objects (`class` + `point`) to an aerial takeoff | **Write measurements** | No |
| `update_landscape_item` | Reclassify an area or tree, or set its phase; on a plan, move a dot to another plant or set its quantity | **Write measurements** | No |
| `delete_landscape_item` | Remove an area, tree or plant dot | **Write measurements** + **Delete records** | No |
| `update_plant` | Rename a plant on a plan takeoff and/or type its total (null goes back to the counted total) | **Write measurements** | No |
| `merge_plants` | Fold one plant's dots into another plant and remove the first | **Write measurements** | No |
| `delete_plant` | Remove a plant and its dots | **Write measurements** + **Delete records** | No |
| `get_plant_schedule` | Where the plan's plant schedule is (or that it has none) and its rows | Read measurements | No |
| `set_plant_schedule` | Say where the schedule is (`page`, optional `box`), that the plan has none, or fix its rows | **Write measurements** | No |
| `run_irrigation_reader` | Read one irrigation sheet: heads, valves and equipment by type, pipe feet by class and size, valve zones, checked against the sheet's legend. Changes nothing on the takeoff | Read measurements | No |
| `find_plan_text` | Find text on a plan's sheets (a plant code, "PLANT SCHEDULE", a note) with each match's page and position | Read measurements | No |
| `run_plant_finder` | Dot every plant symbol and callout tag on a plan sheet (Find plants). Only adds | **Write measurements** | No |
| `run_plant_grouping` | Sort a sheet's unsorted plant dots into species and name them from the schedule (`replace_existing` re-sorts all) | **Write measurements** | No |
| `run_plant_naming` | Name plant groups still called "Type N" from the schedule (`replace_existing` renames all) | **Write measurements** | No |
| `run_callout_reader` | Read unread callout tags into items with the amounts they state; false tags come back flagged | **Write measurements** | No |
| `run_advanced_plant_reader` | A close read of a sheet for plant symbols or text labels, adding the clear proposals (`include_flagged` adds the rest, marked for review) | **Write measurements** | No |
| `run_schedule_reader` | The plant schedule's codes, names and stated quantities. Changes nothing | Read measurements | No |
| `run_irrigation_takeoff` | Read an irrigation sheet and put the heads, valves, zones and pipe runs on the takeoff (`replace_existing` to replace earlier results) | **Write measurements** | No |
| `apply_landscape_run` | Put a run held for review (`apply: false`) on the takeoff; for an Advanced run, `proposal_ids` picks proposals | **Write measurements** | No |
| `undo_landscape_run` | Take an applied run back off the takeoff | **Write measurements** | No |
| `get_landscape_sheet_image` | A plan sheet as a picture with every dot drawn in its item's color, plus a 24-hour `view_url` | Read measurements | No |

The run tools wait up to about 45 seconds. A big site or a busy reader can take longer: then they return a `run_id`, and the assistant calls `get_landscape_run` to keep waiting instead of starting the run again. Plant runs only add unless `replace_existing` is sent, can be held for review with `apply: false`, and can be undone; `get_landscape_sheet_image` shows the result on the sheet. Plan sheets are rendered on TruTec's servers for these runs, which can differ slightly from the editor's browser render.

### Images

`get_map_image` returns the map as an MCP image (a JPEG at most 1,568 pixels on its long side), so Claude shows it in the chat. Some chat apps, ChatGPT among them, may not show tool images to you, so the tool also returns `view_url`: a link that opens the same map in a browser for 24 hours, with no sign-in. Anyone with the link can see the map until it expires, so share it only with people who may see the site. The link leaves out internal assessment pins and takeoff notes; the picture in the chat keeps them. The link stops working early when the key is revoked. See [Get a link to the map](measurements.md#get-a-link-to-the-map).

A takeoff has a map once it has areas drawn on the aerial. Takeoffs from `create_takeoff` and `save_measurement_to_job` have one right away; plan-sheet takeoffs never do. For those, `get_blueprint_sheet` shows a sheet the same way (a picture in the chat plus a 24-hour `view_url`).

### Tool hints

Every tool declares MCP annotations so clients can decide what to confirm with you:

- Read tools are marked `readOnlyHint: true`.
- Tools that create or change records are marked `readOnlyHint: false`. Most clients ask before running them.
- `measure_site` and `rate_pavement_condition` change no records but use your plan's allowance, so they are marked `readOnlyHint: false`. `create_takeoff` saves a takeoff and uses a detection, so it is marked `readOnlyHint: false` and `openWorldHint: true`. `get_map_image` is read-only. `export_file`, `export_kinds` and `export_job_documents` only read: making a link changes nothing.
- `upload_job_photo` fetches a link on the internet and `send_proposal` emails your customer, so both are marked `openWorldHint: true`. `find_parcels` reads public parcel records, so it is marked `openWorldHint: true` too.
- Tools that overwrite, delete or send are marked `destructiveHint: true`: updates (`update_job`, `update_estimate_item`, `reschedule_visit` and the like), status changes (`set_estimate_outcome`, `complete_task`, `issue_estimate`), `remove_estimate_item`, `send_proposal`, `publish_schedule` and `call_api`. Only `remove_estimate_item` and `call_api` (with a key that has Delete records) can delete anything.
- The tools made from the API reference follow the same rules: `get_*` tools are read-only; `create_*`, `add_*`, `upload_*` and `start_*` only add, so they are not destructive; every other change (`update_*`, `set_*`, status changes such as `void_invoice` or `approve_time_entry`, and every `delete_*`, `remove_*` or `archive_*`) is marked `destructiveHint: true`. `send_invoice`, `send_change_order`, `cancel_work_order` and `invite_user` reach people outside your workspace, `add_assessment_pin_photo` fetches a link on the internet, and the webhook tools send your data to the address you give, so those are marked `openWorldHint: true`.
- Blueprint tools: `create_blueprint_takeoff` and `add_blueprint_sheets` fetch a link you give them, so they are marked `openWorldHint: true`; they, the fill, match and count tools, `add_blueprint_shapes` and `create_blueprint_item` only add. `run_blueprint_ai_takeoff`, `detect_blueprint_curbs`, `detect_blueprint_scale` and `detect_blueprint_sheet_labels` replace earlier results, and `set_blueprint_scale`, `update_blueprint_shape`, `update_blueprint_item` and `remove_blueprint_shapes` overwrite or erase, so those are marked `destructiveHint: true`. `get_blueprint_*` and `find_blueprint_text` are read-only.
- Landscaping: `run_landscape_detection` saves results on the takeoff and can replace items (`replace_existing`), so it is marked `destructiveHint: true` and `openWorldHint: true` (it uses aerial imagery and a credit). `update_landscape_item`, `delete_landscape_item`, `update_plant`, `merge_plants`, `delete_plant` and `set_plant_schedule` change or remove what is there, so they are destructive too; `create_landscape_aerial_takeoff`, `create_landscape_plan_takeoff` and `add_landscape_items` only add (`create_landscape_plan_takeoff` fetches a link, so `openWorldHint: true`). `run_irrigation_reader` and `find_plan_text` change nothing, so they are read-only. Of the plant tools, `run_plant_finder` and `run_advanced_plant_reader` only add, so they are not destructive; `run_plant_grouping`, `run_plant_naming`, `run_callout_reader` and `run_irrigation_takeoff` move, rename or replace what is there, and `apply_landscape_run` / `undo_landscape_run` change the takeoff, so they are `destructiveHint: true`. `run_schedule_reader` and `get_landscape_sheet_image` are read-only.

- `send_customer_email`, `send_customer_text` and `request_customer_payment` reach your customer, and `start_follow_up_sequence` sets up emails to them, so all four are marked `destructiveHint: true` and `openWorldHint: true` and the assistant confirms with you first. `delete_job` and `merge_jobs` delete a job; `update_proposal`, `create_proposal_link` (it can replace the link), `set_proposal_link_access`, `turn_off_customer_portal`, `reset_customer_portal_link` and `stop_follow_up_sequence` overwrite or switch something off: all are `destructiveHint: true`. `turn_on_customer_portal` only turns the portal on, so it is not. Reports are read-only.
- `mark_invoice_draft` changes an invoice's status, so it is marked `destructiveHint: true`; `duplicate_site_assessment` only adds a copy, so it is not.
- Crew operations tools that email or text people outside the app (`send_schedule_notice_to_customer`, `retry_customer_schedule_text`, `send_dispatch_to_crew`, `send_day_dispatch_to_crews`, `send_daily_report`, `send_crew_message`, `send_crew_broadcast`, `send_job_warranty_to_customer`) are marked `openWorldHint: true` and `destructiveHint: true`, and say "Confirm with the user first." Their status changes and overwrites (stages, holds, requests, material records, voids) and the two that remove something (`revoke_dispatch_packet_link`, `release_schedule_hold`) are destructive too; `create_schedule_hold`, `create_schedule_request`, `create_job_warranty`, `add_material_actual_cost` and `create_dispatch_packet_link` only add.
- `send_warranty_packet`, `resend_warranty_packet` and `send_warranty_notice` email your customer, so they are marked `openWorldHint: true` and `destructiveHint: true`; `get_full_email` reads your connected mailbox, so it is read-only and `openWorldHint: true`. Voids (`void_warranty_packet`, `void_warranty_notice`) and ticks (`mark_email_handled`, `mark_emails_handled`, `mark_notification_read`, `mark_all_notifications_read`, `mark_mention_read`) change a status, so they are marked `destructiveHint: true`. `clone_job` only adds a job, so it is not.
- Sales & CRM extras: reads (`get_*`, `search_workspace`, `get_merge_preview`) are read-only. Changes, status changes and switches (`update_*`, `snooze_rebid`, `mark_rebid_not_doing`, `reset_rebid`, `complete_reminder`, `dismiss_reminder`, `set_job_site`, `revoke_photo_selection_link`, `stop_sharing_capital_plan`, `rotate_capital_plan_link`, the automation and campaign switches), merges and deletes are marked `destructiveHint: true`. Tools that can reach a customer outside the app are also marked `openWorldHint: true` and ask the assistant to confirm with you first: `reply_in_customer_portal` (the customer sees it), `update_pipeline_automation`, `create_company_automation`, `update_company_automation` and `enable_company_automation` (sequences and rules can email customers), `launch_email_campaign` and `resume_email_campaign`. Share and review link tools only return a link; nothing is sent.
- `edit_takeoff_items` can remove and reclassify a takeoff's shapes, so it is marked `destructiveHint: true`; `get_takeoff_shapes` is read-only.
- Catalog and settings tools that overwrite what is there (`update_catalog_item`, `set_service_recipe`, `set_service_qualifiers`, `set_vendor_price`, `approve_catalog_items`, `edit_catalog_group`, `import_catalog_rows`, `update_settings`, `edit_workspace_list`) or archive (`archive_catalog_item`) are marked `destructiveHint: true`; `edit_catalog_group` and `edit_workspace_list` can remove a group, stage or list entry. `create_catalog_item` and `create_catalog_group` only add, so they are not. `update_settings` can copy a logo from a link you give it, so it is marked `openWorldHint: true`.
- Quote, agreement and proposal tools that reach people are marked `openWorldHint: true` and `destructiveHint: true`: `send_rfq_to_vendors`, `send_combined_rfqs_to_vendors`, `text_rfq_link_to_vendor`, `send_quote_accepted_email`, `send_agreement_for_signature`, `text_agreement_signing_link`, `countersign_agreement` (a legal signature in the key creator's name, and it emails the executed copy) and `resend_proposal_signature_request`. `void_proposal`, `delete_proposal` and `void_proposal_signature_request` cancel an open request with the e-signature provider and `set_proposal_client_logo` downloads the link you give it, so they are open-world too. `accept_vendor_quote` and `use_vendor_quote_in_estimate` re-price the estimate, so they are destructive; `create_quote_request`, `add_vendor_quote` and the other `create_*` / `add_*` tools only add.
- `update_custom_field`, `move_custom_field`, `restore_custom_field` and `update_photo_tag` change what is there, and `delete_company_task`, `archive_custom_field` and `delete_photo_tag` remove or archive, so all of them are marked `destructiveHint: true` and the removing ones ask the assistant to confirm with you first. `create_photo_tag` only adds, so it is not.

## Try it

Business questions:

- "Which estimates went out last week and haven't been signed?"
- "Total costs vs invoiced on job 4812."
- "What changed in TruTec since Monday?"
- "List every job for Northgate Properties and the value of each."
- "Show me the before and after photos on the Northgate Plaza job."
- "Which sites on Commerce Dr do we have, and who owns them?"
- "Create a lead for a sealcoat and restripe at 1200 Commerce Dr, Austin, TX for Northgate Properties."
- "Give me the proposal PDF for the Northgate Plaza job."
- "Export last week's payroll as a spreadsheet."
- "Download the takeoff on job 4812 as a KMZ for Google Earth."

Measurement and condition:

- "Measure the parking lot at 1200 Commerce Dr, Austin, TX. How many stalls, and how much striping by color?"
- "Which parcels make up the shopping center at 1200 Commerce Dr? Measure the two owned by Northgate Properties together."
- "Here's the property outline: (30.2672, -97.7431), (30.2672, -97.7405), (30.2650, -97.7405), (30.2650, -97.7431). Pavement area in square yards?"
- "Rate the pavement condition at that outline on the PCI scale and tell me the recommended treatment."
- "Find our Northgate Plaza job and summarize its saved takeoff."
- "Measure 1200 Commerce Dr, put it on job 4812 and show me the map."
- "Show me the takeoff map for the Northgate Plaza job, and the clean aerial next to it."

## Good to know

- **Results are compact.** Tools return trimmed JSON (empty fields dropped, ids kept) so the assistant spends fewer tokens. For full records, call the REST endpoint the tool wraps.
- **Lists are paged.** Tools that list take `limit` and `offset` (events page with `after_id`, histories with `before`). A result says `total` and, when there is more, `next_offset`. The assistant pages on its own when it needs more.
- **Files.** `upload_job_file` and `upload_customer_file` take the file's contents as text or base64. `download_file` returns a short-lived link. Pictures (`get_imagery_tile`) come back as images.
- **Coordinates are `[latitude, longitude]`**, the same as the REST API. GeoJSON lists `[longitude, latitude]`, so swap the order when you paste GeoJSON.
- **Quantities and ratings are AI estimates from imagery.** Check them before you commit to a price or a treatment.
- **Errors come back as tool errors** with the REST API's message (for example `API key lacks job write permission`), so the assistant can tell you what to fix.
- **Use a workspace key.** Partner management keys are not supported over MCP.
- **Turning it off.** Revoke or deactivate the key under **Settings › API keys**. The assistant loses access on its next call.
