{"id":"950e006c-ce5c-4fd3-9126-ea0f70ba783d","entityType":"agent","slug":"clawhub-chrischall-accessoticketing-mcp","name":"accessoticketing-mcp","canonicalUrl":"https://www.xpersona.co/agent/clawhub-chrischall-accessoticketing-mcp","canonicalPath":"/agent/clawhub-chrischall-accessoticketing-mcp","generatedAt":"2026-10-12T12:09:00.292Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T21:30:43.624Z","emptyReason":null},"description":"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl. Skill: accessoticketing-mcp Owner: chrischall Summary: Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl. Tags: latest:1.1.0 Version history: v1.1.0 | 2026-10-09T23:26:59.659Z | auto Version 1.1.0 - Added logic to ensure barcode PNG files written by --barcodes","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 990 downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:accessoticketing-mcp","sourceUrl":"https://clawhub.ai/chrischall/accessoticketing-mcp","homepage":"https://clawhub.ai/chrischall/skills/accessoticketing-mcp","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/chrischall/accessoticketing-mcp","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/chrischall/skills/accessoticketing-mcp","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":60,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T21:30:43.624Z","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-11T21:30:43.624Z","emptyReason":null},"stars":null,"forks":null,"downloads":990,"likes":null,"task":null,"library":null,"packageName":null,"latestVersion":"1.1.0","tractionLabel":"990 downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T21:30:43.611Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T21:30:43.624Z","lastCrawledAt":"2026-10-11T21:30:43.611Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T21:30:43.611Z","lastVerifiedAt":null,"highlights":[{"version":"1.1.0","createdAt":"2026-10-09T23:26:59.659Z","changelog":"Version 1.1.0 - Added logic to ensure barcode PNG files written by --barcodes are never overwritten; repeated runs now write incrementing filenames (…-2.png, …-3.png, etc.). - Updated documentation to describe non-overwriting barcode file behavior. - Removed the redundant skill-card.md file.","fileCount":5,"zipByteSize":11269},{"version":"1.0.6","createdAt":"2026-10-07T13:37:08.202Z","changelog":"- Removed the file skill-card.md. - No changes to skill functionality, description, or usage documentation.","fileCount":5,"zipByteSize":10990},{"version":"1.0.5","createdAt":"2026-10-05T02:47:38.467Z","changelog":"- Removed the sample file skill-card.md. - No changes to functionality or documentation content.","fileCount":5,"zipByteSize":10984},{"version":"1.0.4","createdAt":"2026-10-03T01:38:58.119Z","changelog":"- Removed the file skill-card.md. - No functional or user-facing changes were made; documentation only.","fileCount":5,"zipByteSize":11023},{"version":"1.0.3","createdAt":"2026-09-25T15:52:48.494Z","changelog":"- Removed the sample file skill-card.md. - No changes to functionality or user-facing documentation.","fileCount":5,"zipByteSize":10985},{"version":"1.0.2","createdAt":"2026-09-23T21:40:55.066Z","changelog":"- Removed the sample skill-card.md documentation file. - No changes to core logic or features.","fileCount":5,"zipByteSize":11217},{"version":"1.0.1","createdAt":"2026-09-23T15:45:35.655Z","changelog":"- Removed the file skill-card.md. - No functional or behavioral changes to the skill code.","fileCount":5,"zipByteSize":11141},{"version":"1.0.0","createdAt":"2026-09-20T02:51:32.204Z","changelog":"- Removed the sample skill card file (skill-card.md). - No functional or user-facing changes; this is a housekeeping update.","fileCount":5,"zipByteSize":11203}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:accessoticketing-mcp","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"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-accessoticketing-mcp/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-accessoticketing-mcp/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-accessoticketing-mcp/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-accessoticketing-mcp/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-accessoticketing-mcp/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-accessoticketing-mcp/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-12T12:09:00.289Z"}},"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-accessoticketing-mcp/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-accessoticketing-mcp/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-accessoticketing-mcp/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-accessoticketing-mcp/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":"high","updatedAt":"2026-10-11T21:30:43.624Z","emptyReason":null},"readme":"Skill: accessoticketing-mcp\n\nOwner: chrischall\n\nSummary: Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\n\nTags: latest:1.1.0\n\nVersion history:\n\nv1.1.0 | 2026-10-09T23:26:59.659Z | auto\n\nVersion 1.1.0\n\n- Added logic to ensure barcode PNG files written by --barcodes are never overwritten; repeated runs now write incrementing filenames (…-2.png, …-3.png, etc.).\n- Updated documentation to describe non-overwriting barcode file behavior.\n- Removed the redundant skill-card.md file.\n\nv1.0.6 | 2026-10-07T13:37:08.202Z | auto\n\n- Removed the file skill-card.md.\n- No changes to skill functionality, description, or usage documentation.\n\nv1.0.5 | 2026-10-05T02:47:38.467Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to functionality or documentation content.\n\nv1.0.4 | 2026-10-03T01:38:58.119Z | auto\n\n- Removed the file skill-card.md.\n- No functional or user-facing changes were made; documentation only.\n\nv1.0.3 | 2026-09-25T15:52:48.494Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to functionality or user-facing documentation.\n\nv1.0.2 | 2026-09-23T21:40:55.066Z | auto\n\n- Removed the sample skill-card.md documentation file.\n- No changes to core logic or features.\n\nv1.0.1 | 2026-09-23T15:45:35.655Z | auto\n\n- Removed the file skill-card.md.\n- No functional or behavioral changes to the skill code.\n\nv1.0.0 | 2026-09-20T02:51:32.204Z | auto\n\n- Removed the sample skill card file (skill-card.md).\n- No functional or user-facing changes; this is a housekeeping update.\n\nv0.3.0 | 2026-09-17T23:35:55.047Z | auto\n\n- Removed the skill-card.md file.\n- No changes to skill functionality or usage.\n\nv0.2.1 | 2026-09-10T17:53:07.061Z | auto\n\n- Removed file: skill-card.md.  \n- No functionality or interface changes.  \n- Documentation and core usage remain unchanged.\n\nv0.2.0 | 2026-09-04T22:24:25.644Z | auto\n\n- Removed the `skill-card.md` file.\n- No user-facing or functional changes to the core skill.\n\nv0.1.1 | 2026-08-19T02:17:25.920Z | auto\n\n- Changed implementation in references/parse-tickets.mjs (details not specified).\n- Removed skill-card.md file.\n- No changes to user-facing documentation or behavior described in SKILL.md.\n\nv0.1.0 | 2026-08-16T20:58:20.377Z | auto\n\n- Initial release: accesso-tickets skill for reading mobile ticket links from accessoticketing.com (media-engine URLs).\n- Extracts order details, per-ticket info (product, participant, date/time), barcodes, and Google Wallet passes using curl — no login or browser required.\n- Parses credentialed ticket links directly; emphasizes security of URLs.\n- Outputs JSON with detailed order and ticket fields, including handling extra merchant-specific fields generically.\n- Includes options to save barcodes as PNGs and to show terms and conditions.\n- Handles known parsing challenges: duplicated elements, non-standard HTTP errors, and positional ticket ID mapping.\n\nArchive index:\n\nArchive v1.1.0: 5 files, 11269 bytes\n\nFiles: references/parse-tickets.mjs (12419b), references/recipes.md (5610b), skill-card.md (1689b), SKILL.md (3382b), _meta.json (139b)\n\nFile v1.1.0:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`; an existing file is never overwritten — a re-run writes `…-2.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v1.1.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"1.1.0\",\n  \"publishedAt\": 1791588419659\n}\n\nFile v1.1.0:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v1.1.0:skill-card.md\n\n## Description:\n\nReads accesso mobile-ticket links to extract order and ticket details, barcodes, and Google Wallet pass links.\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\nTicket holders and their agents use the skill to inspect accesso confirmation links, summarize ticket times and participants, and retrieve barcodes or Google Wallet pass links.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket URLs, barcodes, parsed JSON, and wallet links may function as live ticket credentials.\n\nMitigation: Keep them out of issues, commits, shared chats, logs, screenshots, and synced folders; delete exported barcode files when no longer needed.\n\n## Reference(s):\n\n- [ClawHub skill release](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n- [Ticket parsing recipes](references/recipes.md)\n- [Ticket parser](references/parse-tickets.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, JSON, PNG images]\n\n**Output Format:** [Text and shell commands; structured JSON ticket details and optional PNG barcodes]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Ticket links, parsed details, barcode files, and wallet links can grant access to tickets.]\n\n## Skill Version(s):\n\n1.1.0 (source: ClawHub 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.0.6: 5 files, 10990 bytes\n\nFiles: references/parse-tickets.mjs (11831b), references/recipes.md (5610b), skill-card.md (1790b), SKILL.md (3311b), _meta.json (139b)\n\nFile v1.0.6:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v1.0.6:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"1.0.6\",\n  \"publishedAt\": 1791380228202\n}\n\nFile v1.0.6:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v1.0.6:skill-card.md\n\n## Description:\n\nReads accesso-powered mobile ticket links to extract order and ticket details, barcodes, and Google Wallet links.\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\nTicket holders and their assistants use this skill to read accesso mobile-ticket links and summarize order details, attendance times, barcodes, and wallet-pass options.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket links, parsed details, wallet URLs, and saved barcodes can expose access to tickets.\n\nMitigation: Treat these as secrets; keep them out of logs, commits, shared chats, and issue trackers.\n\nRisk: Fetching an untrusted ticket URL can contact an unintended destination.\n\nMitigation: Use URL mode only with trusted accesso ticket links.\n\n## Reference(s):\n\n- [ClawHub skill release](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n- [Ticket parsing script](artifact/references/parse-tickets.mjs)\n- [Ticket usage recipes](artifact/references/recipes.md)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, JSON, PNG images]\n\n**Output Format:** [Markdown guidance and structured ticket JSON; optional PNG barcode files]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Ticket JSON may include order details, attendee names, barcodes, and Google Wallet URLs.]\n\n## Skill Version(s):\n\n1.0.6 (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.0.5: 5 files, 10984 bytes\n\nFiles: references/parse-tickets.mjs (11831b), references/recipes.md (5610b), skill-card.md (1796b), SKILL.md (3311b), _meta.json (139b)\n\nFile v1.0.5:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v1.0.5:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"1.0.5\",\n  \"publishedAt\": 1791168458467\n}\n\nFile v1.0.5:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v1.0.5:skill-card.md\n\n## Description:\n\nReads accesso mobile-ticket links from confirmation emails to extract order details, ticket information, barcodes, and Google Wallet pass links.\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\nPeople managing accesso-powered mobile tickets can use this skill to review order and per-ticket details, retrieve wallet pass links, and optionally export scannable barcodes.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket links, barcode values, and wallet links can grant access to tickets if shared.\n\nMitigation: Treat these values as secrets and avoid sharing them or placing them in commits and issues.\n\nRisk: Exported barcode images can remain accessible after use.\n\nMitigation: Avoid shared, synced, or long-lived storage and delete exported images when finished.\n\n## Reference(s):\n\n- [ClawHub skill listing](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n- [Ticket parsing and usage recipes](references/recipes.md)\n- [Ticket parser](references/parse-tickets.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Shell commands, Files]\n\n**Output Format:** [JSON ticket details and optional PNG barcode files]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include order details, participants, dates, barcode values, and Google Wallet pass links.]\n\n## Skill Version(s):\n\n1.0.5 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.0.4: 5 files, 11023 bytes\n\nFiles: references/parse-tickets.mjs (11831b), references/recipes.md (5610b), skill-card.md (1857b), SKILL.md (3311b), _meta.json (139b)\n\nFile v1.0.4:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v1.0.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"1.0.4\",\n  \"publishedAt\": 1790991538119\n}\n\nFile v1.0.4:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v1.0.4:skill-card.md\n\n## Description:\n\nReads accesso mobile ticket links to identify orders, participants, dates, barcodes, and Google Wallet passes.\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\nTicket holders and their assistants use this skill to inspect accesso ticket links from confirmation emails and summarize order details, schedules, and available barcodes or wallet passes.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket URLs and embedded tokens can grant access to orders and barcodes.\n\nMitigation: Treat links and tokens like passwords; do not commit or share them in public chats or issues.\n\nRisk: Parser output, barcode images, and Google Wallet links may expose ticket details or enable ticket use.\n\nMitigation: Keep outputs private and avoid leaving exported barcode files in accessible locations.\n\n## Reference(s):\n\n- [ClawHub skill release](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n- [Ticket parsing recipes](references/recipes.md)\n- [Ticket parser](references/parse-tickets.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [Text, JSON, Shell commands, Guidance]\n\n**Output Format:** [Markdown guidance and shell commands; structured ticket data as JSON]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Optional barcode PNG files and Google Wallet links; outputs may contain sensitive ticket credentials and personal details.]\n\n## Skill Version(s):\n\n1.0.4 (source: ClawHub 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.0.3: 5 files, 10985 bytes\n\nFiles: references/parse-tickets.mjs (11831b), references/recipes.md (5610b), skill-card.md (1781b), SKILL.md (3311b), _meta.json (139b)\n\nFile v1.0.3:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v1.0.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"1.0.3\",\n  \"publishedAt\": 1790351568494\n}\n\nFile v1.0.3:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v1.0.3:skill-card.md\n\n## Description:\n\nReads accesso-powered mobile ticket links to extract order details, ticket holders, dates, barcodes, and Google Wallet pass links.\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\nTicket holders and their assistants use this skill to read accesso mobile-ticket links from confirmation emails and organize ticket details, barcodes, and wallet-pass links.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket links and wallet-pass URLs grant access to tickets and may be exposed if shared or committed.\n\nMitigation: Treat ticket URLs, barcode text, and wallet-pass links as secrets; do not paste, share, or commit them.\n\nRisk: Exported barcode PNGs can be used like the tickets they represent.\n\nMitigation: Export barcodes only when needed and choose a private output directory.\n\n## Reference(s):\n\n- [ClawHub release listing](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n- [Ticket parsing recipes](artifact/references/recipes.md)\n\n## Skill Output:\n\n**Output Type(s):** [JSON, Shell commands, Files]\n\n**Output Format:** [JSON ticket details, shell examples, and optional PNG barcode files]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Barcode files are written only when requested; dates and times remain in the merchant's local format.]\n\n## Skill Version(s):\n\n1.0.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.0.2: 5 files, 11217 bytes\n\nFiles: references/parse-tickets.mjs (11831b), references/recipes.md (5610b), skill-card.md (2338b), SKILL.md (3311b), _meta.json (139b)\n\nFile v1.0.2:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v1.0.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"1.0.2\",\n  \"publishedAt\": 1790199655066\n}\n\nFile v1.0.2:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v1.0.2:skill-card.md\n\n## Description:\n\nRead accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) -- order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes -- with curl.\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 extract structured details from accesso Passport ticket links they already possess, including order data, ticket fields, barcodes, and Google Wallet pass URLs.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket URLs, Google Wallet save links, parser output, and exported barcode images can reveal or grant access to tickets.\n\nMitigation: Treat them as secrets, avoid placing them in commits, issues, or logs, and delete exported barcode files when they are no longer needed.\n\nRisk: Expired or invalid accesso links can return HTTP 200 with a no-tickets body, so status-only checks can be misleading.\n\nMitigation: Use the parser body checks and refresh from the latest confirmation email when no tickets are found.\n\nRisk: Accesso page layout changes can prevent reliable ticketId or Google Wallet URL mapping.\n\nMitigation: Review parser warnings and raw page bytes before relying on affected fields.\n\n## Reference(s):\n\n- [accesso ticket recipes](artifact/references/recipes.md)\n- [parse-tickets.mjs](artifact/references/parse-tickets.mjs)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Code, JSON, Files]\n\n**Output Format:** [Markdown guidance with inline bash examples; parser output is structured JSON and optional PNG barcode files.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Ticket URLs, Google Wallet save links, parser output, and exported barcode images should be treated as secrets.]\n\n## Skill Version(s):\n\n1.0.2 (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.1: 5 files, 11141 bytes\n\nFiles: references/parse-tickets.mjs (11831b), references/recipes.md (5610b), skill-card.md (2140b), SKILL.md (3311b), _meta.json (139b)\n\nFile v1.0.1:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v1.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1790178335655\n}\n\nFile v1.0.1:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v1.0.1:skill-card.md\n\n## Description:\n\nRead accesso-powered mobile ticket links from confirmation emails to extract order details, per-ticket fields, barcodes, and Google Wallet pass URLs with curl and a dependency-free Node parser.\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 users and developers use this skill to inspect accesso Passport mobile ticket links from confirmation emails. It helps agents produce commands and parse ticket pages into order numbers, participant names, dates, times, barcode data, and wallet-pass URLs.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket links function like credentials and can grant access to order tickets and barcodes.\n\nMitigation: Keep accesso URLs, generated wallet URLs, JSON output, and barcode PNGs private; do not paste them into commits, issues, or shared files.\n\nRisk: Barcode exports and parsed ticket JSON can expose scannable ticket data.\n\nMitigation: Write exports only to a secure temporary folder, restrict access to the files, and delete them when finished.\n\n## Reference(s):\n\n- [Skill instructions](artifact/SKILL.md)\n- [Ticket parser](artifact/references/parse-tickets.mjs)\n- [accesso ticket recipes](artifact/references/recipes.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n\n## Skill Output:\n\n**Output Type(s):** [guidance, shell commands, code, JSON, files]\n\n**Output Format:** [Markdown guidance with shell commands; parser output is JSON with optional PNG barcode files.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Outputs can contain sensitive ticket URLs, barcode text, wallet URLs, and generated barcode image files.]\n\n## Skill Version(s):\n\n1.0.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.0.0: 5 files, 11203 bytes\n\nFiles: references/parse-tickets.mjs (11831b), references/recipes.md (5610b), skill-card.md (2241b), SKILL.md (3311b), _meta.json (139b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1789872692204\n}\n\nFile v1.0.0:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v1.0.0:skill-card.md\n\n## Description:\n\nRead accesso-powered mobile ticket links from confirmation emails to extract order numbers, ticket details, participants, dates and times, barcodes, and Google Wallet pass links with curl.\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 technically comfortable users use this skill to inspect accesso Passport ticket links from confirmation emails and turn them into structured ticket data, barcode assets, and Google Wallet pass links. It is intended for cases where the user already has the ticket URL and needs curl-oriented parsing and recipes.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket URLs, parsed JSON, barcode PNGs, Google Wallet URLs, and JWT/save links may allow ticket use if exposed.\n\nMitigation: Treat these values as secrets; store generated barcode files only in private directories and avoid commits, shared folders, issues, and support tickets.\n\nRisk: The skill fetches and parses user-provided accesso ticket links, including links that can expire or return a no-tickets page.\n\nMitigation: Review the destination before running commands and rely on the parser's no-ticket and HTTP-error handling rather than HTTP status alone.\n\n## Reference(s):\n\n- [ClawHub skill release page](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n- [recipes.md](references/recipes.md)\n- [parse-tickets.mjs](references/parse-tickets.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, guidance]\n\n**Output Format:** [Markdown guidance with inline shell commands, JavaScript parser usage, and JSON output examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [The referenced parser can optionally write barcode PNG files when run with --barcodes.]\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.3.0: 5 files, 11358 bytes\n\nFiles: references/parse-tickets.mjs (11831b), references/recipes.md (5610b), skill-card.md (2591b), SKILL.md (3311b), _meta.json (139b)\n\nFile v0.3.0:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v0.3.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"0.3.0\",\n  \"publishedAt\": 1789688155047\n}\n\nFile v0.3.0:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v0.3.0:skill-card.md\n\n## Description:\n\nRead accesso-powered mobile ticket links from accessoticketing.com confirmation emails, including order number, ticket details, dates, times, barcodes, and Google Wallet pass links, using curl and a dependency-free Node parser.\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 agent users use this skill to inspect user-provided accesso Passport mobile-ticket links and extract ticket metadata, barcode data, and wallet-pass endpoints into structured output. It is useful when a ticket confirmation email contains a media-engine accessoticketing.com URL that needs to be resolved or summarized without a browser session.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket URLs, oToken and cToken values, barcode images, Google Wallet JWTs, and generated save links can grant access to tickets.\n\nMitigation: Treat these values like passwords: do not paste them into shared logs, commits, issues, or transcripts, and delete generated barcode files when no longer needed.\n\nRisk: Expired or invalid accesso links may return HTTP 200 with an unavailable-ticket message, so status code alone can be misleading.\n\nMitigation: Use the parser's body checks and exit-code handling before relying on parsed ticket output.\n\nRisk: Accesso page layout changes or ticketId count mismatches can make wallet-pass URLs unreliable.\n\nMitigation: Review parser warnings and re-check the raw page before trusting ticketId or googleWalletUrl values when counts disagree.\n\n## Reference(s):\n\n- [accesso ticket recipes](artifact/references/recipes.md)\n- [parse-tickets.mjs](artifact/references/parse-tickets.mjs)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n\n## Skill Output:\n\n**Output Type(s):** [guidance, shell commands, code, JSON, files]\n\n**Output Format:** [Markdown guidance with inline shell commands; parser output is structured JSON and optional PNG barcode files.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include secret-bearing ticket URLs, barcode values, Google Wallet URLs, and generated barcode image paths.]\n\n## Skill Version(s):\n\n0.3.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.2.1: 5 files, 11267 bytes\n\nFiles: references/parse-tickets.mjs (11831b), references/recipes.md (5610b), skill-card.md (2488b), SKILL.md (3311b), _meta.json (139b)\n\nFile v0.2.1:SKILL.md\n\n---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order. The parser maps it positionally and *omits* it (with a warning) if the counts disagree, rather than guessing.\n- `date`/`time` are the merchant's local wall-clock strings (`MM/DD/YYYY`, `9:00 AM`) with no timezone. Don't convert them.\n- Barcodes are inline base64 PNGs of the same order-level code (`%RC<order>`), not per-ticket unique on every merchant.\n\nFile v0.2.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"0.2.1\",\n  \"publishedAt\": 1789062787061\n}\n\nFile v0.2.1:references/recipes.md\n\n# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHeader__orderNumber` → `Order #  <n>` (a `span`, not a `div`).\n- `img.ticketHeader__merchantLogo` → `…/assets/<island>/images/…`, the island fallback.\n- Mobile grouping: `div#ticket-item-container<N>[data-ticketidx][data-totaltickets]`, one per ticket,\n  containing `.ticket__packageName`, `.ticket__customerName`, `.ticket__barcodeText` and\n  `img#barcode<N>` (inline base64 PNG).\n- The expandable detail panel `#flip-ticket-info-container<N>` is a **sibling** of that block, not a\n  child — both hang off `#ticket-grouping-container`. Guest Number, Web Sales ID, Instructions and\n  Terms all live in the panel, so they have to be paired back by index. (A text-slicing parser like\n  the `.mjs` here picks them up incidentally because the panel falls between two ticket boundaries;\n  anything walking the DOM must join them explicitly.)\n- Detail rows are generic pairs: `div.gap-font--overline` (label) + `div.gap-font--body-2` (value) —\n  this is how `Date`, `Time`, `Guest Number`, `Web Sales ID` all arrive. Harvest them generically.\n- Long-form panels: `div.flipTicket__content-heading` + `div.flipTicket__content` → `Instructions:`, `Terms and Conditions:`.\n- `div#desktop-ticket-section.desktopTicketGrouping` repeats **all 12 tickets** with\n  `desktopTicket__*` classes and the *same* `img#barcode<N>` ids. Bound parsing above it.\n- Hidden `input#ticketId` / `#oToken` / `#merchantId` appear only in the trailing\n  per-ticket \"Register this ticket\" modal forms, after the desktop grouping.\n\n## Failure modes\n\n- **Expired/invalid token → HTTP 200** with body `\"We are sorry, but there are no tickets available to print on this order.\"`\n  Confirmed by probing a bogus `oToken`. Never gate on status code alone.\n- Merchant detail rows differ by venue; unknown labels are camel-cased into the ticket\n  object rather than dropped, so new rows appear automatically.\n- If `ticketId` is `null` and stderr warned about a count mismatch, the registration-modal\n  section changed shape — re-check before trusting any `googleWalletUrl`.\n\n## Not covered here\n\nThe same platform runs a separate **accesso Passport store API** at\n`https://ecomm.api.<region>.accessoticketing.com/api/request/<service>` (POST; ~150 services\nincluding `GetMerchantPackageList`, `GetParkHours`, `GetEventCalendarDates`, `CP_Login`,\n`GuestOrderSearch`, `GuestViewOrder`, cart/purchase). It is server-side reachable (a malformed\nrequest returns 400, not a bot wall) but needs merchant identification captured from a live\nstore session, and credentials for anything account-scoped. Out of scope for this skill.\n\nFile v0.2.1:skill-card.md\n\n## Description:\n\nRead accesso-powered mobile ticket links from accessoticketing.com confirmation emails to extract order details, ticket details, barcodes, and Google Wallet pass URLs using curl and a local Node.js parser.\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 users and developers use this skill to parse Accesso Passport mobile ticket links from confirmation emails into ticket summaries, barcode data, and wallet pass URLs. It is intended for trusted Accesso ticket URLs where the URL token is treated as a credential.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket URLs, barcode PNGs, wallet URLs, and JWTs can expose access to tickets.\n\nMitigation: Use only trusted Accesso ticket URLs, keep all generated outputs private, and avoid committing or sharing ticket-derived data.\n\nRisk: The helper script can fetch arbitrary URLs supplied by the user.\n\nMitigation: Constrain use to expected accessoticketing.com media-engine ticket links and review the helper before installation.\n\nRisk: Barcode output can write sensitive PNG files to shared or highly visible folders.\n\nMitigation: Write barcodes only to a private directory and avoid Desktop or shared folders for barcode output.\n\nRisk: Running barcode extraction on untrusted saved HTML may write attacker-influenced files.\n\nMitigation: Do not run --barcodes on untrusted saved HTML or arbitrary URLs.\n\n## Reference(s):\n\n- [accesso ticket recipes](references/recipes.md)\n- [parse-tickets.mjs](references/parse-tickets.mjs)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\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 and JSON parser output]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [The helper can emit structured JSON and optionally write barcode PNG files; ticket URLs, wallet URLs, JWTs, and barcode files should be treated as secrets.]\n\n## Skill Version(s):\n\n0.2.1 (source: evidence.release.version)\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.","readmeExcerpt":"Skill: accessoticketing-mcp Owner: chrischall Summary: Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl. Tags: latest:1.1.0 Version history: v1.1.0 | 2026-10-09T23:26:59.659Z | auto Version 1.1.0 - Added logic to ensure barcode PNG files written by --barcodes","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"https://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>"},{"language":"bash","snippet":"P=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq ."},{"language":"bash","snippet":"P=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs"},{"language":"bash","snippet":"curl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1"},{"language":"bash","snippet":"curl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1"},{"language":"bash","snippet":"# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: accesso-tickets\ndescription: \"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl.\"\n---\n\n# accesso tickets\n\nFor accesso Passport mobile-ticket links, which look like:\n\n```\nhttps://media-engine.<region>.accessoticketing.com/tickets/v1/<island>?oToken=A1:…&cToken=A1:…&merchant_id=<n>\n```\n\nMany venues deliver tickets this way (US National Whitewater Center, theme parks, festivals). The link arrives in the order-confirmation email, often behind a click-tracker — resolve that first with `curl -sSIL <tracking-url> | grep -i ^location`.\n\n**No login, no browser bridge.** The `oToken` in the URL *is* the credential, and the page is plain-`curl` reachable server-side. Treat the URL as a secret: it grants anyone the order's tickets and barcodes. Never paste one into a file, a commit, or an issue.\n\n## Core pattern\n\n`references/parse-tickets.mjs` is dependency-free (no `node_modules`) and takes a URL, a file, or `-` for stdin:\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\nnode \"$P\" \"$TICKET_URL\" | jq .\n```\n\nUseful flags:\n\n- `--barcodes <dir>` — write each ticket's barcode as a PNG (`ticket-<idx>-<ticketId>.png`; an existing file is never overwritten — a re-run writes `…-2.png`) and add `barcodeFile` to the output.\n- `--terms` — include the (long, boilerplate, per-ticket-identical) `termsAndConditions`. Omitted by default.\n- `--url <original-url>` — supply the source URL when parsing a saved file, so `googleWalletUrl` can be built.\n\nExit codes: `3` no tickets parsed (expired link, or layout drift — the message says which), `4` HTTP error fetching, `64` bad usage.\n\n## Output\n\nTop level: `orderNumber`, `island`, `merchantId`, `merchantLogo`, `ticketCount`, `declaredTicketCount`, `tickets[]`.\n\nPer ticket: `index`, `ticketId`, `packageName`, `participant`, `additionalGuests[]`, `date`, `time` (absent on all-day items), `barcodeText`, `barcodeIsDataUrl`, `instructions`, `googleWalletUrl`, plus any other label/value pair the merchant rendered (commonly `guestNumber`, `webSalesId`).\n\nExtra fields are harvested generically from the page's label/value markup rather than hardcoded, so a merchant with different detail rows still comes through. See `references/recipes.md` for jq recipes and the verified request shapes.\n\n## Gotchas\n\n- **An expired or invalid link returns HTTP 200**, not 4xx — the body just says \"no tickets available to print on this order.\" Status alone never tells you the link is dead; the parser checks the body and says so.\n- **Every ticket is rendered twice** (a mobile `ticket-item-container<N>` grouping and a `desktopTicketGrouping` one) sharing barcode element ids. Parse only the mobile blocks or you get doubles.\n- **`ticketId` lives in neither rendering** — it is in the trailing \"Register this ticket\" modal section, one hidden input per ticket in ticket order"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"accessoticketing-mcp\",\n  \"version\": \"1.1.0\",\n  \"publishedAt\": 1791588419659\n}"},{"path":"references/recipes.md","content":"# accesso ticket recipes\n\nAll shapes below were captured live against a real accesso order\n(`media-engine.na3.accessoticketing.com`, island `accesso155`, 12 tickets).\nReplace `$TICKET_URL` with the link from the confirmation email — never commit one.\n\n```bash\nP=~/.claude/skills/accesso-tickets/references/parse-tickets.mjs\n```\n\n## Resolve a tracked link first\n\nConfirmation emails wrap the link in a click-tracker (SendGrid `…/ls/click?upn=…`, etc.):\n\n```bash\ncurl -sSIL \"$TRACKED_URL\" | grep -i '^location:' | tail -1\n```\n\nTake the final `media-engine.*/tickets/v1/*` URL. Keep the whole query string —\n`oToken` **and** `cToken` are both required.\n\n## Everyday queries\n\n```bash\n# One line per ticket: who, when, what\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets[] | [.date, (.time // \"all-day\"), .participant, .packageName] | @tsv' \\\n  | column -t -s $'\\t'\n\n# Group by participant\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '.tickets | group_by(.participant)[] | \"\\(.[0].participant): \\(map(.packageName) | join(\", \"))\"'\n\n# Just the timed items (the ones with a start time you must not miss)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '[.tickets[] | select(.time)] | map({time, participant, packageName})'\n\n# Distinct products and counts\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets | group_by(.packageName)[] | \"\\(length)x \\(.[0].packageName)\"'\n\n# Order header\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq '{orderNumber, island, merchantId, ticketCount}'\n\n# Per-product instructions (deduped — merchants repeat them on every ticket)\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null \\\n  | jq -r '[.tickets[] | {packageName, instructions}] | unique_by(.packageName)[] | \"## \\(.packageName)\\n\\(.instructions)\\n\"'\n```\n\n## Barcodes\n\n```bash\nnode \"$P\" \"$TICKET_URL\" --barcodes ~/Desktop/tickets >/dev/null\nopen ~/Desktop/tickets   # macOS\n```\n\nEach file is `ticket-<index>-<ticketId>.png`. The scannable value is also in\n`barcodeText` (e.g. `ID %RC<order-number>`) if you'd rather regenerate the code yourself.\n\n## Google Wallet passes\n\nVerified endpoint — returns `{\"jwt\": \"...\"}` at HTTP 200:\n\n```\nGET https://media-engine.<region>.accessoticketing.com/google-wallet/v1/<island>/<oToken>/<ticketId>\n```\n\nThe page sends an `X-ACCESSOPASSPORT-ORIGIN: <store-origin>` header; it is **not**\nrequired (a bare curl returns 200). Turn a JWT into a save link with Google's\nstandard form, `https://pay.google.com/gp/v/save/<jwt>` (verified: resolves, and\nprompts Google sign-in as expected):\n\n```bash\nnode \"$P\" \"$TICKET_URL\" 2>/dev/null | jq -r '.tickets[] | select(.googleWalletUrl) | \"\\(.ticketId)\\t\\(.googleWalletUrl)\"' \\\n| while IFS=$'\\t' read -r id url; do\n    jwt=$(curl -sS \"$url\" | jq -r .jwt)\n    printf '%s\\thttps://pay.google.com/gp/v/save/%s\\n' \"$id\" \"$jwt\"\n  done\n```\n\nThese URLs embed the order token — treat the output as secret.\n\n## Page structure (why the parser looks the way it does)\n\nServer-rendered HTML, no JSON store, no bot wall, no auth beyond the URL tokens.\n\n- `span.ticketHe"},{"path":"skill-card.md","content":"## Description:\n\nReads accesso mobile-ticket links to extract order and ticket details, barcodes, and Google Wallet pass links.\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\nTicket holders and their agents use the skill to inspect accesso confirmation links, summarize ticket times and participants, and retrieve barcodes or Google Wallet pass links.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ticket URLs, barcodes, parsed JSON, and wallet links may function as live ticket credentials.\n\nMitigation: Keep them out of issues, commits, shared chats, logs, screenshots, and synced folders; delete exported barcode files when no longer needed.\n\n## Reference(s):\n\n- [ClawHub skill release](https://clawhub.ai/chrischall/skills/accessoticketing-mcp)\n- [Ticket parsing recipes](references/recipes.md)\n- [Ticket parser](references/parse-tickets.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, JSON, PNG images]\n\n**Output Format:** [Text and shell commands; structured JSON ticket details and optional PNG barcodes]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Ticket links, parsed details, barcode files, and wallet links can grant access to tickets.]\n\n## Skill Version(s):\n\n1.1.0 (source: ClawHub 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."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl. Skill: accessoticketing-mcp Owner: chrischall Summary: Read accesso-powered mobile ticket links (accessoticketing.com media-engine URLs from confirmation emails) — order number, per-ticket product, participant, date/time, barcodes, Google Wallet passes — with curl. Tags: latest:1.1.0 Version history: v1.1.0 | 2026-10-09T23:26:59.659Z | auto Version 1.1.0 - Added logic to ensure barcode PNG files written by --barcodes","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1328,"uniquenessScore":53,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T21:30:43.624Z","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-11T21:30:43.624Z","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-12T12:09:00.292Z","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"}]}}}