{"id":"cb68cf0a-124b-4ba6-8dc7-37d7aecf73a2","entityType":"agent","slug":"clawhub-chrischall-etix-fpx","name":"etix-fpx","canonicalUrl":"https://www.xpersona.co/agent/clawhub-chrischall-etix-fpx","canonicalPath":"/agent/clawhub-chrischall-etix-fpx","generatedAt":"2026-10-10T21:53:43.329Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T16:31:10.761Z","emptyReason":null},"description":"Query etix.com (event ticketing) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the etix-mcp server — search events, venues, and performers, and pull event/venue detail, via one-shot calls through a signed-in browser tab. Use when you want Etix discovery 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:etix-fpx","sourceUrl":"https://clawhub.ai/chrischall/etix-fpx","homepage":"https://clawhub.ai/chrischall/skills/etix-fpx","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/chrischall/etix-fpx","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/chrischall/skills/etix-fpx","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":63,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"etix-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:31:10.761Z","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:31:10.761Z","emptyReason":null},"stars":null,"forks":null,"downloads":1338,"packageName":null,"latestVersion":"1.1.7","tractionLabel":"1.3K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T16:31:10.760Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T16:31:10.761Z","lastCrawledAt":"2026-10-10T16:31:10.760Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T16:31:10.760Z","lastVerifiedAt":null,"highlights":[{"version":"1.1.7","createdAt":"2026-10-09T23:24:09.234Z","changelog":"etix-fpx 1.1.7 - Removed redundant skill-card.md documentation file. - Minor update to references/extract-datalayer.mjs. - No user-facing changes in functionality.","fileCount":5,"zipByteSize":9303},{"version":"1.1.6","createdAt":"2026-10-07T13:34:58.831Z","changelog":"- Removed the skill-card.md file. - No changes to functionality or user-facing documentation.","fileCount":5,"zipByteSize":9195},{"version":"1.1.5","createdAt":"2026-10-05T02:48:39.563Z","changelog":"- Removed the skill-card.md file. - No user-facing feature or documentation changes.","fileCount":5,"zipByteSize":9261},{"version":"1.1.4","createdAt":"2026-10-03T02:53:46.214Z","changelog":"etix-fpx 1.1.4 - Removed the skill-card.md file. - Updated references/extract-datalayer.mjs. - No changes to functionality or usage documented in SKILL.md.","fileCount":5,"zipByteSize":9171},{"version":"1.1.3","createdAt":"2026-09-28T13:56:35.131Z","changelog":"- Updated references from \"Transporter\" browser extension to \"ContextMint Bridge\", reflecting new extension name and location. - Added installation and verification details for ContextMint Bridge, including download, maintainer info, and shasum verification. - Removed the file skill-card.md. - No changes to CLI usage or functional behavior.","fileCount":5,"zipByteSize":8981},{"version":"1.1.2","createdAt":"2026-09-23T21:40:03.812Z","changelog":"etix-fpx v1.1.2 - Internal: Removed the skill-card.md file. - Internal: Made changes to references/extract-datalayer.mjs. No user-facing changes noted. - No changes to user-facing documentation or core usage.","fileCount":5,"zipByteSize":8909},{"version":"1.1.1","createdAt":"2026-09-23T15:47:42.854Z","changelog":"- Removed the file skill-card.md. - No changes to functionality or usage; documentation and interface remain the same.","fileCount":5,"zipByteSize":8319},{"version":"1.1.0","createdAt":"2026-09-20T02:49:10.357Z","changelog":"- Removed the skill-card.md file. - No changes to functionality or documentation.","fileCount":5,"zipByteSize":8308}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:etix-fpx","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:etix-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/etix-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-etix-fpx/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-etix-fpx/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-etix-fpx/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-etix-fpx/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-etix-fpx/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-etix-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:53:43.325Z"}},"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-etix-fpx/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-etix-fpx/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-etix-fpx/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-etix-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:31:10.761Z","emptyReason":null},"readme":"Skill: etix-fpx\n\nOwner: chrischall\n\nSummary: Query etix.com (event ticketing) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the etix-mcp server — search events, venues, and performers, and pull event/venue detail, via one-shot calls through a signed-in browser tab. Use when you want Etix discovery data without the MCP, in a script, or on a machine where the MCP isn't installed.\n\nTags: latest:1.1.7\n\nVersion history:\n\nv1.1.7 | 2026-10-09T23:24:09.234Z | auto\n\netix-fpx 1.1.7\n\n- Removed redundant skill-card.md documentation file.\n- Minor update to references/extract-datalayer.mjs.\n- No user-facing changes in functionality.\n\nv1.1.6 | 2026-10-07T13:34:58.831Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or user-facing documentation.\n\nv1.1.5 | 2026-10-05T02:48:39.563Z | auto\n\n- Removed the skill-card.md file.\n- No user-facing feature or documentation changes.\n\nv1.1.4 | 2026-10-03T02:53:46.214Z | auto\n\netix-fpx 1.1.4\n\n- Removed the skill-card.md file.\n- Updated references/extract-datalayer.mjs.\n- No changes to functionality or usage documented in SKILL.md.\n\nv1.1.3 | 2026-09-28T13:56:35.131Z | auto\n\n- Updated references from \"Transporter\" browser extension to \"ContextMint Bridge\", reflecting new extension name and location.\n- Added installation and verification details for ContextMint Bridge, including download, maintainer info, and shasum verification.\n- Removed the file skill-card.md.\n- No changes to CLI usage or functional behavior.\n\nv1.1.2 | 2026-09-23T21:40:03.812Z | auto\n\netix-fpx v1.1.2\n\n- Internal: Removed the skill-card.md file.\n- Internal: Made changes to references/extract-datalayer.mjs. No user-facing changes noted.\n- No changes to user-facing documentation or core usage.\n\nv1.1.1 | 2026-09-23T15:47:42.854Z | auto\n\n- Removed the file skill-card.md.\n- No changes to functionality or usage; documentation and interface remain the same.\n\nv1.1.0 | 2026-09-20T02:49:10.357Z | auto\n\n- Removed the skill-card.md file.  \n- No changes to functionality or documentation.\n\nv1.0.0 | 2026-09-17T23:36:52.617Z | auto\n\n- Removed the file: skill-card.md\n- No changes to user-facing features or documentation, other than the file removal.\n\nv0.6.4 | 2026-09-15T19:25:09.354Z | auto\n\n- Removed the file skill-card.md.\n- No functional or documentation changes to the rest of the skill.\n\nv0.6.3 | 2026-09-14T14:07:11.228Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or documentation in SKILL.md.\n\nv0.6.2 | 2026-09-10T17:49:25.679Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to user-facing functionality.\n\nv0.6.1 | 2026-09-09T21:16:16.755Z | auto\n\n- Removed the file skill-card.md.\n- No functional or behavioral changes to the skill itself.\n\nv0.6.0 | 2026-09-04T22:20:44.041Z | auto\n\n- Removed the skill card file (skill-card.md).\n- No user-facing or functional changes to the CLI or documentation content.\n\nv0.5.0 | 2026-08-29T13:53:51.201Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or documentation in SKILL.md.\n\nv0.4.5 | 2026-08-28T21:07:19.432Z | auto\n\n- Removed the skill-card.md file.\n- No functional changes to the skill's behavior or user instructions.\n\nv0.4.4 | 2026-08-28T11:34:22.231Z | auto\n\n- Removed the skill-card.md file.\n- No functional or user-facing changes; documentation and usage remain the same.\n\nv0.4.3 | 2026-08-06T00:42:31.508Z | auto\n\n- Removed the nonessential skill-card.md file.\n- No functional or user-facing changes.\n\nv0.4.2 | 2026-07-30T12:53:52.219Z | auto\n\netix-fpx 0.4.2\n\n- Reworked and clarified documentation in SKILL.md, detailing one-shot querying of Etix discovery data via the fpx CLI without needing the etix-mcp server.\n- Instructions added for pairing and using the fpx CLI with the Transporter browser extension to bypass DataDome bot-wall.\n- Outlined supported endpoints, call patterns, and exact usage recipes for querying events, venues, and performers.\n- Explained error exit codes and troubleshooting steps for common issues.\n- Clearly stated limitations: consumer-level, read-only access (no login or ticket purchasing); encrypted endpoints are not supported.\n\nArchive index:\n\nArchive v1.1.7: 5 files, 9303 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (3664b), skill-card.md (1917b), SKILL.md (4339b), _meta.json (127b)\n\nFile v1.1.7:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the ContextMint Bridge extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n(from https://github.com/nullnet-app/contextmint-bridge/releases — the fetchproxy\nextension under its new name, same maintainer, as https://github.com/chrischall/fetchproxy#extension\nsays; verify the zip with `shasum -a 256 -c <zip>.sha256` or build from source), with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.7:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.1.7\",\n  \"publishedAt\": 1791588249234\n}\n\nFile v1.1.7:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link?.text?.trim(),\n      start_date: row.querySelector(\"meta[itemprop=\\\"startDate\\\"]\")?.getAttribute(\"content\"),\n      when: row.querySelector(\".performance-datetime\")?.text?.trim(),\n    }));\n  }\n'\n```\n\nThe header Place is identified the same way `etix-mcp` does it: the first\nschema.org `Place` block that is NOT inside a `.row.performance` row.\nWithout any HTML parser on hand, a cruder `grep -oE` pass over the same\n`itemprop=\"...\"` attributes still gets the event ids + names — those\nattribute names are the load-bearing part; reproduce them with whatever\nHTML tool is available.\n\n`dataLayer` (org id/name, venue id/name) extracts the same way as §2.\n\n---\n\n## 4. Geolocation — `POST /ticket/api/online/geolocation/search`\n\nBody: `{ \"cityOrPostalCode\": \"<city or zip>\", \"country\": \"<country, default USA>\" }`.\n`keywords` is NOT a valid field — the endpoint 400s naming the two known\nproperties.\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '{latitude, longitude, city, state, postal_code: .postalCode}'\n```\n\n## 5. Healthcheck probe — `GET /robots.txt`\n\nA small public path to isolate \"bridge/extension down\" from \"DataDome\nchallenge on the tab\":\n\n```sh\nfpx get 'https://www.etix.com/robots.txt' -p etix | head -3\n```\n\nA DataDome interstitial body contains `captcha-delivery` and \"Please\nenable JS and disable any ad blocker\" — grep for that before trusting any\nHTML response as real content:\n\n```sh\ngrep -q 'captcha-delivery' /tmp/event.html && echo \"BOT WALL — reopen etix.com and let it clear\" || echo \"ok\"\n```\n\nFile v1.1.7:skill-card.md\n\n## Description:\n\nGuides agents to search public Etix events, venues, and performers and retrieve event and venue details through the fpx browser bridge instead of 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\nDevelopers and other agents use this skill to find public Etix events and venues from shell workflows without running the Etix MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The browser bridge grants fetch access and retains a local fpx pairing.\n\nMitigation: Limit the bridge profile to etix.com, verify the extension and package sources, and review the persistent pairing before installation.\n\nRisk: Use in a logged-in session could expose account or purchase actions beyond public event lookup.\n\nMitigation: Restrict use to anonymous Etix discovery; do not use the skill for purchases or logged-in account actions.\n\n## Reference(s):\n\n- [Etix consumer endpoints and extraction recipes](references/etix-endpoints.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [fetchproxy extension setup](https://github.com/chrischall/fetchproxy#extension)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Shell commands, JSON]\n\n**Output Format:** [Markdown guidance with shell commands and public event or venue data]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Public discovery only; no purchasing or logged-in account actions.]\n\n## Skill Version(s):\n\n1.1.7 (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.1.6: 5 files, 9195 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (3170b), skill-card.md (2179b), SKILL.md (4339b), _meta.json (127b)\n\nFile v1.1.6:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the ContextMint Bridge extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n(from https://github.com/nullnet-app/contextmint-bridge/releases — the fetchproxy\nextension under its new name, same maintainer, as https://github.com/chrischall/fetchproxy#extension\nsays; verify the zip with `shasum -a 256 -c <zip>.sha256` or build from source), with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.6:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.1.6\",\n  \"publishedAt\": 1791380098831\n}\n\nFile v1.1.6:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link?.text?.trim(),\n      start_date: row.querySelector(\"meta[itemprop=\\\"startDate\\\"]\")?.getAttribute(\"content\"),\n      when: row.querySelector(\".performance-datetime\")?.text?.trim(),\n    }));\n  }\n'\n```\n\nThe header Place is identified the same way `etix-mcp` does it: the first\nschema.org `Place` block that is NOT inside a `.row.performance` row.\nWithout any HTML parser on hand, a cruder `grep -oE` pass over the same\n`itemprop=\"...\"` attributes still gets the event ids + names — those\nattribute names are the load-bearing part; reproduce them with whatever\nHTML tool is available.\n\n`dataLayer` (org id/name, venue id/name) extracts the same way as §2.\n\n---\n\n## 4. Geolocation — `POST /ticket/api/online/geolocation/search`\n\nBody: `{ \"cityOrPostalCode\": \"<city or zip>\", \"country\": \"<country, default USA>\" }`.\n`keywords` is NOT a valid field — the endpoint 400s naming the two known\nproperties.\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '{latitude, longitude, city, state, postal_code: .postalCode}'\n```\n\n## 5. Healthcheck probe — `GET /robots.txt`\n\nA small public path to isolate \"bridge/extension down\" from \"DataDome\nchallenge on the tab\":\n\n```sh\nfpx get 'https://www.etix.com/robots.txt' -p etix | head -3\n```\n\nA DataDome interstitial body contains `captcha-delivery` and \"Please\nenable JS and disable any ad blocker\" — grep for that before trusting any\nHTML response as real content:\n\n```sh\ngrep -q 'captcha-delivery' /tmp/event.html && echo \"BOT WALL — reopen etix.com and let it clear\" || echo \"ok\"\n```\n\nFile v1.1.6:skill-card.md\n\n## Description:\n\nHelps agents find public Etix events, venues, and performers and retrieve event and venue details through a paired browser using the fpx CLI.\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 users can search public Etix listings and inspect event or venue details from an agent or shell without running the Etix MCP server. The skill is for anonymous discovery, not purchases or account actions.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Use requires installing a CLI and browser extension and pairing a browser tab.\n\nMitigation: Install only if comfortable with both components; verify the extension package or build from source, and limit pairing to etix.com.\n\nRisk: A successful request may return a bot-check page instead of event details.\n\nMitigation: Check the returned page before extracting or reporting event information.\n\nRisk: Discovery results could be mistaken for permission to buy tickets or act on an account.\n\nMitigation: Keep use limited to anonymous event discovery; do not perform purchases or account actions.\n\n## Reference(s):\n\n- [Etix FPX ClawHub release](https://clawhub.ai/chrischall/skills/etix-fpx)\n- [Etix consumer endpoint guide](references/etix-endpoints.md)\n- [Etix dataLayer extraction helper](references/extract-datalayer.mjs)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Shell commands, JSON]\n\n**Output Format:** [Markdown guidance with shell commands and public event data]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Read-only event discovery; no ticket purchases or account actions.]\n\n## Skill Version(s):\n\n1.1.6 (source: server-resolved 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.1.5: 5 files, 9261 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (3170b), skill-card.md (2353b), SKILL.md (4339b), _meta.json (127b)\n\nFile v1.1.5:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the ContextMint Bridge extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n(from https://github.com/nullnet-app/contextmint-bridge/releases — the fetchproxy\nextension under its new name, same maintainer, as https://github.com/chrischall/fetchproxy#extension\nsays; verify the zip with `shasum -a 256 -c <zip>.sha256` or build from source), with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.5:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.1.5\",\n  \"publishedAt\": 1791168519563\n}\n\nFile v1.1.5:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link?.text?.trim(),\n      start_date: row.querySelector(\"meta[itemprop=\\\"startDate\\\"]\")?.getAttribute(\"content\"),\n      when: row.querySelector(\".performance-datetime\")?.text?.trim(),\n    }));\n  }\n'\n```\n\nThe header Place is identified the same way `etix-mcp` does it: the first\nschema.org `Place` block that is NOT inside a `.row.performance` row.\nWithout any HTML parser on hand, a cruder `grep -oE` pass over the same\n`itemprop=\"...\"` attributes still gets the event ids + names — those\nattribute names are the load-bearing part; reproduce them with whatever\nHTML tool is available.\n\n`dataLayer` (org id/name, venue id/name) extracts the same way as §2.\n\n---\n\n## 4. Geolocation — `POST /ticket/api/online/geolocation/search`\n\nBody: `{ \"cityOrPostalCode\": \"<city or zip>\", \"country\": \"<country, default USA>\" }`.\n`keywords` is NOT a valid field — the endpoint 400s naming the two known\nproperties.\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '{latitude, longitude, city, state, postal_code: .postalCode}'\n```\n\n## 5. Healthcheck probe — `GET /robots.txt`\n\nA small public path to isolate \"bridge/extension down\" from \"DataDome\nchallenge on the tab\":\n\n```sh\nfpx get 'https://www.etix.com/robots.txt' -p etix | head -3\n```\n\nA DataDome interstitial body contains `captcha-delivery` and \"Please\nenable JS and disable any ad blocker\" — grep for that before trusting any\nHTML response as real content:\n\n```sh\ngrep -q 'captcha-delivery' /tmp/event.html && echo \"BOT WALL — reopen etix.com and let it clear\" || echo \"ok\"\n```\n\nFile v1.1.5:skill-card.md\n\n## Description:\n\nFind public Etix events, venues, and performers and retrieve event and venue details with fpx through a paired browser tab, without running 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\nDevelopers and other users can look up public Etix events, venues, and performers and extract event or venue details from a shell without running the Etix MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The globally installed CLI and browser extension require trust in their distribution sources.\n\nMitigation: Review the npm package and extension provenance before installation; verify the extension download or build it from source.\n\nRisk: The paired browser bridge can access sites permitted by its profile.\n\nMitigation: Keep the fpx profile limited to etix.com and use the skill only for public, anonymous discovery.\n\nRisk: Example commands may overwrite fixed filenames under /tmp.\n\nMitigation: Choose different output paths when those temporary filenames contain data you need to keep.\n\nRisk: Ticket purchases or authenticated account actions fall outside this lookup skill.\n\nMitigation: Do not use this skill for purchases or authenticated Etix account actions.\n\n## Reference(s):\n\n- [Etix endpoint and extraction reference](references/etix-endpoints.md)\n- [Etix dataLayer extraction helper](references/extract-datalayer.mjs)\n- [ClawHub release listing](https://clawhub.ai/chrischall/skills/etix-fpx)\n- [ContextMint Bridge extension releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration instructions, Guidance]\n\n**Output Format:** [Markdown with shell commands]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Includes steps for interpreting JSON search results and extracting event or venue details from HTML.]\n\n## Skill Version(s):\n\n1.1.5 (source: server-resolved 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.1.4: 5 files, 9171 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (3170b), skill-card.md (2091b), SKILL.md (4339b), _meta.json (127b)\n\nFile v1.1.4:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the ContextMint Bridge extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n(from https://github.com/nullnet-app/contextmint-bridge/releases — the fetchproxy\nextension under its new name, same maintainer, as https://github.com/chrischall/fetchproxy#extension\nsays; verify the zip with `shasum -a 256 -c <zip>.sha256` or build from source), with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.1.4\",\n  \"publishedAt\": 1790996026214\n}\n\nFile v1.1.4:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link?.text?.trim(),\n      start_date: row.querySelector(\"meta[itemprop=\\\"startDate\\\"]\")?.getAttribute(\"content\"),\n      when: row.querySelector(\".performance-datetime\")?.text?.trim(),\n    }));\n  }\n'\n```\n\nThe header Place is identified the same way `etix-mcp` does it: the first\nschema.org `Place` block that is NOT inside a `.row.performance` row.\nWithout any HTML parser on hand, a cruder `grep -oE` pass over the same\n`itemprop=\"...\"` attributes still gets the event ids + names — those\nattribute names are the load-bearing part; reproduce them with whatever\nHTML tool is available.\n\n`dataLayer` (org id/name, venue id/name) extracts the same way as §2.\n\n---\n\n## 4. Geolocation — `POST /ticket/api/online/geolocation/search`\n\nBody: `{ \"cityOrPostalCode\": \"<city or zip>\", \"country\": \"<country, default USA>\" }`.\n`keywords` is NOT a valid field — the endpoint 400s naming the two known\nproperties.\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '{latitude, longitude, city, state, postal_code: .postalCode}'\n```\n\n## 5. Healthcheck probe — `GET /robots.txt`\n\nA small public path to isolate \"bridge/extension down\" from \"DataDome\nchallenge on the tab\":\n\n```sh\nfpx get 'https://www.etix.com/robots.txt' -p etix | head -3\n```\n\nA DataDome interstitial body contains `captcha-delivery` and \"Please\nenable JS and disable any ad blocker\" — grep for that before trusting any\nHTML response as real content:\n\n```sh\ngrep -q 'captcha-delivery' /tmp/event.html && echo \"BOT WALL — reopen etix.com and let it clear\" || echo \"ok\"\n```\n\nFile v1.1.4:skill-card.md\n\n## Description:\n\nGuides agents to discover Etix events, venues, and performers and retrieve event or venue details through a paired browser using the fpx CLI.\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 search public Etix listings and retrieve event or venue information from a shell without running the Etix MCP server. It does not support ticket purchases.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Persistent browser pairing enables automated requests through a browser session that has passed Etix's bot check.\n\nMitigation: Confirm the automation is permitted for your use case, restrict extension site access, and revoke pairing or extension access when finished.\n\nRisk: The workflow requires an external CLI and browser extension with access to the browser session.\n\nMitigation: Review the CLI and extension before installation, and verify the extension package or build it from source.\n\n## Reference(s):\n\n- [Etix consumer endpoints and extraction recipes](references/etix-endpoints.md)\n- [Etix dataLayer extraction helper](references/extract-datalayer.mjs)\n- [ContextMint Bridge extension releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [etix-fpx ClawHub release](https://clawhub.ai/chrischall/skills/etix-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Text, JSON, Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell examples and structured event or venue details]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Search returns JSON; detail pages require extraction from HTML.]\n\n## Skill Version(s):\n\n1.1.4 (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.1.3: 5 files, 8981 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (2654b), skill-card.md (2143b), SKILL.md (4339b), _meta.json (127b)\n\nFile v1.1.3:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the ContextMint Bridge extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n(from https://github.com/nullnet-app/contextmint-bridge/releases — the fetchproxy\nextension under its new name, same maintainer, as https://github.com/chrischall/fetchproxy#extension\nsays; verify the zip with `shasum -a 256 -c <zip>.sha256` or build from source), with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.1.3\",\n  \"publishedAt\": 1790603795131\n}\n\nFile v1.1.3:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link?.text?.trim(),\n      start_date: row.querySelector(\"meta[itemprop=\\\"startDate\\\"]\")?.getAttribute(\"content\"),\n      when: row.querySelector(\".performance-datetime\")?.text?.trim(),\n    }));\n  }\n'\n```\n\nThe header Place is identified the same way `etix-mcp` does it: the first\nschema.org `Place` block that is NOT inside a `.row.performance` row.\nWithout any HTML parser on hand, a cruder `grep -oE` pass over the same\n`itemprop=\"...\"` attributes still gets the event ids + names — those\nattribute names are the load-bearing part; reproduce them with whatever\nHTML tool is available.\n\n`dataLayer` (org id/name, venue id/name) extracts the same way as §2.\n\n---\n\n## 4. Geolocation — `POST /ticket/api/online/geolocation/search`\n\nBody: `{ \"cityOrPostalCode\": \"<city or zip>\", \"country\": \"<country, default USA>\" }`.\n`keywords` is NOT a valid field — the endpoint 400s naming the two known\nproperties.\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '{latitude, longitude, city, state, postal_code: .postalCode}'\n```\n\n## 5. Healthcheck probe — `GET /robots.txt`\n\nA small public path to isolate \"bridge/extension down\" from \"DataDome\nchallenge on the tab\":\n\n```sh\nfpx get 'https://www.etix.com/robots.txt' -p etix | head -3\n```\n\nA DataDome interstitial body contains `captcha-delivery` and \"Please\nenable JS and disable any ad blocker\" — grep for that before trusting any\nHTML response as real content:\n\n```sh\ngrep -q 'captcha-delivery' /tmp/event.html && echo \"BOT WALL — reopen etix.com and let it clear\" || echo \"ok\"\n```\n\nFile v1.1.3:skill-card.md\n\n## Description:\n\nHelps agents search Etix events, venues, and performers and retrieve event or venue details using shell calls through a paired browser tab instead of 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\nDevelopers and other agents use this skill to discover public Etix event and venue information from a shell without running an Etix MCP server. It covers search and detail lookups, not ticket purchases.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Access uses a third-party browser bridge with persistent pairing and Etix site access.\n\nMitigation: Review the bridge before installation, verify the extension archive checksum or build from source, and pair only the intended Etix profile.\n\nRisk: Example commands store event searches and page contents in local temporary files.\n\nMitigation: Treat temporary outputs as local search traces and delete them when no longer needed.\n\n## Reference(s):\n\n- [Etix consumer endpoints and extraction examples](references/etix-endpoints.md)\n- [Etix dataLayer extraction helper](references/extract-datalayer.mjs)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [FetchProxy extension information](https://github.com/chrischall/fetchproxy#extension)\n- [Etix FPX skill release](https://clawhub.ai/chrischall/skills/etix-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance, Code]\n\n**Output Format:** [Markdown with shell and JavaScript examples; Etix responses may be JSON or HTML]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Event and venue detail may require extracting structured data from HTML.]\n\n## Skill Version(s):\n\n1.1.3 (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.1.2: 5 files, 8909 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (2654b), skill-card.md (2248b), SKILL.md (4053b), _meta.json (127b)\n\nFile v1.1.2:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the Transporter extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** browser extension installed, with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.1.2\",\n  \"publishedAt\": 1790199603812\n}\n\nFile v1.1.2:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link?.text?.trim(),\n      start_date: row.querySelector(\"meta[itemprop=\\\"startDate\\\"]\")?.getAttribute(\"content\"),\n      when: row.querySelector(\".performance-datetime\")?.text?.trim(),\n    }));\n  }\n'\n```\n\nThe header Place is identified the same way `etix-mcp` does it: the first\nschema.org `Place` block that is NOT inside a `.row.performance` row.\nWithout any HTML parser on hand, a cruder `grep -oE` pass over the same\n`itemprop=\"...\"` attributes still gets the event ids + names — those\nattribute names are the load-bearing part; reproduce them with whatever\nHTML tool is available.\n\n`dataLayer` (org id/name, venue id/name) extracts the same way as §2.\n\n---\n\n## 4. Geolocation — `POST /ticket/api/online/geolocation/search`\n\nBody: `{ \"cityOrPostalCode\": \"<city or zip>\", \"country\": \"<country, default USA>\" }`.\n`keywords` is NOT a valid field — the endpoint 400s naming the two known\nproperties.\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '{latitude, longitude, city, state, postal_code: .postalCode}'\n```\n\n## 5. Healthcheck probe — `GET /robots.txt`\n\nA small public path to isolate \"bridge/extension down\" from \"DataDome\nchallenge on the tab\":\n\n```sh\nfpx get 'https://www.etix.com/robots.txt' -p etix | head -3\n```\n\nA DataDome interstitial body contains `captcha-delivery` and \"Please\nenable JS and disable any ad blocker\" — grep for that before trusting any\nHTML response as real content:\n\n```sh\ngrep -q 'captcha-delivery' /tmp/event.html && echo \"BOT WALL — reopen etix.com and let it clear\" || echo \"ok\"\n```\n\nFile v1.1.2:skill-card.md\n\n## Description:\n\nQuery etix.com event-ticketing data from a shell with the fpx CLI, using a signed-in browser tab to search events, venues, and performers and retrieve event or venue detail without running the etix-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\nExternal developers and agents use this skill to query public Etix event-discovery data from shell workflows when the MCP server is not installed or not desired. It supports resolving IDs, fetching event and venue details, and parsing JSON or HTML response data through fpx.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Etix requests are routed through the Transporter browser extension and a paired fpx profile, which can expose browser session context to the workflow.\n\nMitigation: Use a dedicated browser profile or Etix tab, limit Chrome site access to etix.com, and install only if this routing model is acceptable.\n\nRisk: Example commands write fetched Etix HTML or JSON to /tmp files.\n\nMitigation: Delete temporary files after use, especially on shared machines.\n\nRisk: Etix HTML fetches can succeed at the transport layer while returning a DataDome interstitial instead of event or venue content.\n\nMitigation: Check for captcha-delivery or similar interstitial text before parsing saved HTML.\n\n## Reference(s):\n\n- [Etix consumer endpoints for fpx](references/etix-endpoints.md)\n- [extract-datalayer.mjs](references/extract-datalayer.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [markdown, shell commands, code, configuration, guidance]\n\n**Output Format:** [Markdown with shell commands and JavaScript snippets]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Guidance may produce fpx command output as JSON or parsed HTML-derived event and venue data.]\n\n## Skill Version(s):\n\n1.1.2 (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.1: 5 files, 8319 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (1542b), skill-card.md (2245b), SKILL.md (4053b), _meta.json (127b)\n\nFile v1.1.1:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the Transporter extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** browser extension installed, with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.1.1\",\n  \"publishedAt\": 1790178462854\n}\n\nFile v1.1.1:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link?.text?.trim(),\n      start_date: row.querySelector(\"meta[itemprop=\\\"startDate\\\"]\")?.getAttribute(\"content\"),\n      when: row.querySelector(\".performance-datetime\")?.text?.trim(),\n    }));\n  }\n'\n```\n\nThe header Place is identified the same way `etix-mcp` does it: the first\nschema.org `Place` block that is NOT inside a `.row.performance` row.\nWithout any HTML parser on hand, a cruder `grep -oE` pass over the same\n`itemprop=\"...\"` attributes still gets the event ids + names — those\nattribute names are the load-bearing part; reproduce them with whatever\nHTML tool is available.\n\n`dataLayer` (org id/name, venue id/name) extracts the same way as §2.\n\n---\n\n## 4. Geolocation — `POST /ticket/api/online/geolocation/search`\n\nBody: `{ \"cityOrPostalCode\": \"<city or zip>\", \"country\": \"<country, default USA>\" }`.\n`keywords` is NOT a valid field — the endpoint 400s naming the two known\nproperties.\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '{latitude, longitude, city, state, postal_code: .postalCode}'\n```\n\n## 5. Healthcheck probe — `GET /robots.txt`\n\nA small public path to isolate \"bridge/extension down\" from \"DataDome\nchallenge on the tab\":\n\n```sh\nfpx get 'https://www.etix.com/robots.txt' -p etix | head -3\n```\n\nA DataDome interstitial body contains `captcha-delivery` and \"Please\nenable JS and disable any ad blocker\" — grep for that before trusting any\nHTML response as real content:\n\n```sh\ngrep -q 'captcha-delivery' /tmp/event.html && echo \"BOT WALL — reopen etix.com and let it clear\" || echo \"ok\"\n```\n\nFile v1.1.1:skill-card.md\n\n## Description:\n\nQuery Etix event-ticketing data from a shell with the fpx CLI to search events, venues, and performers and retrieve event or venue details through a signed-in browser tab.\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 external users use this skill to run public Etix discovery lookups from scripts or agent sessions without running an Etix MCP server. It supports searching Etix events, venues, and performers and extracting event, venue, and geolocation details.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The workflow depends on fetchproxy and a paired browser extension for etix.com.\n\nMitigation: Install and pair only in environments where using a browser-mediated fetch bridge is approved.\n\nRisk: Search terms and city or postal-code inputs are sent to Etix.\n\nMitigation: Treat those values as intentionally disclosed to Etix and avoid submitting sensitive personal data.\n\nRisk: Ticket purchasing and credentialed seller APIs are out of scope.\n\nMitigation: Use the skill only for anonymous discovery reads and do not use it for financial actions or venue credential workflows.\n\n## Reference(s):\n\n- [Etix consumer endpoints for fpx](references/etix-endpoints.md)\n- [extract-datalayer.mjs](references/extract-datalayer.mjs)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/etix-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, code, configuration, guidance]\n\n**Output Format:** [Markdown guidance with shell commands, jq filters, JSON examples, and Node.js extraction snippets]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Produces instructions for anonymous read-only Etix discovery requests through fpx and local parsing of JSON or HTML responses.]\n\n## Skill Version(s):\n\n1.1.1 (source: server 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.1.0: 5 files, 8308 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (1542b), skill-card.md (2284b), SKILL.md (4053b), _meta.json (127b)\n\nFile v1.1.0:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the Transporter extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** browser extension installed, with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.1.0\",\n  \"publishedAt\": 1789872550357\n}\n\nFile v1.1.0:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link?.text?.trim(),\n      start_date: row.querySelector(\"meta[itemprop=\\\"startDate\\\"]\")?.getAttribute(\"content\"),\n      when: row.querySelector(\".performance-datetime\")?.text?.trim(),\n    }));\n  }\n'\n```\n\nThe header Place is identified the same way `etix-mcp` does it: the first\nschema.org `Place` block that is NOT inside a `.row.performance` row.\nWithout any HTML parser on hand, a cruder `grep -oE` pass over the same\n`itemprop=\"...\"` attributes still gets the event ids + names — those\nattribute names are the load-bearing part; reproduce them with whatever\nHTML tool is available.\n\n`dataLayer` (org id/name, venue id/name) extracts the same way as §2.\n\n---\n\n## 4. Geolocation — `POST /ticket/api/online/geolocation/search`\n\nBody: `{ \"cityOrPostalCode\": \"<city or zip>\", \"country\": \"<country, default USA>\" }`.\n`keywords` is NOT a valid field — the endpoint 400s naming the two known\nproperties.\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '{latitude, longitude, city, state, postal_code: .postalCode}'\n```\n\n## 5. Healthcheck probe — `GET /robots.txt`\n\nA small public path to isolate \"bridge/extension down\" from \"DataDome\nchallenge on the tab\":\n\n```sh\nfpx get 'https://www.etix.com/robots.txt' -p etix | head -3\n```\n\nA DataDome interstitial body contains `captcha-delivery` and \"Please\nenable JS and disable any ad blocker\" — grep for that before trusting any\nHTML response as real content:\n\n```sh\ngrep -q 'captcha-delivery' /tmp/event.html && echo \"BOT WALL — reopen etix.com and let it clear\" || echo \"ok\"\n```\n\nFile v1.1.0:skill-card.md\n\n## Description:\n\nQuery public etix.com event discovery data from a shell with the @fetchproxy/cli fpx tool through a signed-in browser tab.\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 automation users use this skill to retrieve public Etix event, venue, performer, and geolocation discovery data from shell scripts without running the etix-mcp server. The documented scope is anonymous discovery reads only, not ticket purchasing or account actions.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill depends on a persistent paired browser fetch profile and Transporter extension access for etix.com.\n\nMitigation: Install and pair fpx only in an environment where that browser-extension access is acceptable, and keep the profile scoped to etix.com fetch use.\n\nRisk: Etix HTML responses can still be a DataDome interstitial even when the fetch command exits successfully.\n\nMitigation: Check fetched HTML for DataDome challenge text before parsing or trusting event and venue detail output.\n\nRisk: Ticket purchasing and account actions are financial or account workflows outside the documented skill scope.\n\nMitigation: Use the skill only for anonymous public discovery reads and avoid purchase, login, or account-management actions.\n\n## Reference(s):\n\n- [Etix consumer endpoints for fpx](references/etix-endpoints.md)\n- [extract-datalayer.mjs](references/extract-datalayer.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with shell commands, JSON extraction examples, and configuration steps]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires the fpx CLI, the Transporter browser extension, and a paired etix.com browser profile for live fetches.]\n\n## Skill Version(s):\n\n1.1.0 (source: server 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.0.0: 5 files, 8525 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (1542b), skill-card.md (2797b), SKILL.md (4053b), _meta.json (127b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the Transporter extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** browser extension installed, with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1789688212617\n}\n\nFile v1.0.0:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link?.text?.trim(),\n      start_date: row.querySelector(\"meta[itemprop=\\\"startDate\\\"]\")?.getAttribute(\"content\"),\n      when: row.querySelector(\".performance-datetime\")?.text?.trim(),\n    }));\n  }\n'\n```\n\nThe header Place is identified the same way `etix-mcp` does it: the first\nschema.org `Place` block that is NOT inside a `.row.performance` row.\nWithout any HTML parser on hand, a cruder `grep -oE` pass over the same\n`itemprop=\"...\"` attributes still gets the event ids + names — those\nattribute names are the load-bearing part; reproduce them with whatever\nHTML tool is available.\n\n`dataLayer` (org id/name, venue id/name) extracts the same way as §2.\n\n---\n\n## 4. Geolocation — `POST /ticket/api/online/geolocation/search`\n\nBody: `{ \"cityOrPostalCode\": \"<city or zip>\", \"country\": \"<country, default USA>\" }`.\n`keywords` is NOT a valid field — the endpoint 400s naming the two known\nproperties.\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '{latitude, longitude, city, state, postal_code: .postalCode}'\n```\n\n## 5. Healthcheck probe — `GET /robots.txt`\n\nA small public path to isolate \"bridge/extension down\" from \"DataDome\nchallenge on the tab\":\n\n```sh\nfpx get 'https://www.etix.com/robots.txt' -p etix | head -3\n```\n\nA DataDome interstitial body contains `captcha-delivery` and \"Please\nenable JS and disable any ad blocker\" — grep for that before trusting any\nHTML response as real content:\n\n```sh\ngrep -q 'captcha-delivery' /tmp/event.html && echo \"BOT WALL — reopen etix.com and let it clear\" || echo \"ok\"\n```\n\nFile v1.0.0:skill-card.md\n\n## Description:\n\nQuery etix.com event, venue, and performer discovery data from a shell with the fpx CLI instead of running the etix-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 generate fpx commands and parsing recipes for public Etix discovery data, including search suggestions, event detail, venue detail, and geolocation lookups. It is intended for anonymous discovery reads and explicitly excludes ticket purchasing or seller API workflows.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Pairing Fetchproxy/Transporter with an etix.com-scoped profile sends search terms and geolocation inputs to Etix through the user's browser session.\n\nMitigation: Install only when comfortable with that scoped pairing, keep the profile limited to etix.com, and treat search and location inputs as data sent to Etix.\n\nRisk: Fetched event or venue pages may be saved to temporary files that remain on disk.\n\nMitigation: Remove temporary files when fetched event or venue content should not persist locally.\n\nRisk: Etix HTML responses can be DataDome interstitial pages even when the fetch command exits successfully.\n\nMitigation: Check HTML responses for captcha-delivery or the DataDome enable-JS message before parsing them as event or venue content.\n\nRisk: Ticket purchasing and seller API use could involve financial actions or venue credentials outside the skill's intended scope.\n\nMitigation: Use the skill only for anonymous public discovery reads and do not use it for purchases or OAuth-protected seller API workflows.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/etix-fpx)\n- [Publisher profile](https://clawhub.ai/user/chrischall)\n- [Etix consumer endpoints for fpx](artifact/references/etix-endpoints.md)\n- [Etix dataLayer extractor](artifact/references/extract-datalayer.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown guidance with shell commands, JSON extraction examples, and small JavaScript snippets]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include fpx commands, jq filters, temporary file paths, endpoint paths, and troubleshooting guidance for DataDome or Fetchproxy bridge failures.]\n\n## Skill Version(s):\n\n1.0.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 v0.6.4: 5 files, 8329 bytes\n\nFiles: references/etix-endpoints.md (7897b), references/extract-datalayer.mjs (1542b), skill-card.md (2267b), SKILL.md (4053b), _meta.json (127b)\n\nFile v0.6.4:SKILL.md\n\n---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the Transporter extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** browser extension installed, with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never a name. Always hit\n`search/suggest` first, take the `eventId`/`venueId`, then fetch detail:\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'\n```\n\n## What's NOT usable\n\nThe full-text `POST /ticket/api/online/search` endpoint returns an\n**encrypted/opaque (base64-looking) payload**, not JSON — `etix-mcp`\ndeliberately doesn't use it, and neither should this skill. Stick to\n`search/suggest` (clean JSON) for search.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. For HTML pages, a `0` can still be a DataDome interstitial\n  body (check for `captcha-delivery` / \"Please enable JS and disable any\n  ad blocker\" before parsing).\n- `2` — bridge unavailable: extension not connected or pairing pending →\n  run `fpx pair -p etix`, confirm an etix.com tab is open.\n- `3` — bot wall: the tab hasn't cleared DataDome → open/refresh a\n  `www.etix.com` tab, let it finish loading, and retry.\n- `4` — upstream non-2xx from Etix.\n\n## Notes\n\n- Anonymous discovery reads only — no Etix login, no purchasing (buying\n  tickets is a financial action and out of scope for this skill).\n- `fpx health -p etix` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v0.6.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"0.6.4\",\n  \"publishedAt\": 1789500309354\n}\n\nFile v0.6.4:references/etix-endpoints.md\n\n# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.parse(b[1]);\n    const entity = doc[\"@graph\"]?.find(e => e[\"@type\"] === \"Event\")\n      ?? (doc.mainEntity?.[\"@type\"] === \"Event\" ? doc.mainEntity : undefined)\n      ?? (doc[\"@type\"] === \"Event\" ? doc : undefined);\n    if (entity) { console.log(JSON.stringify(entity)); break; }\n  }\n' > /tmp/event.jsonld\n\njq '{name, url, start_date: .startDate, status: .eventStatus,\n     venue: .location.name, city: .location.address.addressLocality,\n     state: .location.address.addressRegion, lat: .location.geo.latitude,\n     lng: .location.geo.longitude,\n     offers: [.offers.offers[] | {name, price, currency: .priceCurrency, availability}]}' \\\n  /tmp/event.jsonld\n```\n\nPull the numeric org id + cobrand out of the page's `dataLayer` (it's\nsingle-quoted `'key' : 'value'` pairs spread across multiple lines, not\nJSON, so `jq` can't touch it directly — and a single-line `grep -oE` can't\neither: grep's `.` doesn't match newlines and grep has no lazy quantifier,\nso a `dataLayer\\s*=\\s*\\[.*?\\]` pattern never matches the real multi-line\nblock and silently returns nothing):\n\n```sh\nnode references/extract-datalayer.mjs /tmp/event.html | jq '.'\n# {\"org_id\":\"1744\",\"org_name\":\"...\",\"venue_id\":\"4332\",\"venue_name\":\"...\",\"cobrand\":\"ncjazz\",...}\n```\n\n`references/extract-datalayer.mjs` is a small, dependency-free port of\n`extractDataLayer` from `etix-mcp`'s own `src/parse.ts` — its regex spans\nnewlines lazily (`[\\s\\S]*?`), which grep's ERE dialect cannot express.\n\nPrice range is derived locally from `offers[].price` (min/max) — there's no\nseparate range field.\n\n---\n\n## 3. Venue detail — `GET /ticket/v/<venueId>` → SSR HTML\n\nCarries a header `<div itemscope itemtype=\"http://schema.org/Place\">` (the\nvenue's own address microdata) plus a list of\n`<div class=\"row performance\" itemscope itemtype=\"http://schema.org/MusicEvent\">`\nrows, one per upcoming event. Venue identity (id/name/org) also rides the\npage's `dataLayer` (same format as §2).\n\n```sh\nfpx get 'https://www.etix.com/ticket/v/17987' -p etix > /tmp/venue.html\n```\n\nMicrodata isn't JSON — `node-html-parser` (the same package `etix-mcp`\nparses this with) is a one-time local install away, no repo checkout\nneeded:\n\n```sh\nmkdir -p /tmp/etix-parse && cd /tmp/etix-parse && npm install --no-save node-html-parser >/dev/null 2>&1\n\nnode -e '\n  const { parse } = require(\"node-html-parser\");\n  const html = require(\"fs\").readFileSync(\"/tmp/venue.html\", \"utf8\");\n  const root = parse(html);\n  const places = root.querySelectorAll(\"[itemtype$=\\\"/Place\\\"]\");\n  const header = places.find(p => !p.closest(\".row.performance\"));\n  const text = (el, prop) => {\n    const n = el?.querySelector(`[itemprop=\"${prop}\"]`);\n    return (n?.getAttribute(\"content\") ?? n?.text)?.trim();\n  };\n  console.log(JSON.stringify({\n    name: text(header, \"name\"),\n    street: text(header, \"streetAddress\"),\n    city: text(header, \"addressLocality\"),\n    state: text(header, \"addressRegion\"),\n    postal_code: text(header, \"postalCode\"),\n  }));\n  const rows = root.querySelectorAll(\".row.performance\");\n  for (const row of rows) {\n    const link = row.querySelector(\".performance-name a\");\n    console.log(JSON.stringify({\n      href: link?.getAttribute(\"href\"),\n      name: link","readmeExcerpt":"Skill: etix-fpx Owner: chrischall Summary: Query etix.com (event ticketing) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the etix-mcp server — search events, venues, and performers, and pull event/venue detail, via one-shot calls through a signed-in browser tab. Use when you want Etix discovery data without the MCP, in a script, or on a machine where the MCP isn't installed. Tags: latest:1.1.7 V","codeSnippets":[],"executableExamples":[{"language":"sh","snippet":"npm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in ContextMint Bridge"},{"language":"sh","snippet":"# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html"},{"language":"sh","snippet":"printf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'"},{"language":"sh","snippet":"fpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=Marion+Meadows' -p etix \\\n  | jq -r '.events[] | \"\\(.eventId)\\t\\(.eventName)\"'"},{"language":"sh","snippet":"fpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json"},{"language":"jsonc","snippet":"{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: etix-fpx\ndescription: >-\n  Query etix.com (event ticketing) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the etix-mcp server — search events,\n  venues, and performers, and pull event/venue detail, via one-shot calls\n  through a signed-in browser tab. Use when you want Etix discovery data\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# Etix via fpx (no MCP)\n\nEtix fronts its whole consumer surface (`www.etix.com/ticket/...`) with a\n**DataDome** bot-wall that 403s/202s any plain `curl`/Node request\n(`captcha-delivery` interstitial). `fpx` routes the request through the\nuser's own signed-in browser tab (the ContextMint Bridge extension), which has\nalready cleared DataDome, so the same request succeeds. No Etix account is\nneeded — this is public event-discovery data.\n\nEtix also publishes a documented OpenAPI seller API (`api.etix.com/v3`),\nbut every endpoint requires OAuth2 password-grant with venue/box-office\ncredentials — it's out of reach for a consumer and out of scope here.\n\nThis is the same data the `etix_*` MCP tools return, reached with one CLI\ncall instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add etix --domain etix.com      # only the fetch capability is needed\nfpx pair -p etix                            # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n(from https://github.com/nullnet-app/contextmint-bridge/releases — the fetchproxy\nextension under its new name, same maintainer, as https://github.com/chrischall/fetchproxy#extension\nsays; verify the zip with `shasum -a 256 -c <zip>.sha256` or build from source), with an open\n`www.etix.com` tab that has finished loading (so the DataDome check\nclears), and Chrome **Site access** allowing `etix.com`. Pairing persists —\nafter the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo response shapes, both fetched with the plain `get` verb:\n\n```sh\n# Clean JSON — pipe straight to jq\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix \\\n  | jq '.'\n\n# Server-rendered HTML — save it, then pull the embedded JSON-LD/microdata\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nA `POST` (only `geolocation/search` needs one) uses `post-json`:\n\n```sh\nprintf '{\"cityOrPostalCode\":\"Charlotte, NC\",\"country\":\"USA\"}' > /tmp/geo.json\nfpx post-json 'https://www.etix.com/ticket/api/online/geolocation/search' @/tmp/geo.json -p etix \\\n  | jq '.'\n```\n\nReady-to-run paths + extraction recipes (JSON `jq`, HTML JSON-LD/microdata\nvia a small `node` one-liner, and the page's `dataLayer` analytics object\nvia `references/extract-datalayer.mjs` — the same shapes `etix-mcp`'s\n`src/parse.ts` parses) are in `references/etix-endpoints.md`.\n\n## The one rule: resolve ids first\n\nEvent/venue detail take a numeric id, never "},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"etix-fpx\",\n  \"version\": \"1.1.7\",\n  \"publishedAt\": 1791588249234\n}"},{"path":"references/etix-endpoints.md","content":"# Etix consumer endpoints for fpx\n\nReady-to-run request paths for\n`fpx get|post-json 'https://www.etix.com<path>' -p etix`. All paths, field\nnames, and shapes are transcribed from `etix-mcp`'s `docs/ETIX-API.md` and\n`src/parse.ts` — live-verified 2026-06-12 against venue `17987` (Middle C\nJazz Club) and performance `39004863` (NC Jazz Festival). Nothing here is\nguessed.\n\nEndpoint host: `https://www.etix.com`. Auth: none — every call still rides\nthe fetchproxy bridge because the whole zone sits behind DataDome, not\nbecause these routes need an Etix account.\n\n---\n\n## 1. Search — `GET /ticket/api/online/search/suggest?keywords=<kw>` (do this first)\n\nClean JSON. Returns matching venues, events (performances), and performers.\nUse this to resolve a name to an id before calling event/venue detail.\n\n```sh\nfpx get 'https://www.etix.com/ticket/api/online/search/suggest?keywords=jazz' -p etix > /tmp/suggest.json\njq '.' /tmp/suggest.json\n```\n\nShape:\n\n```jsonc\n{\n  \"keywords\": \"jazz\",\n  \"venues\":     [ { \"venueId\": 17987, \"venueName\": \"...\", \"organization\": \"...\",\n                    \"address\": \"...\", \"city\": \"...\", \"state\": \"...\", \"country\": \"USA\",\n                    \"venueSaleUrl\": \"/ticket/v/17987/middle-c-jazz-club\" } ],\n  \"events\":     [ { \"eventId\": 39004863, \"eventName\": \"...\", \"categoryName\": \"Concerts\",\n                    \"dateTime\": \"2027-02-05T00:30:00Z\", \"venueId\": 4332, \"venueName\": \"...\",\n                    \"city\": \"...\", \"state\": \"...\", \"imageUrl\": \"https://cdn.etix.com/...\",\n                    \"directSaleUrl\": \"/ticket/p/39004863/...\" } ],\n  \"performers\": [ { \"id\": 103251, \"name\": \"Marion Meadows\", \"muzookaId\": \"mzoMB0Ym8l\",\n                    \"imageUrl\": \"https://www.etix.com/ticket/api/online/search/muzooka/.../image\" } ]\n}\n```\n\n`performers` uses `id`/`name` — NOT `artistId`/`artistName` — and only\npopulates on a close artist match.\n\n```sh\njq -r '.events[]     | \"\\(.eventId)\\t\\(.eventName)\\t\\(.dateTime)\"' /tmp/suggest.json\njq -r '.venues[]     | \"\\(.venueId)\\t\\(.venueName)\\t\\(.city), \\(.state)\"' /tmp/suggest.json\njq -r '.performers[] | \"\\(.id)\\t\\(.name)\"' /tmp/suggest.json\n```\n\n**Do NOT use** `POST /ticket/api/online/search` (the full-text search) — it\nreturns an encrypted/opaque payload, not JSON.\n\n---\n\n## 2. Event detail — `GET /ticket/p/<eventId>` → SSR HTML\n\nThe bare numeric id resolves without the URL slug. No clean JSON endpoint\nexists; the page carries a schema.org **JSON-LD `Event`** block (sometimes\nnested under `WebPage.mainEntity`) plus a single-quoted `dataLayer`\nanalytics object and an `og:image` meta tag.\n\n```sh\nfpx get 'https://www.etix.com/ticket/p/39004863' -p etix > /tmp/event.html\n```\n\nExtract the JSON-LD (it's real JSON once pulled out of the `<script>` tag —\npipe straight to `jq`):\n\n```sh\nnode -e '\n  const html = require(\"fs\").readFileSync(\"/tmp/event.html\", \"utf8\");\n  const blocks = [...html.matchAll(/<script type=\"application\\/ld\\+json\">([\\s\\S]*?)<\\/script>/g)];\n  for (const b of blocks) {\n    const doc = JSON.pars"},{"path":"skill-card.md","content":"## Description:\n\nGuides agents to search public Etix events, venues, and performers and retrieve event and venue details through the fpx browser bridge instead of 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\nDevelopers and other agents use this skill to find public Etix events and venues from shell workflows without running the Etix MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The browser bridge grants fetch access and retains a local fpx pairing.\n\nMitigation: Limit the bridge profile to etix.com, verify the extension and package sources, and review the persistent pairing before installation.\n\nRisk: Use in a logged-in session could expose account or purchase actions beyond public event lookup.\n\nMitigation: Restrict use to anonymous Etix discovery; do not use the skill for purchases or logged-in account actions.\n\n## Reference(s):\n\n- [Etix consumer endpoints and extraction recipes](references/etix-endpoints.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [fetchproxy extension setup](https://github.com/chrischall/fetchproxy#extension)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Shell commands, JSON]\n\n**Output Format:** [Markdown guidance with shell commands and public event or venue data]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Public discovery only; no purchasing or logged-in account actions.]\n\n## Skill Version(s):\n\n1.1.7 (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."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1459,"uniquenessScore":44,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T16:31:10.761Z","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:31:10.761Z","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:53:43.329Z","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"}]}}}