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 with event, calendar, hosts[], ticket_info, and guest_info (if user is registered).
  • discover_entities[] includes type: "discover-place" (cities) and type: "discover-category" (topics) with api_id values like cat-crypto, discplace-4fa7ldlAkBTTivm.

Search workflow:

  1. Call search/get-results?query=... — check events[] first for direct hits.
  2. 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/update instead.
  • geo_address_json accepts 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_limit and next_cursor / after params
  • The url endpoint is versatile — use it to resolve event slugs, city names, calendar slugs, and usernames