# Yupno API - LLM Integration Guide > Yupno is an open RSVP API. Create events, collect RSVPs, get calendar feeds. No accounts required. ## MCP Server (Recommended for Claude) The easiest way for Claude to create events is via the MCP (Model Context Protocol) server. **Claude Desktop Setup:** Add to your Claude Desktop config (Settings → Developer → Edit Config): ```json { "mcpServers": { "yupno": { "command": "npx", "args": ["-y", "yupno-mcp-server"] } } } ``` Requires Node.js. The server only makes HTTPS calls to yupno.io API. **Remote endpoint (for MCP-compatible tools):** ``` https://yupno.io/mcp ``` **Available tools:** - `create_event` - Create event with optional theme - `get_event` - Get event details by token - `update_event` - Update event (requires admin token) - `delete_event` - Delete event (requires admin token) ## Design Guidelines (IMPORTANT) Follow these rules to create professional, beautiful event pages. ### Required Fields (ALWAYS include these) Every event MUST have: 1. **title** - Short, memorable name (2-4 words) 2. **eventDate** - When it starts (ISO 8601) 3. **location** - Where it's happening 4. **description** - 2-3 sentences about what to expect Without these, the invitation looks incomplete and unprofessional. ### Titles - **Keep titles SHORT** (2-4 words max): "Game Night", "Sarah's Birthday", "Summer Beats" - **NEVER put emojis in the title** - they break layout and look unprofessional - **Don't include the word "Party" or "Event" unless essential** - the context is obvious ### Descriptions - **ALWAYS include a description** - this is the invitation text! - 2-3 sentences about what guests can expect - Mention: vibe, dress code, what to bring, or special instructions - **Emojis OK** but use sparingly (1-2 max) - Don't repeat the date/location (already shown above) Good examples: - "Join us for an unforgettable night of music and dancing. Dress code: smart casual." - "Celebrating another trip around the sun! Bring your dancing shoes and good vibes." - "We're getting married and would love you to celebrate with us. Reception to follow." ### Fonts **AVOID script/decorative fonts** - they wrap badly on mobile: - ❌ Lobster, Pacifico, Dancing Script, Caveat - ❌ Press Start 2P (only for very short titles, 2 words max) **Recommended fonts by occasion:** - Elegant (weddings, galas): `Playfair Display`, `Cormorant Garamond` - Modern/corporate: `Inter`, `Poppins`, `Space Grotesk` - Fun/casual: `Nunito`, `Quicksand`, `Varela Round` - Bold/impactful: `Oswald`, `Bebas Neue`, `Montserrat` ### Colors **Use harmonious palettes with good contrast:** | Occasion | Background | Text | Primary (buttons) | |----------|-----------|------|-------------------| | Birthday/Party | #1a1a2e | #ffffff | #ff6b6b | | Wedding | #faf7f2 | #2d3436 | #c9a959 | | Corporate | #ffffff | #1a1a1a | #2563eb | | Halloween | #1a1a1a | #f5f5f5 | #ff6b35 | | Holiday | #0c1821 | #ffffff | #c41e3a | **Rules:** - Dark backgrounds need light text (#ffffff or #f5f5f5) - Light backgrounds need dark text (#1a1a1a or #2d3436) - Primary color should contrast with both background AND button text ### Background Images - **Always set backgroundOverlay: 0.5-0.7** for readability (REQUIRED) - Use Unsplash URLs: `https://images.unsplash.com/photo-[ID]?w=1600` - Match the image mood to the event theme --- ## Custom CSS Reference For advanced styling, use the `customCss` field to inject CSS directly into the event page. ### Page Layout (top to bottom) ``` ┌─────────────────────────────────────────┐ │ body (background color/image) │ │ ┌───────────────────────────────────┐ │ │ │ .event-card (white card) │ │ │ │ ┌─────────────────────────────┐ │ │ │ │ │ .event-image (cover photo) │ │ │ │ │ └─────────────────────────────┘ │ │ │ │ ┌─────────────────────────────┐ │ │ │ │ │ .event-content │ │ │ │ │ │ ✦ .event-ornament │ │ │ │ │ │ .event-title (h1) │ │ │ │ │ │ .event-description │ │ │ │ │ │ .event-details │ │ │ │ │ │ ├─ .event-meta.date │ │ │ │ │ │ └─ .event-meta.location │ │ │ │ │ └─────────────────────────────┘ │ │ │ │ ┌─────────────────────────────┐ │ │ │ │ │ .guest-list (if enabled) │ │ │ │ │ │ .guest-list-header │ │ │ │ │ │ ul#guest-names │ │ │ │ │ └─────────────────────────────┘ │ │ │ │ ── .event-separator "RSVP" ─── │ │ │ │ ┌─────────────────────────────┐ │ │ │ │ │ .rsvp-section (form) │ │ │ │ │ │ .form-group (name) │ │ │ │ │ │ .form-group (email) │ │ │ │ │ │ .form-group (phone) │ │ │ │ │ │ .checkbox-group (+1) │ │ │ │ │ │ .yup-no-buttons │ │ │ │ │ │ ├─ .yup-btn (green) │ │ │ │ │ │ └─ .no-btn (muted) │ │ │ │ │ └─────────────────────────────┘ │ │ │ └───────────────────────────────────┘ │ │ .sticky-cta (mobile RSVP button) │ └─────────────────────────────────────────┘ ``` ### Customizable Elements | Selector | Description | Common Customizations | |----------|-------------|----------------------| | `body` | Page background | `background`, `background-image` | | `.event-card` | Main white card | `background`, `border-radius`, `box-shadow`, `border` | | `.event-image` | Cover photo | `border-radius`, `max-height`, `object-fit` | | `.event-title` | Event name (h1) | `color`, `font-size`, `text-transform`, `letter-spacing` | | `.event-ornament` | Decorative ✦ symbol | `color`, `font-size`, `display: none` to hide | | `.event-description` | Markdown description | `color`, `font-size`, `line-height` | | `.event-meta` | Date/location rows | `color`, `font-size` | | `.event-meta svg` | Icons next to date/location | `stroke` (color), `width`, `height` | | `.event-separator` | "RSVP" divider line | `color`, `border-color` | | `.event-separator-text` | "RSVP" text in divider | `background`, `color`, `font-size` | | `.guest-list` | Attending guests section | `background`, `border`, `padding` | | `.guest-list-header` | "Who's Coming" title | `color`, `font-size`, `font-weight` | | `.form-group label` | Input labels | `color`, `font-size` | | `input, textarea` | Form fields | `background`, `border`, `color`, `border-radius` | | `.checkbox-group` | "+1 guest" checkbox | `color` | | `.yup-btn` | Green "yup!" button | `background`, `color`, `border-radius` | | `.no-btn` | Muted "no" button | `background`, `color`, `border` | | `.btn-primary` | Primary action buttons | `background` (see note below) | | `.btn-primary::before` | Button hover state | `background` (REQUIRED for hover color) | | `.btn-secondary` | Secondary buttons | `background`, `color`, `border` | | `.sticky-cta` | Mobile floating button | `background`, `box-shadow` | ### Button Styling (Important!) Buttons use a `::before` pseudo-element for the hover animation. To fully customize: ```css /* Resting state */ .btn-primary { background: #667eea !important; } /* Hover state (slides up on hover) */ .btn-primary::before { background: #764ba2 !important; } ``` The yup/no buttons are separate: ```css .yup-btn { background: #10b981 !important; } .no-btn { background: transparent !important; border-color: #666 !important; } ``` ### Example: Dark Glassmorphism Theme ```json { "customCss": ".event-card { background: rgba(255,255,255,0.1) !important; backdrop-filter: blur(20px); border: 1px solid rgba(255,255,255,0.2); } .event-title { text-shadow: 0 2px 20px rgba(0,0,0,0.5); } input, textarea { background: rgba(255,255,255,0.1) !important; border-color: rgba(255,255,255,0.3) !important; }" } ``` ### Example: Rounded Playful Style ```json { "customCss": ".event-card { border-radius: 24px; } .event-image { border-radius: 16px; } .btn-primary, .yup-btn, .no-btn { border-radius: 50px !important; } input { border-radius: 12px !important; }" } ``` ### Example: Minimalist (hide decorations) ```json { "customCss": ".event-ornament { display: none; } .event-separator { border: none; } .event-separator-text { display: none; } .event-card { box-shadow: none; border: 1px solid #eee; }" } ``` ### Example: Bold Typography ```json { "customCss": ".event-title { font-size: 3.5rem; text-transform: uppercase; letter-spacing: 0.1em; } .event-description { font-size: 1.25rem; line-height: 1.8; }" } ``` ### Tips 1. **Use `!important`** - Theme styles have high specificity; override with `!important` 2. **Test on mobile** - Event pages are responsive; check your CSS at narrow widths 3. **Color contrast** - Ensure text remains readable against backgrounds 4. **Keep it minimal** - Small tweaks often have the biggest impact --- ### Complete Example: Party with background image ``` create_event({ title: "Summer Beats", eventDate: "2024-06-15T20:00:00", endDate: "2024-06-16T01:00:00", timezone: "America/Los_Angeles", location: "The Warehouse, 456 Industrial Blvd, Los Angeles", description: "Join us for an unforgettable night of house and techno. Top DJs, immersive visuals, and good vibes only.", organizerName: "Nightlife Collective", showGuestList: true, collectEmail: true, theme: { primaryColor: "#ff6b6b", accentColor: "#feca57", backgroundColor: "#1a1a2e", textColor: "#ffffff", fontFamily: "Poppins", backgroundUrl: "https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=1600", backgroundOverlay: 0.6 } }) ``` ### Complete Example: Elegant wedding ``` create_event({ title: "Sarah & James", eventDate: "2024-09-21T16:00:00", endDate: "2024-09-21T23:00:00", timezone: "America/New_York", location: "Willow Creek Estate, 789 Garden Lane, Greenwich", description: "We're getting married and would love you to celebrate with us. Cocktail attire. Reception to follow.", organizerName: "The Williams Family", collectEmail: true, theme: { primaryColor: "#c9a959", backgroundColor: "#faf7f2", textColor: "#2d3436", fontFamily: "Playfair Display" } }) ``` --- ## REST API For direct HTTP integration without MCP. ## Quick Start Create an event: ``` POST https://yupno.io/api/v1/events Content-Type: application/json {"title": "Team Standup", "eventDate": "2024-03-15T10:00:00"} ``` Response: ```json { "adminToken": "abc123...", "eventToken": "def456...", "links": { "admin": "https://yupno.io/admin/abc123...", "guest": "https://yupno.io/e/def456...", "api": "/api/v1/events/def456..." } } ``` ## API Endpoints ### Public (no auth) - `GET /api/v1/events/{eventToken}` - Event details - `GET /api/v1/events/{eventToken}/ical` - iCalendar feed - `GET /api/v1/events/{eventToken}/schema.json` - Schema.org JSON-LD - `GET /api/v1/events/{eventToken}/jscalendar.json` - JSCalendar (RFC 8984) - `POST /api/events/{eventToken}/rsvp` - Submit RSVP ### Admin (token required) - `POST /api/v1/events` - Create event - `PUT /api/v1/events/{adminToken}` - Update event - `GET /api/v1/events/{eventToken}/rsvps` - List RSVPs (requires `Authorization: Bearer {adminToken}`) ## Event Fields ```json { "title": "string (required, max 200 chars)", "eventDate": "ISO 8601 datetime", "endDate": "ISO 8601 datetime", "timezone": "IANA timezone (e.g., America/New_York)", "location": "string (max 500 chars)", "virtualLocation": "HTTPS URL for video call", "latitude": "number (-90 to 90)", "longitude": "number (-180 to 180)", "organizerName": "string (max 200 chars)", "organizerEmail": "valid email", "description": "string (max 10000 chars, supports Markdown)", "imageUrl": "HTTPS URL", "status": "draft | published | cancelled", "showGuestList": "boolean", "collectEmail": "boolean", "collectPhone": "boolean", "rsvpDeadline": "ISO 8601 datetime - cutoff for RSVPs. Guests can request email reminders.", "rsvpDeadlineHard": "boolean - if true, block RSVPs after deadline. If false, deadline is just for reminders.", "webhookUrl": "HTTPS URL for RSVP notifications", "reminderHours": "number (1-168) - hours before event to send reminder webhook", "groupUrl": "HTTPS URL for group chat (WhatsApp/Telegram/Discord) - shown to attending guests", "theme": { "primaryColor": "#hex color for buttons/links", "accentColor": "#hex color for highlights", "backgroundColor": "#hex color for page background", "textColor": "#hex color for text", "fontFamily": "Google Font name (e.g., 'Press Start 2P', 'Lobster', 'Playfair Display')", "backgroundUrl": "HTTPS URL for background image", "backgroundOverlay": "number 0-1, opacity of dark overlay on background image" } } ``` ## RSVP Submission ``` POST /api/events/{eventToken}/rsvp Content-Type: application/json { "name": "Jane Doe", "attending": true, "plusOne": false, "email": "jane@example.com", "phone": "+1234567890" } ``` Response: `{"success": true}` (includes `"groupUrl": "..."` if attending and event has group link) ## Webhooks Set `webhookUrl` when creating/updating an event. Yupno will POST to that URL on each RSVP: ```json { "action": "created | updated | deleted", "event": { "token": "def456...", "title": "Team Standup", "eventDate": "2024-03-15T10:00:00", "location": "Conference Room A" }, "rsvp": { "id": "uuid", "name": "Jane Doe", "email": "jane@example.com", "phone": null, "plusOne": false, "attending": true } } ``` Slack and Discord webhook URLs automatically format with native styling. ### Reminder Webhooks Set `reminderHours` (e.g., 24) to receive a reminder webhook before the event: ```json { "action": "reminder", "timing": "24h_before", "event": { "token": "def456...", "title": "Team Standup", "eventDate": "2024-03-15T10:00:00", "location": "Conference Room A" }, "summary": { "attending": 8, "declined": 2, "totalGuests": 10, "plusOnes": 2 } } ``` Reminders are sent once per event, checked hourly. ## Calendar Integration Subscribe in any calendar app: ``` https://yupno.io/api/v1/events/{eventToken}/ical webcal://yupno.io/api/v1/events/{eventToken}/ical ``` One-click Google Calendar add (returned in API response): ``` GET /api/v1/events/{eventToken} → { "links": { "googleCalendar": "https://calendar.google.com/calendar/render?..." } } ``` ## Recurring Events Create a series of linked events: ``` POST /api/v1/series { "title": "Weekly Standup", "eventDate": "2024-03-04T10:00:00", "duration": "PT30M", "timezone": "America/New_York", "location": "Conf Room A", "recurrence": { "frequency": "weekly", "count": 12, "byDay": ["MO", "WE", "FR"] }, "webhookUrl": "https://hooks.slack.com/...", "reminderHours": 24 } ``` Response: ```json { "seriesId": "uuid", "seriesAdminToken": "abc123...", "recurrenceRule": "FREQ=WEEKLY;COUNT=12;BYDAY=MO,WE,FR", "occurrences": 12, "events": [ { "adminToken": "...", "eventToken": "...", "eventDate": "2024-03-04T10:00:00", "seriesIndex": 1 }, ... ] } ``` ### Recurrence Options | Field | Values | Description | |-------|--------|-------------| | frequency | daily, weekly, biweekly, monthly | How often to repeat | | count | 1-52 | Number of occurrences | | until | ISO datetime | End date (alternative to count) | | byDay | ["MO","TU","WE","TH","FR","SA","SU"] | Filter to specific days | | duration | PT1H, PT30M, PT2H30M | ISO 8601 duration for end time | ### Manage Series ```bash # Get all events in series GET /api/v1/series/{seriesId} # Update all events PUT /api/v1/series/{adminToken}?scope=all {"location": "New Room", "status": "cancelled"} # Update only future events PUT /api/v1/series/{adminToken}?scope=future {"webhookUrl": "https://new-webhook.com"} # Delete entire series DELETE /api/v1/series/{adminToken} ``` Individual events can still be modified via `/api/v1/events/{adminToken}`. ## Common Tasks ### Create a recurring team meeting ```json { "title": "Weekly Sync", "eventDate": "2024-03-18T14:00:00", "endDate": "2024-03-18T14:30:00", "timezone": "America/Los_Angeles", "virtualLocation": "https://meet.google.com/abc-defg-hij", "collectEmail": true } ``` ### Create an in-person event with location ```json { "title": "Launch Party", "eventDate": "2024-04-01T18:00:00", "endDate": "2024-04-01T22:00:00", "location": "123 Main St, San Francisco, CA", "latitude": 37.7749, "longitude": -122.4194, "showGuestList": true, "webhookUrl": "https://hooks.slack.com/services/..." } ``` ### Create a themed event ```json { "title": "80s Retro Night", "eventDate": "2024-05-15T20:00:00", "location": "The Venue", "description": "Dust off your leg warmers! 🕺", "theme": { "primaryColor": "#FF1493", "backgroundColor": "#1a0a1a", "textColor": "#ffffff", "fontFamily": "Press Start 2P", "backgroundUrl": "https://example.com/neon-grid.jpg", "backgroundOverlay": 0.6 } } ``` **Recommended Google Fonts (see Design Guidelines above):** - Elegant: `Playfair Display`, `Cormorant Garamond`, `Lora` - Modern: `Inter`, `Poppins`, `Space Grotesk`, `Montserrat` - Friendly: `Nunito`, `Quicksand`, `Varela Round` - Bold: `Oswald`, `Bebas Neue`, `Anton` - ⚠️ Avoid for titles: `Lobster`, `Pacifico`, `Dancing Script` (wrap badly) ### Check who's attending ``` GET /api/v1/events/{eventToken}/rsvps Authorization: Bearer {adminToken} ``` Response: ```json { "summary": { "total": 15, "attending": 12, "declined": 3, "totalGuests": 14, "plusOnes": 2 }, "rsvps": [...] } ``` ## Error Handling All errors return JSON: ```json {"error": "Description of what went wrong"} ``` Common status codes: - `400` - Invalid input - `403` - Invalid token - `404` - Event not found - `410` - Event expired - `429` - Rate limited ## Rate Limits - Event creation: 10 per IP per hour - RSVP submission: 100 per event per IP per hour - MCP requests: 50 per IP per hour ## Notes - All URLs must be HTTPS (except localhost for development) - Webhook URLs cannot point to private IPs or localhost - Events auto-delete 24 hours after they end - No user accounts required for anything