agentsclimarketplace

Honeybook fpx

Skill chrischall/honeybook-mcp/skills/honeybook-fpx

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.From its SKILL.md

Install
npx -y skills add chrischall/honeybook-mcp --skill honeybook-fpx

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

6.8 KB, as published. Nobody here has run it

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 <vendor>.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://<vendor>.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 <vendor> (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/<file_id>/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).

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.