Browser

honeybook-fpx

Try it

Read HoneyBook client-portal data (contracts, invoices, proposals, payment methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the honeybook-mcp server — capture a vendor session once via the signed-in browser tab, then curl api.honeybook.com directly. Use when you want HoneyBook data without the MCP, in a script, or on a machine where the MCP isn't installed.

What it does

Read HoneyBook client-portal data (contracts, invoices, proposals, payment methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the honeybook-mcp server — capture a vendor session once via the signed-in browser tab, then curl api.honeybook.com directly. Use when you want HoneyBook data without the MCP, in a script, or on a machine where the MCP isn't installed.

The skill document

HoneyBook via fpx + curl (no MCP)

HoneyBook has no server-side login a script can drive — a client never gets a password, only a magic-link email per vendor. The credential is whatever the signed-in *.hbportal.co portal tab already holds: a bearer token + user id in localStorage["HONEYBOOK_REACT_CURR_USER"]. (HoneyBook used to keep these in the AngularJS localStorage["jStorage"] blob as HB_AUTH_TOKEN/HB_AUTH_USER_ID; that blob is now down to HB_TRUSTED_DEVICE, SESSION_COMPANY_ID and routing state.) There's no bot wall on the API itself once you have those — honeybook-mcp's own client.ts proves plain Node fetch works fine against api.honeybook.com. So this skill is hybrid: fpx captures the session once (per vendor), then plain curl does every read from then on.

This mirrors src/auth.ts (captureSessionViaFetchproxy) and src/client.ts (HoneyBookClient.request) in honeybook-mcp — same headers, same base URL, same retry rules.

Multi-domain scope

Two apexes are declared on one profile:

  • hbportal.co — the vendor's branded portal (e.g. acme.hbportal.co), where the stored session lives.
  • honeybook.com — the main app, where the same session is also valid.

One-time setup

npm install -g @fetchproxy/cli   # provides `fpx`
fpx profile add honeybook --domain honeybook.com --domain hbportal.co
fpx profile declare honeybook \
  --local-storage HONEYBOOK_REACT_CURR_USER \
  --local-storage jStorage
fpx pair -p honeybook            # prints a pair code → approve in Transporter

Requirements: the Transporter browser extension installed, its Chrome Site access allowing both honeybook.com and hbportal.co, and a vendor magic-link URL already open (signed in) in that browser. Pairing persists — after the first approval every later fpx call reuses it.

Capture a vendor session (once per vendor, and again when it expires)

  1. Click the vendor's HoneyBook magic-link email in the browser with Transporter installed. This signs you into .hbportal.co.
  2. While that tab is open, run:
fpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json

The tab only has to be open and signed in. Nothing is sniffed off a live request, so it does not matter whether the page has gone idle.

  1. Extract the fields client.ts needs (each localStorage value is one raw JSON string — parse it with fromjson):
AUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)
USER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)
# Optional — the API returns 200 without it. The React blob and jStorage hold
# DIFFERENT values; either is accepted. `// empty` keeps an absent field from
# becoming the literal string "null".
TRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)
PORTAL_ORIGIN='https://.hbportal.co'   # the magic-link URL's origin

If AUTH_TOKEN or USER_ID comes back empty/null, the capture didn't see what it needed — re-open the magic link and re-run step 2.

With more than one vendor tab open at once, disambiguate with --storage-subdomain (e.g. --storage-subdomain acme).

  1. Get the current API version (client.ts's fetchApiVersion parses this same endpoint):
API_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \
  | grep -oE '"api_version":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')

Core call pattern

Every real request carries the same headers (client.ts's HoneyBookClient.request). Only hb-api-auth-token, hb-api-user-id and a current hb-api-client-version are load-bearing — hb-trusted-device is optional and hb-api-fingerprint is no longer required at all:

curl -s "https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files" \
  -H 'accept: application/json, text/plain, */*' \
  -H "hb-api-auth-token: $AUTH_TOKEN" \
  -H "hb-api-user-id: $USER_ID" \
  ${TRUSTED_DEVICE:+-H "hb-trusted-device: $TRUSTED_DEVICE"} \
  -H "hb-api-client-version: $API_VERSION" \
  -H "hb-api-duplicate-calls-prevention-uuid: $(uuidgen)" \
  -H 'hb-admin-login: false' \
  | jq '.data'

hb-api-duplicate-calls-prevention-uuid must be a fresh random UUID on every request — the MCP mints one with crypto.randomUUID() per call, not once per session. Reusing a value risks HoneyBook treating a legitimate repeat as a duplicate.

Ready-to-run commands for all four read endpoints are in references/requests.md.

The rules that matter

  • 401, or 404 with an HBUnauthorizedError body → session expired. A revoked token does not reliably come back as 401, so check the body type before concluding a resource is missing. Re-run the capture (magic link tab must still be open and signed in).
  • 429 → rate limited. client.ts waits 2s and retries once; do the same before giving up.
  • Body contains "HBWrongAPIVersionError" → stale hb-api-client-version. The error body itself carries the correct value at .error_data.server_api_version — read that (or re-run the /api/gon fetch above) and retry the SAME request with the fresh version.
  • sign_contract / pay_invoice are not real API calls. honeybook-mcp can't replay HoneyBook's browser-side signing/SCA flow, so those tools just return a deep link — $PORTAL_ORIGIN/app/workspace_file//agreement (sign) or /invoice (pay) — for the user to open themselves. There's no POST body to transcribe for either; don't invent one.

Output / exit-code contract

  • fpx session/fpx pair/fpx health are bridge round-trips: exit 0 on a successful bridge read regardless of upstream status, 1 on a usage error (bad flag, undeclared scope), 2 if the bridge/extension is unreachable or pairing is still pending. There's no bot-wall (3)/upstream-HTTP (4) exit code on these — HoneyBook's own API isn't bridge-walled.
  • The actual reads go through plain curl afterward — check the HTTP status and the HBWrongAPIVersionError body text yourself, as above.

Notes

  • Session data (AUTH_TOKEN, TRUSTED_DEVICE) is opaque and long-lived server-side (no client-visible JWT expiry) — keep it in shell variables, not a world-readable file, if you must persist it at all.
  • This project is developed and maintained by AI (Claude).

Related skills

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", "message the planner", "reply to my vendor", "next meeting with the planner", or any request involving wedding-vendor contracts, invoices, brochures, proposals, payments, messages, meetings or tasks via HoneyBook.

26 installs

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.

18 installs

Read a SimplePractice Client Portal (`<practice>.clientsecure.me`) from a shell — appointments, invoices/statements/superbills/receipts, documents to sign, announcements, practice and clinician info — with plain `curl` against its JSON:API, instead of running the simplepractice-mcp server. Sign in headlessly with an emailed magic link, or capture the session cookie from an already-signed-in browser tab with `fpx`. Use when you want Client Portal data without the MCP, in a script, or on a machine where the MCP isn't installed.

Read Workday HR data (org chart, worker profiles, tasks, pay, benefits, compensation, your app menu) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the workday-mcp server — fetch any *.htmld data endpoint through your own signed-in *.myworkday.com tab (SSO/Ping/Okta/Entra already cleared). Use when you want Workday data without the MCP, in a script, or on a machine where the MCP isn't installed.

1 installs

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.

1 installs

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.