luma — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited luma (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Manage events on Luma (lu.ma) — discover, list, RSVP, check guests, and create events.
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.
All endpoints use https://api.lu.ma with the cookie header:
Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEYSet 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.
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:
search/get-results?query=... — check events[] first for direct hits.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.
curl -s -H "Cookie: luma.auth-session-key=$LUMA_AUTH_SESSION_KEY" \
"https://api.lu.ma/user" | python3 -m json.toolReturns: user profile with name, email, avatar, timezone, social handles, personal calendar ID.
# 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.
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.
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:
# 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.
# 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": [ ... ]
}
]
}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.
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:
/event/update — it redirects. The real endpoint is /event/admin/update./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".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.
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 registeredGuest list:
- **Name** (@twitter) — Companypagination_limit and next_cursor / after paramsurl endpoint is versatile — use it to resolve event slugs, city names, calendar slugs, and usernames~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.