luma
Manage Luma events: discover local events, view your calendar, check guest lists, RSVP, and create events. Use when: user asks about events, meetups, RSVPs, or anything related to lu.ma / Luma.
Luma Skill
Manage events on Luma (lu.ma) — discover, list, RSVP, check guests, and create events.
When to Use
- "What events are happening in <city name>?"
- "Show my upcoming events"
- "Who's going to [event]?"
- "RSVP me to [event]"
- "Create an event for Friday"
- "Show me tech meetups near me"
When NOT to Use
- Eventbrite, Meetup.com, or other event platforms
- Google Calendar management (use a calendar skill)
Authentication
All requests require the LUMA_AUTH_SESSION_KEY env var (Luma session cookie).
Set it:
export LUMA_AUTH_SESSION_KEY="usr-..."
To get it: open lu.ma in browser, open dev tools, Application > Cookies > lu.ma, copy luma.auth-session-key value.
API Base
All endpoints use https://api.lu.ma with the cookie header:
Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY
User Info
Set your personal calendar ID by calling the /user endpoint (look for personal_calendar_api_id in the response). Your location coordinates are used for local event discovery.
Commands
Search Events
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/search/get-results?query=SEARCH_TERMS"
Returns: events[] (direct matches), calendars[], discover_entities[] (places and categories), help_pages[].
events[]contains full event objects withevent,calendar,hosts[],ticket_info, andguest_info(if user is registered).discover_entities[]includestype: "discover-place"(cities) andtype: "discover-category"(topics) withapi_idvalues likecat-crypto,discplace-4fa7ldlAkBTTivm.
Search workflow:
- Call
search/get-results?query=...— checkevents[]first for direct hits. - If no direct events but
discover_entities[]has a relevant category, use it to browse:
# Events by category near a location (e.g. crypto events near Vancouver)
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/discover/get-paginated-events?discover_category_api_id=cat-crypto&geo_latitude=49.2827&geo_longitude=-123.1207&pagination_limit=20"
# Events by place (city discover feed — only shows city-tagged events)
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/discover/get-paginated-events?discover_place_api_id=discplace-4fa7ldlAkBTTivm&pagination_limit=20"
Tips: Use specific terms (organizer names, event titles) for direct event hits. Generic terms (like "crypto") tend to return categories instead. Combine both: search for direct matches, then browse the returned category near the user's location.
Get Current User Profile
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/user" | python3 -m json.tool
Returns: user profile with name, email, avatar, timezone, social handles, personal calendar ID.
Discover Events by City
# First resolve the city to a discover place
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/url?url=vancouver"
# Then get paginated events for that place
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/discover/get-paginated-events?discover_place_api_id=discplace-XXXX&pagination_limit=10"
The url endpoint returns kind: "discover-place" with data.place.api_id and data.place.featured_event_api_ids.
Get Event Details
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/event/get?event_api_id=evt-XXXX"
Returns: full event details including name, start/end times, location, calendar info, cover image, visibility, waitlist status.
Get Event Guest List
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/event/get-guest-list?event_api_id=evt-XXXX"
Returns: entries[] with user profiles (name, avatar, social handles, tickets registered). Supports next_cursor pagination.
Only works for events you host or events with public guest lists (show_guest_list: true).
Guest entry shape: each entry has two distinct api_ids — a guest-registration ID at the root, and a user ID under user. They are not interchangeable.
{
"entries": [
{
"api_id": "gst-xxx",
"num_tickets_registered": 1,
"user": {
"api_id": "usr-xxx",
"name": "...",
"first_name": "...",
"last_name": "...",
"email": "..."
}
}
]
}
Use the root api_id as a stable key for a specific signup (e.g. for diffing to detect cancellations). Use user.api_id to reference the person. Not every entry has the root api_id populated — fall back to user.api_id when it's missing.
Detecting new signups / cancellations: the endpoint returns the current snapshot only — there is no per-guest timestamp and no "new" marker. To build a signup notifier, persist the previous set of guest IDs and diff across polls:
- IDs present now but not before → new signups
- IDs present before but not now → cancellations
User Profile Events
# Overview: hosting, past, and events-together for a user (by username)
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/user/profile/events?username=USERNAME"
Returns: events_hosting[], events_past[], events_together[] — a snapshot of the user's event history and overlap with you.
# Paginated list of events a user is hosting (by user_api_id)
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/user/profile/events-hosting?user_api_id=usr-XXXX&pagination_limit=10"
Returns: entries[] with full event objects. Supports has_more / cursor pagination.
# Paginated list of events in common with the authenticated user (by user_api_id)
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/user/profile/events-together?user_api_id=usr-XXXX&pagination_limit=10"
Returns: entries[] of events both you and the target user have attended or are registered for.
Note: /events requires username, while /events-hosting and /events-together require user_api_id. Get user_api_id from the /user endpoint or from event guest lists.
List Your Calendar Events
# Past events on your personal calendar
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/calendar/get-items?calendar_api_id=YOUR_CALENDAR_API_ID&period=past&pagination_limit=10"
# Future events on your personal calendar
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/calendar/get-items?calendar_api_id=YOUR_CALENDAR_API_ID&period=future&pagination_limit=10"
Returns: entries[] with event details, cover images, calendar info. Supports has_more pagination.
Response shape: each entry wraps the actual event object one level deep under event — access fields like entry.event.api_id, not entry.api_id.
{
"entries": [
{
"event": { "api_id": "evt-xxx", "name": "...", "start_at": "...", "cover_url": "..." },
"calendar": { "api_id": "cal-xxx", "name": "..." },
"hosts": [ ... ]
}
]
}
Resolve URL / Event Slug
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/url?url=EVENT_SLUG_OR_CITY"
Returns: kind ("event", "discover-place", "calendar", "user") and data with the resolved object. Use this to look up events by their lu.ma URL slug.
Create Event (two-step)
Event creation is a two-step process. /event/create only accepts the minimum scheduling fields — everything else (description, location, visibility, capacity, ticketing) must be set afterward via /event/admin/update. If you try to cram them into the create call, they are silently dropped.
Step 1 — Create the shell:
curl -s -X POST \
-H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
-H "Content-Type: application/json" \
"https://api.lu.ma/event/create" \
-d '{
"name": "Event Name",
"start_at": "2026-04-01T18:00:00.000Z",
"end_at": "2026-04-01T20:00:00.000Z",
"timezone": "America/Vancouver"
}'
Returns: { "api_id": "evt-xxx", "url": "slug", "calendar_event_api_id": "calev-xxx" }. The public event URL is https://lu.ma/<url> — the url field is the slug, not the api_id.
Step 2 — Update details:
curl -s -X POST \
-H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
-H "Content-Type: application/json" \
"https://api.lu.ma/event/admin/update" \
-d '{
"event_api_id": "evt-xxx",
"location_type": "offline",
"geo_address_json": { "type": "google", "place_id": "ChIJ..." },
"description_mirror": {
"type": "doc",
"content": [
{ "type": "paragraph", "content": [{ "type": "text", "text": "Event details here." }] }
]
}
}'
Gotchas:
- Do not POST to
/event/update— it redirects. The real endpoint is/event/admin/update. - Fields silently dropped by
/event/create:description,description_mirror,geo_address_json,location_type,visibility,calendar_api_id. Pass them to/event/admin/updateinstead. geo_address_jsonaccepts either a structured address{type, full_address, city, region, country}or a Google Places ID{type: "google", place_id}. It rejects plain strings with"That doesn't look like a valid address".
Event Descriptions — description_mirror (ProseMirror)
Luma stores rich descriptions in the description_mirror field using ProseMirror / TipTap JSON document format. The plain description string field is effectively write-nothing / read-null: setting it on create or update has no visible effect, and it always returns null when you fetch the event. Always write rich content to description_mirror via /event/admin/update.
Structure: a doc with a content array of block nodes (paragraphs, headings, lists). Each paragraph has its own content array of text nodes. Links are applied as marks on text nodes.
{
"type": "doc",
"content": [
{ "type": "paragraph", "content": [{ "type": "text", "text": "First paragraph." }] },
{ "type": "paragraph", "content": [] },
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Register here: " },
{
"type": "text",
"text": "https://lu.ma/abc",
"marks": [{ "type": "link", "attrs": { "href": "https://lu.ma/abc", "target": "_blank" } }]
}
]
}
]
}
An empty paragraph ({"type": "paragraph", "content": []}) is how you insert a blank line between blocks — putting \n\n inside a text node does not render as a blank line.
Reading descriptions back: on the GET /event/get?event_api_id=... response, description_mirror sits at the root of the response object, not nested under event. The nested event.description (plain-text) field is always null.
Response Formatting
When showing events to the user, format them clearly:
Event listing:
- **Event Name** — Apr 1, 6:00 PM PDT
Location: DCTRL, Vancouver
URL: https://lu.ma/SLUG
Guests: 25 registered
Guest list:
- **Name** (@twitter) — Company
Notes
- Session cookies expire; if you get auth errors, the user needs to refresh the cookie
- Rate limit: don't spam requests; batch where possible
- Event times are in UTC; convert to the user's timezone (America/Vancouver) for display
- Pagination: use
pagination_limitandnext_cursor/afterparams - The
urlendpoint is versatile — use it to resolve event slugs, city names, calendar slugs, and usernames