Read your Jobber Client Hub — the customer portal a service business (pest control, lawn care, HVAC, cleaning) uses to send you appointments, quotes and invoices — from a shell with the fpx CLI (@fetchproxy/cli), instead of running the jobber-mcp server. Use when you want your Jobber data without the MCP, in a script, or on a machine where the MCP isn't installed.
Design & media
housecallpro-mcp
Try itRead a Housecall Pro estimate or invoice your contractor sent you — line items, totals, tax, what is still owed, the company behind it — from a shell with plain curl, instead of running the housecallpro-mcp server. Use when you want the data without the MCP, in a script, or on a machine where the MCP isn't installed. Covers declining an option, and why approving cannot be scripted.
What it does
Read a Housecall Pro estimate or invoice your contractor sent you — line items, totals, tax, what is still owed, the company behind it — from a shell with plain curl, instead of running the housecallpro-mcp server. Use when you want the data without the MCP, in a script, or on a machine where the MCP isn't installed. Covers declining an option, and why approving cannot be scripted.
The skill document
Housecall Pro customer portal via curl (no MCP)
This reads the customer side of Housecall Pro: the estimate or invoice a contractor emails or texts you. It is not the Housecall Pro public API — that one serves the business running on Housecall Pro and needs an API key from their account.
No browser bridge is needed. app.housecallpro.com is not bot-walled; a
plain curl gets a 200. Do not reach for fpx here.
The one thing to know first
There is no account and no standing link: Housecall Pro sends a separate, disposable link per document, so you work from whichever link you were sent.
Your link is a bearer credential. Anyone holding it can read the estimate and decline it. Keep it in a variable or a file you control, never in a command you paste into a shared transcript, and never commit it.
Setup
Two forms of link exist, and estimates and invoices use different token
shapes: an estimate token is 129 characters (two 64-char hex halves joined by
_), an invoice token is a bare 32-char hex string. The short link redirects to
whichever applies:
# What your contractor sent (short form)
SHORT='https://pro.housecallpro.com/mobile_estimate/XXXXXXXXXX' # or /mobile_invoice/…
# Resolve it to the retrieval token (an estimate's is 129 chars, an invoice's 32)
HCP_TOKEN=$(curl -sI "$SHORT" | tr -d '\r' | awk 'tolower($1)=="location:"{print $2}' | sed 's#.*/##')
# If you already have a client.housecallpro.com/estimates/… or /invoices/… link, just:
# HCP_TOKEN=''
echo "${#HCP_TOKEN}" # 129 for an estimate, 32 for an invoice
Read the estimate
curl -s -H 'Accept: application/json' \
"https://app.housecallpro.com/alpha/customer_estimates/$HCP_TOKEN" > estimate.json
No Authorization header — reads carry the token in the path.
Money is integer cents. total_amount: 34639 means $346.39. Divide by 100
before reporting anything. tax.rate is the exception: it is a fraction
(0.0825 = 8.25%) and must not be scaled.
A one-line summary:
jq -r '
"\(.company_name) — estimate #\(.estimate.data.estimate_number)",
"For: \(.customer_name) at \(.estimate.data.address.data.printable_address)",
(.options.data[] |
" \(.name) [\(.status)] $\(.total_amount/100)"),
(.options.data[].line_items.data[] |
" \(.quantity) x \(.name) @ $\(.unit_price/100) = $\(.amount/100)")
' estimate.json
More recipes, including the company lookup and the full field map, are in
references/recipes.md.
Is it still awaiting me?
jq -r '.options.data[] | select(.approval_date == null and .status != "Declined")
| "OPEN: \(.name) $\(.total_amount/100)"' estimate.json
Declining an option
Declining works from a shell. It is the only mutation that does — and it tells the contractor you are not proceeding, so read the estimate first.
OPTION_ID=$(jq -r '.options.data[0].id' estimate.json) # est_…
curl -s -X POST \
-H "Authorization: Token $HCP_TOKEN" \
--data-urlencode "estimate_option_uuids[]=$OPTION_ID" \
https://app.housecallpro.com/api/estimates/estimate_options/customer_declines
Note the asymmetry: writes put the token in the Authorization header,
reads put it in the path.
A 2xx is not proof. Re-read and check the option actually moved:
curl -s "https://app.housecallpro.com/alpha/customer_estimates/$HCP_TOKEN" \
| jq -r '.options.data[] | "\(.id) \(.status) approval_date=\(.approval_date)"'
Approving cannot be scripted
POST /api/estimates/estimate_options/customer_approvals requires a
response_token — a reCAPTCHA v3 token minted in-page for the action
estimates_customer_approvals. No shell client can produce one, and neither can
the fetchproxy bridge (it issues fetch calls; it does not run page JS).
Open the link in a browser and press Approve there. Do not try to work around this: approving is a binding commitment to a quoted price.
Failure modes
| Symptom | Meaning |
|---|---|
401 / 403 | The link expired or was revoked. Ask your contractor to resend it. |
404 (HTML) | The document was deleted or re-issued; the old link is dead. |
$HCP_TOKEN is empty or not 129 chars | The short link expired before it could redirect. |
A 200 that isn't JSON | Same as above — the link is no longer valid. |
Invoices
curl -s -H 'Accept: application/json' \
"https://app.housecallpro.com/api/invoices/consumer/v1/invoices/$HCP_TOKEN" > invoice.json
The v1 segment is required. /api/invoices/consumer/invoices/ —
the same path without it — returns 404, as do the /api/v2/consumer/invoices/…
and /api/v2/consumer/sent_invoices/… paths that also appear in the app's
JavaScript. Only this one answers.
jq -r '"\(.company_info.name) — invoice #\(.invoice_number) [\(.status)]",
" subtotal $\(.subtotal/100)",
" tax $\((.total - .subtotal)/100)",
" total $\(.total/100)",
" due $\(.due_amount/100)"' invoice.json
Two things to know: the invoice document has no line items (a paid invoice
renders as a summary in the portal, and the API returns exactly that), and no
tax field — the tax figure is total - subtotal. Money is integer cents here
too. Use due_amount rather than status to decide whether it is settled.
Related skills
Query FreshBooks (invoices, clients, estimates, payments, expenses, projects, time tracking) from a shell with curl and a rotating OAuth token. Use when you want FreshBooks data without running the freshbooks-mcp server, in a script, or on a machine where the MCP isn't installed.
Query thumbtack.com from a shell — search local service pros by trade and zip, and read pro profiles with ratings, reviews, credentials, business hours and pricing. Uses plain curl against Thumbtack's SSR pages and its anonymous GraphQL endpoint; no API key, no login, no browser extension. Use when you want Thumbtack data without an MCP server, in a script, or one-shot.
This skill should be used when the user asks about HoneyBook client-portal data. Triggers on phrases like "check HoneyBook", "sign contract", "pay invoice", "HoneyBook vendors", "unsigned contracts", "open invoices", or any request involving wedding-vendor contracts, invoices, brochures, proposals, or payments via HoneyBook.
Read angi.com (US home-services directory) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the angi-mcp server — find pros by trade and city, read a pro's profile, ratings breakdown and reviews, and list the trade/city taxonomy. Use when you want Angi data without the MCP, in a script, or on a machine where the MCP isn't installed.
Read a SimplePractice Client Portal through the simplepractice-mcp server — upcoming appointments, invoices/statements/superbills/receipts, balance and saved cards, paperwork waiting to be signed, and practice announcements. Use when the user asks about their therapy or healthcare appointments, what they owe a practice, a superbill for insurance, or forms their provider has sent them.