{"id":"9dc7e98c-47f4-4387-b0f7-3814dbfe5882","entityType":"agent","slug":"clawhub-chrischall-evite-api","name":"evite-api","canonicalUrl":"https://www.xpersona.co/agent/clawhub-chrischall-evite-api","canonicalPath":"/agent/clawhub-chrischall-evite-api","generatedAt":"2026-10-10T13:39:31.133Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T11:12:54.589Z","emptyReason":null},"description":"Query and act on Evite (evite.com) events, guest lists, RSVPs, and messages from a shell with curl and a cookie jar — instead of running the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no browser or extension involved. Use when you want Evite data/actions without the MCP, in a script, or on a machine where the MCP isn't installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on Evite from the shell\", \"curl Evite\", \"evite-api skill\". Skill: evite-api Owner: chrischall Summary: Query and act on Evite (evite.com) events, guest lists, RSVPs, and messages from a shell with curl and a cookie jar — instead of running the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no browser or extension involved. Use when you want Evite data/actions without the MCP, in a script","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.5K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:evite-api","sourceUrl":"https://clawhub.ai/chrischall/evite-api","homepage":"https://clawhub.ai/chrischall/skills/evite-api","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/chrischall/evite-api","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/chrischall/skills/evite-api","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":63,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Query and act on Evite (evite.com) events, guest lists, RSVPs, and messages from a shell with curl and a cookie jar — instead of running the evite-mcp server. D"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:12:54.589Z","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-10T11:12:54.589Z","emptyReason":null},"stars":null,"forks":null,"downloads":1472,"packageName":null,"latestVersion":"1.3.5","tractionLabel":"1.5K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:12:54.589Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T11:12:54.589Z","lastCrawledAt":"2026-10-10T11:12:54.589Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T11:12:54.589Z","lastVerifiedAt":null,"highlights":[{"version":"1.3.5","createdAt":"2026-10-09T23:28:13.973Z","changelog":"- Removed the sample skill card file (skill-card.md) from the repository. - No functional or behavior changes to the skill itself.","fileCount":4,"zipByteSize":9053},{"version":"1.3.4","createdAt":"2026-10-07T13:39:45.369Z","changelog":"- removed 1 file(s). - Updated SKILL.md and bundle contents.","fileCount":4,"zipByteSize":9019},{"version":"1.3.3","createdAt":"2026-10-05T02:51:22.392Z","changelog":"- Removed the sample file skill-card.md. - No functional changes to the evite-api skill code or documentation.","fileCount":4,"zipByteSize":9020},{"version":"1.3.2","createdAt":"2026-10-03T01:40:55.315Z","changelog":"- Removed the file skill-card.md from the repository. - No changes to core functionality or documentation; this is a minor cleanup release.","fileCount":4,"zipByteSize":9087},{"version":"1.3.1","createdAt":"2026-09-28T13:59:12.268Z","changelog":"- Updated documentation to clarify the skill no longer relies on the \"ContextMint Bridge extension\". - Removed the outdated skill-card.md file.","fileCount":4,"zipByteSize":9021},{"version":"1.3.0","createdAt":"2026-09-25T15:50:32.761Z","changelog":"- Removed the file skill-card.md. - No changes to functionality or user-facing features.","fileCount":4,"zipByteSize":8984},{"version":"1.2.0","createdAt":"2026-09-24T15:10:56.274Z","changelog":"- Removed the skill summary file (skill-card.md). - No changes to core functionality or usage; documentation and operation remain as before.","fileCount":4,"zipByteSize":9307},{"version":"1.1.3","createdAt":"2026-09-23T21:41:07.456Z","changelog":"- Removed the skill-card.md file. - No changes to API or usage; documentation and core behavior remain unchanged. - No user action required for this update.","fileCount":4,"zipByteSize":9162}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:evite-api","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-evite-api/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-evite-api/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-evite-api/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-evite-api/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-evite-api/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-evite-api/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-10T13:39:31.130Z"}},"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-evite-api/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-evite-api/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-evite-api/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-evite-api/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-10T11:12:54.589Z","emptyReason":null},"readme":"Skill: evite-api\n\nOwner: chrischall\n\nSummary: Query and act on Evite (evite.com) events, guest lists, RSVPs, and messages from a shell with curl and a cookie jar — instead of running the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no browser or extension involved. Use when you want Evite data/actions without the MCP, in a script, or on a machine where the MCP isn't installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on Evite from the shell\", \"curl Evite\", \"evite-api skill\".\n\nTags: latest:1.3.5\n\nVersion history:\n\nv1.3.5 | 2026-10-09T23:28:13.973Z | auto\n\n- Removed the sample skill card file (skill-card.md) from the repository.\n- No functional or behavior changes to the skill itself.\n\nv1.3.4 | 2026-10-07T13:39:45.369Z | auto\n\n- removed 1 file(s).\n- Updated SKILL.md and bundle contents.\n\nv1.3.3 | 2026-10-05T02:51:22.392Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional changes to the evite-api skill code or documentation.\n\nv1.3.2 | 2026-10-03T01:40:55.315Z | auto\n\n- Removed the file skill-card.md from the repository.\n- No changes to core functionality or documentation; this is a minor cleanup release.\n\nv1.3.1 | 2026-09-28T13:59:12.268Z | auto\n\n- Updated documentation to clarify the skill no longer relies on the \"ContextMint Bridge extension\".\n- Removed the outdated skill-card.md file.\n\nv1.3.0 | 2026-09-25T15:50:32.761Z | auto\n\n- Removed the file skill-card.md.\n- No changes to functionality or user-facing features.\n\nv1.2.0 | 2026-09-24T15:10:56.274Z | auto\n\n- Removed the skill summary file (skill-card.md).\n- No changes to core functionality or usage; documentation and operation remain as before.\n\nv1.1.3 | 2026-09-23T21:41:07.456Z | auto\n\n- Removed the skill-card.md file.\n- No changes to API or usage; documentation and core behavior remain unchanged.\n- No user action required for this update.\n\nv1.1.2 | 2026-09-23T15:44:30.248Z | auto\n\n- Removed the file skill-card.md.\n- No changes to functionality or documentation in SKILL.md.  \n- Internal cleanup; no user-facing changes.\n\nv1.1.1 | 2026-09-21T04:12:29.491Z | auto\n\n- Removed the file skill-card.md.\n- No other changes to functionality or documentation.\n\nv1.1.0 | 2026-09-20T02:50:38.700Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or usage; documentation and core logic remain unchanged.\n\nv1.0.0 | 2026-09-19T11:19:30.387Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or documentation in SKILL.md.\n\nv0.8.3 | 2026-09-15T19:21:33.387Z | auto\n\n- Removed the sample skill card file (skill-card.md) from the repository.\n- No changes to core functionality or user-facing documentation.\n\nv0.8.2 | 2026-09-14T14:07:16.023Z | auto\n\n- Removed the file skill-card.md.\n- No changes to functionality or usage.\n- Internal documentation cleanup only; no impact to users.\n\nv0.8.1 | 2026-09-10T17:49:10.414Z | auto\n\n- Removed the skill-card.md file.\n- No changes to code or core functionality.\n\nv0.8.0 | 2026-09-04T22:21:02.452Z | auto\n\n- Removed the sample skill card documentation file (skill-card.md).\n- No changes to core functionality or user-facing features.\n\nv0.7.0 | 2026-08-29T13:53:44.411Z | auto\n\n- Removed the skill-card.md file for a leaner codebase.\n- No functional or behavioral changes; usage and capabilities remain the same.\n\nv0.6.1 | 2026-08-28T21:07:09.410Z | auto\n\n- Removed the sample skill card file (skill-card.md).\n- No functional or documentation changes to the main skill.\n\nv0.6.0 | 2026-08-28T11:34:21.035Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional or documentation changes to the skill itself.\n\nv0.5.1 | 2026-08-06T00:42:35.889Z | auto\n\n- Removed the skill-card.md file.\n- No changes to code or functionality; documentation-only update.\n\nv0.5.0 | 2026-07-30T14:20:13.977Z | auto\n\n- Removed the sample skill card file (skill-card.md).\n- No user-facing changes to functionality or documentation.\n\nv0.4.2 | 2026-07-30T13:51:49.194Z | auto\n\n- Added detailed documentation for using Evite’s internal APIs via shell with curl and a cookie jar, no evite-mcp server required.\n- Clarified authentication model using session and CSRF cookies, including procedures for one-time login and cookie management.\n- Provided explicit examples for reading and writing Evite data (events, RSVPs, messages) directly from the shell.\n- Noted caveats about unverified write bodies and advised caution when executing write actions.\n- Aimed at users seeking Evite data/actions in scriptable or MCP-free environments.\n\nArchive index:\n\nArchive v1.3.5: 4 files, 9053 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (2005b), SKILL.md (5007b), _meta.json (128b)\n\nFile v1.3.5:SKILL.md\n\n---\nname: evite-api\ndescription: >-\n  Query and act on Evite (evite.com) events, guest lists, RSVPs, and\n  messages from a shell with curl and a cookie jar — instead of running\n  the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login\n  against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no\n  browser or extension involved. Use when you want Evite data/actions\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on\n  Evite from the shell\", \"curl Evite\", \"evite-api skill\".\n---\n\n# Evite via curl (no MCP)\n\nEvite has no public API — this hits the site's own internal `/services/`\n(+ `/ajax/`, `/tsunami/`) layer using session cookies, exactly like\nevite-mcp does. **This is a curl skill, not fpx**: Evite's login is a\nplain CSRF-protected Django form POST, and reads/writes with the\nresulting cookies are **not** bot-walled (Cloudflare sits in front but\ndoesn't trip on a plain `curl`/Node request carrying a valid session) —\nso the whole flow runs server-side with a cookie jar. No signed-in\nbrowser tab, no ContextMint Bridge extension, no bridge.\n\nAuth model: session cookies `x-evite-session` + `evtsession`, plus a\nCSRF cookie `csrftoken` sent back as the `X-CSRFToken` header on every\nwrite. The CSRF cookie **rotates** — re-read it from the jar immediately\nbefore each write, never cache it.\n\n## One-time login (per shell session, until the jar's session expires)\n\n```sh\nexport EVITE_EMAIL=you@example.com EVITE_PASSWORD=yourpassword   # or: op read op://.../evite/password\nJAR=/tmp/evite-jar.txt\ntouch \"$JAR\" && chmod 600 \"$JAR\"   # jar holds the live session cookie — restrict before writing\nUA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36'\n\n# 1. Prime: GET the homepage to obtain the csrftoken (+ anonymous session) cookies.\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null\n\n# 2. Login: POST creds with the primed jar + X-CSRFToken + Origin/Referer (Django's\n#    HTTPS CSRF check needs all three; a cold POST without them 403s).\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\\n  'https://www.evite.com/ajax_login'\n```\n\nA `200` means the jar now holds the authenticated cookies\n(`x-evite-session`, `evtsession`, a freshly-rotated `csrftoken`,\n`x-evite-features`) and a JSON body with `full_name`/`user_id`/`token`.\n`401 {\"error\":\"Invalid Email Address / Password\"}` = bad creds; `403\n{\"error\":\"HTTP_403\"}` = the priming GET was skipped, or its `csrftoken`\nwasn't echoed back.\n\n## Reads: jar + `Accept: application/json`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation' | jq .\n```\n\nAlways pass `-b \"$JAR\" -c \"$JAR\"` **together** (read AND rewrite the same\nfile) — Evite occasionally re-sets cookies even on a GET, and a write's\n`csrftoken` rotation must land back in the jar for the next call.\n\n## Writes: jar + fresh `X-CSRFToken` + `Content-Type: application/json`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nRe-read `$CSRF` from `$JAR` **right before every write** — a stale value\n403s. If a write still 401/403s with the cookie unchanged in the jar\n(check `awk -F'\\t' '$6==\"csrftoken\"'  \"$JAR\"` before/after), the session\nitself expired: redo the one-time login.\n\nFull endpoint list (every read + write, real paths/bodies, `jq`\nrecipes) is in `references/endpoints.md`.\n\n## Notes / caveats\n\n- **Two write bodies are still unverified upstream** (tracked as\n  evite-mcp issue #3): `send_message` (`POST\n  /tsunami/v1/services/event/{id}/guest/{gid}/messages`) and\n  `send_invitation` (`POST /services/event/v1/{id}/send/`) — only the\n  endpoints were captured live, not their exact request bodies. The MCP\n  (and this skill) send `{\"message\": \"...\"}` / `{}` as the best-known\n  shape; treat those two as slightly less certain than the rest.\n- `list_templates` is HTML scraping (`GET /invites/{category}/`), not\n  JSON — see the reference for the extraction pattern.\n- **Writes here really mutate Evite** — RSVPs, cancellations,\n  broadcast emails to real guests. There's no MCP-side confirm-gate in\n  this skill: you are the confirm gate. Test broadcasts/sends only\n  against a throwaway event with a blackholed `@example.com` guest.\n- This project (evite-mcp) is developed and maintained by AI (Claude).\n\nFile v1.3.5:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"evite-api\",\n  \"version\": \"1.3.5\",\n  \"publishedAt\": 1791588493973\n}\n\nFile v1.3.5:references/endpoints.md\n\n# Evite endpoint reference (curl)\n\nBase host: `https://www.evite.com`. All paths below assume `$JAR` already\nholds an authenticated session (see `SKILL.md`'s one-time login) and that\nevery call passes `-b \"$JAR\" -c \"$JAR\"` so a rotated `csrftoken` lands\nback in the jar. Reads add `-H 'Accept: application/json'`; writes add\n`-H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\"` with `CSRF`\nre-read from the jar immediately before the call:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n```\n\nTranscribed from `evite-mcp`'s `src/client.ts` + `docs/EVITE-API.md`\n(live-verified 2026-06-01/02). Every shape below is a REAL captured\nrequest/response unless marked \"assumed\" (URL captured, body not).\n\n---\n\n## Reads\n\n### 1. List events — `GET /services/events/v1/`\n\nQuery: `filterBy` (`all|host|others`, others = you're a guest), `status`\n(repeatable — `upcoming|draft|archived|past|canceled`), `type=invitation`,\n`offset`, `numResults`, `filter` (free text).\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation&status=upcoming&status=draft&numResults=25' \\\n  | jq '.totals, (.events[] | {id: .event_id, title, start, status, is_host, rsvp})'\n```\n\n→ `{ events: Event[], totals }`. `totals` = `{all, sending, draft,\nreceived, canceled, past, upcoming, archived}` (sending = hosting,\nreceived = invited). `Event` fields: `event_id, title, start, end,\nstatus, past, is_host, rsvp(yes|no|maybe), guest_status(0|1|2),\nguest_id, host_id, host_name, location{location_name, street_address,\nunit_num, city, state, zip_code, place_id}, timezone,\nknown_timezone_name, template_name, event_category, rsvp_off, is_invite,\nis_pending_cohost, rendered_image_url, updated`.\n\n### 2. Get event detail — `GET /services/event/v1/{id}`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\" \\\n  | jq '{title: .event.title, start: .event.startDatetime, status: .event.status, settings}'\n```\n\n→ top keys: `event, calculatedFields, design, location,\nuserEventContext, attributes, registries, settings, charity, gifting,\nrendered, calendar, features`. `event`: `id, title, message,\nstartDatetime, endDatetime, knownTimezoneName,\nknownTimezoneAbbreviation, eventHostName, eventPhoneNumber, hostId,\nhostIds, status, isPast, eventType, category, superCategory,\ntemplateName, origin, isFabricPremium, shareableLink, sendOn`.\n`settings`: `enableMaybe, privateGuestList, headCountByFamily, plusOne,\nmaxEventCapacity, allowViewMap, showGifting, rsvpBy, strictRsvpBy,\nenableHostPhotoGallery, enablePhotoSharing, allowGuestNumber, rsvpOff`.\n\n### 3. Guest list + RSVP summary — `GET /services/event/v1/{id}/guests/`\n\nOne endpoint powers both `evite_list_guests` and `evite_rsvp_summary`.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/\" \\\n  | jq '.summary, (.guests[] | {guestId, name, rsvpResponse, numberOfAdults, numberOfKids})'\n```\n\n→ `{ guests: Guest[], summary }`. `Guest`: `guestId, userId, name,\nemail, phone, guestType(host|cohost|guest),\nrsvpResponse(yes|no|maybe|noreply), numberOfAdults, numberOfKids,\ncheckedIn, comments, deliveryStatus, inviteMethod(email|null),\ninvitedBy, sentOn, timesViewed, avatarUrl, shortLink, longLink,\ncreated, updated`. `summary`: `{yes, no, maybe, noReply, adultsYes,\nkidsYes, adultsMaybe, kidsMaybe, inviteesYes, inviteesMaybe,\nlockedStatus}`.\n\n### 4. Messages — `GET /services/event/v1/{id}/posts/`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/posts/\" | jq '.posts'\n```\n\n→ `{ posts: Post[] }` — the event's \"Messages\" tab thread.\n\n### 5. List templates (HTML scrape, not JSON) — `GET /invites/{category}/`\n\nThe gallery is server-rendered — no JSON API. Grep the SSR HTML for\n`/invitation/{slug}/(create|preview|details)` links (mirrors\n`EviteClient.listTemplates`'s regex). Add `?active_filter=free_premium%2Cfree`\nfor free-only.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" \\\n  'https://www.evite.com/invites/kids-birthday/?active_filter=free_premium%2Cfree' \\\n  | grep -oE '/invitation/[a-z0-9][a-z0-9_-]+/(create|preview|details)' \\\n  | sed -E 's#/invitation/([a-z0-9_-]+)/.*#\\1#' | sort -u\n```\n\nEach slug is a `templateName` — pass it to `create_event` below.\n\n---\n\n## Writes\n\nEvery write really mutates Evite. `-d` bodies below are exact JSON\nunless marked \"assumed\" (endpoint captured live; the exact body field\nwasn't).\n\n### 6. RSVP — `PUT /services/event/v1/{id}/guests/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0,\"comments\":\"can'\\''t wait!\"}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nBody: `rsvpResponse` (`yes|no|maybe`), `numberOfAdults`,\n`numberOfKids`, optional `comments` — field names match the read\n`Guest` shape. `comments` is optional (omit the key entirely to leave\nit unchanged).\n\n### 7. Send a private guest message (assumed body) — `POST /tsunami/v1/services/event/{id}/guest/{gid}/messages`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"See you Saturday!\"}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/guest/$GUEST_ID/messages\"\n```\n\nA **third API base** (`/tsunami/`, not `/services/…/posts/`, which is\nGET-only). Body assumed `{message}`.\n\n### 8. Broadcast to RSVP segments — `POST /tsunami/v1/services/event/{id}/broadcast/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"Reminder: parking is on the north side!\",\"captcha\":null,\"virtual_groups\":[\"yes\",\"maybe\"],\"participantCount\":12}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/broadcast/\"\n```\n\nBody **fully captured** (not assumed): `message`, `captcha` (always\n`null`), `virtual_groups` (array of RSVP segments to reach — the\nvalues the dashboard uses are `yes`/`no`/`maybe`), optional\n`participantCount` (informational — the count the web UI sends along).\nThis emails every guest in the named segments.\n\n### 9. Upload a photo to the shared gallery — 4-step Google Cloud Storage flow\n\nThe file goes to GCS, not Evite. All 4 steps, in order:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n\n# Step 1 — get a signed-upload ticket from Evite.\nTICKET=$(curl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg eid \"$EVENT_ID\" --arg gid \"$GUEST_ID\" --arg mt \"image/jpeg\" \\\n        '{upload_path:\"feed_photos\",event_id:$eid,photo_id:\"\",guest_id:$gid,redirect:true,mimetype:$mt,width:1200,height:900}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/upload/request/\")\nUPLOAD_URL=$(jq -r '.upload_url' <<<\"$TICKET\")\nPHOTO_ID=$(jq -r '.upload_form.key' <<<\"$TICKET\" | awk -F/ '{print $NF}')\n\n# Step 2 — multipart POST straight to GCS: signed fields first, `file` LAST,\n# no Evite cookies (the policy/signature ARE the auth). Capture the redirect.\nFORM_ARGS=()\nfor k in $(jq -r '.upload_form | keys[]' <<<\"$TICKET\"); do\n  v=$(jq -r --arg k \"$k\" '.upload_form[$k]' <<<\"$TICKET\")\n  FORM_ARGS+=(-F \"$k=$v\")\ndone\nFINISH_URL=$(curl -sS -D - -o /dev/null \"${FORM_ARGS[@]}\" -F \"file=@/path/to/photo.jpg\" \"$UPLOAD_URL\" \\\n  | grep -i '^location:' | tr -d '\\r' | awk '{print $2}')\n\n# Step 3 — finalize the object into the album (best-effort; ignore failures).\ncurl -sS -b \"$JAR\" -c \"$JAR\" \"$FINISH_URL\" -o /dev/null\n\n# Step 4 — register the photo in the event's shared gallery.\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg pid \"$PHOTO_ID\" '{photo_ids:[$pid]}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/shared-gallery/?gid=$GUEST_ID\"\n```\n\nStep 1's mimetype must match the file's real content type; the GCS\npolicy enforces `Content-Type == mimetype` and a 20 MB cap.\n\n### 10. Create an event — `POST /services/event/v1/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ\",\"startDatetime\":\"2026-08-15T18:00:00Z\",\"templateName\":\"camp-confetti_vanilla_kids\"}}' \\\n  'https://www.evite.com/services/event/v1/'\n```\n\nRequired (Pydantic-enforced): `title`, `startDatetime`, `templateName`\n(get a valid slug from endpoint 5). **Quirk**: a successful create\nstill returns `500 \"Unknown error\"` (a secondary post-create step\nfails) — the draft is created anyway. Treat a 500 here as \"possibly\ncreated\" and re-list drafts (endpoint 1, `status=draft`) rather than\nretrying blindly.\n\n### 11. Update an event — `PATCH /services/event/v1/{id}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ — Rain Date\"}}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\"\n```\n\nFields MUST be nested under `event` (a bare `{\"title\":...}` 200s as a\nno-op). `PUT` 500s — only `PATCH` works.\n\n### 12. Add draft guests — `POST /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '[{\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\"}]' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\n**Legacy `/ajax/` base**, and the body is a **top-level JSON array**\n(NOT wrapped in an object) — the server does `for g in payload:\nDraftGuest(**g)`. Only persists once the event is finalized (status\n`sending`); on a bare `draft` the POST 200s but silently drops the\nguest.\n\n### 13. Edit a draft guest — `PATCH /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"guest_id\":\"'\"$GUEST_ID\"'\",\"event_id\":\"'\"$EVENT_ID\"'\",\"invite_method\":\"email\",\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\",\"phone\":\"\"}' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\nFull object every time (`guest_id` selects, the rest are the new\nvalues) — this is a set, not a merge.\n\n### 14. Remove a draft guest — `DELETE /ajax/event/{id}/guestlist/draft/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X DELETE \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/$GUEST_ID\"\n```\n\nNo body, but `src/client.ts`'s `write()` always sends `Content-Type:\napplication/json` on every write regardless of body presence — include it\nhere too.\n\n### 15. Send the invitation (\"Send now\", assumed body) — `POST /services/event/v1/{id}/send/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/send/\"\n```\n\nSends the draft guest list. Body assumed empty — this really emails\nevery draft guest; test on a throwaway event first.\n\n### 16. Cancel an event (also \"delete draft\") — `POST /services/event/v1/{id}/actions/cancel/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/cancel/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. The `/actions/{verb}/` convention for host lifecycle\nactions.\n\n### 17. Reinstate a canceled event — `POST /services/event/v1/{id}/actions/reinstate/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/reinstate/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. Restores a `cancelled` event to `sending`.\n\n### 18. Duplicate an event — `GET /plus/create/{id}/copy/?previous=my_events`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -D - -o /dev/null \\\n  \"https://www.evite.com/plus/create/$EVENT_ID/copy/?previous=my_events\" \\\n  | grep -i '^location:'\n# Location: /invitation/{newId}/customize?...&source_event={id}\n```\n\nA plain `GET` that `302`s; the new draft's id is the `/invitation/{newId}/`\npath segment of the `Location` header (no request body).\n\n---\n\n## Endpoint count\n\n19 distinct HTTP calls across 3 bases (`/services/`, `/ajax/`,\n`/tsunami/`) + 1 external GCS call — mirrors all 6 read tools and 13\nwrite tools evite-mcp exposes (`evite_list_events`, `evite_get_event`,\n`evite_list_guests`, `evite_rsvp_summary`, `evite_list_messages`,\n`evite_list_templates`, `evite_rsvp`, `evite_send_message`,\n`evite_broadcast`, `evite_upload_photo`, `evite_create_event`,\n`evite_update_event`, `evite_add_guest`, `evite_update_guest`,\n`evite_remove_guest`, `evite_send`, `evite_cancel_event`,\n`evite_reinstate_event`, `evite_duplicate_event`).\n\nFile v1.3.5:skill-card.md\n\n## Description:\n\nGuides agents in reading and managing Evite events, guest lists, RSVPs, and messages from the shell using authenticated requests.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other Evite account holders use this skill to check events and guest responses or manage invitations from a shell without an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Account credentials and live session cookies can expose Evite account access or private guest lists.\n\nMitigation: Avoid passwords in shell history and store cookie jars and guest lists only in restricted, non-shared locations.\n\nRisk: Actions can change events or email real guests without a built-in confirmation step.\n\nMitigation: Require explicit confirmation before sends, broadcasts, cancellations, guest-list changes, or photo uploads; test with a throwaway event.\n\nRisk: Two message and invitation request bodies are not verified, so these operations may fail or act unexpectedly.\n\nMitigation: Test unverified operations on a throwaway event before using them with real guests.\n\n## Reference(s):\n\n- [Evite endpoint reference](artifact/references/endpoints.md)\n- [Evite API skill on ClawHub](https://clawhub.ai/chrischall/skills/evite-api)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell code blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands use an Evite account session; some actions change events or contact guests.]\n\n## Skill Version(s):\n\n1.3.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.3.4: 4 files, 9019 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (1893b), SKILL.md (5007b), _meta.json (128b)\n\nFile v1.3.4:SKILL.md\n\n---\nname: evite-api\ndescription: >-\n  Query and act on Evite (evite.com) events, guest lists, RSVPs, and\n  messages from a shell with curl and a cookie jar — instead of running\n  the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login\n  against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no\n  browser or extension involved. Use when you want Evite data/actions\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on\n  Evite from the shell\", \"curl Evite\", \"evite-api skill\".\n---\n\n# Evite via curl (no MCP)\n\nEvite has no public API — this hits the site's own internal `/services/`\n(+ `/ajax/`, `/tsunami/`) layer using session cookies, exactly like\nevite-mcp does. **This is a curl skill, not fpx**: Evite's login is a\nplain CSRF-protected Django form POST, and reads/writes with the\nresulting cookies are **not** bot-walled (Cloudflare sits in front but\ndoesn't trip on a plain `curl`/Node request carrying a valid session) —\nso the whole flow runs server-side with a cookie jar. No signed-in\nbrowser tab, no ContextMint Bridge extension, no bridge.\n\nAuth model: session cookies `x-evite-session` + `evtsession`, plus a\nCSRF cookie `csrftoken` sent back as the `X-CSRFToken` header on every\nwrite. The CSRF cookie **rotates** — re-read it from the jar immediately\nbefore each write, never cache it.\n\n## One-time login (per shell session, until the jar's session expires)\n\n```sh\nexport EVITE_EMAIL=you@example.com EVITE_PASSWORD=yourpassword   # or: op read op://.../evite/password\nJAR=/tmp/evite-jar.txt\ntouch \"$JAR\" && chmod 600 \"$JAR\"   # jar holds the live session cookie — restrict before writing\nUA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36'\n\n# 1. Prime: GET the homepage to obtain the csrftoken (+ anonymous session) cookies.\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null\n\n# 2. Login: POST creds with the primed jar + X-CSRFToken + Origin/Referer (Django's\n#    HTTPS CSRF check needs all three; a cold POST without them 403s).\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\\n  'https://www.evite.com/ajax_login'\n```\n\nA `200` means the jar now holds the authenticated cookies\n(`x-evite-session`, `evtsession`, a freshly-rotated `csrftoken`,\n`x-evite-features`) and a JSON body with `full_name`/`user_id`/`token`.\n`401 {\"error\":\"Invalid Email Address / Password\"}` = bad creds; `403\n{\"error\":\"HTTP_403\"}` = the priming GET was skipped, or its `csrftoken`\nwasn't echoed back.\n\n## Reads: jar + `Accept: application/json`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation' | jq .\n```\n\nAlways pass `-b \"$JAR\" -c \"$JAR\"` **together** (read AND rewrite the same\nfile) — Evite occasionally re-sets cookies even on a GET, and a write's\n`csrftoken` rotation must land back in the jar for the next call.\n\n## Writes: jar + fresh `X-CSRFToken` + `Content-Type: application/json`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nRe-read `$CSRF` from `$JAR` **right before every write** — a stale value\n403s. If a write still 401/403s with the cookie unchanged in the jar\n(check `awk -F'\\t' '$6==\"csrftoken\"'  \"$JAR\"` before/after), the session\nitself expired: redo the one-time login.\n\nFull endpoint list (every read + write, real paths/bodies, `jq`\nrecipes) is in `references/endpoints.md`.\n\n## Notes / caveats\n\n- **Two write bodies are still unverified upstream** (tracked as\n  evite-mcp issue #3): `send_message` (`POST\n  /tsunami/v1/services/event/{id}/guest/{gid}/messages`) and\n  `send_invitation` (`POST /services/event/v1/{id}/send/`) — only the\n  endpoints were captured live, not their exact request bodies. The MCP\n  (and this skill) send `{\"message\": \"...\"}` / `{}` as the best-known\n  shape; treat those two as slightly less certain than the rest.\n- `list_templates` is HTML scraping (`GET /invites/{category}/`), not\n  JSON — see the reference for the extraction pattern.\n- **Writes here really mutate Evite** — RSVPs, cancellations,\n  broadcast emails to real guests. There's no MCP-side confirm-gate in\n  this skill: you are the confirm gate. Test broadcasts/sends only\n  against a throwaway event with a blackholed `@example.com` guest.\n- This project (evite-mcp) is developed and maintained by AI (Claude).\n\nFile v1.3.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"evite-api\",\n  \"version\": \"1.3.4\",\n  \"publishedAt\": 1791380385369\n}\n\nFile v1.3.4:references/endpoints.md\n\n# Evite endpoint reference (curl)\n\nBase host: `https://www.evite.com`. All paths below assume `$JAR` already\nholds an authenticated session (see `SKILL.md`'s one-time login) and that\nevery call passes `-b \"$JAR\" -c \"$JAR\"` so a rotated `csrftoken` lands\nback in the jar. Reads add `-H 'Accept: application/json'`; writes add\n`-H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\"` with `CSRF`\nre-read from the jar immediately before the call:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n```\n\nTranscribed from `evite-mcp`'s `src/client.ts` + `docs/EVITE-API.md`\n(live-verified 2026-06-01/02). Every shape below is a REAL captured\nrequest/response unless marked \"assumed\" (URL captured, body not).\n\n---\n\n## Reads\n\n### 1. List events — `GET /services/events/v1/`\n\nQuery: `filterBy` (`all|host|others`, others = you're a guest), `status`\n(repeatable — `upcoming|draft|archived|past|canceled`), `type=invitation`,\n`offset`, `numResults`, `filter` (free text).\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation&status=upcoming&status=draft&numResults=25' \\\n  | jq '.totals, (.events[] | {id: .event_id, title, start, status, is_host, rsvp})'\n```\n\n→ `{ events: Event[], totals }`. `totals` = `{all, sending, draft,\nreceived, canceled, past, upcoming, archived}` (sending = hosting,\nreceived = invited). `Event` fields: `event_id, title, start, end,\nstatus, past, is_host, rsvp(yes|no|maybe), guest_status(0|1|2),\nguest_id, host_id, host_name, location{location_name, street_address,\nunit_num, city, state, zip_code, place_id}, timezone,\nknown_timezone_name, template_name, event_category, rsvp_off, is_invite,\nis_pending_cohost, rendered_image_url, updated`.\n\n### 2. Get event detail — `GET /services/event/v1/{id}`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\" \\\n  | jq '{title: .event.title, start: .event.startDatetime, status: .event.status, settings}'\n```\n\n→ top keys: `event, calculatedFields, design, location,\nuserEventContext, attributes, registries, settings, charity, gifting,\nrendered, calendar, features`. `event`: `id, title, message,\nstartDatetime, endDatetime, knownTimezoneName,\nknownTimezoneAbbreviation, eventHostName, eventPhoneNumber, hostId,\nhostIds, status, isPast, eventType, category, superCategory,\ntemplateName, origin, isFabricPremium, shareableLink, sendOn`.\n`settings`: `enableMaybe, privateGuestList, headCountByFamily, plusOne,\nmaxEventCapacity, allowViewMap, showGifting, rsvpBy, strictRsvpBy,\nenableHostPhotoGallery, enablePhotoSharing, allowGuestNumber, rsvpOff`.\n\n### 3. Guest list + RSVP summary — `GET /services/event/v1/{id}/guests/`\n\nOne endpoint powers both `evite_list_guests` and `evite_rsvp_summary`.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/\" \\\n  | jq '.summary, (.guests[] | {guestId, name, rsvpResponse, numberOfAdults, numberOfKids})'\n```\n\n→ `{ guests: Guest[], summary }`. `Guest`: `guestId, userId, name,\nemail, phone, guestType(host|cohost|guest),\nrsvpResponse(yes|no|maybe|noreply), numberOfAdults, numberOfKids,\ncheckedIn, comments, deliveryStatus, inviteMethod(email|null),\ninvitedBy, sentOn, timesViewed, avatarUrl, shortLink, longLink,\ncreated, updated`. `summary`: `{yes, no, maybe, noReply, adultsYes,\nkidsYes, adultsMaybe, kidsMaybe, inviteesYes, inviteesMaybe,\nlockedStatus}`.\n\n### 4. Messages — `GET /services/event/v1/{id}/posts/`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/posts/\" | jq '.posts'\n```\n\n→ `{ posts: Post[] }` — the event's \"Messages\" tab thread.\n\n### 5. List templates (HTML scrape, not JSON) — `GET /invites/{category}/`\n\nThe gallery is server-rendered — no JSON API. Grep the SSR HTML for\n`/invitation/{slug}/(create|preview|details)` links (mirrors\n`EviteClient.listTemplates`'s regex). Add `?active_filter=free_premium%2Cfree`\nfor free-only.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" \\\n  'https://www.evite.com/invites/kids-birthday/?active_filter=free_premium%2Cfree' \\\n  | grep -oE '/invitation/[a-z0-9][a-z0-9_-]+/(create|preview|details)' \\\n  | sed -E 's#/invitation/([a-z0-9_-]+)/.*#\\1#' | sort -u\n```\n\nEach slug is a `templateName` — pass it to `create_event` below.\n\n---\n\n## Writes\n\nEvery write really mutates Evite. `-d` bodies below are exact JSON\nunless marked \"assumed\" (endpoint captured live; the exact body field\nwasn't).\n\n### 6. RSVP — `PUT /services/event/v1/{id}/guests/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0,\"comments\":\"can'\\''t wait!\"}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nBody: `rsvpResponse` (`yes|no|maybe`), `numberOfAdults`,\n`numberOfKids`, optional `comments` — field names match the read\n`Guest` shape. `comments` is optional (omit the key entirely to leave\nit unchanged).\n\n### 7. Send a private guest message (assumed body) — `POST /tsunami/v1/services/event/{id}/guest/{gid}/messages`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"See you Saturday!\"}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/guest/$GUEST_ID/messages\"\n```\n\nA **third API base** (`/tsunami/`, not `/services/…/posts/`, which is\nGET-only). Body assumed `{message}`.\n\n### 8. Broadcast to RSVP segments — `POST /tsunami/v1/services/event/{id}/broadcast/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"Reminder: parking is on the north side!\",\"captcha\":null,\"virtual_groups\":[\"yes\",\"maybe\"],\"participantCount\":12}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/broadcast/\"\n```\n\nBody **fully captured** (not assumed): `message`, `captcha` (always\n`null`), `virtual_groups` (array of RSVP segments to reach — the\nvalues the dashboard uses are `yes`/`no`/`maybe`), optional\n`participantCount` (informational — the count the web UI sends along).\nThis emails every guest in the named segments.\n\n### 9. Upload a photo to the shared gallery — 4-step Google Cloud Storage flow\n\nThe file goes to GCS, not Evite. All 4 steps, in order:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n\n# Step 1 — get a signed-upload ticket from Evite.\nTICKET=$(curl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg eid \"$EVENT_ID\" --arg gid \"$GUEST_ID\" --arg mt \"image/jpeg\" \\\n        '{upload_path:\"feed_photos\",event_id:$eid,photo_id:\"\",guest_id:$gid,redirect:true,mimetype:$mt,width:1200,height:900}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/upload/request/\")\nUPLOAD_URL=$(jq -r '.upload_url' <<<\"$TICKET\")\nPHOTO_ID=$(jq -r '.upload_form.key' <<<\"$TICKET\" | awk -F/ '{print $NF}')\n\n# Step 2 — multipart POST straight to GCS: signed fields first, `file` LAST,\n# no Evite cookies (the policy/signature ARE the auth). Capture the redirect.\nFORM_ARGS=()\nfor k in $(jq -r '.upload_form | keys[]' <<<\"$TICKET\"); do\n  v=$(jq -r --arg k \"$k\" '.upload_form[$k]' <<<\"$TICKET\")\n  FORM_ARGS+=(-F \"$k=$v\")\ndone\nFINISH_URL=$(curl -sS -D - -o /dev/null \"${FORM_ARGS[@]}\" -F \"file=@/path/to/photo.jpg\" \"$UPLOAD_URL\" \\\n  | grep -i '^location:' | tr -d '\\r' | awk '{print $2}')\n\n# Step 3 — finalize the object into the album (best-effort; ignore failures).\ncurl -sS -b \"$JAR\" -c \"$JAR\" \"$FINISH_URL\" -o /dev/null\n\n# Step 4 — register the photo in the event's shared gallery.\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg pid \"$PHOTO_ID\" '{photo_ids:[$pid]}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/shared-gallery/?gid=$GUEST_ID\"\n```\n\nStep 1's mimetype must match the file's real content type; the GCS\npolicy enforces `Content-Type == mimetype` and a 20 MB cap.\n\n### 10. Create an event — `POST /services/event/v1/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ\",\"startDatetime\":\"2026-08-15T18:00:00Z\",\"templateName\":\"camp-confetti_vanilla_kids\"}}' \\\n  'https://www.evite.com/services/event/v1/'\n```\n\nRequired (Pydantic-enforced): `title`, `startDatetime`, `templateName`\n(get a valid slug from endpoint 5). **Quirk**: a successful create\nstill returns `500 \"Unknown error\"` (a secondary post-create step\nfails) — the draft is created anyway. Treat a 500 here as \"possibly\ncreated\" and re-list drafts (endpoint 1, `status=draft`) rather than\nretrying blindly.\n\n### 11. Update an event — `PATCH /services/event/v1/{id}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ — Rain Date\"}}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\"\n```\n\nFields MUST be nested under `event` (a bare `{\"title\":...}` 200s as a\nno-op). `PUT` 500s — only `PATCH` works.\n\n### 12. Add draft guests — `POST /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '[{\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\"}]' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\n**Legacy `/ajax/` base**, and the body is a **top-level JSON array**\n(NOT wrapped in an object) — the server does `for g in payload:\nDraftGuest(**g)`. Only persists once the event is finalized (status\n`sending`); on a bare `draft` the POST 200s but silently drops the\nguest.\n\n### 13. Edit a draft guest — `PATCH /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"guest_id\":\"'\"$GUEST_ID\"'\",\"event_id\":\"'\"$EVENT_ID\"'\",\"invite_method\":\"email\",\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\",\"phone\":\"\"}' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\nFull object every time (`guest_id` selects, the rest are the new\nvalues) — this is a set, not a merge.\n\n### 14. Remove a draft guest — `DELETE /ajax/event/{id}/guestlist/draft/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X DELETE \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/$GUEST_ID\"\n```\n\nNo body, but `src/client.ts`'s `write()` always sends `Content-Type:\napplication/json` on every write regardless of body presence — include it\nhere too.\n\n### 15. Send the invitation (\"Send now\", assumed body) — `POST /services/event/v1/{id}/send/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/send/\"\n```\n\nSends the draft guest list. Body assumed empty — this really emails\nevery draft guest; test on a throwaway event first.\n\n### 16. Cancel an event (also \"delete draft\") — `POST /services/event/v1/{id}/actions/cancel/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/cancel/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. The `/actions/{verb}/` convention for host lifecycle\nactions.\n\n### 17. Reinstate a canceled event — `POST /services/event/v1/{id}/actions/reinstate/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/reinstate/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. Restores a `cancelled` event to `sending`.\n\n### 18. Duplicate an event — `GET /plus/create/{id}/copy/?previous=my_events`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -D - -o /dev/null \\\n  \"https://www.evite.com/plus/create/$EVENT_ID/copy/?previous=my_events\" \\\n  | grep -i '^location:'\n# Location: /invitation/{newId}/customize?...&source_event={id}\n```\n\nA plain `GET` that `302`s; the new draft's id is the `/invitation/{newId}/`\npath segment of the `Location` header (no request body).\n\n---\n\n## Endpoint count\n\n19 distinct HTTP calls across 3 bases (`/services/`, `/ajax/`,\n`/tsunami/`) + 1 external GCS call — mirrors all 6 read tools and 13\nwrite tools evite-mcp exposes (`evite_list_events`, `evite_get_event`,\n`evite_list_guests`, `evite_rsvp_summary`, `evite_list_messages`,\n`evite_list_templates`, `evite_rsvp`, `evite_send_message`,\n`evite_broadcast`, `evite_upload_photo`, `evite_create_event`,\n`evite_update_event`, `evite_add_guest`, `evite_update_guest`,\n`evite_remove_guest`, `evite_send`, `evite_cancel_event`,\n`evite_reinstate_event`, `evite_duplicate_event`).\n\nFile v1.3.4:skill-card.md\n\n## Description:\n\nGuides agents in reading and updating Evite events, guest lists, RSVPs, and messages through authenticated shell commands.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nEvite users and developers use this skill to inspect events and guest responses or perform authorized event updates from a shell without an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Authenticated commands can send invitations or broadcasts and change RSVPs, guests, photos, or event status without a built-in confirmation step.\n\nMitigation: Review exact event and guest IDs, recipient counts, and message bodies; require explicit human approval before sends, deletions, uploads, RSVP changes, or cancellations.\n\nRisk: A saved cookie jar grants live Evite account access, and shell credentials may be exposed.\n\nMitigation: Keep credentials out of shell history; protect and delete the cookie jar after use, and test with a throwaway event.\n\n## Reference(s):\n\n- [Evite API endpoint reference](artifact/references/endpoints.md)\n- [evite-api ClawHub release](https://clawhub.ai/chrischall/skills/evite-api)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell commands and explanatory text]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands can return Evite data or change live account and event records when executed.]\n\n## Skill Version(s):\n\n1.3.4 (source: server-resolved release)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.3.3: 4 files, 9020 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (1973b), SKILL.md (5007b), _meta.json (128b)\n\nFile v1.3.3:SKILL.md\n\n---\nname: evite-api\ndescription: >-\n  Query and act on Evite (evite.com) events, guest lists, RSVPs, and\n  messages from a shell with curl and a cookie jar — instead of running\n  the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login\n  against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no\n  browser or extension involved. Use when you want Evite data/actions\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on\n  Evite from the shell\", \"curl Evite\", \"evite-api skill\".\n---\n\n# Evite via curl (no MCP)\n\nEvite has no public API — this hits the site's own internal `/services/`\n(+ `/ajax/`, `/tsunami/`) layer using session cookies, exactly like\nevite-mcp does. **This is a curl skill, not fpx**: Evite's login is a\nplain CSRF-protected Django form POST, and reads/writes with the\nresulting cookies are **not** bot-walled (Cloudflare sits in front but\ndoesn't trip on a plain `curl`/Node request carrying a valid session) —\nso the whole flow runs server-side with a cookie jar. No signed-in\nbrowser tab, no ContextMint Bridge extension, no bridge.\n\nAuth model: session cookies `x-evite-session` + `evtsession`, plus a\nCSRF cookie `csrftoken` sent back as the `X-CSRFToken` header on every\nwrite. The CSRF cookie **rotates** — re-read it from the jar immediately\nbefore each write, never cache it.\n\n## One-time login (per shell session, until the jar's session expires)\n\n```sh\nexport EVITE_EMAIL=you@example.com EVITE_PASSWORD=yourpassword   # or: op read op://.../evite/password\nJAR=/tmp/evite-jar.txt\ntouch \"$JAR\" && chmod 600 \"$JAR\"   # jar holds the live session cookie — restrict before writing\nUA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36'\n\n# 1. Prime: GET the homepage to obtain the csrftoken (+ anonymous session) cookies.\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null\n\n# 2. Login: POST creds with the primed jar + X-CSRFToken + Origin/Referer (Django's\n#    HTTPS CSRF check needs all three; a cold POST without them 403s).\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\\n  'https://www.evite.com/ajax_login'\n```\n\nA `200` means the jar now holds the authenticated cookies\n(`x-evite-session`, `evtsession`, a freshly-rotated `csrftoken`,\n`x-evite-features`) and a JSON body with `full_name`/`user_id`/`token`.\n`401 {\"error\":\"Invalid Email Address / Password\"}` = bad creds; `403\n{\"error\":\"HTTP_403\"}` = the priming GET was skipped, or its `csrftoken`\nwasn't echoed back.\n\n## Reads: jar + `Accept: application/json`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation' | jq .\n```\n\nAlways pass `-b \"$JAR\" -c \"$JAR\"` **together** (read AND rewrite the same\nfile) — Evite occasionally re-sets cookies even on a GET, and a write's\n`csrftoken` rotation must land back in the jar for the next call.\n\n## Writes: jar + fresh `X-CSRFToken` + `Content-Type: application/json`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nRe-read `$CSRF` from `$JAR` **right before every write** — a stale value\n403s. If a write still 401/403s with the cookie unchanged in the jar\n(check `awk -F'\\t' '$6==\"csrftoken\"'  \"$JAR\"` before/after), the session\nitself expired: redo the one-time login.\n\nFull endpoint list (every read + write, real paths/bodies, `jq`\nrecipes) is in `references/endpoints.md`.\n\n## Notes / caveats\n\n- **Two write bodies are still unverified upstream** (tracked as\n  evite-mcp issue #3): `send_message` (`POST\n  /tsunami/v1/services/event/{id}/guest/{gid}/messages`) and\n  `send_invitation` (`POST /services/event/v1/{id}/send/`) — only the\n  endpoints were captured live, not their exact request bodies. The MCP\n  (and this skill) send `{\"message\": \"...\"}` / `{}` as the best-known\n  shape; treat those two as slightly less certain than the rest.\n- `list_templates` is HTML scraping (`GET /invites/{category}/`), not\n  JSON — see the reference for the extraction pattern.\n- **Writes here really mutate Evite** — RSVPs, cancellations,\n  broadcast emails to real guests. There's no MCP-side confirm-gate in\n  this skill: you are the confirm gate. Test broadcasts/sends only\n  against a throwaway event with a blackholed `@example.com` guest.\n- This project (evite-mcp) is developed and maintained by AI (Claude).\n\nFile v1.3.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"evite-api\",\n  \"version\": \"1.3.3\",\n  \"publishedAt\": 1791168682392\n}\n\nFile v1.3.3:references/endpoints.md\n\n# Evite endpoint reference (curl)\n\nBase host: `https://www.evite.com`. All paths below assume `$JAR` already\nholds an authenticated session (see `SKILL.md`'s one-time login) and that\nevery call passes `-b \"$JAR\" -c \"$JAR\"` so a rotated `csrftoken` lands\nback in the jar. Reads add `-H 'Accept: application/json'`; writes add\n`-H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\"` with `CSRF`\nre-read from the jar immediately before the call:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n```\n\nTranscribed from `evite-mcp`'s `src/client.ts` + `docs/EVITE-API.md`\n(live-verified 2026-06-01/02). Every shape below is a REAL captured\nrequest/response unless marked \"assumed\" (URL captured, body not).\n\n---\n\n## Reads\n\n### 1. List events — `GET /services/events/v1/`\n\nQuery: `filterBy` (`all|host|others`, others = you're a guest), `status`\n(repeatable — `upcoming|draft|archived|past|canceled`), `type=invitation`,\n`offset`, `numResults`, `filter` (free text).\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation&status=upcoming&status=draft&numResults=25' \\\n  | jq '.totals, (.events[] | {id: .event_id, title, start, status, is_host, rsvp})'\n```\n\n→ `{ events: Event[], totals }`. `totals` = `{all, sending, draft,\nreceived, canceled, past, upcoming, archived}` (sending = hosting,\nreceived = invited). `Event` fields: `event_id, title, start, end,\nstatus, past, is_host, rsvp(yes|no|maybe), guest_status(0|1|2),\nguest_id, host_id, host_name, location{location_name, street_address,\nunit_num, city, state, zip_code, place_id}, timezone,\nknown_timezone_name, template_name, event_category, rsvp_off, is_invite,\nis_pending_cohost, rendered_image_url, updated`.\n\n### 2. Get event detail — `GET /services/event/v1/{id}`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\" \\\n  | jq '{title: .event.title, start: .event.startDatetime, status: .event.status, settings}'\n```\n\n→ top keys: `event, calculatedFields, design, location,\nuserEventContext, attributes, registries, settings, charity, gifting,\nrendered, calendar, features`. `event`: `id, title, message,\nstartDatetime, endDatetime, knownTimezoneName,\nknownTimezoneAbbreviation, eventHostName, eventPhoneNumber, hostId,\nhostIds, status, isPast, eventType, category, superCategory,\ntemplateName, origin, isFabricPremium, shareableLink, sendOn`.\n`settings`: `enableMaybe, privateGuestList, headCountByFamily, plusOne,\nmaxEventCapacity, allowViewMap, showGifting, rsvpBy, strictRsvpBy,\nenableHostPhotoGallery, enablePhotoSharing, allowGuestNumber, rsvpOff`.\n\n### 3. Guest list + RSVP summary — `GET /services/event/v1/{id}/guests/`\n\nOne endpoint powers both `evite_list_guests` and `evite_rsvp_summary`.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/\" \\\n  | jq '.summary, (.guests[] | {guestId, name, rsvpResponse, numberOfAdults, numberOfKids})'\n```\n\n→ `{ guests: Guest[], summary }`. `Guest`: `guestId, userId, name,\nemail, phone, guestType(host|cohost|guest),\nrsvpResponse(yes|no|maybe|noreply), numberOfAdults, numberOfKids,\ncheckedIn, comments, deliveryStatus, inviteMethod(email|null),\ninvitedBy, sentOn, timesViewed, avatarUrl, shortLink, longLink,\ncreated, updated`. `summary`: `{yes, no, maybe, noReply, adultsYes,\nkidsYes, adultsMaybe, kidsMaybe, inviteesYes, inviteesMaybe,\nlockedStatus}`.\n\n### 4. Messages — `GET /services/event/v1/{id}/posts/`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/posts/\" | jq '.posts'\n```\n\n→ `{ posts: Post[] }` — the event's \"Messages\" tab thread.\n\n### 5. List templates (HTML scrape, not JSON) — `GET /invites/{category}/`\n\nThe gallery is server-rendered — no JSON API. Grep the SSR HTML for\n`/invitation/{slug}/(create|preview|details)` links (mirrors\n`EviteClient.listTemplates`'s regex). Add `?active_filter=free_premium%2Cfree`\nfor free-only.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" \\\n  'https://www.evite.com/invites/kids-birthday/?active_filter=free_premium%2Cfree' \\\n  | grep -oE '/invitation/[a-z0-9][a-z0-9_-]+/(create|preview|details)' \\\n  | sed -E 's#/invitation/([a-z0-9_-]+)/.*#\\1#' | sort -u\n```\n\nEach slug is a `templateName` — pass it to `create_event` below.\n\n---\n\n## Writes\n\nEvery write really mutates Evite. `-d` bodies below are exact JSON\nunless marked \"assumed\" (endpoint captured live; the exact body field\nwasn't).\n\n### 6. RSVP — `PUT /services/event/v1/{id}/guests/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0,\"comments\":\"can'\\''t wait!\"}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nBody: `rsvpResponse` (`yes|no|maybe`), `numberOfAdults`,\n`numberOfKids`, optional `comments` — field names match the read\n`Guest` shape. `comments` is optional (omit the key entirely to leave\nit unchanged).\n\n### 7. Send a private guest message (assumed body) — `POST /tsunami/v1/services/event/{id}/guest/{gid}/messages`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"See you Saturday!\"}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/guest/$GUEST_ID/messages\"\n```\n\nA **third API base** (`/tsunami/`, not `/services/…/posts/`, which is\nGET-only). Body assumed `{message}`.\n\n### 8. Broadcast to RSVP segments — `POST /tsunami/v1/services/event/{id}/broadcast/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"Reminder: parking is on the north side!\",\"captcha\":null,\"virtual_groups\":[\"yes\",\"maybe\"],\"participantCount\":12}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/broadcast/\"\n```\n\nBody **fully captured** (not assumed): `message`, `captcha` (always\n`null`), `virtual_groups` (array of RSVP segments to reach — the\nvalues the dashboard uses are `yes`/`no`/`maybe`), optional\n`participantCount` (informational — the count the web UI sends along).\nThis emails every guest in the named segments.\n\n### 9. Upload a photo to the shared gallery — 4-step Google Cloud Storage flow\n\nThe file goes to GCS, not Evite. All 4 steps, in order:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n\n# Step 1 — get a signed-upload ticket from Evite.\nTICKET=$(curl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg eid \"$EVENT_ID\" --arg gid \"$GUEST_ID\" --arg mt \"image/jpeg\" \\\n        '{upload_path:\"feed_photos\",event_id:$eid,photo_id:\"\",guest_id:$gid,redirect:true,mimetype:$mt,width:1200,height:900}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/upload/request/\")\nUPLOAD_URL=$(jq -r '.upload_url' <<<\"$TICKET\")\nPHOTO_ID=$(jq -r '.upload_form.key' <<<\"$TICKET\" | awk -F/ '{print $NF}')\n\n# Step 2 — multipart POST straight to GCS: signed fields first, `file` LAST,\n# no Evite cookies (the policy/signature ARE the auth). Capture the redirect.\nFORM_ARGS=()\nfor k in $(jq -r '.upload_form | keys[]' <<<\"$TICKET\"); do\n  v=$(jq -r --arg k \"$k\" '.upload_form[$k]' <<<\"$TICKET\")\n  FORM_ARGS+=(-F \"$k=$v\")\ndone\nFINISH_URL=$(curl -sS -D - -o /dev/null \"${FORM_ARGS[@]}\" -F \"file=@/path/to/photo.jpg\" \"$UPLOAD_URL\" \\\n  | grep -i '^location:' | tr -d '\\r' | awk '{print $2}')\n\n# Step 3 — finalize the object into the album (best-effort; ignore failures).\ncurl -sS -b \"$JAR\" -c \"$JAR\" \"$FINISH_URL\" -o /dev/null\n\n# Step 4 — register the photo in the event's shared gallery.\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg pid \"$PHOTO_ID\" '{photo_ids:[$pid]}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/shared-gallery/?gid=$GUEST_ID\"\n```\n\nStep 1's mimetype must match the file's real content type; the GCS\npolicy enforces `Content-Type == mimetype` and a 20 MB cap.\n\n### 10. Create an event — `POST /services/event/v1/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ\",\"startDatetime\":\"2026-08-15T18:00:00Z\",\"templateName\":\"camp-confetti_vanilla_kids\"}}' \\\n  'https://www.evite.com/services/event/v1/'\n```\n\nRequired (Pydantic-enforced): `title`, `startDatetime`, `templateName`\n(get a valid slug from endpoint 5). **Quirk**: a successful create\nstill returns `500 \"Unknown error\"` (a secondary post-create step\nfails) — the draft is created anyway. Treat a 500 here as \"possibly\ncreated\" and re-list drafts (endpoint 1, `status=draft`) rather than\nretrying blindly.\n\n### 11. Update an event — `PATCH /services/event/v1/{id}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ — Rain Date\"}}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\"\n```\n\nFields MUST be nested under `event` (a bare `{\"title\":...}` 200s as a\nno-op). `PUT` 500s — only `PATCH` works.\n\n### 12. Add draft guests — `POST /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '[{\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\"}]' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\n**Legacy `/ajax/` base**, and the body is a **top-level JSON array**\n(NOT wrapped in an object) — the server does `for g in payload:\nDraftGuest(**g)`. Only persists once the event is finalized (status\n`sending`); on a bare `draft` the POST 200s but silently drops the\nguest.\n\n### 13. Edit a draft guest — `PATCH /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"guest_id\":\"'\"$GUEST_ID\"'\",\"event_id\":\"'\"$EVENT_ID\"'\",\"invite_method\":\"email\",\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\",\"phone\":\"\"}' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\nFull object every time (`guest_id` selects, the rest are the new\nvalues) — this is a set, not a merge.\n\n### 14. Remove a draft guest — `DELETE /ajax/event/{id}/guestlist/draft/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X DELETE \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/$GUEST_ID\"\n```\n\nNo body, but `src/client.ts`'s `write()` always sends `Content-Type:\napplication/json` on every write regardless of body presence — include it\nhere too.\n\n### 15. Send the invitation (\"Send now\", assumed body) — `POST /services/event/v1/{id}/send/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/send/\"\n```\n\nSends the draft guest list. Body assumed empty — this really emails\nevery draft guest; test on a throwaway event first.\n\n### 16. Cancel an event (also \"delete draft\") — `POST /services/event/v1/{id}/actions/cancel/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/cancel/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. The `/actions/{verb}/` convention for host lifecycle\nactions.\n\n### 17. Reinstate a canceled event — `POST /services/event/v1/{id}/actions/reinstate/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/reinstate/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. Restores a `cancelled` event to `sending`.\n\n### 18. Duplicate an event — `GET /plus/create/{id}/copy/?previous=my_events`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -D - -o /dev/null \\\n  \"https://www.evite.com/plus/create/$EVENT_ID/copy/?previous=my_events\" \\\n  | grep -i '^location:'\n# Location: /invitation/{newId}/customize?...&source_event={id}\n```\n\nA plain `GET` that `302`s; the new draft's id is the `/invitation/{newId}/`\npath segment of the `Location` header (no request body).\n\n---\n\n## Endpoint count\n\n19 distinct HTTP calls across 3 bases (`/services/`, `/ajax/`,\n`/tsunami/`) + 1 external GCS call — mirrors all 6 read tools and 13\nwrite tools evite-mcp exposes (`evite_list_events`, `evite_get_event`,\n`evite_list_guests`, `evite_rsvp_summary`, `evite_list_messages`,\n`evite_list_templates`, `evite_rsvp`, `evite_send_message`,\n`evite_broadcast`, `evite_upload_photo`, `evite_create_event`,\n`evite_update_event`, `evite_add_guest`, `evite_update_guest`,\n`evite_remove_guest`, `evite_send`, `evite_cancel_event`,\n`evite_reinstate_event`, `evite_duplicate_event`).\n\nFile v1.3.3:skill-card.md\n\n## Description:\n\nGuides agents in using shell commands to read Evite events, guest lists, RSVPs, and messages and to make changes through an authenticated Evite session.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other Evite account holders can use this skill to check events and guest responses or manage invitations and event communications from a shell without an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Account credentials and live session cookies can grant access to the Evite account.\n\nMitigation: Restrict access to credentials and the cookie jar, and delete the jar after use.\n\nRisk: Write commands can change real events or email real guests without a built-in confirmation step.\n\nMitigation: Confirm each write command and its destination before execution; test sends on throwaway events.\n\nRisk: Photo upload commands can read local files and send them to an unintended destination.\n\nMitigation: Limit allowed upload paths and verify the destination before uploading.\n\n## Reference(s):\n\n- [Evite API skill on ClawHub](https://clawhub.ai/chrischall/skills/evite-api)\n- [Evite endpoint reference](references/endpoints.md)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell commands and JSON response examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands interact with a live Evite account; write operations can change events or contact guests.]\n\n## Skill Version(s):\n\n1.3.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.3.2: 4 files, 9087 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (2143b), SKILL.md (5007b), _meta.json (128b)\n\nFile v1.3.2:SKILL.md\n\n---\nname: evite-api\ndescription: >-\n  Query and act on Evite (evite.com) events, guest lists, RSVPs, and\n  messages from a shell with curl and a cookie jar — instead of running\n  the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login\n  against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no\n  browser or extension involved. Use when you want Evite data/actions\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on\n  Evite from the shell\", \"curl Evite\", \"evite-api skill\".\n---\n\n# Evite via curl (no MCP)\n\nEvite has no public API — this hits the site's own internal `/services/`\n(+ `/ajax/`, `/tsunami/`) layer using session cookies, exactly like\nevite-mcp does. **This is a curl skill, not fpx**: Evite's login is a\nplain CSRF-protected Django form POST, and reads/writes with the\nresulting cookies are **not** bot-walled (Cloudflare sits in front but\ndoesn't trip on a plain `curl`/Node request carrying a valid session) —\nso the whole flow runs server-side with a cookie jar. No signed-in\nbrowser tab, no ContextMint Bridge extension, no bridge.\n\nAuth model: session cookies `x-evite-session` + `evtsession`, plus a\nCSRF cookie `csrftoken` sent back as the `X-CSRFToken` header on every\nwrite. The CSRF cookie **rotates** — re-read it from the jar immediately\nbefore each write, never cache it.\n\n## One-time login (per shell session, until the jar's session expires)\n\n```sh\nexport EVITE_EMAIL=you@example.com EVITE_PASSWORD=yourpassword   # or: op read op://.../evite/password\nJAR=/tmp/evite-jar.txt\ntouch \"$JAR\" && chmod 600 \"$JAR\"   # jar holds the live session cookie — restrict before writing\nUA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36'\n\n# 1. Prime: GET the homepage to obtain the csrftoken (+ anonymous session) cookies.\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null\n\n# 2. Login: POST creds with the primed jar + X-CSRFToken + Origin/Referer (Django's\n#    HTTPS CSRF check needs all three; a cold POST without them 403s).\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\\n  'https://www.evite.com/ajax_login'\n```\n\nA `200` means the jar now holds the authenticated cookies\n(`x-evite-session`, `evtsession`, a freshly-rotated `csrftoken`,\n`x-evite-features`) and a JSON body with `full_name`/`user_id`/`token`.\n`401 {\"error\":\"Invalid Email Address / Password\"}` = bad creds; `403\n{\"error\":\"HTTP_403\"}` = the priming GET was skipped, or its `csrftoken`\nwasn't echoed back.\n\n## Reads: jar + `Accept: application/json`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation' | jq .\n```\n\nAlways pass `-b \"$JAR\" -c \"$JAR\"` **together** (read AND rewrite the same\nfile) — Evite occasionally re-sets cookies even on a GET, and a write's\n`csrftoken` rotation must land back in the jar for the next call.\n\n## Writes: jar + fresh `X-CSRFToken` + `Content-Type: application/json`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nRe-read `$CSRF` from `$JAR` **right before every write** — a stale value\n403s. If a write still 401/403s with the cookie unchanged in the jar\n(check `awk -F'\\t' '$6==\"csrftoken\"'  \"$JAR\"` before/after), the session\nitself expired: redo the one-time login.\n\nFull endpoint list (every read + write, real paths/bodies, `jq`\nrecipes) is in `references/endpoints.md`.\n\n## Notes / caveats\n\n- **Two write bodies are still unverified upstream** (tracked as\n  evite-mcp issue #3): `send_message` (`POST\n  /tsunami/v1/services/event/{id}/guest/{gid}/messages`) and\n  `send_invitation` (`POST /services/event/v1/{id}/send/`) — only the\n  endpoints were captured live, not their exact request bodies. The MCP\n  (and this skill) send `{\"message\": \"...\"}` / `{}` as the best-known\n  shape; treat those two as slightly less certain than the rest.\n- `list_templates` is HTML scraping (`GET /invites/{category}/`), not\n  JSON — see the reference for the extraction pattern.\n- **Writes here really mutate Evite** — RSVPs, cancellations,\n  broadcast emails to real guests. There's no MCP-side confirm-gate in\n  this skill: you are the confirm gate. Test broadcasts/sends only\n  against a throwaway event with a blackholed `@example.com` guest.\n- This project (evite-mcp) is developed and maintained by AI (Claude).\n\nFile v1.3.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"evite-api\",\n  \"version\": \"1.3.2\",\n  \"publishedAt\": 1790991655315\n}\n\nFile v1.3.2:references/endpoints.md\n\n# Evite endpoint reference (curl)\n\nBase host: `https://www.evite.com`. All paths below assume `$JAR` already\nholds an authenticated session (see `SKILL.md`'s one-time login) and that\nevery call passes `-b \"$JAR\" -c \"$JAR\"` so a rotated `csrftoken` lands\nback in the jar. Reads add `-H 'Accept: application/json'`; writes add\n`-H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\"` with `CSRF`\nre-read from the jar immediately before the call:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n```\n\nTranscribed from `evite-mcp`'s `src/client.ts` + `docs/EVITE-API.md`\n(live-verified 2026-06-01/02). Every shape below is a REAL captured\nrequest/response unless marked \"assumed\" (URL captured, body not).\n\n---\n\n## Reads\n\n### 1. List events — `GET /services/events/v1/`\n\nQuery: `filterBy` (`all|host|others`, others = you're a guest), `status`\n(repeatable — `upcoming|draft|archived|past|canceled`), `type=invitation`,\n`offset`, `numResults`, `filter` (free text).\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation&status=upcoming&status=draft&numResults=25' \\\n  | jq '.totals, (.events[] | {id: .event_id, title, start, status, is_host, rsvp})'\n```\n\n→ `{ events: Event[], totals }`. `totals` = `{all, sending, draft,\nreceived, canceled, past, upcoming, archived}` (sending = hosting,\nreceived = invited). `Event` fields: `event_id, title, start, end,\nstatus, past, is_host, rsvp(yes|no|maybe), guest_status(0|1|2),\nguest_id, host_id, host_name, location{location_name, street_address,\nunit_num, city, state, zip_code, place_id}, timezone,\nknown_timezone_name, template_name, event_category, rsvp_off, is_invite,\nis_pending_cohost, rendered_image_url, updated`.\n\n### 2. Get event detail — `GET /services/event/v1/{id}`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\" \\\n  | jq '{title: .event.title, start: .event.startDatetime, status: .event.status, settings}'\n```\n\n→ top keys: `event, calculatedFields, design, location,\nuserEventContext, attributes, registries, settings, charity, gifting,\nrendered, calendar, features`. `event`: `id, title, message,\nstartDatetime, endDatetime, knownTimezoneName,\nknownTimezoneAbbreviation, eventHostName, eventPhoneNumber, hostId,\nhostIds, status, isPast, eventType, category, superCategory,\ntemplateName, origin, isFabricPremium, shareableLink, sendOn`.\n`settings`: `enableMaybe, privateGuestList, headCountByFamily, plusOne,\nmaxEventCapacity, allowViewMap, showGifting, rsvpBy, strictRsvpBy,\nenableHostPhotoGallery, enablePhotoSharing, allowGuestNumber, rsvpOff`.\n\n### 3. Guest list + RSVP summary — `GET /services/event/v1/{id}/guests/`\n\nOne endpoint powers both `evite_list_guests` and `evite_rsvp_summary`.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/\" \\\n  | jq '.summary, (.guests[] | {guestId, name, rsvpResponse, numberOfAdults, numberOfKids})'\n```\n\n→ `{ guests: Guest[], summary }`. `Guest`: `guestId, userId, name,\nemail, phone, guestType(host|cohost|guest),\nrsvpResponse(yes|no|maybe|noreply), numberOfAdults, numberOfKids,\ncheckedIn, comments, deliveryStatus, inviteMethod(email|null),\ninvitedBy, sentOn, timesViewed, avatarUrl, shortLink, longLink,\ncreated, updated`. `summary`: `{yes, no, maybe, noReply, adultsYes,\nkidsYes, adultsMaybe, kidsMaybe, inviteesYes, inviteesMaybe,\nlockedStatus}`.\n\n### 4. Messages — `GET /services/event/v1/{id}/posts/`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/posts/\" | jq '.posts'\n```\n\n→ `{ posts: Post[] }` — the event's \"Messages\" tab thread.\n\n### 5. List templates (HTML scrape, not JSON) — `GET /invites/{category}/`\n\nThe gallery is server-rendered — no JSON API. Grep the SSR HTML for\n`/invitation/{slug}/(create|preview|details)` links (mirrors\n`EviteClient.listTemplates`'s regex). Add `?active_filter=free_premium%2Cfree`\nfor free-only.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" \\\n  'https://www.evite.com/invites/kids-birthday/?active_filter=free_premium%2Cfree' \\\n  | grep -oE '/invitation/[a-z0-9][a-z0-9_-]+/(create|preview|details)' \\\n  | sed -E 's#/invitation/([a-z0-9_-]+)/.*#\\1#' | sort -u\n```\n\nEach slug is a `templateName` — pass it to `create_event` below.\n\n---\n\n## Writes\n\nEvery write really mutates Evite. `-d` bodies below are exact JSON\nunless marked \"assumed\" (endpoint captured live; the exact body field\nwasn't).\n\n### 6. RSVP — `PUT /services/event/v1/{id}/guests/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0,\"comments\":\"can'\\''t wait!\"}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nBody: `rsvpResponse` (`yes|no|maybe`), `numberOfAdults`,\n`numberOfKids`, optional `comments` — field names match the read\n`Guest` shape. `comments` is optional (omit the key entirely to leave\nit unchanged).\n\n### 7. Send a private guest message (assumed body) — `POST /tsunami/v1/services/event/{id}/guest/{gid}/messages`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"See you Saturday!\"}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/guest/$GUEST_ID/messages\"\n```\n\nA **third API base** (`/tsunami/`, not `/services/…/posts/`, which is\nGET-only). Body assumed `{message}`.\n\n### 8. Broadcast to RSVP segments — `POST /tsunami/v1/services/event/{id}/broadcast/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"Reminder: parking is on the north side!\",\"captcha\":null,\"virtual_groups\":[\"yes\",\"maybe\"],\"participantCount\":12}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/broadcast/\"\n```\n\nBody **fully captured** (not assumed): `message`, `captcha` (always\n`null`), `virtual_groups` (array of RSVP segments to reach — the\nvalues the dashboard uses are `yes`/`no`/`maybe`), optional\n`participantCount` (informational — the count the web UI sends along).\nThis emails every guest in the named segments.\n\n### 9. Upload a photo to the shared gallery — 4-step Google Cloud Storage flow\n\nThe file goes to GCS, not Evite. All 4 steps, in order:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n\n# Step 1 — get a signed-upload ticket from Evite.\nTICKET=$(curl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg eid \"$EVENT_ID\" --arg gid \"$GUEST_ID\" --arg mt \"image/jpeg\" \\\n        '{upload_path:\"feed_photos\",event_id:$eid,photo_id:\"\",guest_id:$gid,redirect:true,mimetype:$mt,width:1200,height:900}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/upload/request/\")\nUPLOAD_URL=$(jq -r '.upload_url' <<<\"$TICKET\")\nPHOTO_ID=$(jq -r '.upload_form.key' <<<\"$TICKET\" | awk -F/ '{print $NF}')\n\n# Step 2 — multipart POST straight to GCS: signed fields first, `file` LAST,\n# no Evite cookies (the policy/signature ARE the auth). Capture the redirect.\nFORM_ARGS=()\nfor k in $(jq -r '.upload_form | keys[]' <<<\"$TICKET\"); do\n  v=$(jq -r --arg k \"$k\" '.upload_form[$k]' <<<\"$TICKET\")\n  FORM_ARGS+=(-F \"$k=$v\")\ndone\nFINISH_URL=$(curl -sS -D - -o /dev/null \"${FORM_ARGS[@]}\" -F \"file=@/path/to/photo.jpg\" \"$UPLOAD_URL\" \\\n  | grep -i '^location:' | tr -d '\\r' | awk '{print $2}')\n\n# Step 3 — finalize the object into the album (best-effort; ignore failures).\ncurl -sS -b \"$JAR\" -c \"$JAR\" \"$FINISH_URL\" -o /dev/null\n\n# Step 4 — register the photo in the event's shared gallery.\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg pid \"$PHOTO_ID\" '{photo_ids:[$pid]}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/shared-gallery/?gid=$GUEST_ID\"\n```\n\nStep 1's mimetype must match the file's real content type; the GCS\npolicy enforces `Content-Type == mimetype` and a 20 MB cap.\n\n### 10. Create an event — `POST /services/event/v1/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ\",\"startDatetime\":\"2026-08-15T18:00:00Z\",\"templateName\":\"camp-confetti_vanilla_kids\"}}' \\\n  'https://www.evite.com/services/event/v1/'\n```\n\nRequired (Pydantic-enforced): `title`, `startDatetime`, `templateName`\n(get a valid slug from endpoint 5). **Quirk**: a successful create\nstill returns `500 \"Unknown error\"` (a secondary post-create step\nfails) — the draft is created anyway. Treat a 500 here as \"possibly\ncreated\" and re-list drafts (endpoint 1, `status=draft`) rather than\nretrying blindly.\n\n### 11. Update an event — `PATCH /services/event/v1/{id}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ — Rain Date\"}}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\"\n```\n\nFields MUST be nested under `event` (a bare `{\"title\":...}` 200s as a\nno-op). `PUT` 500s — only `PATCH` works.\n\n### 12. Add draft guests — `POST /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '[{\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\"}]' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\n**Legacy `/ajax/` base**, and the body is a **top-level JSON array**\n(NOT wrapped in an object) — the server does `for g in payload:\nDraftGuest(**g)`. Only persists once the event is finalized (status\n`sending`); on a bare `draft` the POST 200s but silently drops the\nguest.\n\n### 13. Edit a draft guest — `PATCH /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"guest_id\":\"'\"$GUEST_ID\"'\",\"event_id\":\"'\"$EVENT_ID\"'\",\"invite_method\":\"email\",\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\",\"phone\":\"\"}' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\nFull object every time (`guest_id` selects, the rest are the new\nvalues) — this is a set, not a merge.\n\n### 14. Remove a draft guest — `DELETE /ajax/event/{id}/guestlist/draft/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X DELETE \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/$GUEST_ID\"\n```\n\nNo body, but `src/client.ts`'s `write()` always sends `Content-Type:\napplication/json` on every write regardless of body presence — include it\nhere too.\n\n### 15. Send the invitation (\"Send now\", assumed body) — `POST /services/event/v1/{id}/send/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/send/\"\n```\n\nSends the draft guest list. Body assumed empty — this really emails\nevery draft guest; test on a throwaway event first.\n\n### 16. Cancel an event (also \"delete draft\") — `POST /services/event/v1/{id}/actions/cancel/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/cancel/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. The `/actions/{verb}/` convention for host lifecycle\nactions.\n\n### 17. Reinstate a canceled event — `POST /services/event/v1/{id}/actions/reinstate/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/reinstate/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. Restores a `cancelled` event to `sending`.\n\n### 18. Duplicate an event — `GET /plus/create/{id}/copy/?previous=my_events`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -D - -o /dev/null \\\n  \"https://www.evite.com/plus/create/$EVENT_ID/copy/?previous=my_events\" \\\n  | grep -i '^location:'\n# Location: /invitation/{newId}/customize?...&source_event={id}\n```\n\nA plain `GET` that `302`s; the new draft's id is the `/invitation/{newId}/`\npath segment of the `Location` header (no request body).\n\n---\n\n## Endpoint count\n\n19 distinct HTTP calls across 3 bases (`/services/`, `/ajax/`,\n`/tsunami/`) + 1 external GCS call — mirrors all 6 read tools and 13\nwrite tools evite-mcp exposes (`evite_list_events`, `evite_get_event`,\n`evite_list_guests`, `evite_rsvp_summary`, `evite_list_messages`,\n`evite_list_templates`, `evite_rsvp`, `evite_send_message`,\n`evite_broadcast`, `evite_upload_photo`, `evite_create_event`,\n`evite_update_event`, `evite_add_guest`, `evite_update_guest`,\n`evite_remove_guest`, `evite_send`, `evite_cancel_event`,\n`evite_reinstate_event`, `evite_duplicate_event`).\n\nFile v1.3.2:skill-card.md\n\n## Description:\n\nGuides agents to query Evite events, guest lists, RSVPs, and messages and perform account actions using shell commands and an authenticated session.\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 Evite account holders use this skill to inspect event details and guest responses or manage invitations, messages, and events from a shell without an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Account credentials and session cookies grant access to guest contact details.\n\nMitigation: Restrict access to credentials and cookie files, and avoid logging or retaining guest information unless necessary.\n\nRisk: Account actions can change guest lists, RSVPs, and events or send invitations and broadcasts without a built-in confirmation step.\n\nMitigation: Require explicit user confirmation before any write, send, cancellation, guest-list change, or photo upload; test sends only with a throwaway event.\n\nRisk: Some message and invitation request bodies are unverified, and event creation may appear to fail after creating a draft.\n\nMitigation: Verify the intended action and its result before repeating a request; check draft events before retrying a failed creation.\n\n## Reference(s):\n\n- [Evite endpoint reference](artifact/references/endpoints.md)\n- [Evite API skill on ClawHub](https://clawhub.ai/chrischall/skills/evite-api)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Uses Evite account credentials and session cookies; commands may act on live events.]\n\n## Skill Version(s):\n\n1.3.2 (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.3.1: 4 files, 9021 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (1913b), SKILL.md (5007b), _meta.json (128b)\n\nFile v1.3.1:SKILL.md\n\n---\nname: evite-api\ndescription: >-\n  Query and act on Evite (evite.com) events, guest lists, RSVPs, and\n  messages from a shell with curl and a cookie jar — instead of running\n  the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login\n  against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no\n  browser or extension involved. Use when you want Evite data/actions\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on\n  Evite from the shell\", \"curl Evite\", \"evite-api skill\".\n---\n\n# Evite via curl (no MCP)\n\nEvite has no public API — this hits the site's own internal `/services/`\n(+ `/ajax/`, `/tsunami/`) layer using session cookies, exactly like\nevite-mcp does. **This is a curl skill, not fpx**: Evite's login is a\nplain CSRF-protected Django form POST, and reads/writes with the\nresulting cookies are **not** bot-walled (Cloudflare sits in front but\ndoesn't trip on a plain `curl`/Node request carrying a valid session) —\nso the whole flow runs server-side with a cookie jar. No signed-in\nbrowser tab, no ContextMint Bridge extension, no bridge.\n\nAuth model: session cookies `x-evite-session` + `evtsession`, plus a\nCSRF cookie `csrftoken` sent back as the `X-CSRFToken` header on every\nwrite. The CSRF cookie **rotates** — re-read it from the jar immediately\nbefore each write, never cache it.\n\n## One-time login (per shell session, until the jar's session expires)\n\n```sh\nexport EVITE_EMAIL=you@example.com EVITE_PASSWORD=yourpassword   # or: op read op://.../evite/password\nJAR=/tmp/evite-jar.txt\ntouch \"$JAR\" && chmod 600 \"$JAR\"   # jar holds the live session cookie — restrict before writing\nUA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36'\n\n# 1. Prime: GET the homepage to obtain the csrftoken (+ anonymous session) cookies.\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null\n\n# 2. Login: POST creds with the primed jar + X-CSRFToken + Origin/Referer (Django's\n#    HTTPS CSRF check needs all three; a cold POST without them 403s).\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\\n  'https://www.evite.com/ajax_login'\n```\n\nA `200` means the jar now holds the authenticated cookies\n(`x-evite-session`, `evtsession`, a freshly-rotated `csrftoken`,\n`x-evite-features`) and a JSON body with `full_name`/`user_id`/`token`.\n`401 {\"error\":\"Invalid Email Address / Password\"}` = bad creds; `403\n{\"error\":\"HTTP_403\"}` = the priming GET was skipped, or its `csrftoken`\nwasn't echoed back.\n\n## Reads: jar + `Accept: application/json`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation' | jq .\n```\n\nAlways pass `-b \"$JAR\" -c \"$JAR\"` **together** (read AND rewrite the same\nfile) — Evite occasionally re-sets cookies even on a GET, and a write's\n`csrftoken` rotation must land back in the jar for the next call.\n\n## Writes: jar + fresh `X-CSRFToken` + `Content-Type: application/json`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nRe-read `$CSRF` from `$JAR` **right before every write** — a stale value\n403s. If a write still 401/403s with the cookie unchanged in the jar\n(check `awk -F'\\t' '$6==\"csrftoken\"'  \"$JAR\"` before/after), the session\nitself expired: redo the one-time login.\n\nFull endpoint list (every read + write, real paths/bodies, `jq`\nrecipes) is in `references/endpoints.md`.\n\n## Notes / caveats\n\n- **Two write bodies are still unverified upstream** (tracked as\n  evite-mcp issue #3): `send_message` (`POST\n  /tsunami/v1/services/event/{id}/guest/{gid}/messages`) and\n  `send_invitation` (`POST /services/event/v1/{id}/send/`) — only the\n  endpoints were captured live, not their exact request bodies. The MCP\n  (and this skill) send `{\"message\": \"...\"}` / `{}` as the best-known\n  shape; treat those two as slightly less certain than the rest.\n- `list_templates` is HTML scraping (`GET /invites/{category}/`), not\n  JSON — see the reference for the extraction pattern.\n- **Writes here really mutate Evite** — RSVPs, cancellations,\n  broadcast emails to real guests. There's no MCP-side confirm-gate in\n  this skill: you are the confirm gate. Test broadcasts/sends only\n  against a throwaway event with a blackholed `@example.com` guest.\n- This project (evite-mcp) is developed and maintained by AI (Claude).\n\nFile v1.3.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"evite-api\",\n  \"version\": \"1.3.1\",\n  \"publishedAt\": 1790603952268\n}\n\nFile v1.3.1:references/endpoints.md\n\n# Evite endpoint reference (curl)\n\nBase host: `https://www.evite.com`. All paths below assume `$JAR` already\nholds an authenticated session (see `SKILL.md`'s one-time login) and that\nevery call passes `-b \"$JAR\" -c \"$JAR\"` so a rotated `csrftoken` lands\nback in the jar. Reads add `-H 'Accept: application/json'`; writes add\n`-H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\"` with `CSRF`\nre-read from the jar immediately before the call:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n```\n\nTranscribed from `evite-mcp`'s `src/client.ts` + `docs/EVITE-API.md`\n(live-verified 2026-06-01/02). Every shape below is a REAL captured\nrequest/response unless marked \"assumed\" (URL captured, body not).\n\n---\n\n## Reads\n\n### 1. List events — `GET /services/events/v1/`\n\nQuery: `filterBy` (`all|host|others`, others = you're a guest), `status`\n(repeatable — `upcoming|draft|archived|past|canceled`), `type=invitation`,\n`offset`, `numResults`, `filter` (free text).\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation&status=upcoming&status=draft&numResults=25' \\\n  | jq '.totals, (.events[] | {id: .event_id, title, start, status, is_host, rsvp})'\n```\n\n→ `{ events: Event[], totals }`. `totals` = `{all, sending, draft,\nreceived, canceled, past, upcoming, archived}` (sending = hosting,\nreceived = invited). `Event` fields: `event_id, title, start, end,\nstatus, past, is_host, rsvp(yes|no|maybe), guest_status(0|1|2),\nguest_id, host_id, host_name, location{location_name, street_address,\nunit_num, city, state, zip_code, place_id}, timezone,\nknown_timezone_name, template_name, event_category, rsvp_off, is_invite,\nis_pending_cohost, rendered_image_url, updated`.\n\n### 2. Get event detail — `GET /services/event/v1/{id}`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\" \\\n  | jq '{title: .event.title, start: .event.startDatetime, status: .event.status, settings}'\n```\n\n→ top keys: `event, calculatedFields, design, location,\nuserEventContext, attributes, registries, settings, charity, gifting,\nrendered, calendar, features`. `event`: `id, title, message,\nstartDatetime, endDatetime, knownTimezoneName,\nknownTimezoneAbbreviation, eventHostName, eventPhoneNumber, hostId,\nhostIds, status, isPast, eventType, category, superCategory,\ntemplateName, origin, isFabricPremium, shareableLink, sendOn`.\n`settings`: `enableMaybe, privateGuestList, headCountByFamily, plusOne,\nmaxEventCapacity, allowViewMap, showGifting, rsvpBy, strictRsvpBy,\nenableHostPhotoGallery, enablePhotoSharing, allowGuestNumber, rsvpOff`.\n\n### 3. Guest list + RSVP summary — `GET /services/event/v1/{id}/guests/`\n\nOne endpoint powers both `evite_list_guests` and `evite_rsvp_summary`.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/\" \\\n  | jq '.summary, (.guests[] | {guestId, name, rsvpResponse, numberOfAdults, numberOfKids})'\n```\n\n→ `{ guests: Guest[], summary }`. `Guest`: `guestId, userId, name,\nemail, phone, guestType(host|cohost|guest),\nrsvpResponse(yes|no|maybe|noreply), numberOfAdults, numberOfKids,\ncheckedIn, comments, deliveryStatus, inviteMethod(email|null),\ninvitedBy, sentOn, timesViewed, avatarUrl, shortLink, longLink,\ncreated, updated`. `summary`: `{yes, no, maybe, noReply, adultsYes,\nkidsYes, adultsMaybe, kidsMaybe, inviteesYes, inviteesMaybe,\nlockedStatus}`.\n\n### 4. Messages — `GET /services/event/v1/{id}/posts/`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/posts/\" | jq '.posts'\n```\n\n→ `{ posts: Post[] }` — the event's \"Messages\" tab thread.\n\n### 5. List templates (HTML scrape, not JSON) — `GET /invites/{category}/`\n\nThe gallery is server-rendered — no JSON API. Grep the SSR HTML for\n`/invitation/{slug}/(create|preview|details)` links (mirrors\n`EviteClient.listTemplates`'s regex). Add `?active_filter=free_premium%2Cfree`\nfor free-only.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" \\\n  'https://www.evite.com/invites/kids-birthday/?active_filter=free_premium%2Cfree' \\\n  | grep -oE '/invitation/[a-z0-9][a-z0-9_-]+/(create|preview|details)' \\\n  | sed -E 's#/invitation/([a-z0-9_-]+)/.*#\\1#' | sort -u\n```\n\nEach slug is a `templateName` — pass it to `create_event` below.\n\n---\n\n## Writes\n\nEvery write really mutates Evite. `-d` bodies below are exact JSON\nunless marked \"assumed\" (endpoint captured live; the exact body field\nwasn't).\n\n### 6. RSVP — `PUT /services/event/v1/{id}/guests/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0,\"comments\":\"can'\\''t wait!\"}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nBody: `rsvpResponse` (`yes|no|maybe`), `numberOfAdults`,\n`numberOfKids`, optional `comments` — field names match the read\n`Guest` shape. `comments` is optional (omit the key entirely to leave\nit unchanged).\n\n### 7. Send a private guest message (assumed body) — `POST /tsunami/v1/services/event/{id}/guest/{gid}/messages`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"See you Saturday!\"}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/guest/$GUEST_ID/messages\"\n```\n\nA **third API base** (`/tsunami/`, not `/services/…/posts/`, which is\nGET-only). Body assumed `{message}`.\n\n### 8. Broadcast to RSVP segments — `POST /tsunami/v1/services/event/{id}/broadcast/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"Reminder: parking is on the north side!\",\"captcha\":null,\"virtual_groups\":[\"yes\",\"maybe\"],\"participantCount\":12}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/broadcast/\"\n```\n\nBody **fully captured** (not assumed): `message`, `captcha` (always\n`null`), `virtual_groups` (array of RSVP segments to reach — the\nvalues the dashboard uses are `yes`/`no`/`maybe`), optional\n`participantCount` (informational — the count the web UI sends along).\nThis emails every guest in the named segments.\n\n### 9. Upload a photo to the shared gallery — 4-step Google Cloud Storage flow\n\nThe file goes to GCS, not Evite. All 4 steps, in order:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n\n# Step 1 — get a signed-upload ticket from Evite.\nTICKET=$(curl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg eid \"$EVENT_ID\" --arg gid \"$GUEST_ID\" --arg mt \"image/jpeg\" \\\n        '{upload_path:\"feed_photos\",event_id:$eid,photo_id:\"\",guest_id:$gid,redirect:true,mimetype:$mt,width:1200,height:900}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/upload/request/\")\nUPLOAD_URL=$(jq -r '.upload_url' <<<\"$TICKET\")\nPHOTO_ID=$(jq -r '.upload_form.key' <<<\"$TICKET\" | awk -F/ '{print $NF}')\n\n# Step 2 — multipart POST straight to GCS: signed fields first, `file` LAST,\n# no Evite cookies (the policy/signature ARE the auth). Capture the redirect.\nFORM_ARGS=()\nfor k in $(jq -r '.upload_form | keys[]' <<<\"$TICKET\"); do\n  v=$(jq -r --arg k \"$k\" '.upload_form[$k]' <<<\"$TICKET\")\n  FORM_ARGS+=(-F \"$k=$v\")\ndone\nFINISH_URL=$(curl -sS -D - -o /dev/null \"${FORM_ARGS[@]}\" -F \"file=@/path/to/photo.jpg\" \"$UPLOAD_URL\" \\\n  | grep -i '^location:' | tr -d '\\r' | awk '{print $2}')\n\n# Step 3 — finalize the object into the album (best-effort; ignore failures).\ncurl -sS -b \"$JAR\" -c \"$JAR\" \"$FINISH_URL\" -o /dev/null\n\n# Step 4 — register the photo in the event's shared gallery.\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg pid \"$PHOTO_ID\" '{photo_ids:[$pid]}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/shared-gallery/?gid=$GUEST_ID\"\n```\n\nStep 1's mimetype must match the file's real content type; the GCS\npolicy enforces `Content-Type == mimetype` and a 20 MB cap.\n\n### 10. Create an event — `POST /services/event/v1/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ\",\"startDatetime\":\"2026-08-15T18:00:00Z\",\"templateName\":\"camp-confetti_vanilla_kids\"}}' \\\n  'https://www.evite.com/services/event/v1/'\n```\n\nRequired (Pydantic-enforced): `title`, `startDatetime`, `templateName`\n(get a valid slug from endpoint 5). **Quirk**: a successful create\nstill returns `500 \"Unknown error\"` (a secondary post-create step\nfails) — the draft is created anyway. Treat a 500 here as \"possibly\ncreated\" and re-list drafts (endpoint 1, `status=draft`) rather than\nretrying blindly.\n\n### 11. Update an event — `PATCH /services/event/v1/{id}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ — Rain Date\"}}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\"\n```\n\nFields MUST be nested under `event` (a bare `{\"title\":...}` 200s as a\nno-op). `PUT` 500s — only `PATCH` works.\n\n### 12. Add draft guests — `POST /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '[{\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\"}]' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\n**Legacy `/ajax/` base**, and the body is a **top-level JSON array**\n(NOT wrapped in an object) — the server does `for g in payload:\nDraftGuest(**g)`. Only persists once the event is finalized (status\n`sending`); on a bare `draft` the POST 200s but silently drops the\nguest.\n\n### 13. Edit a draft guest — `PATCH /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"guest_id\":\"'\"$GUEST_ID\"'\",\"event_id\":\"'\"$EVENT_ID\"'\",\"invite_method\":\"email\",\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\",\"phone\":\"\"}' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\nFull object every time (`guest_id` selects, the rest are the new\nvalues) — this is a set, not a merge.\n\n### 14. Remove a draft guest — `DELETE /ajax/event/{id}/guestlist/draft/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X DELETE \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/$GUEST_ID\"\n```\n\nNo body, but `src/client.ts`'s `write()` always sends `Content-Type:\napplication/json` on every write regardless of body presence — include it\nhere too.\n\n### 15. Send the invitation (\"Send now\", assumed body) — `POST /services/event/v1/{id}/send/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/send/\"\n```\n\nSends the draft guest list. Body assumed empty — this really emails\nevery draft guest; test on a throwaway event first.\n\n### 16. Cancel an event (also \"delete draft\") — `POST /services/event/v1/{id}/actions/cancel/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/cancel/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. The `/actions/{verb}/` convention for host lifecycle\nactions.\n\n### 17. Reinstate a canceled event — `POST /services/event/v1/{id}/actions/reinstate/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/reinstate/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. Restores a `cancelled` event to `sending`.\n\n### 18. Duplicate an event — `GET /plus/create/{id}/copy/?previous=my_events`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -D - -o /dev/null \\\n  \"https://www.evite.com/plus/create/$EVENT_ID/copy/?previous=my_events\" \\\n  | grep -i '^location:'\n# Location: /invitation/{newId}/customize?...&source_event={id}\n```\n\nA plain `GET` that `302`s; the new draft's id is the `/invitation/{newId}/`\npath segment of the `Location` header (no request body).\n\n---\n\n## Endpoint count\n\n19 distinct HTTP calls across 3 bases (`/services/`, `/ajax/`,\n`/tsunami/`) + 1 external GCS call — mirrors all 6 read tools and 13\nwrite tools evite-mcp exposes (`evite_list_events`, `evite_get_event`,\n`evite_list_guests`, `evite_rsvp_summary`, `evite_list_messages`,\n`evite_list_templates`, `evite_rsvp`, `evite_send_message`,\n`evite_broadcast`, `evite_upload_photo`, `evite_create_event`,\n`evite_update_event`, `evite_add_guest`, `evite_update_guest`,\n`evite_remove_guest`, `evite_send`, `evite_cancel_event`,\n`evite_reinstate_event`, `evite_duplicate_event`).\n\nFile v1.3.1:skill-card.md\n\n## Description:\n\nGuides agents in reading and updating Evite events, guest lists, RSVPs, photos, and messages using shell commands and an authenticated session.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other Evite account holders use this skill to check events and guests or perform RSVP, event-management, messaging, and photo tasks without an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requires Evite credentials and a live session cookie.\n\nMitigation: Start with a low-risk account, restrict access to the cookie jar, and delete it after use.\n\nRisk: Commands can change events, guest lists, and RSVPs or send emails without a built-in confirmation gate.\n\nMitigation: Confirm each write explicitly and test sends, broadcasts, and cancellations on a throwaway event first.\n\nRisk: Photo uploads share files with Google Cloud Storage through Evite's upload flow.\n\nMitigation: Review photos before upload and share only files intended for the event gallery.\n\n## Reference(s):\n\n- [Evite endpoint reference](artifact/references/endpoints.md)\n- [ClawHub evite-api release](https://clawhub.ai/chrischall/skills/evite-api)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell commands and JSON examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands can read or change live Evite account data.]\n\n## Skill Version(s):\n\n1.3.1 (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.3.0: 4 files, 8984 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (1926b), SKILL.md (5000b), _meta.json (128b)\n\nFile v1.3.0:SKILL.md\n\n---\nname: evite-api\ndescription: >-\n  Query and act on Evite (evite.com) events, guest lists, RSVPs, and\n  messages from a shell with curl and a cookie jar — instead of running\n  the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login\n  against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no\n  browser or extension involved. Use when you want Evite data/actions\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on\n  Evite from the shell\", \"curl Evite\", \"evite-api skill\".\n---\n\n# Evite via curl (no MCP)\n\nEvite has no public API — this hits the site's own internal `/services/`\n(+ `/ajax/`, `/tsunami/`) layer using session cookies, exactly like\nevite-mcp does. **This is a curl skill, not fpx**: Evite's login is a\nplain CSRF-protected Django form POST, and reads/writes with the\nresulting cookies are **not** bot-walled (Cloudflare sits in front but\ndoesn't trip on a plain `curl`/Node request carrying a valid session) —\nso the whole flow runs server-side with a cookie jar. No signed-in\nbrowser tab, no Transporter extension, no bridge.\n\nAuth model: session cookies `x-evite-session` + `evtsession`, plus a\nCSRF cookie `csrftoken` sent back as the `X-CSRFToken` header on every\nwrite. The CSRF cookie **rotates** — re-read it from the jar immediately\nbefore each write, never cache it.\n\n## One-time login (per shell session, until the jar's session expires)\n\n```sh\nexport EVITE_EMAIL=you@example.com EVITE_PASSWORD=yourpassword   # or: op read op://.../evite/password\nJAR=/tmp/evite-jar.txt\ntouch \"$JAR\" && chmod 600 \"$JAR\"   # jar holds the live session cookie — restrict before writing\nUA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36'\n\n# 1. Prime: GET the homepage to obtain the csrftoken (+ anonymous session) cookies.\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null\n\n# 2. Login: POST creds with the primed jar + X-CSRFToken + Origin/Referer (Django's\n#    HTTPS CSRF check needs all three; a cold POST without them 403s).\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\\n  'https://www.evite.com/ajax_login'\n```\n\nA `200` means the jar now holds the authenticated cookies\n(`x-evite-session`, `evtsession`, a freshly-rotated `csrftoken`,\n`x-evite-features`) and a JSON body with `full_name`/`user_id`/`token`.\n`401 {\"error\":\"Invalid Email Address / Password\"}` = bad creds; `403\n{\"error\":\"HTTP_403\"}` = the priming GET was skipped, or its `csrftoken`\nwasn't echoed back.\n\n## Reads: jar + `Accept: application/json`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation' | jq .\n```\n\nAlways pass `-b \"$JAR\" -c \"$JAR\"` **together** (read AND rewrite the same\nfile) — Evite occasionally re-sets cookies even on a GET, and a write's\n`csrftoken` rotation must land back in the jar for the next call.\n\n## Writes: jar + fresh `X-CSRFToken` + `Content-Type: application/json`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nRe-read `$CSRF` from `$JAR` **right before every write** — a stale value\n403s. If a write still 401/403s with the cookie unchanged in the jar\n(check `awk -F'\\t' '$6==\"csrftoken\"'  \"$JAR\"` before/after), the session\nitself expired: redo the one-time login.\n\nFull endpoint list (every read + write, real paths/bodies, `jq`\nrecipes) is in `references/endpoints.md`.\n\n## Notes / caveats\n\n- **Two write bodies are still unverified upstream** (tracked as\n  evite-mcp issue #3): `send_message` (`POST\n  /tsunami/v1/services/event/{id}/guest/{gid}/messages`) and\n  `send_invitation` (`POST /services/event/v1/{id}/send/`) — only the\n  endpoints were captured live, not their exact request bodies. The MCP\n  (and this skill) send `{\"message\": \"...\"}` / `{}` as the best-known\n  shape; treat those two as slightly less certain than the rest.\n- `list_templates` is HTML scraping (`GET /invites/{category}/`), not\n  JSON — see the reference for the extraction pattern.\n- **Writes here really mutate Evite** — RSVPs, cancellations,\n  broadcast emails to real guests. There's no MCP-side confirm-gate in\n  this skill: you are the confirm gate. Test broadcasts/sends only\n  against a throwaway event with a blackholed `@example.com` guest.\n- This project (evite-mcp) is developed and maintained by AI (Claude).\n\nFile v1.3.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"evite-api\",\n  \"version\": \"1.3.0\",\n  \"publishedAt\": 1790351432761\n}\n\nFile v1.3.0:references/endpoints.md\n\n# Evite endpoint reference (curl)\n\nBase host: `https://www.evite.com`. All paths below assume `$JAR` already\nholds an authenticated session (see `SKILL.md`'s one-time login) and that\nevery call passes `-b \"$JAR\" -c \"$JAR\"` so a rotated `csrftoken` lands\nback in the jar. Reads add `-H 'Accept: application/json'`; writes add\n`-H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\"` with `CSRF`\nre-read from the jar immediately before the call:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n```\n\nTranscribed from `evite-mcp`'s `src/client.ts` + `docs/EVITE-API.md`\n(live-verified 2026-06-01/02). Every shape below is a REAL captured\nrequest/response unless marked \"assumed\" (URL captured, body not).\n\n---\n\n## Reads\n\n### 1. List events — `GET /services/events/v1/`\n\nQuery: `filterBy` (`all|host|others`, others = you're a guest), `status`\n(repeatable — `upcoming|draft|archived|past|canceled`), `type=invitation`,\n`offset`, `numResults`, `filter` (free text).\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation&status=upcoming&status=draft&numResults=25' \\\n  | jq '.totals, (.events[] | {id: .event_id, title, start, status, is_host, rsvp})'\n```\n\n→ `{ events: Event[], totals }`. `totals` = `{all, sending, draft,\nreceived, canceled, past, upcoming, archived}` (sending = hosting,\nreceived = invited). `Event` fields: `event_id, title, start, end,\nstatus, past, is_host, rsvp(yes|no|maybe), guest_status(0|1|2),\nguest_id, host_id, host_name, location{location_name, street_address,\nunit_num, city, state, zip_code, place_id}, timezone,\nknown_timezone_name, template_name, event_category, rsvp_off, is_invite,\nis_pending_cohost, rendered_image_url, updated`.\n\n### 2. Get event detail — `GET /services/event/v1/{id}`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\" \\\n  | jq '{title: .event.title, start: .event.startDatetime, status: .event.status, settings}'\n```\n\n→ top keys: `event, calculatedFields, design, location,\nuserEventContext, attributes, registries, settings, charity, gifting,\nrendered, calendar, features`. `event`: `id, title, message,\nstartDatetime, endDatetime, knownTimezoneName,\nknownTimezoneAbbreviation, eventHostName, eventPhoneNumber, hostId,\nhostIds, status, isPast, eventType, category, superCategory,\ntemplateName, origin, isFabricPremium, shareableLink, sendOn`.\n`settings`: `enableMaybe, privateGuestList, headCountByFamily, plusOne,\nmaxEventCapacity, allowViewMap, showGifting, rsvpBy, strictRsvpBy,\nenableHostPhotoGallery, enablePhotoSharing, allowGuestNumber, rsvpOff`.\n\n### 3. Guest list + RSVP summary — `GET /services/event/v1/{id}/guests/`\n\nOne endpoint powers both `evite_list_guests` and `evite_rsvp_summary`.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/\" \\\n  | jq '.summary, (.guests[] | {guestId, name, rsvpResponse, numberOfAdults, numberOfKids})'\n```\n\n→ `{ guests: Guest[], summary }`. `Guest`: `guestId, userId, name,\nemail, phone, guestType(host|cohost|guest),\nrsvpResponse(yes|no|maybe|noreply), numberOfAdults, numberOfKids,\ncheckedIn, comments, deliveryStatus, inviteMethod(email|null),\ninvitedBy, sentOn, timesViewed, avatarUrl, shortLink, longLink,\ncreated, updated`. `summary`: `{yes, no, maybe, noReply, adultsYes,\nkidsYes, adultsMaybe, kidsMaybe, inviteesYes, inviteesMaybe,\nlockedStatus}`.\n\n### 4. Messages — `GET /services/event/v1/{id}/posts/`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/posts/\" | jq '.posts'\n```\n\n→ `{ posts: Post[] }` — the event's \"Messages\" tab thread.\n\n### 5. List templates (HTML scrape, not JSON) — `GET /invites/{category}/`\n\nThe gallery is server-rendered — no JSON API. Grep the SSR HTML for\n`/invitation/{slug}/(create|preview|details)` links (mirrors\n`EviteClient.listTemplates`'s regex). Add `?active_filter=free_premium%2Cfree`\nfor free-only.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" \\\n  'https://www.evite.com/invites/kids-birthday/?active_filter=free_premium%2Cfree' \\\n  | grep -oE '/invitation/[a-z0-9][a-z0-9_-]+/(create|preview|details)' \\\n  | sed -E 's#/invitation/([a-z0-9_-]+)/.*#\\1#' | sort -u\n```\n\nEach slug is a `templateName` — pass it to `create_event` below.\n\n---\n\n## Writes\n\nEvery write really mutates Evite. `-d` bodies below are exact JSON\nunless marked \"assumed\" (endpoint captured live; the exact body field\nwasn't).\n\n### 6. RSVP — `PUT /services/event/v1/{id}/guests/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0,\"comments\":\"can'\\''t wait!\"}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nBody: `rsvpResponse` (`yes|no|maybe`), `numberOfAdults`,\n`numberOfKids`, optional `comments` — field names match the read\n`Guest` shape. `comments` is optional (omit the key entirely to leave\nit unchanged).\n\n### 7. Send a private guest message (assumed body) — `POST /tsunami/v1/services/event/{id}/guest/{gid}/messages`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"See you Saturday!\"}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/guest/$GUEST_ID/messages\"\n```\n\nA **third API base** (`/tsunami/`, not `/services/…/posts/`, which is\nGET-only). Body assumed `{message}`.\n\n### 8. Broadcast to RSVP segments — `POST /tsunami/v1/services/event/{id}/broadcast/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"Reminder: parking is on the north side!\",\"captcha\":null,\"virtual_groups\":[\"yes\",\"maybe\"],\"participantCount\":12}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/broadcast/\"\n```\n\nBody **fully captured** (not assumed): `message`, `captcha` (always\n`null`), `virtual_groups` (array of RSVP segments to reach — the\nvalues the dashboard uses are `yes`/`no`/`maybe`), optional\n`participantCount` (informational — the count the web UI sends along).\nThis emails every guest in the named segments.\n\n### 9. Upload a photo to the shared gallery — 4-step Google Cloud Storage flow\n\nThe file goes to GCS, not Evite. All 4 steps, in order:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n\n# Step 1 — get a signed-upload ticket from Evite.\nTICKET=$(curl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg eid \"$EVENT_ID\" --arg gid \"$GUEST_ID\" --arg mt \"image/jpeg\" \\\n        '{upload_path:\"feed_photos\",event_id:$eid,photo_id:\"\",guest_id:$gid,redirect:true,mimetype:$mt,width:1200,height:900}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/upload/request/\")\nUPLOAD_URL=$(jq -r '.upload_url' <<<\"$TICKET\")\nPHOTO_ID=$(jq -r '.upload_form.key' <<<\"$TICKET\" | awk -F/ '{print $NF}')\n\n# Step 2 — multipart POST straight to GCS: signed fields first, `file` LAST,\n# no Evite cookies (the policy/signature ARE the auth). Capture the redirect.\nFORM_ARGS=()\nfor k in $(jq -r '.upload_form | keys[]' <<<\"$TICKET\"); do\n  v=$(jq -r --arg k \"$k\" '.upload_form[$k]' <<<\"$TICKET\")\n  FORM_ARGS+=(-F \"$k=$v\")\ndone\nFINISH_URL=$(curl -sS -D - -o /dev/null \"${FORM_ARGS[@]}\" -F \"file=@/path/to/photo.jpg\" \"$UPLOAD_URL\" \\\n  | grep -i '^location:' | tr -d '\\r' | awk '{print $2}')\n\n# Step 3 — finalize the object into the album (best-effort; ignore failures).\ncurl -sS -b \"$JAR\" -c \"$JAR\" \"$FINISH_URL\" -o /dev/null\n\n# Step 4 — register the photo in the event's shared gallery.\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg pid \"$PHOTO_ID\" '{photo_ids:[$pid]}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/shared-gallery/?gid=$GUEST_ID\"\n```\n\nStep 1's mimetype must match the file's real content type; the GCS\npolicy enforces `Content-Type == mimetype` and a 20 MB cap.\n\n### 10. Create an event — `POST /services/event/v1/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ\",\"startDatetime\":\"2026-08-15T18:00:00Z\",\"templateName\":\"camp-confetti_vanilla_kids\"}}' \\\n  'https://www.evite.com/services/event/v1/'\n```\n\nRequired (Pydantic-enforced): `title`, `startDatetime`, `templateName`\n(get a valid slug from endpoint 5). **Quirk**: a successful create\nstill returns `500 \"Unknown error\"` (a secondary post-create step\nfails) — the draft is created anyway. Treat a 500 here as \"possibly\ncreated\" and re-list drafts (endpoint 1, `status=draft`) rather than\nretrying blindly.\n\n### 11. Update an event — `PATCH /services/event/v1/{id}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ — Rain Date\"}}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\"\n```\n\nFields MUST be nested under `event` (a bare `{\"title\":...}` 200s as a\nno-op). `PUT` 500s — only `PATCH` works.\n\n### 12. Add draft guests — `POST /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '[{\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\"}]' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\n**Legacy `/ajax/` base**, and the body is a **top-level JSON array**\n(NOT wrapped in an object) — the server does `for g in payload:\nDraftGuest(**g)`. Only persists once the event is finalized (status\n`sending`); on a bare `draft` the POST 200s but silently drops the\nguest.\n\n### 13. Edit a draft guest — `PATCH /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"guest_id\":\"'\"$GUEST_ID\"'\",\"event_id\":\"'\"$EVENT_ID\"'\",\"invite_method\":\"email\",\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\",\"phone\":\"\"}' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\nFull object every time (`guest_id` selects, the rest are the new\nvalues) — this is a set, not a merge.\n\n### 14. Remove a draft guest — `DELETE /ajax/event/{id}/guestlist/draft/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X DELETE \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/$GUEST_ID\"\n```\n\nNo body, but `src/client.ts`'s `write()` always sends `Content-Type:\napplication/json` on every write regardless of body presence — include it\nhere too.\n\n### 15. Send the invitation (\"Send now\", assumed body) — `POST /services/event/v1/{id}/send/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/send/\"\n```\n\nSends the draft guest list. Body assumed empty — this really emails\nevery draft guest; test on a throwaway event first.\n\n### 16. Cancel an event (also \"delete draft\") — `POST /services/event/v1/{id}/actions/cancel/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/cancel/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. The `/actions/{verb}/` convention for host lifecycle\nactions.\n\n### 17. Reinstate a canceled event — `POST /services/event/v1/{id}/actions/reinstate/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" -d '{}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/actions/reinstate/\" \\\n  -w '\\n%{http_code}\\n'\n```\n\n→ `202 Accepted`. Restores a `cancelled` event to `sending`.\n\n### 18. Duplicate an event — `GET /plus/create/{id}/copy/?previous=my_events`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -D - -o /dev/null \\\n  \"https://www.evite.com/plus/create/$EVENT_ID/copy/?previous=my_events\" \\\n  | grep -i '^location:'\n# Location: /invitation/{newId}/customize?...&source_event={id}\n```\n\nA plain `GET` that `302`s; the new draft's id is the `/invitation/{newId}/`\npath segment of the `Location` header (no request body).\n\n---\n\n## Endpoint count\n\n19 distinct HTTP calls across 3 bases (`/services/`, `/ajax/`,\n`/tsunami/`) + 1 external GCS call — mirrors all 6 read tools and 13\nwrite tools evite-mcp exposes (`evite_list_events`, `evite_get_event`,\n`evite_list_guests`, `evite_rsvp_summary`, `evite_list_messages`,\n`evite_list_templates`, `evite_rsvp`, `evite_send_message`,\n`evite_broadcast`, `evite_upload_photo`, `evite_create_event`,\n`evite_update_event`, `evite_add_guest`, `evite_update_guest`,\n`evite_remove_guest`, `evite_send`, `evite_cancel_event`,\n`evite_reinstate_event`, `evite_duplicate_event`).\n\nFile v1.3.0:skill-card.md\n\n## Description:\n\nGuides agents in reading and managing Evite events, guest lists, RSVPs, and messages through authenticated shell requests.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nEvent hosts and invited guests can use this skill to inspect Evite events and guest responses, manage invitations and RSVPs, and communicate with guests from a shell.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Authenticated access exposes guest details and a live Evite session.\n\nMitigation: Keep credentials and the cookie jar private, grant access only when needed, and delete the jar afterward.\n\nRisk: Writes can change RSVPs or events, cancel events, and send messages or invitations to real guests.\n\nMitigation: Verify event and guest IDs before writes, preview outgoing messages, and test sends on a throwaway event.\n\nRisk: Some message and invitation request bodies are unverified.\n\nMitigation: Test those actions on a throwaway event before relying on them for real guests.\n\n## Reference(s):\n\n- [Evite API skill release](https://clawhub.ai/chrischall/skills/evite-api)\n- [Evite endpoint reference](references/endpoints.md)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance, Text]\n\n**Output Format:** [Markdown with shell examples and JSON response guidance]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May return event and guest information; write commands can change events or contact guests.]\n\n## Skill Version(s):\n\n1.3.0 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.0: 4 files, 9307 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (2545b), SKILL.md (5000b), _meta.json (128b)\n\nFile v1.2.0:SKILL.md\n\n---\nname: evite-api\ndescription: >-\n  Query and act on Evite (evite.com) events, guest lists, RSVPs, and\n  messages from a shell with curl and a cookie jar — instead of running\n  the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login\n  against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no\n  browser or extension involved. Use when you want Evite data/actions\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on\n  Evite from the shell\", \"curl Evite\", \"evite-api skill\".\n---\n\n# Evite via curl (no MCP)\n\nEvite has no public API — this hits the site's own internal `/services/`\n(+ `/ajax/`, `/tsunami/`) layer using session cookies, exactly like\nevite-mcp does. **This is a curl skill, not fpx**: Evite's login is a\nplain CSRF-protected Django form POST, and reads/writes with the\nresulting cookies are **not** bot-walled (Cloudflare sits in front but\ndoesn't trip on a plain `curl`/Node request carrying a valid session) —\nso the whole flow runs server-side with a cookie jar. No signed-in\nbrowser tab, no Transporter extension, no bridge.\n\nAuth model: session cookies `x-evite-session` + `evtsession`, plus a\nCSRF cookie `csrftoken` sent back as the `X-CSRFToken` header on every\nwrite. The CSRF cookie **rotates** — re-read it from the jar immediately\nbefore each write, never cache it.\n\n## One-time login (per shell session, until the jar's session expires)\n\n```sh\nexport EVITE_EMAIL=you@example.com EVITE_PASSWORD=yourpassword   # or: op read op://.../evite/password\nJAR=/tmp/evite-jar.txt\ntouch \"$JAR\" && chmod 600 \"$JAR\"   # jar holds the live session cookie — restrict before writing\nUA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36'\n\n# 1. Prime: GET the homepage to obtain the csrftoken (+ anonymous session) cookies.\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null\n\n# 2. Login: POST creds with the primed jar + X-CSRFToken + Origin/Referer (Django's\n#    HTTPS CSRF check needs all three; a cold POST without them 403s).\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\\n  'https://www.evite.com/ajax_login'\n```\n\nA `200` means the jar now holds the authenticated cookies\n(`x-evite-session`, `evtsession`, a freshly-rotated `csrftoken`,\n`x-evite-features`) and a JSON body with `full_name`/`user_id`/`token`.\n`401 {\"error\":\"Invalid Email Address / Password\"}` = bad creds; `403\n{\"error\":\"HTTP_403\"}` = the priming GET was skipped, or its `csrftoken`\nwasn't echoed back.\n\n## Reads: jar + `Accept: application/json`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation' | jq .\n```\n\nAlways pass `-b \"$JAR\" -c \"$JAR\"` **together** (read AND rewrite the same\nfile) — Evite occasionally re-sets cookies even on a GET, and a write's\n`csrftoken` rotation must land back in the jar for the next call.\n\n## Writes: jar + fresh `X-CSRFToken` + `Content-Type: application/json`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nRe-read `$CSRF` from `$JAR` **right before every write** — a stale value\n403s. If a write still 401/403s with the cookie unchanged in the jar\n(check `awk -F'\\t' '$6==\"csrftoken\"'  \"$JAR\"` before/after), the session\nitself expired: redo the one-time login.\n\nFull endpoint list (every read + write, real paths/bodies, `jq`\nrecipes) is in `references/endpoints.md`.\n\n## Notes / caveats\n\n- **Two write bodies are still unverified upstream** (tracked as\n  evite-mcp issue #3): `send_message` (`POST\n  /tsunami/v1/services/event/{id}/guest/{gid}/messages`) and\n  `send_invitation` (`POST /services/event/v1/{id}/send/`) — only the\n  endpoints were captured live, not their exact request bodies. The MCP\n  (and this skill) send `{\"message\": \"...\"}` / `{}` as the best-known\n  shape; treat those two as slightly less certain than the rest.\n- `list_templates` is HTML scraping (`GET /invites/{category}/`), not\n  JSON — see the reference for the extraction pattern.\n- **Writes here really mutate Evite** — RSVPs, cancellations,\n  broadcast emails to real guests. There's no MCP-side confirm-gate in\n  this skill: you are the confirm gate. Test broadcasts/sends only\n  against a throwaway event with a blackholed `@example.com` guest.\n- This project (evite-mcp) is developed and maintained by AI (Claude).\n\nFile v1.2.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"evite-api\",\n  \"version\": \"1.2.0\",\n  \"publishedAt\": 1790262656274\n}\n\nFile v1.2.0:references/endpoints.md\n\n# Evite endpoint reference (curl)\n\nBase host: `https://www.evite.com`. All paths below assume `$JAR` already\nholds an authenticated session (see `SKILL.md`'s one-time login) and that\nevery call passes `-b \"$JAR\" -c \"$JAR\"` so a rotated `csrftoken` lands\nback in the jar. Reads add `-H 'Accept: application/json'`; writes add\n`-H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\"` with `CSRF`\nre-read from the jar immediately before the call:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n```\n\nTranscribed from `evite-mcp`'s `src/client.ts` + `docs/EVITE-API.md`\n(live-verified 2026-06-01/02). Every shape below is a REAL captured\nrequest/response unless marked \"assumed\" (URL captured, body not).\n\n---\n\n## Reads\n\n### 1. List events — `GET /services/events/v1/`\n\nQuery: `filterBy` (`all|host|others`, others = you're a guest), `status`\n(repeatable — `upcoming|draft|archived|past|canceled`), `type=invitation`,\n`offset`, `numResults`, `filter` (free text).\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation&status=upcoming&status=draft&numResults=25' \\\n  | jq '.totals, (.events[] | {id: .event_id, title, start, status, is_host, rsvp})'\n```\n\n→ `{ events: Event[], totals }`. `totals` = `{all, sending, draft,\nreceived, canceled, past, upcoming, archived}` (sending = hosting,\nreceived = invited). `Event` fields: `event_id, title, start, end,\nstatus, past, is_host, rsvp(yes|no|maybe), guest_status(0|1|2),\nguest_id, host_id, host_name, location{location_name, street_address,\nunit_num, city, state, zip_code, place_id}, timezone,\nknown_timezone_name, template_name, event_category, rsvp_off, is_invite,\nis_pending_cohost, rendered_image_url, updated`.\n\n### 2. Get event detail — `GET /services/event/v1/{id}`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\" \\\n  | jq '{title: .event.title, start: .event.startDatetime, status: .event.status, settings}'\n```\n\n→ top keys: `event, calculatedFields, design, location,\nuserEventContext, attributes, registries, settings, charity, gifting,\nrendered, calendar, features`. `event`: `id, title, message,\nstartDatetime, endDatetime, knownTimezoneName,\nknownTimezoneAbbreviation, eventHostName, eventPhoneNumber, hostId,\nhostIds, status, isPast, eventType, category, superCategory,\ntemplateName, origin, isFabricPremium, shareableLink, sendOn`.\n`settings`: `enableMaybe, privateGuestList, headCountByFamily, plusOne,\nmaxEventCapacity, allowViewMap, showGifting, rsvpBy, strictRsvpBy,\nenableHostPhotoGallery, enablePhotoSharing, allowGuestNumber, rsvpOff`.\n\n### 3. Guest list + RSVP summary — `GET /services/event/v1/{id}/guests/`\n\nOne endpoint powers both `evite_list_guests` and `evite_rsvp_summary`.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/\" \\\n  | jq '.summary, (.guests[] | {guestId, name, rsvpResponse, numberOfAdults, numberOfKids})'\n```\n\n→ `{ guests: Guest[], summary }`. `Guest`: `guestId, userId, name,\nemail, phone, guestType(host|cohost|guest),\nrsvpResponse(yes|no|maybe|noreply), numberOfAdults, numberOfKids,\ncheckedIn, comments, deliveryStatus, inviteMethod(email|null),\ninvitedBy, sentOn, timesViewed, avatarUrl, shortLink, longLink,\ncreated, updated`. `summary`: `{yes, no, maybe, noReply, adultsYes,\nkidsYes, adultsMaybe, kidsMaybe, inviteesYes, inviteesMaybe,\nlockedStatus}`.\n\n### 4. Messages — `GET /services/event/v1/{id}/posts/`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/posts/\" | jq '.posts'\n```\n\n→ `{ posts: Post[] }` — the event's \"Messages\" tab thread.\n\n### 5. List templates (HTML scrape, not JSON) — `GET /invites/{category}/`\n\nThe gallery is server-rendered — no JSON API. Grep the SSR HTML for\n`/invitation/{slug}/(create|preview|details)` links (mirrors\n`EviteClient.listTemplates`'s regex). Add `?active_filter=free_premium%2Cfree`\nfor free-only.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" \\\n  'https://www.evite.com/invites/kids-birthday/?active_filter=free_premium%2Cfree' \\\n  | grep -oE '/invitation/[a-z0-9][a-z0-9_-]+/(create|preview|details)' \\\n  | sed -E 's#/invitation/([a-z0-9_-]+)/.*#\\1#' | sort -u\n```\n\nEach slug is a `templateName` — pass it to `create_event` below.\n\n---\n\n## Writes\n\nEvery write really mutates Evite. `-d` bodies below are exact JSON\nunless marked \"assumed\" (endpoint captured live; the exact body field\nwasn't).\n\n### 6. RSVP — `PUT /services/event/v1/{id}/guests/{guestId}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0,\"comments\":\"can'\\''t wait!\"}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/$GUEST_ID\"\n```\n\nBody: `rsvpResponse` (`yes|no|maybe`), `numberOfAdults`,\n`numberOfKids`, optional `comments` — field names match the read\n`Guest` shape. `comments` is optional (omit the key entirely to leave\nit unchanged).\n\n### 7. Send a private guest message (assumed body) — `POST /tsunami/v1/services/event/{id}/guest/{gid}/messages`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"See you Saturday!\"}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/guest/$GUEST_ID/messages\"\n```\n\nA **third API base** (`/tsunami/`, not `/services/…/posts/`, which is\nGET-only). Body assumed `{message}`.\n\n### 8. Broadcast to RSVP segments — `POST /tsunami/v1/services/event/{id}/broadcast/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"message\":\"Reminder: parking is on the north side!\",\"captcha\":null,\"virtual_groups\":[\"yes\",\"maybe\"],\"participantCount\":12}' \\\n  \"https://www.evite.com/tsunami/v1/services/event/$EVENT_ID/broadcast/\"\n```\n\nBody **fully captured** (not assumed): `message`, `captcha` (always\n`null`), `virtual_groups` (array of RSVP segments to reach — the\nvalues the dashboard uses are `yes`/`no`/`maybe`), optional\n`participantCount` (informational — the count the web UI sends along).\nThis emails every guest in the named segments.\n\n### 9. Upload a photo to the shared gallery — 4-step Google Cloud Storage flow\n\nThe file goes to GCS, not Evite. All 4 steps, in order:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n\n# Step 1 — get a signed-upload ticket from Evite.\nTICKET=$(curl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg eid \"$EVENT_ID\" --arg gid \"$GUEST_ID\" --arg mt \"image/jpeg\" \\\n        '{upload_path:\"feed_photos\",event_id:$eid,photo_id:\"\",guest_id:$gid,redirect:true,mimetype:$mt,width:1200,height:900}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/upload/request/\")\nUPLOAD_URL=$(jq -r '.upload_url' <<<\"$TICKET\")\nPHOTO_ID=$(jq -r '.upload_form.key' <<<\"$TICKET\" | awk -F/ '{print $NF}')\n\n# Step 2 — multipart POST straight to GCS: signed fields first, `file` LAST,\n# no Evite cookies (the policy/signature ARE the auth). Capture the redirect.\nFORM_ARGS=()\nfor k in $(jq -r '.upload_form | keys[]' <<<\"$TICKET\"); do\n  v=$(jq -r --arg k \"$k\" '.upload_form[$k]' <<<\"$TICKET\")\n  FORM_ARGS+=(-F \"$k=$v\")\ndone\nFINISH_URL=$(curl -sS -D - -o /dev/null \"${FORM_ARGS[@]}\" -F \"file=@/path/to/photo.jpg\" \"$UPLOAD_URL\" \\\n  | grep -i '^location:' | tr -d '\\r' | awk '{print $2}')\n\n# Step 3 — finalize the object into the album (best-effort; ignore failures).\ncurl -sS -b \"$JAR\" -c \"$JAR\" \"$FINISH_URL\" -o /dev/null\n\n# Step 4 — register the photo in the event's shared gallery.\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d \"$(jq -n --arg pid \"$PHOTO_ID\" '{photo_ids:[$pid]}')\" \\\n  \"https://www.evite.com/services/photos/v1/$EVENT_ID/shared-gallery/?gid=$GUEST_ID\"\n```\n\nStep 1's mimetype must match the file's real content type; the GCS\npolicy enforces `Content-Type == mimetype` and a 20 MB cap.\n\n### 10. Create an event — `POST /services/event/v1/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ\",\"startDatetime\":\"2026-08-15T18:00:00Z\",\"templateName\":\"camp-confetti_vanilla_kids\"}}' \\\n  'https://www.evite.com/services/event/v1/'\n```\n\nRequired (Pydantic-enforced): `title`, `startDatetime`, `templateName`\n(get a valid slug from endpoint 5). **Quirk**: a successful create\nstill returns `500 \"Unknown error\"` (a secondary post-create step\nfails) — the draft is created anyway. Treat a 500 here as \"possibly\ncreated\" and re-list drafts (endpoint 1, `status=draft`) rather than\nretrying blindly.\n\n### 11. Update an event — `PATCH /services/event/v1/{id}`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X PATCH \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"event\":{\"title\":\"Backyard BBQ — Rain Date\"}}' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\"\n```\n\nFields MUST be nested under `event` (a bare `{\"title\":...}` 200s as a\nno-op). `PUT` 500s — only `PATCH` works.\n\n### 12. Add draft guests — `POST /ajax/event/{id}/guestlist/draft/`\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -b \"$JAR\" -c \"$JAR\" -X POST \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '[{\"name\":\"Ada Lovelace\",\"email\":\"ada@example.com\"}]' \\\n  \"https://www.evite.com/ajax/event/$EVENT_ID/guestlist/draft/\"\n```\n\n**Legacy `/ajax/` base**, and the body is a **top-level JSON array**\n(NOT wrapped in an object) — the server does `for g in payload:\nDraftGuest(**g)`. Only persists once the event is finalized (status\n`sending`); on a bare `draft` the POST 200s but silent\n\nArchive v1.1.3: 4 files, 9162 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (2261b), SKILL.md (5000b), _meta.json (128b)\n\nArchive v1.1.2: 4 files, 8982 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (1779b), SKILL.md (5000b), _meta.json (128b)\n\nArchive v1.1.1: 4 files, 9140 bytes\n\nFiles: references/endpoints.md (13778b), skill-card.md (2183b), SKILL.md (5000b), _meta.json (128b)","readmeExcerpt":"Skill: evite-api Owner: chrischall Summary: Query and act on Evite (evite.com) events, guest lists, RSVPs, and messages from a shell with curl and a cookie jar — instead of running the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no browser or extension involved. Use when you want Evite data/actions without the MCP, in a script","codeSnippets":[],"executableExamples":[{"language":"sh","snippet":"curl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null"},{"language":"sh","snippet":"curl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\"},{"language":"sh","snippet":"export EVITE_EMAIL=you@example.com EVITE_PASSWORD=yourpassword   # or: op read op://.../evite/password\nJAR=/tmp/evite-jar.txt\ntouch \"$JAR\" && chmod 600 \"$JAR\"   # jar holds the live session cookie — restrict before writing\nUA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36'\n\n# 1. Prime: GET the homepage to obtain the csrftoken (+ anonymous session) cookies.\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null\n\n# 2. Login: POST creds with the primed jar + X-CSRFToken + Origin/Referer (Django's\n#    HTTPS CSRF check needs all three; a cold POST without them 403s).\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\\n  'https://www.evite.com/ajax_login'"},{"language":"sh","snippet":"curl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\"},{"language":"sh","snippet":"curl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation' | jq ."},{"language":"sh","snippet":"curl -sS -b \"$JAR\" -c \"$JAR\" -X PUT \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -d '{\"rsvpResponse\":\"yes\",\"numberOfAdults\":2,\"numberOfKids\":0}' \\"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: evite-api\ndescription: >-\n  Query and act on Evite (evite.com) events, guest lists, RSVPs, and\n  messages from a shell with curl and a cookie jar — instead of running\n  the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login\n  against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no\n  browser or extension involved. Use when you want Evite data/actions\n  without the MCP, in a script, or on a machine where the MCP isn't\n  installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on\n  Evite from the shell\", \"curl Evite\", \"evite-api skill\".\n---\n\n# Evite via curl (no MCP)\n\nEvite has no public API — this hits the site's own internal `/services/`\n(+ `/ajax/`, `/tsunami/`) layer using session cookies, exactly like\nevite-mcp does. **This is a curl skill, not fpx**: Evite's login is a\nplain CSRF-protected Django form POST, and reads/writes with the\nresulting cookies are **not** bot-walled (Cloudflare sits in front but\ndoesn't trip on a plain `curl`/Node request carrying a valid session) —\nso the whole flow runs server-side with a cookie jar. No signed-in\nbrowser tab, no ContextMint Bridge extension, no bridge.\n\nAuth model: session cookies `x-evite-session` + `evtsession`, plus a\nCSRF cookie `csrftoken` sent back as the `X-CSRFToken` header on every\nwrite. The CSRF cookie **rotates** — re-read it from the jar immediately\nbefore each write, never cache it.\n\n## One-time login (per shell session, until the jar's session expires)\n\n```sh\nexport EVITE_EMAIL=you@example.com EVITE_PASSWORD=yourpassword   # or: op read op://.../evite/password\nJAR=/tmp/evite-jar.txt\ntouch \"$JAR\" && chmod 600 \"$JAR\"   # jar holds the live session cookie — restrict before writing\nUA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36'\n\n# 1. Prime: GET the homepage to obtain the csrftoken (+ anonymous session) cookies.\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" 'https://www.evite.com/' -o /dev/null\n\n# 2. Login: POST creds with the primed jar + X-CSRFToken + Origin/Referer (Django's\n#    HTTPS CSRF check needs all three; a cold POST without them 403s).\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\ncurl -sS -c \"$JAR\" -b \"$JAR\" -A \"$UA\" \\\n  -H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\" \\\n  -H 'Origin: https://www.evite.com' -H 'Referer: https://www.evite.com/' \\\n  -d \"$(jq -n --arg e \"$EVITE_EMAIL\" --arg p \"$EVITE_PASSWORD\" '{email:$e,password:$p}')\" \\\n  'https://www.evite.com/ajax_login'\n```\n\nA `200` means the jar now holds the authenticated cookies\n(`x-evite-session`, `evtsession`, a freshly-rotated `csrftoken`,\n`x-evite-features`) and a JSON body with `full_name`/`user_id`/`token`.\n`401 {\"error\":\"Invalid Email Address / Password\"}` = bad creds; `403\n{\"error\":\"HTTP_403\"}` = the priming GET was skipped, or its `csrftoken`\nwasn't echoed back.\n\n## Reads: jar + `Accept: application/json`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json'"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"evite-api\",\n  \"version\": \"1.3.5\",\n  \"publishedAt\": 1791588493973\n}"},{"path":"references/endpoints.md","content":"# Evite endpoint reference (curl)\n\nBase host: `https://www.evite.com`. All paths below assume `$JAR` already\nholds an authenticated session (see `SKILL.md`'s one-time login) and that\nevery call passes `-b \"$JAR\" -c \"$JAR\"` so a rotated `csrftoken` lands\nback in the jar. Reads add `-H 'Accept: application/json'`; writes add\n`-H 'Content-Type: application/json' -H \"X-CSRFToken: $CSRF\"` with `CSRF`\nre-read from the jar immediately before the call:\n\n```sh\nCSRF=$(awk -F'\\t' '$6==\"csrftoken\"{v=$7} END{print v}' \"$JAR\")\n```\n\nTranscribed from `evite-mcp`'s `src/client.ts` + `docs/EVITE-API.md`\n(live-verified 2026-06-01/02). Every shape below is a REAL captured\nrequest/response unless marked \"assumed\" (URL captured, body not).\n\n---\n\n## Reads\n\n### 1. List events — `GET /services/events/v1/`\n\nQuery: `filterBy` (`all|host|others`, others = you're a guest), `status`\n(repeatable — `upcoming|draft|archived|past|canceled`), `type=invitation`,\n`offset`, `numResults`, `filter` (free text).\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  'https://www.evite.com/services/events/v1/?filterBy=all&type=invitation&status=upcoming&status=draft&numResults=25' \\\n  | jq '.totals, (.events[] | {id: .event_id, title, start, status, is_host, rsvp})'\n```\n\n→ `{ events: Event[], totals }`. `totals` = `{all, sending, draft,\nreceived, canceled, past, upcoming, archived}` (sending = hosting,\nreceived = invited). `Event` fields: `event_id, title, start, end,\nstatus, past, is_host, rsvp(yes|no|maybe), guest_status(0|1|2),\nguest_id, host_id, host_name, location{location_name, street_address,\nunit_num, city, state, zip_code, place_id}, timezone,\nknown_timezone_name, template_name, event_category, rsvp_off, is_invite,\nis_pending_cohost, rendered_image_url, updated`.\n\n### 2. Get event detail — `GET /services/event/v1/{id}`\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID\" \\\n  | jq '{title: .event.title, start: .event.startDatetime, status: .event.status, settings}'\n```\n\n→ top keys: `event, calculatedFields, design, location,\nuserEventContext, attributes, registries, settings, charity, gifting,\nrendered, calendar, features`. `event`: `id, title, message,\nstartDatetime, endDatetime, knownTimezoneName,\nknownTimezoneAbbreviation, eventHostName, eventPhoneNumber, hostId,\nhostIds, status, isPast, eventType, category, superCategory,\ntemplateName, origin, isFabricPremium, shareableLink, sendOn`.\n`settings`: `enableMaybe, privateGuestList, headCountByFamily, plusOne,\nmaxEventCapacity, allowViewMap, showGifting, rsvpBy, strictRsvpBy,\nenableHostPhotoGallery, enablePhotoSharing, allowGuestNumber, rsvpOff`.\n\n### 3. Guest list + RSVP summary — `GET /services/event/v1/{id}/guests/`\n\nOne endpoint powers both `evite_list_guests` and `evite_rsvp_summary`.\n\n```sh\ncurl -sS -b \"$JAR\" -c \"$JAR\" -H 'Accept: application/json' \\\n  \"https://www.evite.com/services/event/v1/$EVENT_ID/guests/\" \\\n  | jq '.summary, (.guests[] |"},{"path":"skill-card.md","content":"## Description:\n\nGuides agents in reading and managing Evite events, guest lists, RSVPs, and messages from the shell using authenticated requests.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other Evite account holders use this skill to check events and guest responses or manage invitations from a shell without an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Account credentials and live session cookies can expose Evite account access or private guest lists.\n\nMitigation: Avoid passwords in shell history and store cookie jars and guest lists only in restricted, non-shared locations.\n\nRisk: Actions can change events or email real guests without a built-in confirmation step.\n\nMitigation: Require explicit confirmation before sends, broadcasts, cancellations, guest-list changes, or photo uploads; test with a throwaway event.\n\nRisk: Two message and invitation request bodies are not verified, so these operations may fail or act unexpectedly.\n\nMitigation: Test unverified operations on a throwaway event before using them with real guests.\n\n## Reference(s):\n\n- [Evite endpoint reference](artifact/references/endpoints.md)\n- [Evite API skill on ClawHub](https://clawhub.ai/chrischall/skills/evite-api)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell code blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands use an Evite account session; some actions change events or contact guests.]\n\n## Skill Version(s):\n\n1.3.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."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Query and act on Evite (evite.com) events, guest lists, RSVPs, and messages from a shell with curl and a cookie jar — instead of running the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no browser or extension involved. Use when you want Evite data/actions without the MCP, in a script, or on a machine where the MCP isn't installed. Triggers on \"check my Evite\", \"Evite guest list\", \"RSVP on Evite from the shell\", \"curl Evite\", \"evite-api skill\". Skill: evite-api Owner: chrischall Summary: Query and act on Evite (evite.com) events, guest lists, RSVPs, and messages from a shell with curl and a cookie jar — instead of running the evite-mcp server. Does a headless EVITE_EMAIL/EVITE_PASSWORD login against evite.com's internal /services/, /ajax/, and /tsunami/ APIs, no browser or extension involved. Use when you want Evite data/actions without the MCP, in a script","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1243,"uniquenessScore":45,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T11:12:54.589Z","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-10T11:12:54.589Z","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-10T13:39:31.133Z","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"}]}}}