Jobs, estimates, invoices and customers in Housecall Pro.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Add another option (a separate price) to an estimate, with its own lines. The customer picks one option when approving. Returns the estimate with all its options.
Lines of the new option, in order.
estimate_id from list_estimates.
Option name. Default: Option #N.
The server marks this tool as destructive. Running it may delete or overwrite data on the server or in any account you connected with your headers.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http housecall-pro https://cotlet.dev/mcp/v1/housecall-pro/connect
Add another option (a separate price) to an estimate, with its own lines. The customer picks one option when approving. Returns the estimate with all its options.
| Argument | Type | Description |
|---|---|---|
| name | string | Option name. Default: Option #N. |
| line_items* | array | Lines of the new option, in order. |
| estimate_id* | string | estimate_id from list_estimates. |
{
"type": "object",
"required": [
"estimate_id",
"line_items"
],
"properties": {
"name": {
"type": "string",
"maxLength": 100,
"description": "Option name. Default: Option #N."
},
"line_items": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"unit_price"
],
"properties": {
"kind": {
"enum": [
"service",
"material"
],
"type": "string",
"description": "service (labor) or material. Default: service."
},
"name": {
"type": "string",
"minLength": 1,
"description": "Service or material name."
},
"taxable": {
"type": "boolean",
"description": "Taxable. Default: false."
},
"quantity": {
"type": "number",
"description": "Quantity. Default: 1.",
"exclusiveMinimum": 0
},
"unit_cost": {
"type": "number",
"minimum": 0,
"description": "Your cost per unit in dollars. Default: 0."
},
"unit_price": {
"type": "number",
"minimum": 0,
"description": "Price per unit in dollars."
},
"description": {
"type": "string",
"description": "Line description."
}
},
"additionalProperties": false
},
"maxItems": 50,
"minItems": 1,
"description": "Lines of the new option, in order."
},
"estimate_id": {
"type": "string",
"minLength": 1,
"description": "estimate_id from list_estimates."
}
},
"additionalProperties": false
}How to work in an app by clicking in the user's Cloud Browser with Cotlet's browser tools: which browser to use for this workspace and the sign-in link, the method step by step, the app's screens, menus and actions, notes, and the app's dedicated tools when Cotlet has them. Call it before the first browser step in an app. Works for any web app; one outside Cotlet's catalog gets the general method. Free, no browser session.
| Argument | Type | Description |
|---|---|---|
| app | string | The app's name or Cotlet slug. Default: Housecall Pro. |
| task | string | What the user wants done in the app, in their words. |
{
"type": "object",
"properties": {
"app": {
"type": "string",
"maxLength": 120,
"description": "The app's name or Cotlet slug. Default: Housecall Pro."
},
"task": {
"type": "string",
"maxLength": 500,
"description": "What the user wants done in the app, in their words."
}
},
"additionalProperties": false
}The apps of the user's workspace and whether each works now: the app account in the user's Cloud Browser (connected, signed out, signed in but not connected), and the one step the user has to take, with the console link. Free. Needs the user's Cotlet API key or sign-in.
| Argument | Type | Description |
|---|---|---|
| app | string | Only this app, e.g. skool. |
{
"type": "object",
"properties": {
"app": {
"type": "string",
"description": "Only this app, e.g. skool."
}
},
"additionalProperties": false
}Clicks element `element`, or point `x`, `y` of the screenshot; `double` double-clicks, `button: right` right-clicks. Answers the page after it: the elements and text in view, with the same numbers. Says when nothing changed. A link that would open a new tab opens in this one. Buttons that pay or buy, and sign-in and account pages, are refused.
| Argument | Type | Description |
|---|---|---|
| x | number | Instead of `element`: pixels from the left of the screenshot. |
| y | number | With `x`: pixels from the top of the screenshot. |
| button | string | right opens the page's own context menu. Default: left. |
| dialog | string | How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog. |
| double | boolean | Double-click. |
| element | integer | The element's number from the last read or step answer. |
| dialog_text | string | Text for a prompt dialog this step opens; implies accept. |
{
"type": "object",
"properties": {
"x": {
"type": "number",
"minimum": 0,
"description": "Instead of `element`: pixels from the left of the screenshot."
},
"y": {
"type": "number",
"minimum": 0,
"description": "With `x`: pixels from the top of the screenshot."
},
"button": {
"enum": [
"left",
"right"
],
"type": "string",
"description": "right opens the page's own context menu. Default: left."
},
"dialog": {
"enum": [
"accept",
"dismiss"
],
"type": "string",
"description": "How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog."
},
"double": {
"type": "boolean",
"description": "Double-click."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "The element's number from the last read or step answer."
},
"dialog_text": {
"type": "string",
"maxLength": 1000,
"description": "Text for a prompt dialog this step opens; implies accept."
}
},
"additionalProperties": false
}Ends the browser session when the task is done: the browser keeps its cookies and sleeps, and the per-minute charge stops. Answers the session's minutes and credits.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}Gets the file that clicking `element` (a download link or button) or opening `url` starts: answers its name, type, size and a link valid 15 minutes; CSV, JSON and other text files also as text.
| Argument | Type | Description |
|---|---|---|
| url | string | A public http or https address, e.g. https://example.com. |
| element | integer | The element's number from the last read or step answer. |
{
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "A public http or https address, e.g. https://example.com."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "The element's number from the last read or step answer."
}
},
"additionalProperties": false
}Drags from one element or point to another: a card between columns, a piece on a board, a slider's handle. Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| to* | object | An element number, or x and y of the screenshot. |
| from* | object | An element number, or x and y of the screenshot. |
| dialog | string | How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog. |
| dialog_text | string | Text for a prompt dialog this step opens; implies accept. |
{
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "object",
"properties": {
"x": {
"type": "number",
"minimum": 0,
"description": "Instead of `element`: pixels from the left of the screenshot."
},
"y": {
"type": "number",
"minimum": 0,
"description": "With `x`: pixels from the top of the screenshot."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "The element's number from the last read or step answer."
}
},
"description": "An element number, or x and y of the screenshot.",
"additionalProperties": false
},
"from": {
"type": "object",
"properties": {
"x": {
"type": "number",
"minimum": 0,
"description": "Instead of `element`: pixels from the left of the screenshot."
},
"y": {
"type": "number",
"minimum": 0,
"description": "With `x`: pixels from the top of the screenshot."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "The element's number from the last read or step answer."
}
},
"description": "An element number, or x and y of the screenshot.",
"additionalProperties": false
},
"dialog": {
"enum": [
"accept",
"dismiss"
],
"type": "string",
"description": "How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog."
},
"dialog_text": {
"type": "string",
"maxLength": 1000,
"description": "Text for a prompt dialog this step opens; implies accept."
}
},
"additionalProperties": false
}Runs a JavaScript function body on the page and answers what it returns, as JSON. Use it to extract data the page holds (a table, a list, attributes); act with the other tools. It cannot send requests, click, submit, or read cookies or storage; a filled field's value reads [hidden by Cotlet]. Example: return [...document.querySelectorAll('h2')].map(h => h.innerText)
| Argument | Type | Description |
|---|---|---|
| script* | string | A function body that returns. |
{
"type": "object",
"required": [
"script"
],
"properties": {
"script": {
"type": "string",
"maxLength": 20000,
"description": "A function body that returns."
}
},
"additionalProperties": false
}Fills several fields in one step: text fields take text, drop-downs an option's label, checkboxes, radios and switches true or false. Stops at the first field that fails and names those filled. Does not send the form: click its button after. Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| dialog | string | How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog. |
| fields* | array | The fields in the order a person fills them. |
| dialog_text | string | Text for a prompt dialog this step opens; implies accept. |
{
"type": "object",
"required": [
"fields"
],
"properties": {
"dialog": {
"enum": [
"accept",
"dismiss"
],
"type": "string",
"description": "How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog."
},
"fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"element",
"value"
],
"properties": {
"value": {
"type": [
"string",
"boolean"
],
"description": "Text, option, or true/false."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "The element's number from the last read or step answer."
}
},
"additionalProperties": false
},
"maxItems": 30,
"minItems": 1,
"description": "The fields in the order a person fills them."
},
"dialog_text": {
"type": "string",
"maxLength": 1000,
"description": "Text for a prompt dialog this step opens; implies accept."
}
},
"additionalProperties": false
}Finds `text` on the whole current page, frames included: the elements whose label holds it, with their numbers, and each line of text that holds it. Use it instead of reading or scrolling a long page for one thing.
| Argument | Type | Description |
|---|---|---|
| kind | string | Only elements of this kind. Default: any. |
| text* | string | Words to look for, any case. |
{
"type": "object",
"required": [
"text"
],
"properties": {
"kind": {
"enum": [
"link",
"button",
"field",
"any"
],
"type": "string",
"description": "Only elements of this kind. Default: any."
},
"text": {
"type": "string",
"maxLength": 200,
"description": "Words to look for, any case."
}
},
"additionalProperties": false
}Rests the pointer on an element or a point, to open a hover menu, a tooltip or a preview. Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| x | number | Instead of `element`: pixels from the left of the screenshot. |
| y | number | With `x`: pixels from the top of the screenshot. |
| element | integer | The element's number from the last read or step answer. |
{
"type": "object",
"properties": {
"x": {
"type": "number",
"minimum": 0,
"description": "Instead of `element`: pixels from the left of the screenshot."
},
"y": {
"type": "number",
"minimum": 0,
"description": "With `x`: pixels from the top of the screenshot."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "The element's number from the last read or step answer."
}
},
"additionalProperties": false
}Opens `url` in the current tab, or goes `history` back or forward. Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| url | string | A public http or https address, e.g. https://example.com. |
| dialog | string | How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog. |
| history | string | Instead of `url`. |
| dialog_text | string | Text for a prompt dialog this step opens; implies accept. |
{
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "A public http or https address, e.g. https://example.com."
},
"dialog": {
"enum": [
"accept",
"dismiss"
],
"type": "string",
"description": "How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog."
},
"history": {
"enum": [
"back",
"forward"
],
"type": "string",
"description": "Instead of `url`."
},
"dialog_text": {
"type": "string",
"maxLength": 1000,
"description": "Text for a prompt dialog this step opens; implies accept."
}
},
"additionalProperties": false
}Starts the user's Housecall Pro Cloud Browser (a real Chrome on Cotlet, signed in to Housecall Pro as the user; use it for what the Housecall Pro tools do not cover) and opens `url`. Call it first. Only Housecall Pro pages open. Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| url | string | A public http or https address, e.g. https://example.com. |
{
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "A public http or https address, e.g. https://example.com."
}
},
"additionalProperties": false
}Presses a key on the focused element: Enter to send, Tab to the next field, Escape to close a pop-up, arrows in a list or slider, or one character (a site's shortcut). Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| key* | string | One of Enter, Tab, Shift+Tab, Escape, Backspace, Delete, Space, ArrowDown, ArrowUp, ArrowLeft, ArrowRight, PageDown, PageUp, Home, End, or one character such as a or 5. |
| dialog | string | How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog. |
| repeat | integer | Times to press it. Default 1. |
| dialog_text | string | Text for a prompt dialog this step opens; implies accept. |
{
"type": "object",
"required": [
"key"
],
"properties": {
"key": {
"type": "string",
"anyOf": [
{
"enum": [
"Enter",
"Tab",
"Shift+Tab",
"Escape",
"Backspace",
"Delete",
"Space",
"ArrowDown",
"ArrowUp",
"ArrowLeft",
"ArrowRight",
"PageDown",
"PageUp",
"Home",
"End"
]
},
{
"pattern": "^[ -~]$"
}
],
"description": "One of Enter, Tab, Shift+Tab, Escape, Backspace, Delete, Space, ArrowDown, ArrowUp, ArrowLeft, ArrowRight, PageDown, PageUp, Home, End, or one character such as a or 5."
},
"dialog": {
"enum": [
"accept",
"dismiss"
],
"type": "string",
"description": "How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog."
},
"repeat": {
"type": "integer",
"maximum": 20,
"minimum": 1,
"description": "Times to press it. Default 1."
},
"dialog_text": {
"type": "string",
"maxLength": 1000,
"description": "Text for a prompt dialog this step opens; implies accept."
}
},
"additionalProperties": false
}The whole current page: its text (up to 20,000 characters) and a numbered list of its links, buttons, fields and other controls, open dialogs first. Use it on a new page or when a step's answer is not enough; `element` reads only inside one element (a dialog, a form, a list). Never returns field values, cookies or storage.
| Argument | Type | Description |
|---|---|---|
| element | integer | Read only inside this element. |
{
"type": "object",
"properties": {
"element": {
"type": "integer",
"minimum": 1,
"description": "Read only inside this element."
}
},
"additionalProperties": false
}A JPEG of the visible part of the page. Use it for boards, canvases, maps, charts and layout that the text does not show, then click or drag by `x`, `y` of the image.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}Scrolls with the mouse wheel: `down` or `up` by `screens`, to `top` or `bottom`, until `element` is in view, or inside a panel or list (`inside`). Content that loads on scroll appears. Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| inside | integer | Scroll inside this element, not the page. |
| element | integer | Scroll until this element is in view. |
| screens | number | Viewport heights, for down and up. Default: 0.8. |
| direction | string | Default: down. |
{
"type": "object",
"properties": {
"inside": {
"type": "integer",
"minimum": 1,
"description": "Scroll inside this element, not the page."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "Scroll until this element is in view."
},
"screens": {
"type": "number",
"maximum": 10,
"description": "Viewport heights, for down and up. Default: 0.8.",
"exclusiveMinimum": 0
},
"direction": {
"enum": [
"down",
"up",
"top",
"bottom"
],
"type": "string",
"description": "Default: down."
}
},
"additionalProperties": false
}Chooses `option` (its label as listed, or its value) in drop-down `element`. For a home-made drop-down, click it, then click the option. Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| dialog | string | How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog. |
| option* | string | The option. |
| element* | integer | The element's number from the last read or step answer. |
| dialog_text | string | Text for a prompt dialog this step opens; implies accept. |
{
"type": "object",
"required": [
"element",
"option"
],
"properties": {
"dialog": {
"enum": [
"accept",
"dismiss"
],
"type": "string",
"description": "How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog."
},
"option": {
"type": "string",
"maxLength": 300,
"description": "The option."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "The element's number from the last read or step answer."
},
"dialog_text": {
"type": "string",
"maxLength": 1000,
"description": "Text for a prompt dialog this step opens; implies accept."
}
},
"additionalProperties": false
}Types `text` into field `element`, replacing what it held; `submit` presses Enter after it. For several fields use fill_form. Without `element` the text goes where the focus is, as a person types after clicking: for spreadsheet cells and editors that read does not number (Google Sheets, Docs), click the place first; there a tab in `text` presses Tab and a line break Enter, so one call fills a table. Answers the page after it: the elements and text in view, with the same numbers. Password, card and one-time code fields are refused: the user types them.
| Argument | Type | Description |
|---|---|---|
| text* | string | The text to type. |
| dialog | string | How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog. |
| slowly | boolean | Key by key, for a search or address field whose suggestions do not appear. |
| submit | boolean | Press Enter after typing, e.g. to search. |
| element | integer | The field's number from the last read. Leave it out to type where the focus is. |
| dialog_text | string | Text for a prompt dialog this step opens; implies accept. |
{
"type": "object",
"required": [
"text"
],
"properties": {
"text": {
"type": "string",
"maxLength": 5000,
"description": "The text to type."
},
"dialog": {
"enum": [
"accept",
"dismiss"
],
"type": "string",
"description": "How to answer a native dialog (confirm, prompt, 'leave this page?') this step opens: accept presses OK, dismiss presses Cancel. Default: OK for an alert, Cancel for every other dialog."
},
"slowly": {
"type": "boolean",
"description": "Key by key, for a search or address field whose suggestions do not appear."
},
"submit": {
"type": "boolean",
"description": "Press Enter after typing, e.g. to search."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "The field's number from the last read. Leave it out to type where the focus is."
},
"dialog_text": {
"type": "string",
"maxLength": 1000,
"description": "Text for a prompt dialog this step opens; implies accept."
}
},
"additionalProperties": false
}Puts files into file field or upload button `element`: each file is {"url": "https://..."} or {"file_id": "..."} of a file uploaded to Cotlet; up to 10 files, 20 MB each. Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| files* | array | The files. |
| element* | integer | The element's number from the last read or step answer. |
{
"type": "object",
"required": [
"element",
"files"
],
"properties": {
"files": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "A public file URL."
},
"file_id": {
"type": "string",
"description": "A file uploaded to Cotlet."
}
},
"additionalProperties": false
},
"maxItems": 10,
"minItems": 1,
"description": "The files."
},
"element": {
"type": "integer",
"minimum": 1,
"description": "The element's number from the last read or step answer."
}
},
"additionalProperties": false
}Waits until `text` shows on the page (at most `seconds`, default 5, up to 20), or just `seconds`: for results that load slowly. Says whether the text appeared. Answers the page after it: the elements and text in view, with the same numbers.
| Argument | Type | Description |
|---|---|---|
| text | string | Text to wait for. |
| seconds | number | At most. |
{
"type": "object",
"properties": {
"text": {
"type": "string",
"maxLength": 200,
"description": "Text to wait for."
},
"seconds": {
"type": "number",
"maximum": 20,
"description": "At most.",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}Status of an earlier Cotlet call, by its call_id: pending (waits for the owner's approval in the Cotlet console), approved, running, success, rejected, expired, blocked or failed. For a call that answered with status pending or running.
| Argument | Type | Description |
|---|---|---|
| call_id* | string | call_id from the earlier call |
{
"type": "object",
"required": [
"call_id"
],
"properties": {
"call_id": {
"type": "string",
"description": "call_id from the earlier call"
}
},
"additionalProperties": false
}Sends a report to the Cotlet team: a tool that failed or returned wrong data, an error you could not get past, or a step the browser could not do, with the page, the step and what you saw. A missing dedicated tool is not a problem to report: do the task in the browser. A feature the user asks for goes as feature_request. A report promises nothing: tell the user it was sent, never that something will be built or fixed, or when. The report carries the error text and call_id when there is one, never passwords or tokens. Needs the user's Cotlet API key or sign-in.
| Argument | Type | Description |
|---|---|---|
| app | any | App slug the report is about, e.g. 'skool', or for a site in the web browser its host, e.g. 'app.example.com'. |
| kind* | string | bug: a tool did the wrong thing or returned wrong data. error: a call failed or returned an error you could not get past. feature_request: something new the user asked for. missing_feature: a step the task needed that you tried and could not do, in the browser or with a tool. other: anything else. |
| tool | any | Tool or endpoint name, e.g. 'skool_list_members'. |
| title* | string | One line, e.g. 'skool_list_members stops after 50 members'. |
| client | any | Agent or tool sending this, e.g. 'Claude Code'. |
| call_id | any | call_id of the call that went wrong, if any. |
| description* | string | What the user asked for, what you tried, what you expected and what happened, with the error text if there was one. No passwords, tokens or other secrets. |
{
"type": "object",
"title": "AgentReportCreate",
"required": [
"kind",
"title",
"description"
],
"properties": {
"app": {
"anyOf": [
{
"type": "string",
"maxLength": 80
},
{
"type": "null"
}
],
"title": "App",
"default": null,
"description": "App slug the report is about, e.g. 'skool', or for a site in the web browser its host, e.g. 'app.example.com'."
},
"kind": {
"enum": [
"bug",
"error",
"feature_request",
"missing_feature",
"other"
],
"type": "string",
"title": "Kind",
"description": "bug: a tool did the wrong thing or returned wrong data. error: a call failed or returned an error you could not get past. feature_request: something new the user asked for. missing_feature: a step the task needed that you tried and could not do, in the browser or with a tool. other: anything else."
},
"tool": {
"anyOf": [
{
"type": "string",
"maxLength": 160
},
{
"type": "null"
}
],
"title": "Tool",
"default": null,
"description": "Tool or endpoint name, e.g. 'skool_list_members'."
},
"title": {
"type": "string",
"title": "Title",
"maxLength": 200,
"minLength": 3,
"description": "One line, e.g. 'skool_list_members stops after 50 members'."
},
"client": {
"anyOf": [
{
"type": "string",
"maxLength": 80
},
{
"type": "null"
}
],
"title": "Client",
"default": null,
"description": "Agent or tool sending this, e.g. 'Claude Code'."
},
"call_id": {
"anyOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "null"
}
],
"title": "Call Id",
"default": null,
"description": "call_id of the call that went wrong, if any."
},
"description": {
"type": "string",
"title": "Description",
"maxLength": 5000,
"minLength": 10,
"description": "What the user asked for, what you tried, what you expected and what happened, with the error text if there was one. No passwords, tokens or other secrets."
}
},
"description": "Body of POST /api/v1/reports and the arguments of the MCP tool cotlet_report_issue.",
"additionalProperties": false
}Requests access to apps for the user's workspace, as Request access in the console: every app is granted at once. Not needed to work in an app: the user's AI works in any web app in their Web browser with cotlet_app_guide. An app with dedicated tools gets them after the user signs in to it in their Cloud Browser. Access says nothing about whether a task works there: check that in the browser. Takes app slugs as listed at https://cotlet.dev/api/v1/apps. Free. Needs the user's Cotlet API key or sign-in.
| Argument | Type | Description |
|---|---|---|
| apps* | array | App slugs, e.g. ["skool", "substack"]. |
| use_case | string | What the user wants the agent to do in these apps, in their words. |
{
"type": "object",
"required": [
"apps"
],
"properties": {
"apps": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 10,
"minItems": 1,
"description": "App slugs, e.g. [\"skool\", \"substack\"]."
},
"use_case": {
"type": "string",
"maxLength": 1000,
"description": "What the user wants the agent to do in these apps, in their words."
}
},
"additionalProperties": false
}Returns a one-time upload_url and a file_id for a local file (an image for a post, an attachment). The file is sent to upload_url with an HTTP PUT; an argument that takes a file accepts {"file_id": "..."}. Up to 25 MB, the URL works once. Free. Needs the user's Cotlet API key or sign-in.
| Argument | Type | Description |
|---|---|---|
| name | string | File name, e.g. photo.jpg. |
| content_type | string | MIME type, e.g. image/jpeg. |
{
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "File name, e.g. photo.jpg."
},
"content_type": {
"type": "string",
"description": "MIME type, e.g. image/jpeg."
}
},
"additionalProperties": false
}The Cotlet account this connection acts as: email, workspace, access status, the API key or OAuth client in use with its scope, the apps the workspace may call and its credit balance. Free. Needs the user's Cotlet API key or sign-in.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}New customer with an optional service address; returns its customer_id and address_id.
| Argument | Type | Description |
|---|---|---|
| zip | string | Service address: ZIP code. |
| city | string | Service address: city. |
| kind | string | homeowner or business. Default: homeowner. |
| string | Email address. Housecall Pro sends job, estimate and invoice emails here. | |
| notes | string | Private notes on the customer. |
| state | string | Service address: state, e.g. OH. |
| street | string | Service address: street. |
| company | string | Company name. Needed for a business customer without a first name. |
| last_name | string | Last name. |
| first_name | string | First name. |
| home_number | string | Home phone. |
| work_number | string | Work phone. |
| mobile_number | string | Mobile phone, US 10 digits. |
| street_line_2 | string | Service address: unit or suite. |
| notifications_enabled | boolean | Let Housecall Pro send the customer job and invoice notifications. Default: true. |
{
"type": "object",
"required": [],
"properties": {
"zip": {
"type": "string",
"description": "Service address: ZIP code."
},
"city": {
"type": "string",
"description": "Service address: city."
},
"kind": {
"enum": [
"homeowner",
"business"
],
"type": "string",
"description": "homeowner or business. Default: homeowner."
},
"email": {
"type": "string",
"description": "Email address. Housecall Pro sends job, estimate and invoice emails here."
},
"notes": {
"type": "string",
"description": "Private notes on the customer."
},
"state": {
"type": "string",
"description": "Service address: state, e.g. OH."
},
"street": {
"type": "string",
"description": "Service address: street."
},
"company": {
"type": "string",
"description": "Company name. Needed for a business customer without a first name."
},
"last_name": {
"type": "string",
"description": "Last name."
},
"first_name": {
"type": "string",
"description": "First name."
},
"home_number": {
"type": "string",
"description": "Home phone."
},
"work_number": {
"type": "string",
"description": "Work phone."
},
"mobile_number": {
"type": "string",
"description": "Mobile phone, US 10 digits."
},
"street_line_2": {
"type": "string",
"description": "Service address: unit or suite."
},
"notifications_enabled": {
"type": "boolean",
"description": "Let Housecall Pro send the customer job and invoice notifications. Default: true."
}
},
"additionalProperties": false
}New estimate for a customer with its first option (Option #1) and lines. Sends nothing. Add more options with add_estimate_option, then email it with send_estimate.
| Argument | Type | Description |
|---|---|---|
| message | string | Message to the customer shown on the estimate. |
| address_id | string | Service address of the customer (create_customer). Default: the billing address. |
| line_items* | array | Lines of the first option, in order. |
| customer_id* | string | customer_id from list_customers. |
| employee_ids | array | Employees to assign (list_employees). Default: none. |
{
"type": "object",
"required": [
"customer_id",
"line_items"
],
"properties": {
"message": {
"type": "string",
"description": "Message to the customer shown on the estimate."
},
"address_id": {
"type": "string",
"description": "Service address of the customer (create_customer). Default: the billing address."
},
"line_items": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"unit_price"
],
"properties": {
"kind": {
"enum": [
"service",
"material"
],
"type": "string",
"description": "service (labor) or material. Default: service."
},
"name": {
"type": "string",
"minLength": 1,
"description": "Service or material name."
},
"taxable": {
"type": "boolean",
"description": "Taxable. Default: false."
},
"quantity": {
"type": "number",
"description": "Quantity. Default: 1.",
"exclusiveMinimum": 0
},
"unit_cost": {
"type": "number",
"minimum": 0,
"description": "Your cost per unit in dollars. Default: 0."
},
"unit_price": {
"type": "number",
"minimum": 0,
"description": "Price per unit in dollars."
},
"description": {
"type": "string",
"description": "Line description."
}
},
"additionalProperties": false
},
"maxItems": 50,
"minItems": 1,
"description": "Lines of the first option, in order."
},
"customer_id": {
"type": "string",
"minLength": 1,
"description": "customer_id from list_customers."
},
"employee_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Employees to assign (list_employees). Default: none."
}
},
"additionalProperties": false
}New job for a customer with line items, optionally scheduled and assigned to employees. Creates the job as the New job form does. With start, it is scheduled in the company's time zone and assigned to employee_ids; the customer gets a scheduled email only with notify_customer.
| Argument | Type | Description |
|---|---|---|
| end | string | Scheduled end. Default: start plus duration_minutes. |
| note | string | Private job note. |
| start | string | Scheduled start, 2026-11-04T09:00 in the company's time zone. Default: unscheduled. |
| address_id | string | Service address of the customer (create_customer). Default: the billing address. |
| line_items* | array | Lines, in order. |
| customer_id* | string | customer_id from list_customers. |
| employee_ids | array | Employees to assign (list_employees). Default: none. |
| notify_customer | boolean | Email the customer that the job is scheduled. Default: false. |
| duration_minutes | integer | Default: 60. |
{
"type": "object",
"required": [
"customer_id",
"line_items"
],
"properties": {
"end": {
"type": "string",
"description": "Scheduled end. Default: start plus duration_minutes."
},
"note": {
"type": "string",
"description": "Private job note."
},
"start": {
"type": "string",
"description": "Scheduled start, 2026-11-04T09:00 in the company's time zone. Default: unscheduled."
},
"address_id": {
"type": "string",
"description": "Service address of the customer (create_customer). Default: the billing address."
},
"line_items": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"unit_price"
],
"properties": {
"kind": {
"enum": [
"service",
"material"
],
"type": "string",
"description": "service (labor) or material. Default: service."
},
"name": {
"type": "string",
"minLength": 1,
"description": "Service or material name."
},
"taxable": {
"type": "boolean",
"description": "Taxable. Default: false."
},
"quantity": {
"type": "number",
"description": "Quantity. Default: 1.",
"exclusiveMinimum": 0
},
"unit_cost": {
"type": "number",
"minimum": 0,
"description": "Your cost per unit in dollars. Default: 0."
},
"unit_price": {
"type": "number",
"minimum": 0,
"description": "Price per unit in dollars."
},
"description": {
"type": "string",
"description": "Line description."
}
},
"additionalProperties": false
},
"maxItems": 50,
"minItems": 1,
"description": "Lines, in order."
},
"customer_id": {
"type": "string",
"minLength": 1,
"description": "customer_id from list_customers."
},
"employee_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Employees to assign (list_employees). Default: none."
},
"notify_customer": {
"type": "boolean",
"description": "Email the customer that the job is scheduled. Default: false."
},
"duration_minutes": {
"type": "integer",
"maximum": 1440,
"minimum": 15,
"description": "Default: 60."
}
},
"additionalProperties": false
}Invoice part of a job (an amount or a percent of its total); the rest stays to invoice later. Adds an invoice to the job as Invoice > Add invoice does with progressive invoicing on. Without amount or percent it bills everything left. Sends nothing: email it with send_invoice.
| Argument | Type | Description |
|---|---|---|
| due | string | Payment terms: upon receipt, or net_days after the invoice date. Default: upon_receipt. |
| amount | number | Amount to bill in dollars. |
| job_id* | string | job_id from list_jobs. |
| percent | number | Or a percent of the job total. Default (neither): all that is left to invoice. |
| net_days | integer | Days for net. Default: 30. |
| invoice_date | string | YYYY-MM-DD. Default: today. |
| reference_number | string | Customer reference (PO) number. |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"due": {
"enum": [
"upon_receipt",
"net"
],
"type": "string",
"description": "Payment terms: upon receipt, or net_days after the invoice date. Default: upon_receipt."
},
"amount": {
"type": "number",
"description": "Amount to bill in dollars.",
"exclusiveMinimum": 0
},
"job_id": {
"type": "string",
"minLength": 1,
"description": "job_id from list_jobs."
},
"percent": {
"type": "number",
"maximum": 100,
"description": "Or a percent of the job total. Default (neither): all that is left to invoice.",
"exclusiveMinimum": 0
},
"net_days": {
"type": "integer",
"maximum": 365,
"minimum": 1,
"description": "Days for net. Default: 30."
},
"invoice_date": {
"type": "string",
"description": "YYYY-MM-DD. Default: today."
},
"reference_number": {
"type": "string",
"maxLength": 100,
"description": "Customer reference (PO) number."
}
},
"additionalProperties": false
}One estimate with its customer, status and every option with its lines and total.
| Argument | Type | Description |
|---|---|---|
| estimate_id* | string | estimate_id from list_estimates (best_…); an option_id (est_…) works too. |
{
"type": "object",
"required": [
"estimate_id"
],
"properties": {
"estimate_id": {
"type": "string",
"minLength": 1,
"description": "estimate_id from list_estimates (best_…); an option_id (est_…) works too."
}
},
"additionalProperties": false
}One invoice with its customer, amount, amount due, due terms and when it was sent and paid. amount is this invoice's part; job_total is the whole job's total (progress invoices bill a part).
| Argument | Type | Description |
|---|---|---|
| invoice_id* | string | invoice_id from list_invoices or get_job. |
{
"type": "object",
"required": [
"invoice_id"
],
"properties": {
"invoice_id": {
"type": "string",
"minLength": 1,
"description": "invoice_id from list_invoices or get_job."
}
},
"additionalProperties": false
}One job with its line items, schedule, employees, balance and the invoices made from it.
| Argument | Type | Description |
|---|---|---|
| job_id* | string | job_id from list_jobs. |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string",
"minLength": 1,
"description": "job_id from list_jobs."
}
},
"additionalProperties": false
}Scheduled jobs and estimate visits on the calendar for a date range, with the assigned employees. Days are in the company's time zone; start and end come back in UTC. record_id is a job or estimate.
| Argument | Type | Description |
|---|---|---|
| end_date | string | Last day, YYYY-MM-DD, at most 31 days on. Default: 7 days on. |
| start_date | string | First day, YYYY-MM-DD. Default: today. |
| employee_id | string | Only this employee's visits (list_employees). |
{
"type": "object",
"required": [],
"properties": {
"end_date": {
"type": "string",
"description": "Last day, YYYY-MM-DD, at most 31 days on. Default: 7 days on."
},
"start_date": {
"type": "string",
"description": "First day, YYYY-MM-DD. Default: today."
},
"employee_id": {
"type": "string",
"description": "Only this employee's visits (list_employees)."
}
},
"additionalProperties": false
}Customers of the company with contact details; search by name, email, phone or address. Newest customers first, 25 per page by default (up to 100); pass next_cursor back as cursor. Phone numbers come as the app stores them, and only mobile_number can receive texts.
| Argument | Type | Description |
|---|---|---|
| limit | integer | 1 to 100. Default: 25. |
| cursor | string | next_cursor from the previous call. |
| search | string | Part of a name, email, phone number or address. |
{
"type": "object",
"required": [],
"properties": {
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "1 to 100. Default: 25."
},
"cursor": {
"type": "string",
"description": "next_cursor from the previous call."
},
"search": {
"type": "string",
"description": "Part of a name, email, phone number or address."
}
},
"additionalProperties": false
}The company's employees (field techs, office, admins) with role and contact, for dispatch. Use employee_id in create_job and create_estimate to assign work.
| Argument | Type | Description |
|---|---|---|
| include_archived | boolean | Also archived employees. Default: false. |
{
"type": "object",
"required": [],
"properties": {
"include_archived": {
"type": "boolean",
"description": "Also archived employees. Default: false."
}
},
"additionalProperties": false
}Estimates, newest first, with customer, pipeline status and value. value is the highest option's total. Use estimate_id in get_estimate for options and lines.
| Argument | Type | Description |
|---|---|---|
| limit | integer | 1 to 100. Default: 25. |
| cursor | string | next_cursor from the previous call. |
{
"type": "object",
"required": [],
"properties": {
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "1 to 100. Default: 25."
},
"cursor": {
"type": "string",
"description": "next_cursor from the previous call."
}
},
"additionalProperties": false
}Invoices, newest number first, with customer, amount, amount due and last payment. Filters by status only (open, pending_payment, paid); there is no date or customer filter. To find past-due invoices, read the open ones and compare due_at with today where due_amount is above 0. Statuses come back as the list shows them (Paid); get_invoice returns them in lowercase.
| Argument | Type | Description |
|---|---|---|
| limit | integer | 1 to 100. Default: 25. |
| cursor | string | next_cursor from the previous call. |
| status | array | Only invoices in these statuses. Default: all. |
{
"type": "object",
"required": [],
"properties": {
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "1 to 100. Default: 25."
},
"cursor": {
"type": "string",
"description": "next_cursor from the previous call."
},
"status": {
"type": "array",
"items": {
"enum": [
"open",
"pending_payment",
"paid"
],
"type": "string"
},
"description": "Only invoices in these statuses. Default: all."
}
},
"additionalProperties": false
}Jobs, newest first, with customer, schedule, assigned employees, total and balance. Filters by work status (unscheduled, scheduled, in_progress, completed) and, after each page is read, by customer_id, so a page can come back short while next_cursor is still set: keep paging. Line items and invoices come from get_job.
| Argument | Type | Description |
|---|---|---|
| limit | integer | 1 to 100. Default: 25. |
| cursor | string | next_cursor from the previous call. |
| status | array | Only jobs in these work statuses. Default: all four. |
| customer_id | string | Only this customer's jobs (customer_id from list_customers). |
{
"type": "object",
"required": [],
"properties": {
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "1 to 100. Default: 25."
},
"cursor": {
"type": "string",
"description": "next_cursor from the previous call."
},
"status": {
"type": "array",
"items": {
"enum": [
"unscheduled",
"scheduled",
"in_progress",
"completed"
],
"type": "string"
},
"description": "Only jobs in these work statuses. Default: all four."
},
"customer_id": {
"type": "string",
"description": "Only this customer's jobs (customer_id from list_customers)."
}
},
"additionalProperties": false
}Services or materials of the company's price book with price, cost and category. Use the name, description and price as line items in create_job or create_estimate.
| Argument | Type | Description |
|---|---|---|
| kind | string | services or materials. Default: services. |
| limit | integer | 1 to 100. Default: 25. |
| cursor | string | next_cursor from the previous call. |
| search | string | Part of a name or description. |
{
"type": "object",
"required": [],
"properties": {
"kind": {
"enum": [
"services",
"materials"
],
"type": "string",
"description": "services or materials. Default: services."
},
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "1 to 100. Default: 25."
},
"cursor": {
"type": "string",
"description": "next_cursor from the previous call."
},
"search": {
"type": "string",
"description": "Part of a name or description."
}
},
"additionalProperties": false
}Record a cash or check payment on a job's invoices. No money is charged. Marks money already received as paid, as the job's Pay dialog does with Cash or Check. Housecall Pro applies it to the job's open invoices, oldest first. It can't be more than what is invoiced and unpaid; invoice the rest with create_progress_invoice first.
| Argument | Type | Description |
|---|---|---|
| note | string | Payment note, e.g. the check number. |
| amount | number | Amount received in dollars. Default: everything invoiced and unpaid on the job. |
| job_id* | string | job_id of the invoiced job (get_job). |
| method* | string | cash or check. |
| receipt_email | string | Email a receipt to this address. Default: no receipt. |
{
"type": "object",
"required": [
"job_id",
"method"
],
"properties": {
"note": {
"type": "string",
"maxLength": 500,
"description": "Payment note, e.g. the check number."
},
"amount": {
"type": "number",
"description": "Amount received in dollars. Default: everything invoiced and unpaid on the job.",
"exclusiveMinimum": 0
},
"job_id": {
"type": "string",
"minLength": 1,
"description": "job_id of the invoiced job (get_job)."
},
"method": {
"enum": [
"cash",
"check"
],
"type": "string",
"description": "cash or check."
},
"receipt_email": {
"type": "string",
"description": "Email a receipt to this address. Default: no receipt."
}
},
"additionalProperties": false
}Email an estimate to the customer with a link to approve one of its options. Sends as the Send button of the estimate page does, by email only. The customer gets the email at once and the estimate shows Awaiting Approval. By default you confirm each call; you can switch that off for this tool. Waits for the owner's approval.
| Argument | Type | Description |
|---|---|---|
| string | Send to this address. Default: the customer's email. | |
| message | string | Email text. Default: the panel's greeting. |
| subject | string | Default: Estimate #N from <company>. |
| option_ids | array | Options to include (get_estimate). Default: all. |
| estimate_id* | string | estimate_id from list_estimates. |
{
"type": "object",
"required": [
"estimate_id"
],
"properties": {
"email": {
"type": "string",
"description": "Send to this address. Default: the customer's email."
},
"message": {
"type": "string",
"description": "Email text. Default: the panel's greeting."
},
"subject": {
"type": "string",
"maxLength": 200,
"description": "Default: Estimate #N from <company>."
},
"option_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Options to include (get_estimate). Default: all."
},
"estimate_id": {
"type": "string",
"minLength": 1,
"description": "estimate_id from list_estimates."
}
},
"additionalProperties": false
}Email an invoice to the customer as a PDF with the company's default or your own message. Sends as the Send invoice dialog does, by email only. The customer gets it at once. By default you confirm each call; you can switch that off for this tool. Waits for the owner's approval.
| Argument | Type | Description |
|---|---|---|
| string | Send to this address. Default: the customer's billing email. | |
| message | string | Email text. Default: the company's invoice message. |
| subject | string | Default: the company's invoice subject. |
| invoice_id* | string | invoice_id from list_invoices or get_job. |
{
"type": "object",
"required": [
"invoice_id"
],
"properties": {
"email": {
"type": "string",
"description": "Send to this address. Default: the customer's billing email."
},
"message": {
"type": "string",
"description": "Email text. Default: the company's invoice message."
},
"subject": {
"type": "string",
"maxLength": 200,
"description": "Default: the company's invoice subject."
},
"invoice_id": {
"type": "string",
"minLength": 1,
"description": "invoice_id from list_invoices or get_job."
}
},
"additionalProperties": false
}We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 9, 2026.