{"id":"5ccf5e88-8c0e-414b-95f6-9af46ac39f03","entityType":"agent","slug":"clawhub-chrischall-honeybook-fpx","name":"honeybook-fpx","canonicalUrl":"https://www.xpersona.co/agent/clawhub-chrischall-honeybook-fpx","canonicalPath":"/agent/clawhub-chrischall-honeybook-fpx","generatedAt":"2026-10-10T21:57:16.543Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T16:13:40.927Z","emptyReason":null},"description":"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.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.3K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:honeybook-fpx","sourceUrl":"https://clawhub.ai/chrischall/honeybook-fpx","homepage":"https://clawhub.ai/chrischall/skills/honeybook-fpx","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/chrischall/honeybook-fpx","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/chrischall/skills/honeybook-fpx","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":63,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"honeybook-fpx technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T16:13:40.927Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T16:13:40.927Z","emptyReason":null},"stars":null,"forks":null,"downloads":1344,"packageName":null,"latestVersion":"1.2.6","tractionLabel":"1.3K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T16:13:40.927Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T16:13:40.927Z","lastCrawledAt":"2026-10-10T16:13:40.927Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T16:13:40.927Z","lastVerifiedAt":null,"highlights":[{"version":"1.2.6","createdAt":"2026-10-09T23:26:54.915Z","changelog":"- Removed obsolete skill-card.md file for better clarity and maintenance. - Updated documentation in references/requests.md with clarified or revised instructions. - No changes to core logic or functionality; documentation only.","fileCount":4,"zipByteSize":8105},{"version":"1.2.5","createdAt":"2026-10-07T13:35:33.297Z","changelog":"- Removed the skill-card.md file. - No functional or documentation changes to the skill itself. - Internal documentation/file cleanup only.","fileCount":4,"zipByteSize":8017},{"version":"1.2.4","createdAt":"2026-10-05T02:50:37.130Z","changelog":"- Removed the sample skill-card.md file. - No user-facing changes to skill functionality or documentation.","fileCount":4,"zipByteSize":8118},{"version":"1.2.3","createdAt":"2026-10-03T01:40:58.332Z","changelog":"- Removed the redundant skill-card.md file. - No changes to skill functionality or documentation content.","fileCount":4,"zipByteSize":8080},{"version":"1.2.2","createdAt":"2026-09-28T13:55:26.975Z","changelog":"- Updated setup documentation to reference the new ContextMint Bridge browser extension (formerly fetchproxy), including new install and verification instructions. - Clarified requirements: chrome zip for Chrome, Safari not yet supported. - Updated pairing terminology and references from \"Transporter\" to \"ContextMint Bridge\". - Removed deprecated skill-card.md file.","fileCount":4,"zipByteSize":8015},{"version":"1.2.1","createdAt":"2026-09-25T15:52:52.754Z","changelog":"- Removed the file: skill-card.md. - No changes to functionality or usage. - Documentation and usage instructions remain unchanged.","fileCount":4,"zipByteSize":7742},{"version":"1.2.0","createdAt":"2026-09-24T15:11:46.063Z","changelog":"- Removed the sample file skill-card.md. - No functional or documentation changes to the skill itself.","fileCount":4,"zipByteSize":7853},{"version":"1.1.4","createdAt":"2026-09-23T21:40:37.776Z","changelog":"- Removed the sample file skill-card.md. - No changes to core logic or documentation.","fileCount":4,"zipByteSize":7860}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:honeybook-fpx","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:honeybook-fpx` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/chrischall/honeybook-fpx before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T21:57:16.540Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-honeybook-fpx/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T16:13:40.927Z","emptyReason":null},"readme":"Skill: honeybook-fpx\n\nOwner: chrischall\n\nSummary: 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.\n\nTags: latest:1.2.6\n\nVersion history:\n\nv1.2.6 | 2026-10-09T23:26:54.915Z | auto\n\n- Removed obsolete skill-card.md file for better clarity and maintenance.\n- Updated documentation in references/requests.md with clarified or revised instructions.\n- No changes to core logic or functionality; documentation only.\n\nv1.2.5 | 2026-10-07T13:35:33.297Z | auto\n\n- Removed the skill-card.md file.\n- No functional or documentation changes to the skill itself.\n- Internal documentation/file cleanup only.\n\nv1.2.4 | 2026-10-05T02:50:37.130Z | auto\n\n- Removed the sample skill-card.md file.\n- No user-facing changes to skill functionality or documentation.\n\nv1.2.3 | 2026-10-03T01:40:58.332Z | auto\n\n- Removed the redundant skill-card.md file.\n- No changes to skill functionality or documentation content.\n\nv1.2.2 | 2026-09-28T13:55:26.975Z | auto\n\n- Updated setup documentation to reference the new ContextMint Bridge browser extension (formerly fetchproxy), including new install and verification instructions.\n- Clarified requirements: chrome zip for Chrome, Safari not yet supported.\n- Updated pairing terminology and references from \"Transporter\" to \"ContextMint Bridge\".\n- Removed deprecated skill-card.md file.\n\nv1.2.1 | 2026-09-25T15:52:52.754Z | auto\n\n- Removed the file: skill-card.md.\n- No changes to functionality or usage.\n- Documentation and usage instructions remain unchanged.\n\nv1.2.0 | 2026-09-24T15:11:46.063Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional or documentation changes to the skill itself.\n\nv1.1.4 | 2026-09-23T21:40:37.776Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to core logic or documentation.\n\nv1.1.3 | 2026-09-23T15:40:31.128Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to functionality or documentation in SKILL.md.\n\nv1.1.2 | 2026-09-21T05:03:33.135Z | auto\n\n- Removed the sample file skill-card.md.\n- No features or documentation were changed—functionality remains the same.\n\nv1.1.1 | 2026-09-21T04:13:18.870Z | auto\n\n- Removed the file: skill-card.md\n- No user-facing changes to commands, usage, or behavior\n- Documentation and usage instructions remain unchanged\n\nv1.1.0 | 2026-09-20T02:49:51.338Z | auto\n\n- Removed the sample skill-card.md file.\n- No changes to functionality or usage.\n\nv1.0.0 | 2026-09-19T11:19:46.837Z | auto\n\n- Removed the sample skill-card.md file.\n- No changes to functionality or documentation other than file cleanup.\n- Initial release version 1.0.0.\n\nv0.10.5 | 2026-09-15T19:23:52.679Z | auto\n\n- Removed the file: skill-card.md.\n- No functional or user-facing changes; this release is metadata/packaging only.\n\nv0.10.4 | 2026-09-14T14:09:09.393Z | auto\n\n- Removed the file: skill-card.md\n- No changes to functionality or documentation content.\n- This is a minor maintenance update cleaning up documentation artifacts.\n\nv0.10.3 | 2026-09-10T17:53:05.821Z | auto\n\n- Removed the file: skill-card.md\n- No changes to functionality or documentation beyond this file removal\n\nv0.10.2 | 2026-09-09T21:19:38.366Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional changes to the honeybook-fpx skill itself.\n\nv0.10.1 | 2026-09-05T00:53:54.363Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional changes to the skill itself.\n\nv0.10.0 | 2026-09-04T22:23:32.099Z | auto\n\n- Removed the file: `skill-card.md`.\n- No changes were made to core functionality or documentation in SKILL.md.\n- Maintenance update to clean up unused files.\n\nv0.9.0 | 2026-09-02T23:01:21.038Z | auto\n\n- Expanded documentation with detailed examples of additional supported HoneyBook API endpoints (projects, events, tasks, notes, attachments, payments, feed items).\n- Added explanation of how to send messages (two-step pending task flow) and guidance to prefer MCP's `send_message` for previews.\n- Removed the obsolete skill-card.md file.\n- No changes to core functionality; documentation improvements only.\n\nv0.8.2 | 2026-08-31T21:24:39.265Z | auto\n\n- Removed the sample documentation file skill-card.md.  \n- No changes to skill logic or functionality.  \n- All usage instructions and technical details remain unchanged.\n\nv0.8.1 | 2026-08-31T20:51:06.041Z | auto\n\n- Removed the file: skill-card.md.\n- No changes to functionality or usage; documentation and code remain the same.\n\nv0.8.0 | 2026-08-31T20:15:47.863Z | auto\n\n- Removed the file: skill-card.md\n- No functional or usage changes; this is a documentation cleanup only.\n\nv0.7.1 | 2026-08-31T15:50:22.337Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes were made to the implementation or documentation in SKILL.md.\n\nv0.7.0 | 2026-08-31T00:22:35.224Z | auto\n\n- Removed the file: skill-card.md\n- No other functionality or documentation changes in this release.\n\nv0.6.0 | 2026-08-29T13:54:34.852Z | auto\n\n- Removed the sample skill-card.md file to clean up redundant documentation.\n- No changes to code or functionality; only file cleanup in this release.\n\nv0.5.0 | 2026-08-28T11:34:42.625Z | auto\n\n- Initial release of honeybook-fpx for reading HoneyBook client-portal data using fpx CLI and curl, without requiring the honeybook-mcp server.\n- Supports one-time session capture from a signed-in browser tab via the Transporter extension; subsequent data access uses plain curl.\n- Handles contracts, invoices, proposals, payment methods, and workspace status by extracting session and user tokens from localStorage and crafting authenticated API requests.\n- Documents error handling, rate limiting, API version management, and required call headers for successful automation.\n- Provides detailed setup instructions and ready-to-use shell command patterns for HoneyBook API access outside the official server environment.\n\nArchive index:\n\nArchive v1.2.6: 4 files, 8105 bytes\n\nFiles: references/requests.md (4643b), skill-card.md (2177b), SKILL.md (9165b), _meta.json (132b)\n\nFile v1.2.6:SKILL.md\n\n---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases):\nChrome loads the chrome zip unpacked; Safari isn't available yet, so use\nChrome for now), its Chrome **Site access** allowing both `honeybook.com` and\n`hbportal.co`, and a vendor magic-link URL already open (signed in) in that\nbrowser. Pairing persists — after the first approval every later `fpx` call\nreuses it.\n\nContextMint Bridge is the fetchproxy extension renamed, same maintainer (see [fetchproxy#extension](https://github.com/chrischall/fetchproxy#extension)); source at [nullnet-app/contextmint-bridge](https://github.com/nullnet-app/contextmint-bridge) — build it yourself or verify a release zip with `shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256`.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n   ContextMint Bridge installed. This signs you into `<vendor>.hbportal.co`.\n2. **While that tab is open**, run:\n\n```sh\nfpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json\n```\n\nThe tab only has to be **open and signed in**. Nothing is sniffed off a live\nrequest, so it does not matter whether the page has gone idle.\n\n3. Extract the fields `client.ts` needs (each localStorage value is one raw\n   JSON string — parse it with `fromjson`):\n\n```sh\nAUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin\n```\n\nIf `AUTH_TOKEN` or `USER_ID` comes back empty/`null`, the capture didn't see\nwhat it needed — re-open the magic link and re-run step 2.\n\nWith more than one vendor tab open at once, disambiguate with\n`--storage-subdomain <vendor>` (e.g. `--storage-subdomain acme`).\n\n4. Get the current API version (`client.ts`'s `fetchApiVersion` parses this\n   same endpoint):\n\n```sh\nAPI_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')\n```\n\n## Core call pattern\n\nEvery real request carries the same headers\n(`client.ts`'s `HoneyBookClient.request`). Only `hb-api-auth-token`,\n`hb-api-user-id` and a current `hb-api-client-version` are load-bearing —\n`hb-trusted-device` is optional and `hb-api-fingerprint` is no longer\nrequired at all:\n\n```sh\ncurl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'\n```\n\n`hb-api-duplicate-calls-prevention-uuid` must be a **fresh random UUID on\nevery request** — the MCP mints one with `crypto.randomUUID()` per call, not\nonce per session. Reusing a value risks HoneyBook treating a legitimate\nrepeat as a duplicate.\n\nReady-to-run commands for all four read endpoints are in\n`references/requests.md`.\n\n## Other reads the portal makes (same headers)\n\n```sh\n# Projects (\"events\") and the workspace id each one carries\ncurl -s \"https://api.honeybook.com/api/v2/client/events\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/events/$EVENT_ID/details\" \"${HB_HEADERS[@]}\"\n# The feed: messages (feed_message / workspace_email / workspace_file_email) and activity\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/feed\" \"${HB_HEADERS[@]}\" \\\n  | jq '.feed.feed_items[] | select(.type|test(\"email|message\")) | {id:._id, subject:.data.subject, from:.sender_id, sent:.data.sent_on}'\n# Tasks (curr_date MUST be MM/DD/YYYY), notes, loose files, payment schedule\ncurl -s \"https://api.honeybook.com/api/v2/tasks/workspaces/$WS_ID?page=1&perPage=30&sort_by=due_date&sort_desc=false&curr_date=09%2F02%2F2026\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/notes/workspace/$WS_ID\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/attachments\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/payments\" \"${HB_HEADERS[@]}\"\n```\n\nSending a message is a two-step \"client pending task\", not a POST of the\nmessage: `POST /api/v2/client_pending_task` with\n`{\"task_type\":\"send_workspace_message\",\"task_data\":{\"ws_id\":…,\"subject\":…,\"html_body\":…,\"force\":false,\"general_files\":[],\"image_files\":[],\"flow_attachments\":[]}}`\nreturns `{task_id}`; poll `GET /api/v2/client_pending_tasks?task_ids[]=<id>`\nuntil `pending_task_state_cd` is 2 (Finished) or 3 (Aborted). It emails the\nvendor for real — prefer the MCP's `send_message`, which previews first.\n\n## The rules that matter\n\n- **401, or 404 with an `HBUnauthorizedError` body → session expired.** A\n  revoked token does not reliably come back as 401, so check the body type\n  before concluding a resource is missing. Re-run the capture (magic link tab must still\n  be open and signed in).\n- **429 → rate limited.** `client.ts` waits 2s and retries once; do the same\n  before giving up.\n- **Body contains `\"HBWrongAPIVersionError\"` → stale `hb-api-client-version`.**\n  The error body itself carries the correct value at\n  `.error_data.server_api_version` — read that (or re-run the `/api/gon`\n  fetch above) and retry the SAME request with the fresh version.\n- **`sign_contract` / `pay_invoice` are not real API calls.** `honeybook-mcp`\n  can't replay HoneyBook's browser-side signing/SCA flow, so those tools just\n  return a deep link — `$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement`\n  (sign) or `/invoice` (pay) — for the user to open themselves. There's no\n  POST body to transcribe for either; don't invent one.\n\n## Output / exit-code contract\n\n- `fpx session`/`fpx pair`/`fpx health` are bridge round-trips: exit `0` on a\n  successful bridge read regardless of upstream status, `1` on a usage error\n  (bad flag, undeclared scope), `2` if the bridge/extension is unreachable or\n  pairing is still pending. There's no bot-wall (`3`)/upstream-HTTP (`4`)\n  exit code on these — HoneyBook's own API isn't bridge-walled.\n- The actual reads go through plain `curl` afterward — check the HTTP status\n  and the `HBWrongAPIVersionError` body text yourself, as above.\n\n## Notes\n\n- Session data (`AUTH_TOKEN`, `TRUSTED_DEVICE`) is opaque and\n  long-lived server-side (no client-visible JWT expiry) — keep it in shell\n  variables, not a world-readable file, if you must persist it at all.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.6:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.2.6\",\n  \"publishedAt\": 1791588414915\n}\n\nFile v1.2.6:references/requests.md\n\n# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\nIf `last_page` is `false`, more results exist on later pages. The MCP\nrequests `?page=2`, `?page=3`, … (the parameter `/api/v2/client/events`\ntakes) until `last_page` is true, and reports `complete: false` if a page\nbrings nothing new; do the same here if you need every file.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, type, couple_names}),\n  total_price: .vendor_proposal.total_price,\n  payments: (.payments_container.payments // [] | map({due_date, amount, is_paid}))\n}' /tmp/hb-file.json\n\n# Full contract text + signatures (the \"agreement\" section)\njq '.agreement' /tmp/hb-file.json\n\n# Full line items (the \"pricing\" section)\njq '.vendor_proposal' /tmp/hb-file.json\n```\n\n## 3. Get a workspace (vendor project)\n\n`get_workspace` (`src/tools/workspaces.ts`) — `workspace_id` is\n`.workspace._id` on any workspace_file from endpoint 1 or 2:\n\n```sh\nhb_get \"/api/v2/workspaces/$WORKSPACE_ID\" \\\n  | jq '{id: .[\"_id\"], has_sent_files, has_signed_files, has_paid_payments}'\n```\n\n## 4. List saved payment methods\n\n`list_payment_methods` (`src/tools/payment_methods.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/payment_methods\" | jq '.'\n```\n\nEmpty array (`[]`) if the client has no payment method saved with this\nvendor — not an error.\n\n---\n\n## Error shapes to check on every response\n\n```sh\n# Wrong API version — re-derive it from the error body itself (no /api/gon round-trip needed)\njq -r 'select(type==\"object\") | .error_data.server_api_version // empty' /tmp/hb-resp.json\n\n# HTTP status: 401 = session expired (re-capture), 429 = rate limited (wait 2s, retry once).\n# A 404 whose body names HBUnauthorizedError is ALSO an expired session, not a\n# missing resource — a revoked token does not reliably come back as 401.\n```\n\nA non-2xx HTTP status with a body matching `HBWrongAPIVersionError` means\nretry the *same* request with `hb-api-client-version` set to\n`.error_data.server_api_version` from the body (or a fresh `/api/gon` fetch,\nper `../SKILL.md`).\n\nFile v1.2.6:skill-card.md\n\n## Description:\n\nGuides agents in reading HoneyBook client-portal contracts, invoices, proposals, payment methods, and workspace status using a captured browser session and shell commands without the HoneyBook MCP server.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to inspect HoneyBook client-portal files, project status, and saved payment methods from a shell without running the MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Captured HoneyBook sessions expose credentials and private portal data to shell-level access.\n\nMitigation: Use only in a trusted environment; restrict session captures to private, owner-only temporary storage and delete them promptly.\n\nRisk: Full API responses can contain sensitive client and vendor information.\n\nMitigation: Select only the fields needed and avoid storing raw responses in shared temporary directories.\n\nRisk: The included message-sending workflow emails a real vendor despite the skill's read-oriented purpose.\n\nMitigation: Do not send messages unless explicitly authorized; preview the content before sending.\n\n## Reference(s):\n\n- [HoneyBook FPX on ClawHub](https://clawhub.ai/chrischall/skills/honeybook-fpx)\n- [Request examples](references/requests.md)\n- [ContextMint Bridge source](https://github.com/nullnet-app/contextmint-bridge)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown with shell examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Read responses can contain sensitive client and vendor data.]\n\n## Skill Version(s):\n\n1.2.6 (source: ClawHub release)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.5: 4 files, 8017 bytes\n\nFiles: references/requests.md (4523b), skill-card.md (2104b), SKILL.md (9165b), _meta.json (132b)\n\nFile v1.2.5:SKILL.md\n\n---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases):\nChrome loads the chrome zip unpacked; Safari isn't available yet, so use\nChrome for now), its Chrome **Site access** allowing both `honeybook.com` and\n`hbportal.co`, and a vendor magic-link URL already open (signed in) in that\nbrowser. Pairing persists — after the first approval every later `fpx` call\nreuses it.\n\nContextMint Bridge is the fetchproxy extension renamed, same maintainer (see [fetchproxy#extension](https://github.com/chrischall/fetchproxy#extension)); source at [nullnet-app/contextmint-bridge](https://github.com/nullnet-app/contextmint-bridge) — build it yourself or verify a release zip with `shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256`.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n   ContextMint Bridge installed. This signs you into `<vendor>.hbportal.co`.\n2. **While that tab is open**, run:\n\n```sh\nfpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json\n```\n\nThe tab only has to be **open and signed in**. Nothing is sniffed off a live\nrequest, so it does not matter whether the page has gone idle.\n\n3. Extract the fields `client.ts` needs (each localStorage value is one raw\n   JSON string — parse it with `fromjson`):\n\n```sh\nAUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin\n```\n\nIf `AUTH_TOKEN` or `USER_ID` comes back empty/`null`, the capture didn't see\nwhat it needed — re-open the magic link and re-run step 2.\n\nWith more than one vendor tab open at once, disambiguate with\n`--storage-subdomain <vendor>` (e.g. `--storage-subdomain acme`).\n\n4. Get the current API version (`client.ts`'s `fetchApiVersion` parses this\n   same endpoint):\n\n```sh\nAPI_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')\n```\n\n## Core call pattern\n\nEvery real request carries the same headers\n(`client.ts`'s `HoneyBookClient.request`). Only `hb-api-auth-token`,\n`hb-api-user-id` and a current `hb-api-client-version` are load-bearing —\n`hb-trusted-device` is optional and `hb-api-fingerprint` is no longer\nrequired at all:\n\n```sh\ncurl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'\n```\n\n`hb-api-duplicate-calls-prevention-uuid` must be a **fresh random UUID on\nevery request** — the MCP mints one with `crypto.randomUUID()` per call, not\nonce per session. Reusing a value risks HoneyBook treating a legitimate\nrepeat as a duplicate.\n\nReady-to-run commands for all four read endpoints are in\n`references/requests.md`.\n\n## Other reads the portal makes (same headers)\n\n```sh\n# Projects (\"events\") and the workspace id each one carries\ncurl -s \"https://api.honeybook.com/api/v2/client/events\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/events/$EVENT_ID/details\" \"${HB_HEADERS[@]}\"\n# The feed: messages (feed_message / workspace_email / workspace_file_email) and activity\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/feed\" \"${HB_HEADERS[@]}\" \\\n  | jq '.feed.feed_items[] | select(.type|test(\"email|message\")) | {id:._id, subject:.data.subject, from:.sender_id, sent:.data.sent_on}'\n# Tasks (curr_date MUST be MM/DD/YYYY), notes, loose files, payment schedule\ncurl -s \"https://api.honeybook.com/api/v2/tasks/workspaces/$WS_ID?page=1&perPage=30&sort_by=due_date&sort_desc=false&curr_date=09%2F02%2F2026\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/notes/workspace/$WS_ID\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/attachments\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/payments\" \"${HB_HEADERS[@]}\"\n```\n\nSending a message is a two-step \"client pending task\", not a POST of the\nmessage: `POST /api/v2/client_pending_task` with\n`{\"task_type\":\"send_workspace_message\",\"task_data\":{\"ws_id\":…,\"subject\":…,\"html_body\":…,\"force\":false,\"general_files\":[],\"image_files\":[],\"flow_attachments\":[]}}`\nreturns `{task_id}`; poll `GET /api/v2/client_pending_tasks?task_ids[]=<id>`\nuntil `pending_task_state_cd` is 2 (Finished) or 3 (Aborted). It emails the\nvendor for real — prefer the MCP's `send_message`, which previews first.\n\n## The rules that matter\n\n- **401, or 404 with an `HBUnauthorizedError` body → session expired.** A\n  revoked token does not reliably come back as 401, so check the body type\n  before concluding a resource is missing. Re-run the capture (magic link tab must still\n  be open and signed in).\n- **429 → rate limited.** `client.ts` waits 2s and retries once; do the same\n  before giving up.\n- **Body contains `\"HBWrongAPIVersionError\"` → stale `hb-api-client-version`.**\n  The error body itself carries the correct value at\n  `.error_data.server_api_version` — read that (or re-run the `/api/gon`\n  fetch above) and retry the SAME request with the fresh version.\n- **`sign_contract` / `pay_invoice` are not real API calls.** `honeybook-mcp`\n  can't replay HoneyBook's browser-side signing/SCA flow, so those tools just\n  return a deep link — `$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement`\n  (sign) or `/invoice` (pay) — for the user to open themselves. There's no\n  POST body to transcribe for either; don't invent one.\n\n## Output / exit-code contract\n\n- `fpx session`/`fpx pair`/`fpx health` are bridge round-trips: exit `0` on a\n  successful bridge read regardless of upstream status, `1` on a usage error\n  (bad flag, undeclared scope), `2` if the bridge/extension is unreachable or\n  pairing is still pending. There's no bot-wall (`3`)/upstream-HTTP (`4`)\n  exit code on these — HoneyBook's own API isn't bridge-walled.\n- The actual reads go through plain `curl` afterward — check the HTTP status\n  and the `HBWrongAPIVersionError` body text yourself, as above.\n\n## Notes\n\n- Session data (`AUTH_TOKEN`, `TRUSTED_DEVICE`) is opaque and\n  long-lived server-side (no client-visible JWT expiry) — keep it in shell\n  variables, not a world-readable file, if you must persist it at all.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.5:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.2.5\",\n  \"publishedAt\": 1791380133297\n}\n\nFile v1.2.5:references/requests.md\n\n# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\n**Pagination is not wired up** in the MCP either — if `last_page` is\n`false`, more results exist on later pages that neither the MCP nor this\nskill fetches.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, type, couple_names}),\n  total_price: .vendor_proposal.total_price,\n  payments: (.payments_container.payments // [] | map({due_date, amount, is_paid}))\n}' /tmp/hb-file.json\n\n# Full contract text + signatures (the \"agreement\" section)\njq '.agreement' /tmp/hb-file.json\n\n# Full line items (the \"pricing\" section)\njq '.vendor_proposal' /tmp/hb-file.json\n```\n\n## 3. Get a workspace (vendor project)\n\n`get_workspace` (`src/tools/workspaces.ts`) — `workspace_id` is\n`.workspace._id` on any workspace_file from endpoint 1 or 2:\n\n```sh\nhb_get \"/api/v2/workspaces/$WORKSPACE_ID\" \\\n  | jq '{id: .[\"_id\"], has_sent_files, has_signed_files, has_paid_payments}'\n```\n\n## 4. List saved payment methods\n\n`list_payment_methods` (`src/tools/payment_methods.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/payment_methods\" | jq '.'\n```\n\nEmpty array (`[]`) if the client has no payment method saved with this\nvendor — not an error.\n\n---\n\n## Error shapes to check on every response\n\n```sh\n# Wrong API version — re-derive it from the error body itself (no /api/gon round-trip needed)\njq -r 'select(type==\"object\") | .error_data.server_api_version // empty' /tmp/hb-resp.json\n\n# HTTP status: 401 = session expired (re-capture), 429 = rate limited (wait 2s, retry once).\n# A 404 whose body names HBUnauthorizedError is ALSO an expired session, not a\n# missing resource — a revoked token does not reliably come back as 401.\n```\n\nA non-2xx HTTP status with a body matching `HBWrongAPIVersionError` means\nretry the *same* request with `hb-api-client-version` set to\n`.error_data.server_api_version` from the body (or a fresh `/api/gon` fetch,\nper `../SKILL.md`).\n\nFile v1.2.5:skill-card.md\n\n## Description:\n\nGuides agents in reading HoneyBook client-portal contracts, invoices, proposals, payment methods, and workspace status using a browser-captured session and shell commands without the HoneyBook MCP server.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to inspect a signed-in client's HoneyBook portal records from the shell when the HoneyBook MCP server is unavailable or unnecessary.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Captured browser session tokens could grant access to sensitive client-portal data if exposed.\n\nMitigation: Use a trusted machine, avoid saving tokens or responses to shared temporary files, and promptly delete any captured session files.\n\nRisk: The skill describes a message-sending path that can email a vendor despite its read-oriented purpose.\n\nMitigation: Require explicit human review and approval before sending any message or running other write actions.\n\n## Reference(s):\n\n- [HoneyBook FPX ClawHub release](https://clawhub.ai/chrischall/skills/honeybook-fpx)\n- [HoneyBook request examples](references/requests.md)\n- [ContextMint Bridge source](https://github.com/nullnet-app/contextmint-bridge)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell commands and JSON query examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Read-only examples cover portal files, workspaces, and payment methods; message sending is a separate external action.]\n\n## Skill Version(s):\n\n1.2.5 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.4: 4 files, 8118 bytes\n\nFiles: references/requests.md (4523b), skill-card.md (2331b), SKILL.md (9165b), _meta.json (132b)\n\nFile v1.2.4:SKILL.md\n\n---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases):\nChrome loads the chrome zip unpacked; Safari isn't available yet, so use\nChrome for now), its Chrome **Site access** allowing both `honeybook.com` and\n`hbportal.co`, and a vendor magic-link URL already open (signed in) in that\nbrowser. Pairing persists — after the first approval every later `fpx` call\nreuses it.\n\nContextMint Bridge is the fetchproxy extension renamed, same maintainer (see [fetchproxy#extension](https://github.com/chrischall/fetchproxy#extension)); source at [nullnet-app/contextmint-bridge](https://github.com/nullnet-app/contextmint-bridge) — build it yourself or verify a release zip with `shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256`.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n   ContextMint Bridge installed. This signs you into `<vendor>.hbportal.co`.\n2. **While that tab is open**, run:\n\n```sh\nfpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json\n```\n\nThe tab only has to be **open and signed in**. Nothing is sniffed off a live\nrequest, so it does not matter whether the page has gone idle.\n\n3. Extract the fields `client.ts` needs (each localStorage value is one raw\n   JSON string — parse it with `fromjson`):\n\n```sh\nAUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin\n```\n\nIf `AUTH_TOKEN` or `USER_ID` comes back empty/`null`, the capture didn't see\nwhat it needed — re-open the magic link and re-run step 2.\n\nWith more than one vendor tab open at once, disambiguate with\n`--storage-subdomain <vendor>` (e.g. `--storage-subdomain acme`).\n\n4. Get the current API version (`client.ts`'s `fetchApiVersion` parses this\n   same endpoint):\n\n```sh\nAPI_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')\n```\n\n## Core call pattern\n\nEvery real request carries the same headers\n(`client.ts`'s `HoneyBookClient.request`). Only `hb-api-auth-token`,\n`hb-api-user-id` and a current `hb-api-client-version` are load-bearing —\n`hb-trusted-device` is optional and `hb-api-fingerprint` is no longer\nrequired at all:\n\n```sh\ncurl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'\n```\n\n`hb-api-duplicate-calls-prevention-uuid` must be a **fresh random UUID on\nevery request** — the MCP mints one with `crypto.randomUUID()` per call, not\nonce per session. Reusing a value risks HoneyBook treating a legitimate\nrepeat as a duplicate.\n\nReady-to-run commands for all four read endpoints are in\n`references/requests.md`.\n\n## Other reads the portal makes (same headers)\n\n```sh\n# Projects (\"events\") and the workspace id each one carries\ncurl -s \"https://api.honeybook.com/api/v2/client/events\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/events/$EVENT_ID/details\" \"${HB_HEADERS[@]}\"\n# The feed: messages (feed_message / workspace_email / workspace_file_email) and activity\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/feed\" \"${HB_HEADERS[@]}\" \\\n  | jq '.feed.feed_items[] | select(.type|test(\"email|message\")) | {id:._id, subject:.data.subject, from:.sender_id, sent:.data.sent_on}'\n# Tasks (curr_date MUST be MM/DD/YYYY), notes, loose files, payment schedule\ncurl -s \"https://api.honeybook.com/api/v2/tasks/workspaces/$WS_ID?page=1&perPage=30&sort_by=due_date&sort_desc=false&curr_date=09%2F02%2F2026\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/notes/workspace/$WS_ID\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/attachments\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/payments\" \"${HB_HEADERS[@]}\"\n```\n\nSending a message is a two-step \"client pending task\", not a POST of the\nmessage: `POST /api/v2/client_pending_task` with\n`{\"task_type\":\"send_workspace_message\",\"task_data\":{\"ws_id\":…,\"subject\":…,\"html_body\":…,\"force\":false,\"general_files\":[],\"image_files\":[],\"flow_attachments\":[]}}`\nreturns `{task_id}`; poll `GET /api/v2/client_pending_tasks?task_ids[]=<id>`\nuntil `pending_task_state_cd` is 2 (Finished) or 3 (Aborted). It emails the\nvendor for real — prefer the MCP's `send_message`, which previews first.\n\n## The rules that matter\n\n- **401, or 404 with an `HBUnauthorizedError` body → session expired.** A\n  revoked token does not reliably come back as 401, so check the body type\n  before concluding a resource is missing. Re-run the capture (magic link tab must still\n  be open and signed in).\n- **429 → rate limited.** `client.ts` waits 2s and retries once; do the same\n  before giving up.\n- **Body contains `\"HBWrongAPIVersionError\"` → stale `hb-api-client-version`.**\n  The error body itself carries the correct value at\n  `.error_data.server_api_version` — read that (or re-run the `/api/gon`\n  fetch above) and retry the SAME request with the fresh version.\n- **`sign_contract` / `pay_invoice` are not real API calls.** `honeybook-mcp`\n  can't replay HoneyBook's browser-side signing/SCA flow, so those tools just\n  return a deep link — `$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement`\n  (sign) or `/invoice` (pay) — for the user to open themselves. There's no\n  POST body to transcribe for either; don't invent one.\n\n## Output / exit-code contract\n\n- `fpx session`/`fpx pair`/`fpx health` are bridge round-trips: exit `0` on a\n  successful bridge read regardless of upstream status, `1` on a usage error\n  (bad flag, undeclared scope), `2` if the bridge/extension is unreachable or\n  pairing is still pending. There's no bot-wall (`3`)/upstream-HTTP (`4`)\n  exit code on these — HoneyBook's own API isn't bridge-walled.\n- The actual reads go through plain `curl` afterward — check the HTTP status\n  and the `HBWrongAPIVersionError` body text yourself, as above.\n\n## Notes\n\n- Session data (`AUTH_TOKEN`, `TRUSTED_DEVICE`) is opaque and\n  long-lived server-side (no client-visible JWT expiry) — keep it in shell\n  variables, not a world-readable file, if you must persist it at all.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.2.4\",\n  \"publishedAt\": 1791168637130\n}\n\nFile v1.2.4:references/requests.md\n\n# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\n**Pagination is not wired up** in the MCP either — if `last_page` is\n`false`, more results exist on later pages that neither the MCP nor this\nskill fetches.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, type, couple_names}),\n  total_price: .vendor_proposal.total_price,\n  payments: (.payments_container.payments // [] | map({due_date, amount, is_paid}))\n}' /tmp/hb-file.json\n\n# Full contract text + signatures (the \"agreement\" section)\njq '.agreement' /tmp/hb-file.json\n\n# Full line items (the \"pricing\" section)\njq '.vendor_proposal' /tmp/hb-file.json\n```\n\n## 3. Get a workspace (vendor project)\n\n`get_workspace` (`src/tools/workspaces.ts`) — `workspace_id` is\n`.workspace._id` on any workspace_file from endpoint 1 or 2:\n\n```sh\nhb_get \"/api/v2/workspaces/$WORKSPACE_ID\" \\\n  | jq '{id: .[\"_id\"], has_sent_files, has_signed_files, has_paid_payments}'\n```\n\n## 4. List saved payment methods\n\n`list_payment_methods` (`src/tools/payment_methods.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/payment_methods\" | jq '.'\n```\n\nEmpty array (`[]`) if the client has no payment method saved with this\nvendor — not an error.\n\n---\n\n## Error shapes to check on every response\n\n```sh\n# Wrong API version — re-derive it from the error body itself (no /api/gon round-trip needed)\njq -r 'select(type==\"object\") | .error_data.server_api_version // empty' /tmp/hb-resp.json\n\n# HTTP status: 401 = session expired (re-capture), 429 = rate limited (wait 2s, retry once).\n# A 404 whose body names HBUnauthorizedError is ALSO an expired session, not a\n# missing resource — a revoked token does not reliably come back as 401.\n```\n\nA non-2xx HTTP status with a body matching `HBWrongAPIVersionError` means\nretry the *same* request with `hb-api-client-version` set to\n`.error_data.server_api_version` from the body (or a fresh `/api/gon` fetch,\nper `../SKILL.md`).\n\nFile v1.2.4:skill-card.md\n\n## Description:\n\nGuides agents in reading HoneyBook client-portal contracts, invoices, proposals, payment methods, and workspace data using a browser session and shell commands without the HoneyBook MCP server.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other authorized HoneyBook users can capture their signed-in portal session and retrieve client files, workspace details, and saved payment-method information through shell-based API requests.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Capturing and replaying a live session exposes an account token, user ID, and client data to the agent and browser-bridge tooling.\n\nMitigation: Use only with an account you are authorized to access and only if you trust the agent and browser bridge; limit access to the live session.\n\nRisk: The temporary session file and request headers contain reusable credentials.\n\nMitigation: Treat them as account credentials, avoid shared machines, keep them out of logs, and promptly remove temporary files.\n\nRisk: The documented messaging flow sends a real email to a vendor.\n\nMitigation: Do not run the messaging flow unless you intend to send the email; review its recipient and content first.\n\n## Reference(s):\n\n- [HoneyBook FPX release on ClawHub](https://clawhub.ai/chrischall/skills/honeybook-fpx)\n- [HoneyBook request examples](references/requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [Fetchproxy extension documentation](https://github.com/chrischall/fetchproxy#extension)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands can return sensitive HoneyBook client data as JSON; the messaging example can send a real email.]\n\n## Skill Version(s):\n\n1.2.4 (source: server-resolved release)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.3: 4 files, 8080 bytes\n\nFiles: references/requests.md (4523b), skill-card.md (2293b), SKILL.md (9165b), _meta.json (132b)\n\nFile v1.2.3:SKILL.md\n\n---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases):\nChrome loads the chrome zip unpacked; Safari isn't available yet, so use\nChrome for now), its Chrome **Site access** allowing both `honeybook.com` and\n`hbportal.co`, and a vendor magic-link URL already open (signed in) in that\nbrowser. Pairing persists — after the first approval every later `fpx` call\nreuses it.\n\nContextMint Bridge is the fetchproxy extension renamed, same maintainer (see [fetchproxy#extension](https://github.com/chrischall/fetchproxy#extension)); source at [nullnet-app/contextmint-bridge](https://github.com/nullnet-app/contextmint-bridge) — build it yourself or verify a release zip with `shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256`.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n   ContextMint Bridge installed. This signs you into `<vendor>.hbportal.co`.\n2. **While that tab is open**, run:\n\n```sh\nfpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json\n```\n\nThe tab only has to be **open and signed in**. Nothing is sniffed off a live\nrequest, so it does not matter whether the page has gone idle.\n\n3. Extract the fields `client.ts` needs (each localStorage value is one raw\n   JSON string — parse it with `fromjson`):\n\n```sh\nAUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin\n```\n\nIf `AUTH_TOKEN` or `USER_ID` comes back empty/`null`, the capture didn't see\nwhat it needed — re-open the magic link and re-run step 2.\n\nWith more than one vendor tab open at once, disambiguate with\n`--storage-subdomain <vendor>` (e.g. `--storage-subdomain acme`).\n\n4. Get the current API version (`client.ts`'s `fetchApiVersion` parses this\n   same endpoint):\n\n```sh\nAPI_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')\n```\n\n## Core call pattern\n\nEvery real request carries the same headers\n(`client.ts`'s `HoneyBookClient.request`). Only `hb-api-auth-token`,\n`hb-api-user-id` and a current `hb-api-client-version` are load-bearing —\n`hb-trusted-device` is optional and `hb-api-fingerprint` is no longer\nrequired at all:\n\n```sh\ncurl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'\n```\n\n`hb-api-duplicate-calls-prevention-uuid` must be a **fresh random UUID on\nevery request** — the MCP mints one with `crypto.randomUUID()` per call, not\nonce per session. Reusing a value risks HoneyBook treating a legitimate\nrepeat as a duplicate.\n\nReady-to-run commands for all four read endpoints are in\n`references/requests.md`.\n\n## Other reads the portal makes (same headers)\n\n```sh\n# Projects (\"events\") and the workspace id each one carries\ncurl -s \"https://api.honeybook.com/api/v2/client/events\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/events/$EVENT_ID/details\" \"${HB_HEADERS[@]}\"\n# The feed: messages (feed_message / workspace_email / workspace_file_email) and activity\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/feed\" \"${HB_HEADERS[@]}\" \\\n  | jq '.feed.feed_items[] | select(.type|test(\"email|message\")) | {id:._id, subject:.data.subject, from:.sender_id, sent:.data.sent_on}'\n# Tasks (curr_date MUST be MM/DD/YYYY), notes, loose files, payment schedule\ncurl -s \"https://api.honeybook.com/api/v2/tasks/workspaces/$WS_ID?page=1&perPage=30&sort_by=due_date&sort_desc=false&curr_date=09%2F02%2F2026\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/notes/workspace/$WS_ID\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/attachments\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/payments\" \"${HB_HEADERS[@]}\"\n```\n\nSending a message is a two-step \"client pending task\", not a POST of the\nmessage: `POST /api/v2/client_pending_task` with\n`{\"task_type\":\"send_workspace_message\",\"task_data\":{\"ws_id\":…,\"subject\":…,\"html_body\":…,\"force\":false,\"general_files\":[],\"image_files\":[],\"flow_attachments\":[]}}`\nreturns `{task_id}`; poll `GET /api/v2/client_pending_tasks?task_ids[]=<id>`\nuntil `pending_task_state_cd` is 2 (Finished) or 3 (Aborted). It emails the\nvendor for real — prefer the MCP's `send_message`, which previews first.\n\n## The rules that matter\n\n- **401, or 404 with an `HBUnauthorizedError` body → session expired.** A\n  revoked token does not reliably come back as 401, so check the body type\n  before concluding a resource is missing. Re-run the capture (magic link tab must still\n  be open and signed in).\n- **429 → rate limited.** `client.ts` waits 2s and retries once; do the same\n  before giving up.\n- **Body contains `\"HBWrongAPIVersionError\"` → stale `hb-api-client-version`.**\n  The error body itself carries the correct value at\n  `.error_data.server_api_version` — read that (or re-run the `/api/gon`\n  fetch above) and retry the SAME request with the fresh version.\n- **`sign_contract` / `pay_invoice` are not real API calls.** `honeybook-mcp`\n  can't replay HoneyBook's browser-side signing/SCA flow, so those tools just\n  return a deep link — `$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement`\n  (sign) or `/invoice` (pay) — for the user to open themselves. There's no\n  POST body to transcribe for either; don't invent one.\n\n## Output / exit-code contract\n\n- `fpx session`/`fpx pair`/`fpx health` are bridge round-trips: exit `0` on a\n  successful bridge read regardless of upstream status, `1` on a usage error\n  (bad flag, undeclared scope), `2` if the bridge/extension is unreachable or\n  pairing is still pending. There's no bot-wall (`3`)/upstream-HTTP (`4`)\n  exit code on these — HoneyBook's own API isn't bridge-walled.\n- The actual reads go through plain `curl` afterward — check the HTTP status\n  and the `HBWrongAPIVersionError` body text yourself, as above.\n\n## Notes\n\n- Session data (`AUTH_TOKEN`, `TRUSTED_DEVICE`) is opaque and\n  long-lived server-side (no client-visible JWT expiry) — keep it in shell\n  variables, not a world-readable file, if you must persist it at all.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.2.3\",\n  \"publishedAt\": 1790991658332\n}\n\nFile v1.2.3:references/requests.md\n\n# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\n**Pagination is not wired up** in the MCP either — if `last_page` is\n`false`, more results exist on later pages that neither the MCP nor this\nskill fetches.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, type, couple_names}),\n  total_price: .vendor_proposal.total_price,\n  payments: (.payments_container.payments // [] | map({due_date, amount, is_paid}))\n}' /tmp/hb-file.json\n\n# Full contract text + signatures (the \"agreement\" section)\njq '.agreement' /tmp/hb-file.json\n\n# Full line items (the \"pricing\" section)\njq '.vendor_proposal' /tmp/hb-file.json\n```\n\n## 3. Get a workspace (vendor project)\n\n`get_workspace` (`src/tools/workspaces.ts`) — `workspace_id` is\n`.workspace._id` on any workspace_file from endpoint 1 or 2:\n\n```sh\nhb_get \"/api/v2/workspaces/$WORKSPACE_ID\" \\\n  | jq '{id: .[\"_id\"], has_sent_files, has_signed_files, has_paid_payments}'\n```\n\n## 4. List saved payment methods\n\n`list_payment_methods` (`src/tools/payment_methods.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/payment_methods\" | jq '.'\n```\n\nEmpty array (`[]`) if the client has no payment method saved with this\nvendor — not an error.\n\n---\n\n## Error shapes to check on every response\n\n```sh\n# Wrong API version — re-derive it from the error body itself (no /api/gon round-trip needed)\njq -r 'select(type==\"object\") | .error_data.server_api_version // empty' /tmp/hb-resp.json\n\n# HTTP status: 401 = session expired (re-capture), 429 = rate limited (wait 2s, retry once).\n# A 404 whose body names HBUnauthorizedError is ALSO an expired session, not a\n# missing resource — a revoked token does not reliably come back as 401.\n```\n\nA non-2xx HTTP status with a body matching `HBWrongAPIVersionError` means\nretry the *same* request with `hb-api-client-version` set to\n`.error_data.server_api_version` from the body (or a fresh `/api/gon` fetch,\nper `../SKILL.md`).\n\nFile v1.2.3:skill-card.md\n\n## Description:\n\nGuides agents in reading HoneyBook client-portal contracts, invoices, proposals, payment methods, and workspace status through a signed-in browser session and shell requests.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and HoneyBook clients with an active vendor portal session can inspect shared files, project status, and payment information from a shell without running an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Capturing a live HoneyBook session in a shared temporary file can expose long-lived credentials.\n\nMitigation: Use a private temporary directory with restrictive permissions, delete captures immediately, and never paste tokens into logs or chat.\n\nRisk: The workflow can access private client and business records in the active portal session.\n\nMitigation: Use only with an authorized session and limit or redact retrieved records before sharing.\n\nRisk: The documented message-sending request can email the vendor despite the skill's read-oriented description.\n\nMitigation: Do not run the message-sending POST flow unless explicitly intending to email the vendor; preview the message first.\n\n## Reference(s):\n\n- [HoneyBook request examples](references/requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [ContextMint Bridge source](https://github.com/nullnet-app/contextmint-bridge)\n- [Fetchproxy extension documentation](https://github.com/chrischall/fetchproxy#extension)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell commands and JSON query examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands can retrieve private HoneyBook business records; review outputs before sharing.]\n\n## Skill Version(s):\n\n1.2.3 (source: server-resolved release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.2: 4 files, 8015 bytes\n\nFiles: references/requests.md (4523b), skill-card.md (2074b), SKILL.md (9165b), _meta.json (132b)\n\nFile v1.2.2:SKILL.md\n\n---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases):\nChrome loads the chrome zip unpacked; Safari isn't available yet, so use\nChrome for now), its Chrome **Site access** allowing both `honeybook.com` and\n`hbportal.co`, and a vendor magic-link URL already open (signed in) in that\nbrowser. Pairing persists — after the first approval every later `fpx` call\nreuses it.\n\nContextMint Bridge is the fetchproxy extension renamed, same maintainer (see [fetchproxy#extension](https://github.com/chrischall/fetchproxy#extension)); source at [nullnet-app/contextmint-bridge](https://github.com/nullnet-app/contextmint-bridge) — build it yourself or verify a release zip with `shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256`.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n   ContextMint Bridge installed. This signs you into `<vendor>.hbportal.co`.\n2. **While that tab is open**, run:\n\n```sh\nfpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json\n```\n\nThe tab only has to be **open and signed in**. Nothing is sniffed off a live\nrequest, so it does not matter whether the page has gone idle.\n\n3. Extract the fields `client.ts` needs (each localStorage value is one raw\n   JSON string — parse it with `fromjson`):\n\n```sh\nAUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin\n```\n\nIf `AUTH_TOKEN` or `USER_ID` comes back empty/`null`, the capture didn't see\nwhat it needed — re-open the magic link and re-run step 2.\n\nWith more than one vendor tab open at once, disambiguate with\n`--storage-subdomain <vendor>` (e.g. `--storage-subdomain acme`).\n\n4. Get the current API version (`client.ts`'s `fetchApiVersion` parses this\n   same endpoint):\n\n```sh\nAPI_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')\n```\n\n## Core call pattern\n\nEvery real request carries the same headers\n(`client.ts`'s `HoneyBookClient.request`). Only `hb-api-auth-token`,\n`hb-api-user-id` and a current `hb-api-client-version` are load-bearing —\n`hb-trusted-device` is optional and `hb-api-fingerprint` is no longer\nrequired at all:\n\n```sh\ncurl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'\n```\n\n`hb-api-duplicate-calls-prevention-uuid` must be a **fresh random UUID on\nevery request** — the MCP mints one with `crypto.randomUUID()` per call, not\nonce per session. Reusing a value risks HoneyBook treating a legitimate\nrepeat as a duplicate.\n\nReady-to-run commands for all four read endpoints are in\n`references/requests.md`.\n\n## Other reads the portal makes (same headers)\n\n```sh\n# Projects (\"events\") and the workspace id each one carries\ncurl -s \"https://api.honeybook.com/api/v2/client/events\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/events/$EVENT_ID/details\" \"${HB_HEADERS[@]}\"\n# The feed: messages (feed_message / workspace_email / workspace_file_email) and activity\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/feed\" \"${HB_HEADERS[@]}\" \\\n  | jq '.feed.feed_items[] | select(.type|test(\"email|message\")) | {id:._id, subject:.data.subject, from:.sender_id, sent:.data.sent_on}'\n# Tasks (curr_date MUST be MM/DD/YYYY), notes, loose files, payment schedule\ncurl -s \"https://api.honeybook.com/api/v2/tasks/workspaces/$WS_ID?page=1&perPage=30&sort_by=due_date&sort_desc=false&curr_date=09%2F02%2F2026\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/notes/workspace/$WS_ID\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/attachments\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/payments\" \"${HB_HEADERS[@]}\"\n```\n\nSending a message is a two-step \"client pending task\", not a POST of the\nmessage: `POST /api/v2/client_pending_task` with\n`{\"task_type\":\"send_workspace_message\",\"task_data\":{\"ws_id\":…,\"subject\":…,\"html_body\":…,\"force\":false,\"general_files\":[],\"image_files\":[],\"flow_attachments\":[]}}`\nreturns `{task_id}`; poll `GET /api/v2/client_pending_tasks?task_ids[]=<id>`\nuntil `pending_task_state_cd` is 2 (Finished) or 3 (Aborted). It emails the\nvendor for real — prefer the MCP's `send_message`, which previews first.\n\n## The rules that matter\n\n- **401, or 404 with an `HBUnauthorizedError` body → session expired.** A\n  revoked token does not reliably come back as 401, so check the body type\n  before concluding a resource is missing. Re-run the capture (magic link tab must still\n  be open and signed in).\n- **429 → rate limited.** `client.ts` waits 2s and retries once; do the same\n  before giving up.\n- **Body contains `\"HBWrongAPIVersionError\"` → stale `hb-api-client-version`.**\n  The error body itself carries the correct value at\n  `.error_data.server_api_version` — read that (or re-run the `/api/gon`\n  fetch above) and retry the SAME request with the fresh version.\n- **`sign_contract` / `pay_invoice` are not real API calls.** `honeybook-mcp`\n  can't replay HoneyBook's browser-side signing/SCA flow, so those tools just\n  return a deep link — `$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement`\n  (sign) or `/invoice` (pay) — for the user to open themselves. There's no\n  POST body to transcribe for either; don't invent one.\n\n## Output / exit-code contract\n\n- `fpx session`/`fpx pair`/`fpx health` are bridge round-trips: exit `0` on a\n  successful bridge read regardless of upstream status, `1` on a usage error\n  (bad flag, undeclared scope), `2` if the bridge/extension is unreachable or\n  pairing is still pending. There's no bot-wall (`3`)/upstream-HTTP (`4`)\n  exit code on these — HoneyBook's own API isn't bridge-walled.\n- The actual reads go through plain `curl` afterward — check the HTTP status\n  and the `HBWrongAPIVersionError` body text yourself, as above.\n\n## Notes\n\n- Session data (`AUTH_TOKEN`, `TRUSTED_DEVICE`) is opaque and\n  long-lived server-side (no client-visible JWT expiry) — keep it in shell\n  variables, not a world-readable file, if you must persist it at all.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.2.2\",\n  \"publishedAt\": 1790603726975\n}\n\nFile v1.2.2:references/requests.md\n\n# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\n**Pagination is not wired up** in the MCP either — if `last_page` is\n`false`, more results exist on later pages that neither the MCP nor this\nskill fetches.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, type, couple_names}),\n  total_price: .vendor_proposal.total_price,\n  payments: (.payments_container.payments // [] | map({due_date, amount, is_paid}))\n}' /tmp/hb-file.json\n\n# Full contract text + signatures (the \"agreement\" section)\njq '.agreement' /tmp/hb-file.json\n\n# Full line items (the \"pricing\" section)\njq '.vendor_proposal' /tmp/hb-file.json\n```\n\n## 3. Get a workspace (vendor project)\n\n`get_workspace` (`src/tools/workspaces.ts`) — `workspace_id` is\n`.workspace._id` on any workspace_file from endpoint 1 or 2:\n\n```sh\nhb_get \"/api/v2/workspaces/$WORKSPACE_ID\" \\\n  | jq '{id: .[\"_id\"], has_sent_files, has_signed_files, has_paid_payments}'\n```\n\n## 4. List saved payment methods\n\n`list_payment_methods` (`src/tools/payment_methods.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/payment_methods\" | jq '.'\n```\n\nEmpty array (`[]`) if the client has no payment method saved with this\nvendor — not an error.\n\n---\n\n## Error shapes to check on every response\n\n```sh\n# Wrong API version — re-derive it from the error body itself (no /api/gon round-trip needed)\njq -r 'select(type==\"object\") | .error_data.server_api_version // empty' /tmp/hb-resp.json\n\n# HTTP status: 401 = session expired (re-capture), 429 = rate limited (wait 2s, retry once).\n# A 404 whose body names HBUnauthorizedError is ALSO an expired session, not a\n# missing resource — a revoked token does not reliably come back as 401.\n```\n\nA non-2xx HTTP status with a body matching `HBWrongAPIVersionError` means\nretry the *same* request with `hb-api-client-version` set to\n`.error_data.server_api_version` from the body (or a fresh `/api/gon` fetch,\nper `../SKILL.md`).\n\nFile v1.2.2:skill-card.md\n\n## Description:\n\nGuides agents in reading HoneyBook client-portal contracts, invoices, proposals, payment methods, and workspace status using a captured browser session and shell commands.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and authorized HoneyBook clients use this skill to retrieve vendor-shared documents and account details from a signed-in client portal without running an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Captured browser session credentials can grant access to a HoneyBook account.\n\nMitigation: Use only accounts and vendors you are authorized to access; protect captured credentials and delete local copies promptly.\n\nRisk: Saved session data, contracts, invoices, proposals, or payment-method responses can expose sensitive information.\n\nMitigation: Avoid shared temporary paths, logs, and shell history; retain only the data needed for the task.\n\nRisk: The documented message-sending workflow can email a vendor for real.\n\nMitigation: Do not invoke it unless the account holder explicitly intends to send that message.\n\n## Reference(s):\n\n- [HoneyBook FPX ClawHub release](https://clawhub.ai/chrischall/skills/honeybook-fpx)\n- [HoneyBook request examples](references/requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands can return sensitive HoneyBook account records.]\n\n## Skill Version(s):\n\n1.2.2 (source: server-resolved release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.1: 4 files, 7742 bytes\n\nFiles: references/requests.md (4523b), skill-card.md (1967b), SKILL.md (8615b), _meta.json (132b)\n\nFile v1.2.1:SKILL.md\n\n---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** browser extension installed, its Chrome\n**Site access** allowing both `honeybook.com` and `hbportal.co`, and a vendor\nmagic-link URL already open (signed in) in that browser. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n   Transporter installed. This signs you into `<vendor>.hbportal.co`.\n2. **While that tab is open**, run:\n\n```sh\nfpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json\n```\n\nThe tab only has to be **open and signed in**. Nothing is sniffed off a live\nrequest, so it does not matter whether the page has gone idle.\n\n3. Extract the fields `client.ts` needs (each localStorage value is one raw\n   JSON string — parse it with `fromjson`):\n\n```sh\nAUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin\n```\n\nIf `AUTH_TOKEN` or `USER_ID` comes back empty/`null`, the capture didn't see\nwhat it needed — re-open the magic link and re-run step 2.\n\nWith more than one vendor tab open at once, disambiguate with\n`--storage-subdomain <vendor>` (e.g. `--storage-subdomain acme`).\n\n4. Get the current API version (`client.ts`'s `fetchApiVersion` parses this\n   same endpoint):\n\n```sh\nAPI_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')\n```\n\n## Core call pattern\n\nEvery real request carries the same headers\n(`client.ts`'s `HoneyBookClient.request`). Only `hb-api-auth-token`,\n`hb-api-user-id` and a current `hb-api-client-version` are load-bearing —\n`hb-trusted-device` is optional and `hb-api-fingerprint` is no longer\nrequired at all:\n\n```sh\ncurl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'\n```\n\n`hb-api-duplicate-calls-prevention-uuid` must be a **fresh random UUID on\nevery request** — the MCP mints one with `crypto.randomUUID()` per call, not\nonce per session. Reusing a value risks HoneyBook treating a legitimate\nrepeat as a duplicate.\n\nReady-to-run commands for all four read endpoints are in\n`references/requests.md`.\n\n## Other reads the portal makes (same headers)\n\n```sh\n# Projects (\"events\") and the workspace id each one carries\ncurl -s \"https://api.honeybook.com/api/v2/client/events\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/events/$EVENT_ID/details\" \"${HB_HEADERS[@]}\"\n# The feed: messages (feed_message / workspace_email / workspace_file_email) and activity\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/feed\" \"${HB_HEADERS[@]}\" \\\n  | jq '.feed.feed_items[] | select(.type|test(\"email|message\")) | {id:._id, subject:.data.subject, from:.sender_id, sent:.data.sent_on}'\n# Tasks (curr_date MUST be MM/DD/YYYY), notes, loose files, payment schedule\ncurl -s \"https://api.honeybook.com/api/v2/tasks/workspaces/$WS_ID?page=1&perPage=30&sort_by=due_date&sort_desc=false&curr_date=09%2F02%2F2026\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/notes/workspace/$WS_ID\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/attachments\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/payments\" \"${HB_HEADERS[@]}\"\n```\n\nSending a message is a two-step \"client pending task\", not a POST of the\nmessage: `POST /api/v2/client_pending_task` with\n`{\"task_type\":\"send_workspace_message\",\"task_data\":{\"ws_id\":…,\"subject\":…,\"html_body\":…,\"force\":false,\"general_files\":[],\"image_files\":[],\"flow_attachments\":[]}}`\nreturns `{task_id}`; poll `GET /api/v2/client_pending_tasks?task_ids[]=<id>`\nuntil `pending_task_state_cd` is 2 (Finished) or 3 (Aborted). It emails the\nvendor for real — prefer the MCP's `send_message`, which previews first.\n\n## The rules that matter\n\n- **401, or 404 with an `HBUnauthorizedError` body → session expired.** A\n  revoked token does not reliably come back as 401, so check the body type\n  before concluding a resource is missing. Re-run the capture (magic link tab must still\n  be open and signed in).\n- **429 → rate limited.** `client.ts` waits 2s and retries once; do the same\n  before giving up.\n- **Body contains `\"HBWrongAPIVersionError\"` → stale `hb-api-client-version`.**\n  The error body itself carries the correct value at\n  `.error_data.server_api_version` — read that (or re-run the `/api/gon`\n  fetch above) and retry the SAME request with the fresh version.\n- **`sign_contract` / `pay_invoice` are not real API calls.** `honeybook-mcp`\n  can't replay HoneyBook's browser-side signing/SCA flow, so those tools just\n  return a deep link — `$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement`\n  (sign) or `/invoice` (pay) — for the user to open themselves. There's no\n  POST body to transcribe for either; don't invent one.\n\n## Output / exit-code contract\n\n- `fpx session`/`fpx pair`/`fpx health` are bridge round-trips: exit `0` on a\n  successful bridge read regardless of upstream status, `1` on a usage error\n  (bad flag, undeclared scope), `2` if the bridge/extension is unreachable or\n  pairing is still pending. There's no bot-wall (`3`)/upstream-HTTP (`4`)\n  exit code on these — HoneyBook's own API isn't bridge-walled.\n- The actual reads go through plain `curl` afterward — check the HTTP status\n  and the `HBWrongAPIVersionError` body text yourself, as above.\n\n## Notes\n\n- Session data (`AUTH_TOKEN`, `TRUSTED_DEVICE`) is opaque and\n  long-lived server-side (no client-visible JWT expiry) — keep it in shell\n  variables, not a world-readable file, if you must persist it at all.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.2.1\",\n  \"publishedAt\": 1790351572754\n}\n\nFile v1.2.1:references/requests.md\n\n# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\n**Pagination is not wired up** in the MCP either — if `last_page` is\n`false`, more results exist on later pages that neither the MCP nor this\nskill fetches.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, type, couple_names}),\n  total_price: .vendor_proposal.total_price,\n  payments: (.payments_container.payments // [] | map({due_date, amount, is_paid}))\n}' /tmp/hb-file.json\n\n# Full contract text + signatures (the \"agreement\" section)\njq '.agreement' /tmp/hb-file.json\n\n# Full line items (the \"pricing\" section)\njq '.vendor_proposal' /tmp/hb-file.json\n```\n\n## 3. Get a workspace (vendor project)\n\n`get_workspace` (`src/tools/workspaces.ts`) — `workspace_id` is\n`.workspace._id` on any workspace_file from endpoint 1 or 2:\n\n```sh\nhb_get \"/api/v2/workspaces/$WORKSPACE_ID\" \\\n  | jq '{id: .[\"_id\"], has_sent_files, has_signed_files, has_paid_payments}'\n```\n\n## 4. List saved payment methods\n\n`list_payment_methods` (`src/tools/payment_methods.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/payment_methods\" | jq '.'\n```\n\nEmpty array (`[]`) if the client has no payment method saved with this\nvendor — not an error.\n\n---\n\n## Error shapes to check on every response\n\n```sh\n# Wrong API version — re-derive it from the error body itself (no /api/gon round-trip needed)\njq -r 'select(type==\"object\") | .error_data.server_api_version // empty' /tmp/hb-resp.json\n\n# HTTP status: 401 = session expired (re-capture), 429 = rate limited (wait 2s, retry once).\n# A 404 whose body names HBUnauthorizedError is ALSO an expired session, not a\n# missing resource — a revoked token does not reliably come back as 401.\n```\n\nA non-2xx HTTP status with a body matching `HBWrongAPIVersionError` means\nretry the *same* request with `hb-api-client-version` set to\n`.error_data.server_api_version` from the body (or a fresh `/api/gon` fetch,\nper `../SKILL.md`).\n\nFile v1.2.1:skill-card.md\n\n## Description:\n\nGuides authorized HoneyBook users in capturing an active portal session and reading client records through shell commands without an MCP server.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nAuthorized HoneyBook clients and developers use the skill to retrieve contracts, invoices, proposals, payment methods, and workspace details from a signed-in client portal without running an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Captured session credentials and returned client records can expose sensitive HoneyBook data if left in files or used on shared machines.\n\nMitigation: Use only an authorized signed-in session; restrict access to credentials and API outputs, avoid shared machines, and promptly remove temporary files.\n\nRisk: The message-sending guidance can trigger real outbound communications despite the skill's read-focused description.\n\nMitigation: Do not follow message-sending steps unless you intend to contact the vendor; review the recipient and content before sending.\n\n## Reference(s):\n\n- [HoneyBook FPX release](https://clawhub.ai/chrischall/skills/honeybook-fpx)\n- [Request examples](references/requests.md)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration instructions, Guidance]\n\n**Output Format:** [Markdown with shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands can return sensitive HoneyBook client records; no fixed output length.]\n\n## Skill Version(s):\n\n1.2.1 (source: server-resolved release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.0: 4 files, 7853 bytes\n\nFiles: references/requests.md (4523b), skill-card.md (2174b), SKILL.md (8615b), _meta.json (132b)\n\nFile v1.2.0:SKILL.md\n\n---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** browser extension installed, its Chrome\n**Site access** allowing both `honeybook.com` and `hbportal.co`, and a vendor\nmagic-link URL already open (signed in) in that browser. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n   Transporter installed. This signs you into `<vendor>.hbportal.co`.\n2. **While that tab is open**, run:\n\n```sh\nfpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json\n```\n\nThe tab only has to be **open and signed in**. Nothing is sniffed off a live\nrequest, so it does not matter whether the page has gone idle.\n\n3. Extract the fields `client.ts` needs (each localStorage value is one raw\n   JSON string — parse it with `fromjson`):\n\n```sh\nAUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin\n```\n\nIf `AUTH_TOKEN` or `USER_ID` comes back empty/`null`, the capture didn't see\nwhat it needed — re-open the magic link and re-run step 2.\n\nWith more than one vendor tab open at once, disambiguate with\n`--storage-subdomain <vendor>` (e.g. `--storage-subdomain acme`).\n\n4. Get the current API version (`client.ts`'s `fetchApiVersion` parses this\n   same endpoint):\n\n```sh\nAPI_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')\n```\n\n## Core call pattern\n\nEvery real request carries the same headers\n(`client.ts`'s `HoneyBookClient.request`). Only `hb-api-auth-token`,\n`hb-api-user-id` and a current `hb-api-client-version` are load-bearing —\n`hb-trusted-device` is optional and `hb-api-fingerprint` is no longer\nrequired at all:\n\n```sh\ncurl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'\n```\n\n`hb-api-duplicate-calls-prevention-uuid` must be a **fresh random UUID on\nevery request** — the MCP mints one with `crypto.randomUUID()` per call, not\nonce per session. Reusing a value risks HoneyBook treating a legitimate\nrepeat as a duplicate.\n\nReady-to-run commands for all four read endpoints are in\n`references/requests.md`.\n\n## Other reads the portal makes (same headers)\n\n```sh\n# Projects (\"events\") and the workspace id each one carries\ncurl -s \"https://api.honeybook.com/api/v2/client/events\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/events/$EVENT_ID/details\" \"${HB_HEADERS[@]}\"\n# The feed: messages (feed_message / workspace_email / workspace_file_email) and activity\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/feed\" \"${HB_HEADERS[@]}\" \\\n  | jq '.feed.feed_items[] | select(.type|test(\"email|message\")) | {id:._id, subject:.data.subject, from:.sender_id, sent:.data.sent_on}'\n# Tasks (curr_date MUST be MM/DD/YYYY), notes, loose files, payment schedule\ncurl -s \"https://api.honeybook.com/api/v2/tasks/workspaces/$WS_ID?page=1&perPage=30&sort_by=due_date&sort_desc=false&curr_date=09%2F02%2F2026\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/notes/workspace/$WS_ID\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/attachments\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/payments\" \"${HB_HEADERS[@]}\"\n```\n\nSending a message is a two-step \"client pending task\", not a POST of the\nmessage: `POST /api/v2/client_pending_task` with\n`{\"task_type\":\"send_workspace_message\",\"task_data\":{\"ws_id\":…,\"subject\":…,\"html_body\":…,\"force\":false,\"general_files\":[],\"image_files\":[],\"flow_attachments\":[]}}`\nreturns `{task_id}`; poll `GET /api/v2/client_pending_tasks?task_ids[]=<id>`\nuntil `pending_task_state_cd` is 2 (Finished) or 3 (Aborted). It emails the\nvendor for real — prefer the MCP's `send_message`, which previews first.\n\n## The rules that matter\n\n- **401, or 404 with an `HBUnauthorizedError` body → session expired.** A\n  revoked token does not reliably come back as 401, so check the body type\n  before concluding a resource is missing. Re-run the capture (magic link tab must still\n  be open and signed in).\n- **429 → rate limited.** `client.ts` waits 2s and retries once; do the same\n  before giving up.\n- **Body contains `\"HBWrongAPIVersionError\"` → stale `hb-api-client-version`.**\n  The error body itself carries the correct value at\n  `.error_data.server_api_version` — read that (or re-run the `/api/gon`\n  fetch above) and retry the SAME request with the fresh version.\n- **`sign_contract` / `pay_invoice` are not real API calls.** `honeybook-mcp`\n  can't replay HoneyBook's browser-side signing/SCA flow, so those tools just\n  return a deep link — `$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement`\n  (sign) or `/invoice` (pay) — for the user to open themselves. There's no\n  POST body to transcribe for either; don't invent one.\n\n## Output / exit-code contract\n\n- `fpx session`/`fpx pair`/`fpx health` are bridge round-trips: exit `0` on a\n  successful bridge read regardless of upstream status, `1` on a usage error\n  (bad flag, undeclared scope), `2` if the bridge/extension is unreachable or\n  pairing is still pending. There's no bot-wall (`3`)/upstream-HTTP (`4`)\n  exit code on these — HoneyBook's own API isn't bridge-walled.\n- The actual reads go through plain `curl` afterward — check the HTTP status\n  and the `HBWrongAPIVersionError` body text yourself, as above.\n\n## Notes\n\n- Session data (`AUTH_TOKEN`, `TRUSTED_DEVICE`) is opaque and\n  long-lived server-side (no client-visible JWT expiry) — keep it in shell\n  variables, not a world-readable file, if you must persist it at all.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.2.0\",\n  \"publishedAt\": 1790262706063\n}\n\nFile v1.2.0:references/requests.md\n\n# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\n**Pagination is not wired up** in the MCP either — if `last_page` is\n`false`, more results exist on later pages that neither the MCP nor this\nskill fetches.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, type, couple_names}),\n  total_price: .vendor_proposal.total_price,\n  payments: (.payments_container.payments // [] | map({due_date, amount, is_paid}))\n}' /tmp/hb-file.json\n\n# Full contract text + signatures (the \"agreement\" section)\njq '.agreement' /tmp/hb-file.json\n\n# Full line items (the \"pricing\" section)\njq '.vendor_proposal' /tmp/hb-file.json\n```\n\n## 3. Get a workspace (vendor project)\n\n`get_workspace` (`src/tools/workspaces.ts`) — `workspace_id` is\n`.workspace._id` on any workspace_file from endpoint 1 or 2:\n\n```sh\nhb_get \"/api/v2/workspaces/$WORKSPACE_ID\" \\\n  | jq '{id: .[\"_id\"], has_sent_files, has_signed_files, has_paid_payments}'\n```\n\n## 4. List saved payment methods\n\n`list_payment_methods` (`src/tools/payment_methods.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/payment_methods\" | jq '.'\n```\n\nEmpty array (`[]`) if the client has no payment method saved with this\nvendor — not an error.\n\n---\n\n## Error shapes to check on every response\n\n```sh\n# Wrong API version — re-derive it from the error body itself (no /api/gon round-trip needed)\njq -r 'select(type==\"object\") | .error_data.server_api_version // empty' /tmp/hb-resp.json\n\n# HTTP status: 401 = session expired (re-capture), 429 = rate limited (wait 2s, retry once).\n# A 404 whose body names HBUnauthorizedError is ALSO an expired session, not a\n# missing resource — a revoked token does not reliably come back as 401.\n```\n\nA non-2xx HTTP status with a body matching `HBWrongAPIVersionError` means\nretry the *same* request with `hb-api-client-version` set to\n`.error_data.server_api_version` from the body (or a fresh `/api/gon` fetch,\nper `../SKILL.md`).\n\nFile v1.2.0:skill-card.md\n\n## Description:\n\nhoneybook-fpx helps agents read HoneyBook client-portal data from shell workflows using fpx session capture and curl requests to HoneyBook APIs.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and technical operators use this skill to retrieve HoneyBook contracts, invoices, proposals, payment methods, and workspace status without running the HoneyBook MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill captures and reuses live HoneyBook session credentials, including AUTH_TOKEN and TRUSTED_DEVICE values.\n\nMitigation: Use only on a trusted single-user machine, keep tokens out of shared paths, delete captured session files immediately, and treat all captured values like passwords.\n\nRisk: Session exports and API responses may contain HoneyBook client, vendor, contract, invoice, payment, or workspace data.\n\nMitigation: Avoid writing captures or responses to shared temporary locations; project only the fields needed and remove local files after review.\n\nRisk: The artifact describes a message-sending flow that emails a vendor for real.\n\nMitigation: Do not use the message-sending flow unless the user explicitly requests and confirms it; prefer a previewing workflow before sending.\n\n## Reference(s):\n\n- [HoneyBook requests for fpx + curl](artifact/references/requests.md)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with shell command blocks and JSON API response handling notes]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Guidance includes direct HoneyBook API calls and session-token handling instructions.]\n\n## Skill Version(s):\n\n1.2.0 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.4: 4 files, 7860 bytes\n\nFiles: references/requests.md (4523b), skill-card.md (2188b), SKILL.md (8615b), _meta.json (132b)\n\nFile v1.1.4:SKILL.md\n\n---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** browser extension installed, its Chrome\n**Site access** allowing both `honeybook.com` and `hbportal.co`, and a vendor\nmagic-link URL already open (signed in) in that browser. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n   Transporter installed. This signs you into `<vendor>.hbportal.co`.\n2. **While that tab is open**, run:\n\n```sh\nfpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json\n```\n\nThe tab only has to be **open and signed in**. Nothing is sniffed off a live\nrequest, so it does not matter whether the page has gone idle.\n\n3. Extract the fields `client.ts` needs (each localStorage value is one raw\n   JSON string — parse it with `fromjson`):\n\n```sh\nAUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin\n```\n\nIf `AUTH_TOKEN` or `USER_ID` comes back empty/`null`, the capture didn't see\nwhat it needed — re-open the magic link and re-run step 2.\n\nWith more than one vendor tab open at once, disambiguate with\n`--storage-subdomain <vendor>` (e.g. `--storage-subdomain acme`).\n\n4. Get the current API version (`client.ts`'s `fetchApiVersion` parses this\n   same endpoint):\n\n```sh\nAPI_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')\n```\n\n## Core call pattern\n\nEvery real request carries the same headers\n(`client.ts`'s `HoneyBookClient.request`). Only `hb-api-auth-token`,\n`hb-api-user-id` and a current `hb-api-client-version` are load-bearing —\n`hb-trusted-device` is optional and `hb-api-fingerprint` is no longer\nrequired at all:\n\n```sh\ncurl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'\n```\n\n`hb-api-duplicate-calls-prevention-uuid` must be a **fresh random UUID on\nevery request** — the MCP mints one with `crypto.randomUUID()` per call, not\nonce per session. Reusing a value risks HoneyBook treating a legitimate\nrepeat as a duplicate.\n\nReady-to-run commands for all four read endpoints are in\n`references/requests.md`.\n\n## Other reads the portal makes (same headers)\n\n```sh\n# Projects (\"events\") and the workspace id each one carries\ncurl -s \"https://api.honeybook.com/api/v2/client/events\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/events/$EVENT_ID/details\" \"${HB_HEADERS[@]}\"\n# The feed: messages (feed_message / workspace_email / workspace_file_email) and activity\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/feed\" \"${HB_HEADERS[@]}\" \\\n  | jq '.feed.feed_items[] | select(.type|test(\"email|message\")) | {id:._id, subject:.data.subject, from:.sender_id, sent:.data.sent_on}'\n# Tasks (curr_date MUST be MM/DD/YYYY), notes, loose files, payment schedule\ncurl -s \"https://api.honeybook.com/api/v2/tasks/workspaces/$WS_ID?page=1&perPage=30&sort_by=due_date&sort_desc=false&curr_date=09%2F02%2F2026\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/notes/workspace/$WS_ID\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/attachments\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/payments\" \"${HB_HEADERS[@]}\"\n```\n\nSending a message is a two-step \"client pending task\", not a POST of the\nmessage: `POST /api/v2/client_pending_task` with\n`{\"task_type\":\"send_workspace_message\",\"task_data\":{\"ws_id\":…,\"subject\":…,\"html_body\":…,\"force\":false,\"general_files\":[],\"image_files\":[],\"flow_attachments\":[]}}`\nreturns `{task_id}`; poll `GET /api/v2/client_pending_tasks?task_ids[]=<id>`\nuntil `pending_task_state_cd` is 2 (Finished) or 3 (Aborted). It emails the\nvendor for real — prefer the MCP's `send_message`, which previews first.\n\n## The rules that matter\n\n- **401, or 404 with an `HBUnauthorizedError` body → session expired.** A\n  revoked token does not reliably come back as 401, so check the body type\n  before concluding a resource is missing. Re-run the capture (magic link tab must still\n  be open and signed in).\n- **429 → rate limited.** `client.ts` waits 2s and retries once; do the same\n  before giving up.\n- **Body contains `\"HBWrongAPIVersionError\"` → stale `hb-api-client-version`.**\n  The error body itself carries the correct value at\n  `.error_data.server_api_version` — read that (or re-run the `/api/gon`\n  fetch above) and retry the SAME request with the fresh version.\n- **`sign_contract` / `pay_invoice` are not real API calls.** `honeybook-mcp`\n  can't replay HoneyBook's browser-side signing/SCA flow, so those tools just\n  return a deep link — `$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement`\n  (sign) or `/invoice` (pay) — for the user to open themselves. There's no\n  POST body to transcribe for either; don't invent one.\n\n## Output / exit-code contract\n\n- `fpx session`/`fpx pair`/`fpx health` are bridge round-trips: exit `0` on a\n  successful bridge read regardless of upstream status, `1` on a usage error\n  (bad flag, undeclared scope), `2` if the bridge/extension is unreachable or\n  pairing is still pending. There's no bot-wall (`3`)/upstream-HTTP (`4`)\n  exit code on these — HoneyBook's own API isn't bridge-walled.\n- The actual reads go through plain `curl` afterward — check the HTTP status\n  and the `HBWrongAPIVersionError` body text yourself, as above.\n\n## Notes\n\n- Session data (`AUTH_TOKEN`, `TRUSTED_DEVICE`) is opaque and\n  long-lived server-side (no client-visible JWT expiry) — keep it in shell\n  variables, not a world-readable file, if you must persist it at all.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.1.4\",\n  \"publishedAt\": 1790199637776\n}\n\nFile v1.1.4:references/requests.md\n\n# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\n**Pagination is not wired up** in the MCP either — if `last_page` is\n`false`, more results exist on later pages that neither the MCP nor this\nskill fetches.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, type, couple_names}),\n  total_price: .vendor_proposal.total_price,\n  payments: (.payments_container.payments // [] | map({due_date, amount, is_paid}))\n}' /tmp/hb-file.json\n\n# Full contract text + signatures (the \"agreement\" section)\njq '.agreement' /tmp/hb-file.json\n\n# Full line items (the \"pricing\" section)\njq '.vendor_proposal' /tmp/hb-file.json\n```\n\n## 3. Get a workspace (vendor project)\n\n`get_workspace` (`src/tools/workspaces.ts`) — `workspace_id` is\n`.workspace._id` on any workspace_file from endpoint 1 or 2:\n\n```sh\nhb_get \"/api/v2/workspaces/$WORKSPACE_ID\" \\\n  | jq '{id: .[\"_id\"], has_sent_files, has_signed_files, has_paid_payments}'\n```\n\n## 4. List saved payment methods\n\n`list_payment_methods` (`src/tools/payment_methods.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/payment_methods\" | jq '.'\n```\n\nEmpty array (`[]`) if the client has no payment method saved with this\nvendor — not an error.\n\n---\n\n## Error shapes to check on every response\n\n```sh\n# Wrong API version — re-derive it from the error body itself (no /api/gon round-trip needed)\njq -r 'select(type==\"object\") | .error_data.server_api_version // empty' /tmp/hb-resp.json\n\n# HTTP status: 401 = session expired (re-capture), 429 = rate limited (wait 2s, retry once).\n# A 404 whose body names HBUnauthorizedError is ALSO an expired session, not a\n# missing resource — a revoked token does not reliably come back as 401.\n```\n\nA non-2xx HTTP status with a body matching `HBWrongAPIVersionError` means\nretry the *same* request with `hb-api-client-version` set to\n`.error_data.server_api_version` from the body (or a fresh `/api/gon` fetch,\nper `../SKILL.md`).\n\nFile v1.1.4:skill-card.md\n\n## Description:\n\nGuides an agent through reading HoneyBook client-portal data from a shell by capturing an authorized browser session with fpx and then using curl against HoneyBook API endpoints.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and operators use this skill to inspect HoneyBook contracts, invoices, proposals, payment methods, and workspace status from authorized portal sessions without running the HoneyBook MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill teaches users to extract and reuse browser session credentials.\n\nMitigation: Use only with explicit authorization on accounts and machines the user controls, avoid logging captured values, and delete temporary session captures immediately.\n\nRisk: Captured AUTH_TOKEN, USER_ID, trusted-device values, and session files can function like account credentials.\n\nMitigation: Treat captured values as secrets and keep them out of world-readable files, shell history, logs, and shared artifacts.\n\nRisk: The artifact describes a message-sending flow that can send real HoneyBook communications.\n\nMitigation: Do not use the message-sending flow unless intentionally sending a real communication; prefer a preview-capable flow when available.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/honeybook-fpx)\n- [HoneyBook request examples](references/requests.md)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration]\n\n**Output Format:** [Markdown with inline shell commands and JSON-processing examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Includes credential-handling cautions and HTTP error-handling guidance.]\n\n## Skill Version(s):\n\n1.1.4 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.3: 4 files, 7822 bytes\n\nFiles: references/requests.md (4523b), skill-card.md (2114b), SKILL.md (8615b), _meta.json (132b)\n\nFile v1.1.3:SKILL.md\n\n---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** browser extension installed, its Chrome\n**Site access** allowing both `honeybook.com` and `hbportal.co`, and a vendor\nmagic-link URL already open (signed in) in that browser. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n   Transporter installed. This signs you into `<vendor>.hbportal.co`.\n2. **While that tab is open**, run:\n\n```sh\nfpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json\n```\n\nThe tab only has to be **open and signed in**. Nothing is sniffed off a live\nrequest, so it does not matter whether the page has gone idle.\n\n3. Extract the fields `client.ts` needs (each localStorage value is one raw\n   JSON string — parse it with `fromjson`):\n\n```sh\nAUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin\n```\n\nIf `AUTH_TOKEN` or `USER_ID` comes back empty/`null`, the capture didn't see\nwhat it needed — re-open the magic link and re-run step 2.\n\nWith more than one vendor tab open at once, disambiguate with\n`--storage-subdomain <vendor>` (e.g. `--storage-subdomain acme`).\n\n4. Get the current API version (`client.ts`'s `fetchApiVersion` parses this\n   same endpoint):\n\n```sh\nAPI_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')\n```\n\n## Core call pattern\n\nEvery real request carries the same headers\n(`client.ts`'s `HoneyBookClient.request`). Only `hb-api-auth-token`,\n`hb-api-user-id` and a current `hb-api-client-version` are load-bearing —\n`hb-trusted-device` is optional and `hb-api-fingerprint` is no longer\nrequired at all:\n\n```sh\ncurl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'\n```\n\n`hb-api-duplicate-calls-prevention-uuid` must be a **fresh random UUID on\nevery request** — the MCP mints one with `crypto.randomUUID()` per call, not\nonce per session. Reusing a value risks HoneyBook treating a legitimate\nrepeat as a duplicate.\n\nReady-to-run commands for all four read endpoints are in\n`references/requests.md`.\n\n## Other reads the portal makes (same headers)\n\n```sh\n# Projects (\"events\") and the workspace id each one carries\ncurl -s \"https://api.honeybook.com/api/v2/client/events\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/events/$EVENT_ID/details\" \"${HB_HEADERS[@]}\"\n# The feed: messages (feed_message / workspace_email / workspace_file_email) and activity\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/feed\" \"${HB_HEADERS[@]}\" \\\n  | jq '.feed.feed_items[] | select(.type|test(\"email|message\")) | {id:._id, subject:.data.subject, from:.sender_id, sent:.data.sent_on}'\n# Tasks (curr_date MUST be MM/DD/YYYY), notes, loose files, payment schedule\ncurl -s \"https://api.honeybook.com/api/v2/tasks/workspaces/$WS_ID?page=1&perPage=30&sort_by=due_date&sort_desc=false&curr_date=09%2F02%2F2026\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/notes/workspace/$WS_ID\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/attachments\" \"${HB_HEADERS[@]}\"\ncurl -s \"https://api.honeybook.com/api/v2/workspaces/$WS_ID/payments\" \"${HB_HEADERS[@]}\"\n```\n\nSending a message is a two-step \"client pending task\", not a POST of the\nmessage: `POST /api/v2/client_pending_task` with\n`{\"task_type\":\"send_workspace_message\",\"task_data\":{\"ws_id\":…,\"subject\":…,\"html_body\":…,\"force\":false,\"general_files\":[],\"image_files\":[],\"flow_attachments\":[]}}`\nreturns `{task_id}`; poll `GET /api/v2/client_pending_tasks?task_ids[]=<id>`\nuntil `pending_task_state_cd` is 2 (Finished) or 3 (Aborted). It emails the\nvendor for real — prefer the MCP's `send_message`, which previews first.\n\n## The rules that matter\n\n- **401, or 404 with an `HBUnauthorizedError` body → session expired.** A\n  revoked token does not reliably come back as 401, so check the body type\n  before concluding a resource is missing. Re-run the capture (magic link tab must still\n  be open and signed in).\n- **429 → rate limited.** `client.ts` waits 2s and retries once; do the same\n  before giving up.\n- **Body contains `\"HBWrongAPIVersionError\"` → stale `hb-api-client-version`.**\n  The error body itself carries the correct value at\n  `.error_data.server_api_version` — read that (or re-run the `/api/gon`\n  fetch above) and retry the SAME request with the fresh version.\n- **`sign_contract` / `pay_invoice` are not real API calls.** `honeybook-mcp`\n  can't replay HoneyBook's browser-side signing/SCA flow, so those tools just\n  return a deep link — `$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement`\n  (sign) or `/invoice` (pay) — for the user to open themselves. There's no\n  POST body to transcribe for either; don't invent one.\n\n## Output / exit-code contract\n\n- `fpx session`/`fpx pair`/`fpx health` are bridge round-trips: exit `0` on a\n  successful bridge read regardless of upstream status, `1` on a usage error\n  (bad flag, undeclared scope), `2` if the bridge/extension is unreachable or\n  pairing is still pending. There's no bot-wall (`3`)/upstream-HTTP (`4`)\n  exit code on these — HoneyBook's own API isn't bridge-walled.\n- The actual reads go through plain `curl` afterward — check the HTTP status\n  and the `HBWrongAPIVersionError` body text yourself, as above.\n\n## Notes\n\n- Session data (`AUTH_TOKEN`, `TRUSTED_DEVICE`) is opaque and\n  long-lived server-side (no client-visible JWT expiry) — keep it in shell\n  variables, not a world-readable file, if you must persist it at all.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.1.3\",\n  \"publishedAt\": 1790178031128\n}\n\nFile v1.1.3:references/requests.md\n\n# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\n**Pagination is not wired up** in the MCP either — if `last_page` is\n`false`, more results exist on later pages that neither the MCP nor this\nskill fetches.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, type, couple_names}),\n  total_price: .vendor_proposal.total_price,\n  payments: (.payments_container.payments // [] | map({due_date, amount, is_paid}))\n}' /tmp/hb-file.json\n\n# Full contract text + signatures (the \"agreement\" section)\njq '.agreement' /tmp/hb-file.json\n\n# Full line items (the \"pricing\" section)\njq '.vendor_proposal' /tmp/hb-file.json\n```\n\n## 3. Get a workspace (vendor project)\n\n`get_workspace` (`src/tools/workspaces.ts`) — `workspace_id` is\n`.workspace._id` on any workspace_file from endpoint 1 or 2:\n\n```sh\nhb_get \"/api/v2/workspaces/$WORKSPACE_ID\" \\\n  | jq '{id: .[\"_id\"], has_sent_files, has_signed_files, has_paid_payments}'\n```\n\n## 4. List saved payment methods\n\n`list_payment_methods` (`src/tools/payment_methods.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/payment_methods\" | jq '.'\n```\n\nEmpty array (`[]`) if the client has no payment method saved with this\nvendor — not an error.\n\n---\n\n## Error shapes to check on every response\n\n```sh\n# Wrong API version — re-derive it from the error body itself (no /api/gon round-trip needed)\njq -r 'select(type==\"object\") | .error_data.server_api_version // empty' /tmp/hb-resp.json\n\n# HTTP status: 401 = session expired (re-capture), 429 = rate limited (wait 2s, retry once).\n# A 404 whose body names HBUnauthorizedError is ALSO an expired session, not a\n# missing resource — a revoked token does not reliably come back as 401.\n```\n\nA non-2xx HTTP status with a body matching `HBWrongAPIVersionError` means\nretry the *same* request with `hb-api-client-version` set to\n`.error_data.server_api_version` from the body (or a fresh `/api/gon` fetch,\nper `../SKILL.md`).\n\nFile v1.1.3:skill-card.md\n\n## Description:\n\nRead HoneyBook client-portal data from a shell with the fpx CLI by capturing a signed-in browser session once and then using curl against api.honeybook.com.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and operators use this skill to set up fpx and curl commands for reading HoneyBook contracts, invoices, proposals, payment methods, and workspace status without running the honeybook-mcp server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The workflow handles live HoneyBook session credentials.\n\nMitigation: Use only on a trusted machine, avoid shared or persistent token storage, and clear captured files and shell history after use.\n\nRisk: HoneyBook responses may contain contract, invoice, payment method, workspace, or client data.\n\nMitigation: Limit requests to the needed endpoints and avoid writing responses to shared o\n\nArchive v1.1.2: 4 files, 7928 bytes\n\nFiles: references/requests.md (4523b), skill-card.md (2388b), SKILL.md (8615b), _meta.json (132b)","readmeExcerpt":"Skill: honeybook-fpx Owner: chrischall Summary: 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 machin","codeSnippets":[],"executableExamples":[{"language":"sh","snippet":"npm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in ContextMint Bridge"},{"language":"sh","snippet":"fpx session -p honeybook --storage-domain hbportal.co > /tmp/hb-session.json"},{"language":"sh","snippet":"AUTH_TOKEN=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | .authentication_token' /tmp/hb-session.json)\nUSER_ID=$(jq -r '.localStorage.HONEYBOOK_REACT_CURR_USER | fromjson | ._id' /tmp/hb-session.json)\n# Optional — the API returns 200 without it. The React blob and jStorage hold\n# DIFFERENT values; either is accepted. `// empty` keeps an absent field from\n# becoming the literal string \"null\".\nTRUSTED_DEVICE=$(jq -r '.localStorage.jStorage | fromjson | .HB_TRUSTED_DEVICE // empty' /tmp/hb-session.json)\nPORTAL_ORIGIN='https://<vendor>.hbportal.co'   # the magic-link URL's origin"},{"language":"sh","snippet":"API_VERSION=$(curl -s 'https://api.honeybook.com/api/gon?callback=parseGon' \\\n  | grep -oE '\"api_version\":[[:space:]]*[0-9]+' | grep -oE '[0-9]+$')"},{"language":"sh","snippet":"curl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\"},{"language":"sh","snippet":"curl -s \"https://api.honeybook.com/api/v2/users/$USER_ID/workspace_files\" \\\n  -H 'accept: application/json, text/plain, */*' \\\n  -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n  -H \"hb-api-user-id: $USER_ID\" \\\n  ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n  -H \"hb-api-client-version: $API_VERSION\" \\\n  -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n  -H 'hb-admin-login: false' \\\n  | jq '.data'"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: honeybook-fpx\ndescription: >-\n  Read HoneyBook client-portal data (contracts, invoices, proposals, payment\n  methods, workspace status) from a shell with the fpx CLI (@fetchproxy/cli)\n  instead of running the honeybook-mcp server — capture a vendor session once\n  via the signed-in browser tab, then curl api.honeybook.com directly. Use\n  when you want HoneyBook data without the MCP, in a script, or on a machine\n  where the MCP isn't installed.\n---\n\n# HoneyBook via fpx + curl (no MCP)\n\nHoneyBook has **no server-side login** a script can drive — a client never\ngets a password, only a magic-link email per vendor. The credential is\nwhatever the signed-in `*.hbportal.co` portal tab already holds: a bearer\ntoken + user id in `localStorage[\"HONEYBOOK_REACT_CURR_USER\"]`. (HoneyBook\nused to keep these in the AngularJS `localStorage[\"jStorage\"]` blob as\n`HB_AUTH_TOKEN`/`HB_AUTH_USER_ID`; that blob is now down to\n`HB_TRUSTED_DEVICE`, `SESSION_COMPANY_ID` and routing state.)\nThere's no bot wall on the API itself once you have those — `honeybook-mcp`'s\nown `client.ts` proves plain Node `fetch` works fine against\n`api.honeybook.com`. So this skill is **hybrid**: `fpx` captures the session\n**once** (per vendor), then plain `curl` does every read from then on.\n\nThis mirrors `src/auth.ts` (`captureSessionViaFetchproxy`) and `src/client.ts`\n(`HoneyBookClient.request`) in `honeybook-mcp` — same headers, same base URL,\nsame retry rules.\n\n## Multi-domain scope\n\nTwo apexes are declared on one profile:\n- `hbportal.co` — the vendor's branded portal (e.g. `acme.hbportal.co`),\n  where the stored session lives.\n- `honeybook.com` — the main app, where the same session is also valid.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli   # provides `fpx`\nfpx profile add honeybook --domain honeybook.com --domain hbportal.co\nfpx profile declare honeybook \\\n  --local-storage HONEYBOOK_REACT_CURR_USER \\\n  --local-storage jStorage\nfpx pair -p honeybook            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases):\nChrome loads the chrome zip unpacked; Safari isn't available yet, so use\nChrome for now), its Chrome **Site access** allowing both `honeybook.com` and\n`hbportal.co`, and a vendor magic-link URL already open (signed in) in that\nbrowser. Pairing persists — after the first approval every later `fpx` call\nreuses it.\n\nContextMint Bridge is the fetchproxy extension renamed, same maintainer (see [fetchproxy#extension](https://github.com/chrischall/fetchproxy#extension)); source at [nullnet-app/contextmint-bridge](https://github.com/nullnet-app/contextmint-bridge) — build it yourself or verify a release zip with `shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256`.\n\n## Capture a vendor session (once per vendor, and again when it expires)\n\n1. Click the vendor's HoneyBook magic-link email in the browser with\n"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"honeybook-fpx\",\n  \"version\": \"1.2.6\",\n  \"publishedAt\": 1791588414915\n}"},{"path":"references/requests.md","content":"# HoneyBook requests for fpx + curl\n\nReady-to-run commands for the four live read endpoints `honeybook-mcp`\nactually calls (from `src/tools/*.ts` + `src/client.ts`). All are\n`GET api.honeybook.com/api/v2/*`, carrying the same headers built in\n`HoneyBookClient.request` — see `../SKILL.md` for how to capture\n`$AUTH_TOKEN`/`$USER_ID`/`$API_VERSION` first. `$TRUSTED_DEVICE` is\noptional and `hb-api-fingerprint` is not required at all.\n\n```sh\nhb_get() {   # $1 = path (e.g. /api/v2/users/$USER_ID/workspace_files)\n  curl -s \"https://api.honeybook.com$1\" \\\n    -H 'accept: application/json, text/plain, */*' \\\n    -H \"hb-api-auth-token: $AUTH_TOKEN\" \\\n    -H \"hb-api-user-id: $USER_ID\" \\\n    ${TRUSTED_DEVICE:+-H \"hb-trusted-device: $TRUSTED_DEVICE\"} \\\n    -H \"hb-api-client-version: $API_VERSION\" \\\n    -H \"hb-api-duplicate-calls-prevention-uuid: $(uuidgen)\" \\\n    -H 'hb-admin-login: false'\n}\n```\n\n`sign_contract`/`pay_invoice` are **not** included below — the MCP itself\ndoesn't call a signing/payment API; it returns a deep link\n(`$PORTAL_ORIGIN/app/workspace_file/<file_id>/agreement` or `/invoice`) for\nthe user to open in their browser. There's no request shape to transcribe.\n\n---\n\n## 1. List a vendor's shared files\n\n`list_workspace_files` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/users/$USER_ID/workspace_files\" > /tmp/hb-files.json\n```\n\nResponse envelope (`HBListEnvelope<T>`, `src/types.ts`):\n`{ data: [...], cur_page, last_page, last_id?, total_count? }`.\nIf `last_page` is `false`, more results exist on later pages. The MCP\nrequests `?page=2`, `?page=3`, … (the parameter `/api/v2/client/events`\ntakes) until `last_page` is true, and reports `complete: false` if a page\nbrings nothing new; do the same here if you need every file.\n\n```sh\n# Filter to a file_type client-side (agreement | invoice | brochure | proposal)\njq '[.data[] | select(.file_type == \"agreement\")]' /tmp/hb-files.json\n\n# Compact listing: id, type, title, accepted/paid flags\njq -r '.data[] | [.[\"_id\"], .file_type, .file_title, (.is_file_accepted|tostring), (.has_pending_payment|tostring)] | @tsv' /tmp/hb-files.json\n```\n\n## 2. Get one file's detail\n\n`get_workspace_file` (`src/tools/workspace_files.ts`):\n\n```sh\nhb_get \"/api/v2/workspace_files/$FILE_ID\" > /tmp/hb-file.json\n```\n\nThe raw response is large on proposal-class files (a real one hit ~1.3 MB,\nmostly vendor-internal fields the MCP prunes off `company`:\n`vendor_emails`, `workflow_automation_infos`, `brochure_templates`,\n`questionnaires`, `lead_sources`, `proposals`, `agreements`, `invoices`,\n`vendor_packages`, `contact_forms`, `stripe_persons`,\n`user_pipeline_stages`, `project_types`, `company_assets`). Project what you\nneed instead of dumping the whole body:\n\n```sh\n# Summary-equivalent: identity, status, vendor, event, pricing totals, payments\njq '{\n  id: .[\"_id\"], title: .file_title, type: .file_type,\n  status: .status_name, accepted: .is_file_accepted,\n  vendor: .company.company_name,\n  event: (.event | {date: .event_date, ty"},{"path":"skill-card.md","content":"## Description:\n\nGuides agents in reading HoneyBook client-portal contracts, invoices, proposals, payment methods, and workspace status using a captured browser session and shell commands without the HoneyBook MCP server.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to inspect HoneyBook client-portal files, project status, and saved payment methods from a shell without running the MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Captured HoneyBook sessions expose credentials and private portal data to shell-level access.\n\nMitigation: Use only in a trusted environment; restrict session captures to private, owner-only temporary storage and delete them promptly.\n\nRisk: Full API responses can contain sensitive client and vendor information.\n\nMitigation: Select only the fields needed and avoid storing raw responses in shared temporary directories.\n\nRisk: The included message-sending workflow emails a real vendor despite the skill's read-oriented purpose.\n\nMitigation: Do not send messages unless explicitly authorized; preview the content before sending.\n\n## Reference(s):\n\n- [HoneyBook FPX on ClawHub](https://clawhub.ai/chrischall/skills/honeybook-fpx)\n- [Request examples](references/requests.md)\n- [ContextMint Bridge source](https://github.com/nullnet-app/contextmint-bridge)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown with shell examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Read responses can contain sensitive client and vendor data.]\n\n## Skill Version(s):\n\n1.2.6 (source: ClawHub release)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1534,"uniquenessScore":44,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T16:13:40.927Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T16:13:40.927Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T21:57:16.543Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}