The Public API gives organization admins direct HTTP access to your organization's live data — pull it into any website, app, bot, or automation that can make an HTTP request. It's a Business plan feature.

The Public API page: generate an organization API key, and turn on Sign in with Ludoya

The key is scoped to your organization — all endpoints operate on that group automatically. Base URL: https://api.ludoya.com/public/v1. Auth header: X-Api-Key: YOUR_KEY. Rate limit: 100 requests per minute, after which you'll get a 429.

Keys start with ldy_ and are 36 characters long, so they're easy to spot in a config file — and easy to scan for if one ever ends up somewhere it shouldn't. Treat it like a password: server-side only, and revoke it from the same page if it leaks.

What You Can Access

  • Locations — your group's available venues and their spots; used as input when creating events
  • Events (read) — upcoming and past events with title, description, date/time, timezone, capacity, participant count, and status
  • Events (write) — create and update events via POST and PUT, with full control over location, capacity, permissions, visibility, and image
  • Sub-events — list the events nested inside a parent event
  • Participants — add someone to an event, change their attendance, or remove them
  • Members — member list with user profiles and roles (Owner, Admin, Member), paginated
  • Invites — invite someone into your group
  • Search — look up Ludoya users and the board game catalog, so you can resolve names to IDs before writing
  • Collection — game collection with filtering by ownership, name, player count, and list; sortable and paginated; each game includes BGG metadata
  • Stats — play statistics for any time period: totals, averages, top players with wins and scores, per-player-count breakdowns, per-location, and per-game
  • Campaigns (read) — list the group's campaigns with member and session counts; retrieve full campaign detail including members, past sessions, and scheduled events
  • Campaigns (write) — create campaigns for the group and update their name, description, status, and visibility
  • Campaign members — add, update, and remove the people in a campaign

Endpoints

GET /locations

Returns the group's available locations. Each location has an id, name, optional address, capacity, an isDefault flag, and a spots array (each spot has its own id, name, and capacity). Use location and spot IDs when creating events.

Sign in with Ludoya

The same page that holds your API key also turns your organization into a sign-in provider. Let people log into your own website, forum, or community platform with their Ludoya account — no separate password for them to forget, and no user database for you to run.

Why It's Worth It

When someone approves the sign-in, they're linked to your organization: depending on your group's join policy they either become a member straight away (open), file a join request for an admin to approve (request to join), or simply sign in without joining (invite only). Either way they show up in the /members endpoint like anyone else — so your website's login and your Ludoya member list stay in step automatically.

Setting It Up

It's standard OpenID Connect, so most platforms need nothing but an issuer URL plus a client ID and secret. Register your application from the Public API page to get those credentials, and list the redirect URIs your site will use — you can register several, and each must match the redirect_uri your site sends character for character, trailing slash included. A near-miss is the single most common reason a first sign-in attempt fails.

  • Discourse — install the OpenID Connect plugin, paste the discovery URL, add the credentials
  • WordPress — any generic OpenID Connect plugin, using its "auto discover" option
  • Anything else — if it speaks OIDC, point it at the issuer URL and it configures itself

Each sign-in returns the person's permanent identifier, their username, display name and avatar, and their email. Full setup instructions, including the by-hand flow, live in the in-app developer guide at Developers → Sign in with Ludoya.

For the People Signing In

Anyone who has used Ludoya to sign in somewhere can review those connections under Settings → Connected apps, see when each was connected and last used, and disconnect any of them at any time. Disconnecting cuts the site's access immediately but doesn't remove them from your group.

GET /events

Returns future and past events in separate paginated lists.

Param Type Default Description
onlyFuture boolean true Set false to also return past events

Each event includes: type (MEETUP or PLANNED_PLAY), title, description, startsAt, endsAt, timeZone, imageUrl, capacity, participantCount, canceled, teacher (user object or null), master (user object or null). User objects contain id, username, name, avatarUrl.

POST /events

Creates an event in the group. Required fields: type and title. If locationId is omitted, the group's default location is used; if none exists, returns 400.

Optional fields include: description, locationId, spotId, gameId, parentEventId (for sub-events), startsAt, endsAt, restrictedAttendance, capacity, minParticipants, estimatedDurationMinutes, maxReservationsPerUser, takeGamesPermission, organizePlaysPermission, teacherUserId, masterUserId, visibility, image.

The image field accepts either {"base64": "data:image/jpeg;base64,..."} or {"url": "https://..."}. Max 5 MB.

Returns {"id": "string"} with status 201.

PUT /events/{eventId}

Updates an existing event. Same body shape as POST — only include the fields you want to change. The caller must be the event organizer (the group account). Returns 202 with empty body.

GET /events/{eventId}

Fetches a single event by id, with the same shape as the entries in GET /events.

GET /events/{eventId}/children

Lists the sub-events nested inside a parent event — the individual tracks or sessions of a larger gathering. Each child has the same shape as a regular event.

Managing Participants

Three endpoints let you run an event's attendee list from outside Ludoya — handy if sign-ups happen on your own website, or you're importing an existing list.

Method Path What it does
POST /events/{eventId}/participants Add someone to the event
PUT /events/{eventId}/participants/{userId} Change their attendance status
DELETE /events/{eventId}/participants/{userId} Remove them from the event

Adding someone who isn't on Ludoya. The POST body takes either a userId for an existing account, or a name and email — the two are alternatives, not to be combined. The name-and-email route mints (or reuses) the account behind that address, exactly like the quick sign-up in the app, so the person can claim it later. That's what makes this endpoint genuinely useful: someone fills in the form on your own website, never having heard of Ludoya, and still turns up on your attendee list.

If your event has a custom form, you can send their answers too, as formAnswers keyed by question id.

Use GET /search/users to turn a username into the userId the PUT and DELETE paths expect.

GET /members

Returns a paginated member list.

Param Type Default
pagination.size int 50
pagination.offset int 0

Each member includes: id, username, name, avatarUrl, role (OWNER, ADMIN, or MEMBER).

POST /members/invite

Invites someone into your group. This one takes a username directly — no id lookup needed — so you can onboard members straight from your own site or admin tool.

GET /collection

Returns your game collection with filtering, sorting, and pagination.

Param Type Default Description
filter string Semicolon-separated key=value pairs. Keys: ownership (OWNED, PREVIOUSLY_OWNED, PREORDERED, WISHLISTED), played (boolean), listId, nameFilter, playerCountFilterType (OFFICIAL, GOOD, BEST), playerCountFilter (number)
groupExpansions boolean true Group expansions under their base game
sort string Format: property,direction — e.g. name,asc
pagination string Format: size,pageIndex — e.g. 20,0

Response: totalGames, totalExpansions, games[] — each with: id, slug, name, imageUrl, isExpansion, yearPublished, minPlayerCount, maxPlayerCount.

GET /stats

Returns play statistics for a configurable time window.

Param Type Default Description
period string ALL_TIME Format: PERIOD,date,index. Values: ALL_TIME, ONE_YEAR, ONE_MONTH, THIRTY_DAYS, SEVEN_DAYS, ONE_DAY, CUSTOM. The index shifts the window back (0 = current, 1 = previous). Custom: CUSTOM,startDate,0,endDate

Response includes:

  • Summary — total plays, total/average play time, unique game, player, and location counts
  • By player — per-person plays, wins, average and best score
  • By player count — breakdown of 2-player, 3-player, 4-player games, etc.
  • By location — play count and unique games per venue
  • By game — play count, total/average play time, and unique players per title

GET /campaigns

Returns a paginated list of campaigns belonging to the group.

Param Type Default Description
status string Filter by status: ACTIVE, COMPLETED, or ARCHIVED
pagination.size int 50 Items per page
pagination.offset int 0 Offset

Each campaign includes: id, game (id, name, imageUrl), name, image, status, visibility, createdAt, updatedAt, memberCount, sessionCount.

GET /campaigns/{campaignId}

Returns the full detail of a campaign belonging to the group.

Response includes: All list fields, plus template, description, globalNotes, globalState, members[] (each with user, role, characterNotes, characterState), sessions[], and events[].

POST /campaigns

Creates a campaign for the group. Required fields: gameId and name. Visibility defaults to group-members-only (ONLY_GROUP) if omitted.

Optional fields: templateId, description, visibility (PUBLIC, ONLY_GROUP, ONLY_FRIENDS, or PRIVATE).

Returns {"id": "string"} with status 201.

PUT /campaigns/{campaignId}

Updates an existing campaign. All fields are optional — only include what you want to change.

Optional fields: name, description, status (ACTIVE, COMPLETED, ARCHIVED), visibility.

Returns 202 with empty body.

Campaign Members

Manage who's in a campaign, the same way you manage event participants.

Method Path What it does
POST /campaigns/{id}/members Add someone to the campaign
PUT /campaigns/{id}/members/{userId} Update their membership
DELETE /campaigns/{id}/members/{userId} Remove them

Error Codes

Status When
400 Validation failure (missing required field, no location found, invalid image)
401 Missing or invalid API key
403 Action not permitted or group not on Business plan
404 Resource not found
429 Rate limit exceeded

Getting Started

1. Generate an API Key

A freshly generated API key, shown once with a copy button and a Revoke action

  1. Go to your organization's profile page
  2. Tap the menu () → Public API
  3. Tap Generate key
  4. Copy your key immediately — it is shown only once and cannot be retrieved again

The key is tied to your organization. Keep it secret: anyone with the key can read and write all of the data above.

2. Revoke or Regenerate

To revoke an existing key, go back to the Public API page and tap Revoke. Confirm the dialog. Any system using the old key will immediately start receiving 401 errors. To issue a new key, tap Generate key again.

See also:

  • Organizations — organization accounts and premium features
  • Premium — Business plan pricing
  • Integrations — other ways to connect Ludoya with external tools